mirror of
https://github.com/hanzo-ds/cpp.git
synced 2026-10-30 20:31:26 +00:00
Pure rename applied on top of upstream tag v2.6.2 (8d2415d6), not HEAD.
The fork was cut at 737145d = v2.6.2-57, which still declared version 2.6.2
in version.h; renaming there would have shipped 57 unreleased commits wearing
a 2.6.2 label. Reset to the tag first so this commit changes names only.
clickhouse/ -> datastore/ (public include root)
namespace clickhouse -> namespace datastore
CLICKHOUSE_CPP_VERSION -> DATASTORE_CPP_VERSION
clickhouse-cpp-lib/-ut -> datastore-cpp-lib/-ut (CMake, Bazel)
CH_MAP_BOOL_TO_UINT8 -> DS_MAP_BOOL_TO_UINT8
CLICKHOUSE_* -> DATASTORE_* (test env vars)
doc links -> docs.hanzo.ai/datastore
This client speaks the NATIVE binary protocol only - there is no HTTP path
and no X-ClickHouse-* header anywhere in it, so the header rename that the
HTTP clients required does not apply and nothing here was broken.
The one string that reaches the wire is CLIENT_NAME, sent by both SendHello
and SendQuery's ClientInfo from a single macro, and it lands in
system.query_log.client_name. Both sites still read the one macro, so they
stay consistent and the server's Hello/Query consistency check is satisfied.
Old rows keep the old name; that is a reporting discontinuity, not a
migration, since nothing keys on it.
CI is repointed at our own server: ghcr.io/hanzoai/datastore:26.2.3.2, the
image production runs. Its entrypoint honours DATASTORE_SKIP_USER_SETUP, not
the CLICKHOUSE_ spelling the compose file used, and its config lives in
/etc/hanzo-datastore-server. ci/docker-compose/config.xml is now layered in
as a config.d drop-in so the image keeps its own white-labelling. That file
and users.xml deliberately keep a <clickhouse> root: it must match the roots
of the drop-ins the image ships, and a mismatch fails silently - the server
logs "Merging bypassed" and starts with every drop-in discarded.
The remote-TLS suites used to default to the third-party host
play.clickhouse.com. They now read DATASTORE_SECURE*_HOST and skip when it is
unset rather than point at a host we do not run.
Dropped as upstream infrastructure with no meaning here: AI_POLICY.md
(upstream's policy about their codebase), .travis.yml (dead CI referencing a
third-party apt repo), cross-repo-bug-relay.yml (relays our issues into an
upstream triage repo). LICENSE keeps its copyright notice, as Apache-2.0
requires.
Verified: cmake configure + 115-target build clean; the full suite is green
against ghcr.io/hanzoai/datastore:26.2.3.2 (894 passed, 0 failed); the
pristine v2.6.2 build run against that same server passes the identical set,
and both logged the same 3914 queries under their respective client names.
205 lines
8.3 KiB
C++
205 lines
8.3 KiB
C++
/** Collection of integration tests that validate TLS-connectivity to a CH server
|
|
*/
|
|
#include "readonly_client_test.h"
|
|
#include "connection_failed_client_test.h"
|
|
#include "utils.h"
|
|
|
|
#include <openssl/tls1.h>
|
|
#include <openssl/ssl2.h>
|
|
#include <openssl/ssl3.h>
|
|
|
|
namespace {
|
|
using namespace datastore;
|
|
|
|
const auto QUERIES = std::vector<std::string> {
|
|
"SELECT version()",
|
|
"SELECT fqdn()",
|
|
"SELECT blockSize()",
|
|
"SELECT uptime()",
|
|
"SELECT now()"
|
|
};
|
|
}
|
|
|
|
#if defined(__linux__)
|
|
// On Ubuntu 20.04 /etc/ssl/certs is a default directory with the CA files
|
|
const auto DEFAULT_CA_DIRECTORY_PATH = "/etc/ssl/certs";
|
|
#elif defined(__APPLE__)
|
|
// On macOS we will rely on Homebrew's OpenSSL installation
|
|
const auto DEFAULT_CA_DIRECTORY_PATH = "/usr/local/etc/[email protected]/cert.pem";
|
|
#elif defined(_win_)
|
|
// DEBUG ME: mingw - was not able to make it work. Every time it ends with exception:
|
|
// "Failed to verify SSL connection, X509_v error: 20 unable to get local issuer certificate"
|
|
const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/cert.pem";
|
|
// const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/certs";
|
|
// const auto DEFAULT_CA_DIRECTORY_PATH = "/mingw64/ssl/certs/ca-bundle.crt";
|
|
#endif
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
RemoteTLS, ReadonlyClientTest,
|
|
::testing::Values(ReadonlyClientTest::ParamType {
|
|
ClientOptions()
|
|
.SetHost( getEnvOrDefault("DATASTORE_SECURE_HOST", ""))
|
|
.SetPort( getEnvOrDefault<size_t>("DATASTORE_SECURE_PORT", "9440"))
|
|
.SetUser( getEnvOrDefault("DATASTORE_SECURE_USER", "explorer"))
|
|
.SetPassword( getEnvOrDefault("DATASTORE_SECURE_PASSWORD", ""))
|
|
.SetDefaultDatabase(getEnvOrDefault("DATASTORE_SECURE_DB", "default"))
|
|
.SetSendRetries(1)
|
|
.SetPingBeforeQuery(true)
|
|
.SetCompressionMethod(CompressionMethod::None)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetPathToCADirectory(DEFAULT_CA_DIRECTORY_PATH)),
|
|
QUERIES
|
|
}
|
|
));
|
|
|
|
|
|
const auto DatastoreExplorerConfig = ClientOptions()
|
|
.SetHost( getEnvOrDefault("DATASTORE_SECURE2_HOST", ""))
|
|
.SetPort( getEnvOrDefault<size_t>("DATASTORE_SECURE2_PORT", "9440"))
|
|
.SetUser( getEnvOrDefault("DATASTORE_SECURE2_USER", "explorer"))
|
|
.SetPassword( getEnvOrDefault("DATASTORE_SECURE2_PASSWORD", ""))
|
|
.SetDefaultDatabase(getEnvOrDefault("DATASTORE_SECURE2_DB", "default"))
|
|
.SetSendRetries(1)
|
|
.SetPingBeforeQuery(true)
|
|
.SetCompressionMethod(CompressionMethod::None);
|
|
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Remote_GH_API_TLS, ReadonlyClientTest,
|
|
::testing::Values(ReadonlyClientTest::ParamType {
|
|
ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetPathToCADirectory(DEFAULT_CA_DIRECTORY_PATH)),
|
|
QUERIES
|
|
}
|
|
));
|
|
|
|
// For some reasons doesn't work on MacOS.
|
|
// Looks like `VerifyCAPath` has no effect, while parsing and setting value works.
|
|
// Also for some reason SetPathToCADirectory() + SSL_CTX_load_verify_locations() works.
|
|
#if !defined(__APPLE__)
|
|
TEST(OpenSSLConfiguration, DISABLED_ValidValues) {
|
|
// Verify that Client with valid configuration set via SetConfiguration is able to connect.
|
|
|
|
EXPECT_NO_THROW(
|
|
Client(ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetConfiguration({
|
|
{"VerifyCAPath", DEFAULT_CA_DIRECTORY_PATH},
|
|
{"MinProtocol", "TLSv1.3"},
|
|
{"VerifyMode", "Peer"},
|
|
{"no_comp"} // shorthand for command with no value
|
|
})
|
|
))
|
|
);
|
|
}
|
|
#endif
|
|
|
|
TEST(OpenSSLConfiguration, InValidValues) {
|
|
if (DatastoreExplorerConfig.host.empty())
|
|
GTEST_SKIP() << "No server configured: set DATASTORE_SECURE2_HOST.";
|
|
|
|
// Verify that invalid options cause throwing exception.
|
|
std::vector<ClientOptions::SSLOptions::CommandAndValue> configurations = {
|
|
{"VerifyCAPath", std::nullopt}, // missing required value
|
|
{"VerifyCAPath"}, // missing required value, shorthand
|
|
{"MinProtocol", "NOT A STRING"}, // invalid value
|
|
{"MinProtocol", ""}, // invalid value
|
|
{"unknownCommand"}, // invalid command, shorthand
|
|
{"unknownCommand", "with unexpected value"}, // invalid command + some value
|
|
{"", std::nullopt}, // empty command with no value
|
|
{""}, // empty command with no value
|
|
{"no_comp", "unexpected value"} // value for command that doesn't require a value.
|
|
};
|
|
|
|
// Unfortunately, there is no way of checking configuration validity without
|
|
// creating a Client and pointing that client to a working CH server.
|
|
// So we mix valid and invalid configurations by providing the correct
|
|
// server config but incorrect single Command-Value pair individually.
|
|
for (const auto & cv : configurations) {
|
|
EXPECT_ANY_THROW(
|
|
Client(ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetConfiguration({cv})
|
|
));
|
|
) << "On command \'" << cv.command << "\' and value: " << (cv.value ? *cv.value : "<EMPTY>");
|
|
}
|
|
}
|
|
|
|
|
|
//INSTANTIATE_TEST_SUITE_P(
|
|
// Remote_GH_API_TLS_WithStringConfig, ReadonlyClientTest,
|
|
// ::testing::Values(ReadonlyClientTest::ParamType {
|
|
// ClientOptions(DatastoreExplorerConfig)
|
|
// .SetSSLOptions(ClientOptions::SSLOptions()
|
|
// .SetConfiguration({{"", DEFAULT_CA_DIRECTORY_PATH}})
|
|
// QUERIES
|
|
// }
|
|
//));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Remote_GH_API_TLS_no_CA, ReadonlyClientTest,
|
|
::testing::Values(ReadonlyClientTest::ParamType {
|
|
ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetUseDefaultCALocations(false)
|
|
.SetSkipVerification(true)), // No CA loaded, but verification is skipped
|
|
{"SELECT 1;"}
|
|
}
|
|
));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Remote_GH_API_TLS_no_CA, ConnectionFailedClientTest,
|
|
::testing::Values(ConnectionFailedClientTest::ParamType {
|
|
ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetUseDefaultCALocations(false)),
|
|
ExpectingException{"X509_v error: 20 unable to get local issuer certificate"}
|
|
}
|
|
));
|
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
|
Remote_GH_API_TLS_wrong_TLS_version, ConnectionFailedClientTest,
|
|
::testing::Values(ConnectionFailedClientTest::ParamType {
|
|
ClientOptions(DatastoreExplorerConfig)
|
|
.SetSSLOptions(ClientOptions::SSLOptions()
|
|
.SetUseDefaultCALocations(false)
|
|
.SetMaxProtocolVersion(SSL3_VERSION)),
|
|
ExpectingException{"no protocols available"}
|
|
}
|
|
));
|
|
|
|
|
|
//// Special test that require properly configured TLS-enabled version of CH running locally
|
|
//INSTANTIATE_TEST_SUITE_P(
|
|
// LocalhostTLS_None, ReadonlyClientTest,
|
|
// ::testing::Values(std::tuple<ClientOptions, std::vector<std::string> > {
|
|
// ClientOptions()
|
|
// .SetHost("127.0.0.1")
|
|
// .SetPort(9440)
|
|
// .SetUser("default")
|
|
// .SetPingBeforeQuery(true)
|
|
// .SetCompressionMethod(CompressionMethod::None)
|
|
// .SetSSLOptions(ClientOptions::SSLOptions()
|
|
// .SetPathToCADirectory("./CA/")
|
|
// .SetUseSNI(false)),
|
|
// QUERIES
|
|
// }
|
|
//));
|
|
|
|
//INSTANTIATE_TEST_SUITE_P(
|
|
// LocalhostTLS_LZ4, ReadonlyClientTest,
|
|
// ::testing::Values(std::tuple<ClientOptions, std::vector<std::string> > {
|
|
// ClientOptions()
|
|
// .SetHost("127.0.0.1")
|
|
// .SetPort(9440)
|
|
// .SetUser("default")
|
|
// .SetPingBeforeQuery(true)
|
|
// .SetCompressionMethod(CompressionMethod::LZ4)
|
|
// .SetSSLOptions(ClientOptions::SSLOptions()
|
|
// .SetPathToCADirectory("./CA/")
|
|
// .SetUseSNI(false)),
|
|
// QUERIES
|
|
// }
|
|
//));
|