From b9cc5bab9bf630503cbb64078c3548cdf9f84bfc Mon Sep 17 00:00:00 2001 From: absurdfarce Date: Fri, 12 Jun 2026 15:49:19 -0500 Subject: [PATCH] CASSCPP-4 Update Jenkins to include Ubuntu Noble, Rocky 10 patch by Bret McGuire; reviewed by Bret McGuire --- .build.linux.sh | 92 +-- .build.sh | 14 +- Jenkinsfile | 254 +----- tests/src/integration/tests/test_dbaas.cpp | 750 ------------------ .../test_cloud_secure_connect_config.cpp | 690 ---------------- 5 files changed, 27 insertions(+), 1773 deletions(-) delete mode 100644 tests/src/integration/tests/test_dbaas.cpp delete mode 100644 tests/src/unit/tests/test_cloud_secure_connect_config.cpp diff --git a/.build.linux.sh b/.build.linux.sh index 62f79432a..751b46219 100644 --- a/.build.linux.sh +++ b/.build.linux.sh @@ -23,35 +23,13 @@ configure_testing_environment() { } install_libuv() {( - if [[ "${OS_DISTRO}" = "ubuntu" ]] && [[ "${OS_DISTRO_RELEASE}" > "18.04" ]]; then - true - else - cd packaging - git clone --depth 1 https://github.com/datastax/libuv-packaging.git - - ( - cd libuv-packaging - - # Ensure build directory is cleaned (static nodes are not cleaned) - [[ -d build ]] && rm -rf build - mkdir build - - if [ "${OS_DISTRO}" = "ubuntu" ]; then - ./build_deb.sh ${LIBUV_VERSION} - else - ./build_rpm.sh ${LIBUV_VERSION} - fi - ) - - [[ -d packages ]] || mkdir packages - find libuv-packaging/build -type f \( -name "*.deb" -o -name "*.rpm" \) -exec mv {} packages \; - - if [ "${OS_DISTRO}" = "ubuntu" ]; then - sudo dpkg -i packages/libuv*.deb - else - sudo rpm -U --force packages/libuv*.rpm - fi - fi + git clone https://github.com/libuv/libuv.git + cd libuv + git checkout v${LIBUV_VERSION} + sh autogen.sh + ./configure --prefix=${HOME}/libuv-${LIBUV_VERSION} + make + make install )} install_openssl() { @@ -61,59 +39,3 @@ install_openssl() { install_zlib() { true # Already installed on image } - -install_driver() {( - cd packaging - - ( - # Ensure build directory is cleaned (static nodes are not cleaned) - [[ -d build ]] && rm -rf build - mkdir build - - if [ "${OS_DISTRO}" = "ubuntu" ]; then - ./build_deb.sh - else - ./build_rpm.sh - fi - ) - - [[ -d packages ]] || mkdir packages - find build -type f \( -name "*.deb" -o -name "*.rpm" \) -exec mv {} packages \; - - if [ "${OS_DISTRO}" = "ubuntu" ]; then - sudo dpkg -i packages/*cpp-driver*.deb - else - sudo rpm -i packages/*cpp-driver*.rpm - fi -)} - -test_installed_driver() { - local driver=$1 - - local test_program=$(mktemp) - gcc -x c -o ${test_program} - -Wno-implicit-function-declaration -l${driver} - < - -int main(int argc, char* argv[]) { - CassFuture* connect_future = NULL; - CassCluster* cluster = cass_cluster_new(); - CassSession* session = cass_session_new(); - - cass_cluster_set_contact_points(cluster, "127.0.0.1"); - connect_future = cass_session_connect(session, cluster); - cass_future_wait(connect_future); - printf("Success"); - return 0; -} -EOF - - if [ $? -ne 0 ] ; then - echo "Connection test compilation failed. Marking build as failure." - exit 1 - fi - if [ "$($test_program)" != "Success" ] ; then - echo "Connection test did not return success. Marking build as failure." - exit 1 - fi -} - diff --git a/.build.sh b/.build.sh index e08e4f7ed..45e22a228 100644 --- a/.build.sh +++ b/.build.sh @@ -22,15 +22,9 @@ set -e #Fail fast on non-zero exit status SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" -if [ "${OS_DISTRO}" = "osx" ]; then - LIB_SUFFIX="dylib" - PROCS=$(sysctl -n hw.logicalcpu) - . ${SCRIPT_DIR}/.build.osx.sh -else - LIB_SUFFIX="so" - PROCS=$(grep -e '^processor' -c /proc/cpuinfo) - . ${SCRIPT_DIR}/.build.linux.sh -fi +LIB_SUFFIX="so" +PROCS=$(grep -e '^processor' -c /proc/cpuinfo) +. ${SCRIPT_DIR}/.build.linux.sh get_driver_version() { local header_file=${1} @@ -75,7 +69,7 @@ build_driver() { if [ "${CI_INTEGRATION_ENABLED}" == "true" ]; then BUILD_INTEGRATION_TESTS=On fi - cmake -DCMAKE_BUILD_TYPE=Release \ + LIBUV_ROOT_DIR=${HOME}/libuv-${LIBUV_VERSION} cmake -DCMAKE_BUILD_TYPE=Release \ -D${driver_prefix}_BUILD_SHARED=On \ -D${driver_prefix}_BUILD_EXAMPLES=On \ -D${driver_prefix}_BUILD_UNIT_TESTS=On \ diff --git a/Jenkinsfile b/Jenkinsfile index 9a177771b..f34417270 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -84,10 +84,6 @@ def installDependencies() { . ${DRIVER_BUILD_SCRIPT} install_dependencies - if [ -d packaging/packages ]; then - mkdir -p ${OS_DISTRO}/${OS_DISTRO_RELEASE} - cp packaging/packages/libuv* ${OS_DISTRO}/${OS_DISTRO_RELEASE} - fi ''' } @@ -243,59 +239,6 @@ def executeIntegrationTests() { } } -def buildPackagesAndInstallDriver() { - sh label: 'Build packages and install driver', script: '''#!/bin/bash -le - . ${DRIVER_BUILD_SCRIPT} - - install_driver - if [ -d packaging/packages ]; then - mkdir -p ${OS_DISTRO}/${OS_DISTRO_RELEASE} - cp packaging/packages/${DRIVER_LIBRARY}-cpp-driver* ${OS_DISTRO}/${OS_DISTRO_RELEASE} - fi - ''' -} - -def testDriverInstallation() { - sh label: 'Test driver installation', script: '''#!/bin/bash -le - . ${DRIVER_BUILD_SCRIPT} - - test_installed_driver "${DRIVER_LIBRARY}" - ''' -} - -def deployDriver() { - withCredentials([usernameColonPassword(credentialsId: 'cpp-drivers-artifactory-upload-username-encrypted-password', variable: 'ARTIFACTORY_CREDENTIALS'), - string(credentialsId: 'artifactory-base-url', variable: 'ARTIFACTORY_BASE_URL')]) { - sh label: 'Deploy driver to Artifactory', script: '''#!/bin/bash -le - . ${DRIVER_BUILD_SCRIPT} - - DRIVER_FOLDER=cpp-driver - if [ "${DRIVER_TYPE}" = "DSE" ]; then - DRIVER_FOLDER=cpp-dse-driver - fi - DRIVER_BASE="cpp-php-drivers/${DRIVER_FOLDER}/builds" - curl -u "${ARTIFACTORY_CREDENTIALS}" -T "{$(echo packaging/packages/${DRIVER_LIBRARY}-cpp-driver* | tr ' ' ',')}" "${ARTIFACTORY_BASE_URL}/${DRIVER_BASE}/${DRIVER_VERSION}/${GIT_SHA}/${OS_DISTRO}/${OS_DISTRO_RELEASE}/${DRIVER_LIBRARY}/v${DRIVER_VERSION}/" - curl -u "${ARTIFACTORY_CREDENTIALS}" -T "{$(echo packaging/packages/libuv* | tr ' ' ',')}" "${ARTIFACTORY_BASE_URL}/${DRIVER_BASE}/${DRIVER_VERSION}/${GIT_SHA}/${OS_DISTRO}/${OS_DISTRO_RELEASE}/dependencies/libuv/v${LIBUV_VERSION}/" - ''' - } -} - -def determineBuildType() { - if (params.ADHOC_BUILD_TYPE == 'BUILD-AND-EXECUTE-TESTS') { - return 'adhoc' - } else if (params.ADHOC_BUILD_TYPE == 'BUILD-DOCUMENTS') { - return 'documents' - } - - def buildType = 'commit' - if (env.IS_BUILDING_RELEASE == 'true') { - buildType = 'release' - } else if (params.CI_SCHEDULE != 'DO-NOT-CHANGE-THIS-SELECTION') { - buildType = "${params.CI_SCHEDULE.toLowerCase()}" - } - return buildType -} - def describePerCommitStage() { script { currentBuild.displayName = "Per-Commit build of ${env.BRANCH_NAME}" @@ -303,13 +246,6 @@ def describePerCommitStage() { } } -def describeReleaseAndDeployStage() { - script { - currentBuild.displayName = "Build release v${env.DRIVER_VERSION} [${env.GIT_SHA}]" - currentBuild.description = "Build the DataStax C/C++ driver release v${env.DRIVER_VERSION} [${env.GIT_SHA}] and deploy to Artifactory" - } -} - def describeScheduledAndAdhocTestingStage() { script { if (params.CI_SCHEDULE != 'DO-NOT-CHANGE-THIS-SELECTION') { @@ -350,13 +286,6 @@ def describeScheduledAndAdhocTestingStage() { } } -def describeScheduledAndAdhocBuildDocuments() { - script { - currentBuild.displayName = "Build documents [${env.GIT_SHA}]" - currentBuild.description = "Build the DataStax C/C++ driver documents [${env.GIT_SHA}]" - } -} - pipeline { agent none @@ -370,7 +299,7 @@ pipeline { parameters { choice( name: 'ADHOC_BUILD_TYPE', - choices: ['BUILD', 'BUILD-AND-EXECUTE-TESTS', 'BUILD-RELEASE-AND-DEPLOY', 'BUILD-DOCUMENTS'], + choices: ['BUILD', 'BUILD-AND-EXECUTE-TESTS'], description: '''Perform a adhoc build operation @@ -394,14 +323,6 @@ pipeline { - - - - - - - -
BUILD-RELEASE-AND-DEPLOYPerforms a release build and deploys to Artifactory
BUILD-DOCUMENTSPerforms a document build using documentor

''') choice( @@ -444,10 +365,10 @@ pipeline { ''') choice( name: 'OS_VERSION', - choices: ['rocky/8-64/cpp', - 'rocky/9-64/cpp', - 'ubuntu/focal64/cpp', - 'ubuntu/jammy64/cpp'], + choices: ['rocky/9-64/cpp', + 'rocky/10-64/cpp', + 'ubuntu/jammy64/cpp', + 'ubuntu/noble64/cpp'], description: '''Operating system to use for scheduled or adhoc builds @@ -456,22 +377,22 @@ pipeline { - - - - - - + + + + + +
Choice Description
rocky/8-64/cppRocky Linux 8 x86_64
rocky/9-64/cpp Rocky Linux 9 x86_64
ubuntu/focal64/cppUbuntu 20.04 LTS x86_64rocky/10-64/cppRocky Linux 10 x86_64
ubuntu/jammy64/cpp Ubuntu 22.04 LTS x86_64
ubuntu/noble64/cppUbuntu 24.04 LTS x86_64
''') string( name: 'INTEGRATION_TESTS_FILTER', @@ -480,7 +401,7 @@ pipeline { Run only the tests whose name matches one of the positive patterns but none of the negative patterns. \'?\' matches any single character; \'*\' matches any substring; \':\' separates two patterns.''') string( name: 'LIBUV_VERSION', - defaultValue: '1.38.0', + defaultValue: '1.52.1', description: '''

libuv version to build and use

Note: Rarely does this need to change.''') choice( @@ -516,10 +437,10 @@ pipeline { axes { axis { name 'OS_VERSION' - values 'rocky/8-64/cpp', - 'rocky/9-64/cpp', - 'ubuntu/focal64/cpp', - 'ubuntu/jammy64/cpp' + values 'rocky/9-64/cpp', + 'rocky/10-64/cpp', + 'ubuntu/jammy64/cpp', + 'ubuntu/noble64/cpp' } } @@ -590,95 +511,6 @@ pipeline { } } } - stage('Build-Packages-And-Install-Driver') { - when { - expression { env.OS_VERSION != 'osx/high-sierra' } - } - steps { - buildPackagesAndInstallDriver() - } - post { - success { - script { - def distro = get_os_distro() - archiveArtifacts artifacts: "${distro}/**/*-cpp-driver*" - } - } - } - } - stage('Test-Driver-Installation') { - when { - expression { env.OS_VERSION != 'osx/high-sierra' } - } - steps { - testDriverInstallation() - } - } - } - post { - cleanup { - cleanWs() - } - } - } - } - - stage('Build-Release-And-Deploy') { - options { - timeout(time: 1, unit: 'HOURS') - } - when { - beforeAgent true - anyOf { - buildingTag() - expression { params.ADHOC_BUILD_TYPE == 'BUILD-RELEASE-AND-DEPLOY' } - } - } - - matrix { - axes { - axis { - name 'OS_VERSION' - values 'rocky/8-64/cpp', - 'rocky/9-64/cpp', - 'ubuntu/focal64/cpp', - 'ubuntu/jammy64/cpp' - } - } - - environment { - IS_BUILDING_RELEASE = 'true' - } - agent { - label "${env.OS_VERSION}" - } - - stages { - stage('Initialize-Environment') { - steps { - initializeEnvironment() - } - } - stage('Describe-Release-And-Deploy') { - steps { - describeReleaseAndDeployStage() - } - } - stage('Install-Dependencies') { - steps { - installDependencies() - } - } - stage('Build-Packages-And-Install-Driver') { - steps { - buildPackagesAndInstallDriver() - } - } - stage('Deploy-Driver') { - steps { - deployDriver() - } - } } post { cleanup { @@ -787,59 +619,5 @@ pipeline { } } } - - stage('Scheduled-And-Adhoc-Build-Documents') { - when { - beforeAgent true - allOf { - not { buildingTag() } - anyOf { - allOf { - // User initiated - expression { params.ADHOC_BUILD_TYPE == 'BUILD-DOCUMENTS' } - expression { params.CI_SCHEDULE == 'DO-NOT-CHANGE-THIS-SELECTION' } - expression { params.CI_SCHEDULE_SERVER_VERSIONS == 'DO-NOT-CHANGE-THIS-SELECTION' } - } - allOf { - // Schedule initiated - branch 'master' - expression { params.ADHOC_BUILD_TYPE == 'BUILD-DOCUMENTS' } - expression { params.CI_SCHEDULE == 'WEEKENDS' } - expression { params.CI_SCHEDULE_SERVER_VERSIONS == 'DO-NOT-CHANGE-THIS-SELECTION' } - } - } - } - } - - environment { - OS_VERSION = 'ubuntu/jammy64/cpp' - } - agent { - label 'ubuntu/jammy64/cpp' - } - - stages { - stage('Initialize-Environment') { - steps { - initializeEnvironment() - } - } - stage('Describe-Build') { - steps { - describeScheduledAndAdhocBuildDocuments() - } - } - stage('Build-Documents') { - steps { - buildDocuments() - } - post { - success { - archiveArtifacts artifacts: '*-documents.tgz' - } - } - } - } - } } } diff --git a/tests/src/integration/tests/test_dbaas.cpp b/tests/src/integration/tests/test_dbaas.cpp deleted file mode 100644 index d5aa5985a..000000000 --- a/tests/src/integration/tests/test_dbaas.cpp +++ /dev/null @@ -1,750 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "integration.hpp" - -#include "process.hpp" - -#define PROXY_CREDS_V1_INVALID_CA_FILENAME "creds-v1-invalid-ca.zip" -#define PROXY_CREDS_V1_UNREACHABLE_FILENAME "creds-v1-unreachable.zip" -#define PROXY_CREDS_V1_NO_CERT_FILENAME "creds-v1-wo-cert.zip" -#define PROXY_CREDS_V1_NO_CREDS_FILENAME "creds-v1-wo-creds.zip" -#define PROXY_CREDS_V1_FILENAME "creds-v1.zip" - -#ifdef WIN32 -#define PROXY_RUN_SCRIPT "run.ps1" -#define PROXY_CREDS_BUNDLES "certs\\bundles\\" -#else -#define PROXY_RUN_SCRIPT "run.sh" -#define PROXY_CREDS_BUNDLES "certs/bundles/" -#endif - -using test::Utils; -using utils::Process; - -/** - * Database as a service integration tests - */ -class DbaasTests : public Integration { -public: - typedef std::map ServerNames; - typedef std::pair ServerPair; - - static void SetUpTestCase() { - char* proxy_path = getenv("PROXY_PATH"); - if (proxy_path) { - proxy_path_ = proxy_path; - } else { - proxy_path_ = Utils::home_directory() + Utils::PATH_SEPARATOR + "proxy"; - } - proxy_path_ += Utils::PATH_SEPARATOR; - proxy_run_script_ = proxy_path_ + PROXY_RUN_SCRIPT; - - // Allow the proxy to start itself or use a currently running proxy - if (file_exists(proxy_run_script_)) { - if (!start_proxy()) { - FAIL() << "Unable to start SNI single endpoint proxy service. Check PROXY_PATH environment " - "variable" -#ifdef WIN32 - << " or ensure proper ExecutionPolicy is set (e.g. Set-ExecutionPolicy -Scope " - "CurrentUser Unrestricted); see " - "https:/go.microsoft.com/fwlink/?LinkID=135170" -#endif - << "."; - } - } else { - if (!is_proxy_running()) { - FAIL() - << "SNI single endpoint proxy is not available. Start container before executing test."; - } - } - - if (!file_exists(proxy_cred_bundles_path_)) { - proxy_cred_bundles_path_ = proxy_path_ + proxy_cred_bundles_path_; - } - if (!file_exists(creds_v1_invalid_ca()) || !file_exists(creds_v1_unreachable()) || - !file_exists(creds_v1_no_cert()) || !file_exists(creds_v1_no_creds()) || - !file_exists(creds_v1())) { - FAIL() << "Unable to locate SNI single endpoint credential bundles. Check PROXY_PATH " - "environment variable."; - } - } - - void SetUp() { - // Ensure CCM and session are not created for these tests - is_ccm_requested_ = false; - is_session_requested_ = false; - is_schema_metadata_ = true; // Needed for prepared statements - Integration::SetUp(); - } - - static void TearDownTestCase() { - if (!Options::keep_clusters()) { - stop_proxy(); - } - } - - static std::string creds_v1_invalid_ca() { - return proxy_cred_bundles_path_ + PROXY_CREDS_V1_INVALID_CA_FILENAME; - } - - static std::string creds_v1_unreachable() { - return proxy_cred_bundles_path_ + PROXY_CREDS_V1_UNREACHABLE_FILENAME; - } - - static std::string creds_v1_no_cert() { - return proxy_cred_bundles_path_ + PROXY_CREDS_V1_NO_CERT_FILENAME; - } - - static std::string creds_v1_no_creds() { - return proxy_cred_bundles_path_ + PROXY_CREDS_V1_NO_CREDS_FILENAME; - } - - static std::string creds_v1() { return proxy_cred_bundles_path_ + PROXY_CREDS_V1_FILENAME; } - - int get_node_id(const std::string& rpc_address) { - std::vector octects = explode(rpc_address, '.'); - std::stringstream ss(octects[octects.size() - 1]); - int node = 0; - if ((ss >> node).fail()) { - EXPECT_TRUE(false) << "Unable to parse node number from rpc_address"; - } - return node; - } - - /** - * Vector of server names sorted by node number (e.g. last octet in real IP address) - */ - ServerNames get_server_names() { - ServerNames map; - { - Cluster cluster = default_cluster(false) - .with_randomized_contact_points(false) - .with_load_balance_round_robin(); - EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - Session session = cluster.connect(); - for (int i = 0; i < 3; ++i) { - Row row = session.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).first_row(); - int node = get_node_id(row.column_by_name("rpc_address").str()); - map.insert(ServerPair(node, row.column_by_name("host_id").str())); - } - } - return map; - } - - bool start_cluster() { - Process::Args args; - args.push_back("start"); - args.push_back("--root"); - args.push_back("--wait-for-binary-proto"); - args.push_back("--jvm_arg=-Ddse.product_type=DATASTAX_APOLLO"); - return ccm_execute(args); - } - - bool stop_cluster() { - Process::Args args; - args.push_back("stop"); - return ccm_execute(args); - } - - bool start_node(unsigned int node) { - Process::Args args; - args.push_back(node_name(node)); - args.push_back("start"); - args.push_back("--root"); - args.push_back("--wait-for-binary-proto"); - args.push_back("--jvm_arg=-Ddse.product_type=DATASTAX_APOLLO"); - return ccm_execute(args); - } - - bool stop_node(unsigned int node, bool is_kill = false) { - Process::Args args; - args.push_back(node_name(node)); - args.push_back("stop"); - if (is_kill) { - args.push_back("--not-gently"); - } - return ccm_execute(args); - } - -private: - std::string node_name(int node) { - std::stringstream node_name; - node_name << "node" << node; - return node_name.str(); - } - - bool ccm_execute(Process::Args args) { - Process::Args command; - command.push_back("docker"); - command.push_back("exec"); - command.push_back(get_proxy_id()); - command.push_back("ccm"); - command.insert(command.end(), args.begin(), args.end()); - Process::Result result = Process::execute(command); - return result.exit_status == 0; - } - -private: - static std::string get_proxy_id() { - if (proxy_id_.empty()) { - Process::Args command; - command.push_back("docker"); - command.push_back("ps"); - command.push_back("-aqf"); - command.push_back("ancestor=single_endpoint"); - Process::Result result = Process::execute(command); - proxy_id_ = Utils::trim(result.standard_output); - } - return proxy_id_; - } - - static bool is_proxy_running() { return !get_proxy_id().empty(); } - - static bool start_proxy() { - if (is_proxy_running()) return true; - - Process::Args command; -#ifdef WIN32 - command.push_back("powershell"); -#endif - command.push_back(proxy_run_script_); - Process::Result result = Process::execute(command); - return result.exit_status == 0; - } - - static bool stop_proxy() { - Process::Args command; - command.push_back("docker"); - command.push_back("kill"); - command.push_back(get_proxy_id()); - Process::Result result = Process::execute(command); - return result.exit_status == 0; - } - -private: - static std::string proxy_path_; - static std::string proxy_cred_bundles_path_; - static std::string proxy_run_script_; - static std::string proxy_id_; -}; - -std::string DbaasTests::proxy_path_; -std::string DbaasTests::proxy_cred_bundles_path_ = PROXY_CREDS_BUNDLES; -std::string DbaasTests::proxy_run_script_ = PROXY_RUN_SCRIPT; -std::string DbaasTests::proxy_id_; - -/** - * Perform connection to DBaaS SNI single endpoint docker image. - * - * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper - * automatic cloud configuration with address resolution. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @since 2.14.0 - * @expected_result Successful address resolution and connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ResolveAndConnect) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); -} - -/** - * Perform query using a simple statement against the DBaaS SNI single endpoint docker image. - * - * This test will perform a connection and execute a simple statement query against the - * system.local table to ensure query execution to a DBaaS SNI single endpoint while validating the - * results. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @test_category queries - * @since 2.14.0 - * @expected_result Simple statement is executed and nodes are validated. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, QueryEachNode) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false).with_load_balance_round_robin(); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - ServerNames server_names; - for (int i = 0; i < 3; ++i) { - Result result = session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL); - Uuid expected_host_id = Uuid(result.server_name()); - Row row = result.first_row(); - - Uuid host_id = row.column_by_name("host_id"); - int node = get_node_id(row.column_by_name("rpc_address").str()); - EXPECT_NE(0, node); - EXPECT_EQ(expected_host_id, host_id); - server_names.insert(ServerPair(node, host_id.str())); - } - - EXPECT_EQ(3u, server_names.size()); // Ensure all three nodes were queried -} - -/** - * Create function and aggregate definitions and ensure the schema metadata is reflected when - * execute against the DBaaS SNI single endpoint docker image. - * - * This test will perform a connection and execute create function/aggregate queries to ensure - * schema metadata using a DBaaS SNI single endpoint is handled properly. - * - * @jira_ticket CPP-815 - * @test_category dbaas - * @test_category queries:schema_metadata:udf - * @since 2.14.0 - * @expected_result Function/Aggregate definitions schema metadata are validated. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, SchemaMetadata) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - // clang-format off - session_.execute("CREATE OR REPLACE FUNCTION avg_state(state tuple, val int) " - "CALLED ON NULL INPUT RETURNS tuple " - "LANGUAGE java AS " - "'if (val != null) {" - "state.setInt(0, state.getInt(0) + 1);" - "state.setLong(1, state.getLong(1) + val.intValue());" - "};" - "return state;'" - ";"); - session_.execute("CREATE OR REPLACE FUNCTION avg_final (state tuple) " - "CALLED ON NULL INPUT RETURNS double " - "LANGUAGE java AS " - "'double r = 0;" - "if (state.getInt(0) == 0) return null;" - "r = state.getLong(1);" - "r /= state.getInt(0);" - "return Double.valueOf(r);'" - ";"); - session_.execute("CREATE OR REPLACE AGGREGATE average(int) " - "SFUNC avg_state STYPE tuple FINALFUNC avg_final " - "INITCOND(0, 0);"); - // clang-format on - - const CassSchemaMeta* schema_meta = cass_session_get_schema_meta(session_.get()); - ASSERT_TRUE(schema_meta != NULL); - const CassKeyspaceMeta* keyspace_meta = - cass_schema_meta_keyspace_by_name(schema_meta, default_keyspace().c_str()); - ASSERT_TRUE(keyspace_meta != NULL); - - { // Function `avg_state` - const char* data = NULL; - size_t length = 0; - const CassDataType* datatype = NULL; - - const CassFunctionMeta* function_meta = - cass_keyspace_meta_function_by_name(keyspace_meta, "avg_state", "tuple,int"); - ASSERT_TRUE(function_meta != NULL); - cass_function_meta_name(function_meta, &data, &length); - EXPECT_EQ("avg_state", std::string(data, length)); - cass_function_meta_full_name(function_meta, &data, &length); - EXPECT_EQ("avg_state(tuple,int)", std::string(data, length)); - cass_function_meta_body(function_meta, &data, &length); - EXPECT_EQ("if (val != null) {state.setInt(0, state.getInt(0) + 1);state.setLong(1, " - "state.getLong(1) + val.intValue());};return state;", - std::string(data, length)); - cass_function_meta_language(function_meta, &data, &length); - EXPECT_EQ("java", std::string(data, length)); - EXPECT_TRUE(cass_function_meta_called_on_null_input(function_meta)); - ASSERT_EQ(2u, cass_function_meta_argument_count(function_meta)); - cass_function_meta_argument(function_meta, 0, &data, &length, &datatype); - EXPECT_EQ("state", std::string(data, length)); - EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype)); - ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype)); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0))); - EXPECT_EQ(CASS_VALUE_TYPE_BIGINT, - cass_data_type_type(cass_data_type_sub_data_type(datatype, 1))); - cass_function_meta_argument(function_meta, 1, &data, &length, &datatype); - EXPECT_EQ("val", std::string(data, length)); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype)); - datatype = cass_function_meta_argument_type_by_name(function_meta, "state"); - EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype)); - ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype)); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0))); - EXPECT_EQ(CASS_VALUE_TYPE_BIGINT, - cass_data_type_type(cass_data_type_sub_data_type(datatype, 1))); - datatype = cass_function_meta_argument_type_by_name(function_meta, "val"); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype)); - datatype = cass_function_meta_return_type(function_meta); - EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype)); - ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype)); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0))); - EXPECT_EQ(CASS_VALUE_TYPE_BIGINT, - cass_data_type_type(cass_data_type_sub_data_type(datatype, 1))); - } - - { // Aggregate `average` - const char* data = NULL; - size_t length = 0; - const CassDataType* datatype = NULL; - - const CassAggregateMeta* aggregate_meta = - cass_keyspace_meta_aggregate_by_name(keyspace_meta, "average", "int"); - ASSERT_TRUE(aggregate_meta != NULL); - cass_aggregate_meta_name(aggregate_meta, &data, &length); - EXPECT_EQ("average", std::string(data, length)); - cass_aggregate_meta_full_name(aggregate_meta, &data, &length); - EXPECT_EQ("average(int)", std::string(data, length)); - ASSERT_EQ(1u, cass_aggregate_meta_argument_count(aggregate_meta)); - datatype = cass_aggregate_meta_argument_type(aggregate_meta, 0); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype)); - datatype = cass_aggregate_meta_return_type(aggregate_meta); - EXPECT_EQ(CASS_VALUE_TYPE_DOUBLE, cass_data_type_type(datatype)); - datatype = cass_aggregate_meta_state_type(aggregate_meta); - EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype)); - ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype)); - EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0))); - EXPECT_EQ(CASS_VALUE_TYPE_BIGINT, - cass_data_type_type(cass_data_type_sub_data_type(datatype, 1))); - const CassFunctionMeta* function_meta = cass_aggregate_meta_state_func(aggregate_meta); - cass_function_meta_name(function_meta, &data, &length); - EXPECT_EQ("avg_state", std::string(data, length)); - function_meta = cass_aggregate_meta_final_func(aggregate_meta); - cass_function_meta_name(function_meta, &data, &length); - EXPECT_EQ("avg_final", std::string(data, length)); - const CassValue* initcond = cass_aggregate_meta_init_cond(aggregate_meta); - EXPECT_EQ(CASS_VALUE_TYPE_VARCHAR, cass_value_type(initcond)); - EXPECT_EQ(Text("(0, 0)"), Text(initcond)); - ASSERT_TRUE(true); - } - - cass_schema_meta_free(schema_meta); -} - -/** - * Ensure guardrails are enabled when performing a query against the DBaaS SNI single endpoint - * docker image. - * - * This test will perform a connection and execute a simple insert statement query against the - * server using a valid consistency level.DBaaS SNI single endpoint while validating the - * insert occured. - * - * @jira_ticket CPP-813 - * @test_category dbaas - * @test_category queries:guard_rails - * @since 2.14.0 - * @expected_result Simple statement is executed and is validated. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ConsistencyGuardrails) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - session_.execute( - format_string(CASSANDRA_KEY_VALUE_TABLE_FORMAT, default_table().c_str(), "int", "int")); - CHECK_FAILURE; - - session_.execute(Statement( - format_string(CASSANDRA_KEY_VALUE_INSERT_FORMAT, default_table().c_str(), "0", "1"))); - Result result = session_.execute( - Statement(format_string(CASSANDRA_SELECT_VALUE_FORMAT, default_table().c_str(), "0"))); - EXPECT_EQ(1u, result.row_count()); - ASSERT_EQ(1u, result.column_count()); - ASSERT_EQ(Integer(1), result.first_row().next().as()); -} - -/** - * Ensure guardrails are enabled when performing a query against the DBaaS SNI single endpoint - * docker image. - * - * This test will perform a connection and execute a simple statement query against the - * server using an invalid consistency level.DBaaS SNI single endpoint while validating the - * error. - * - * @jira_ticket CPP-813 - * @test_category dbaas - * @test_category queries:guard_rails - * @since 2.14.0 - * @expected_result Simple statement is executed and guard rail error is validated. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ConsistencyGuardrailsInvalid) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - session_.execute( - format_string(CASSANDRA_KEY_VALUE_TABLE_FORMAT, default_table().c_str(), "int", "int")); - CHECK_FAILURE - - Statement statement( - format_string(CASSANDRA_KEY_VALUE_INSERT_FORMAT, default_table().c_str(), "0", "1")); - statement.set_consistency( - CASS_CONSISTENCY_LOCAL_ONE); // Override default DBaaS configured consistency - Result result = session_.execute(statement, false); - EXPECT_TRUE(result.error_code() != CASS_OK) - << "Statement execution succeeded; guardrails may not be enabled"; - EXPECT_TRUE(contains(result.error_message(), - "Provided value LOCAL_ONE is not allowed for Write Consistency Level")); -} - -/** - * Perform query ensuring token aware is enabled by default. - * - * This test will perform a connection and execute a insert query against to ensure that token - * aware is enabled by default when automatically configured . - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @test_category queries - * @since 2.14.0 - * @expected_result Simple statement is executed and validated against replicas. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, DcAwareTokenAwareRoutingDefault) { - CHECK_FAILURE; - - ServerNames server_names = get_server_names(); - - // Validate replicas are used during token aware routing - std::vector > replicas; - replicas.push_back(std::pair(0, 2)); // query key, node id (last octet of rpc_address) - replicas.push_back(std::pair(1, 2)); - replicas.push_back(std::pair(2, 2)); - replicas.push_back(std::pair(3, 1)); - replicas.push_back(std::pair(4, 3)); - replicas.push_back(std::pair(5, 2)); - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - for (std::vector >::iterator it = replicas.begin(), end = replicas.end(); - it != end; ++it) { - Statement statement(SELECT_ALL_SYSTEM_LOCAL_CQL, 1); - statement.set_consistency(CASS_CONSISTENCY_ONE); - statement.add_key_index(0); - statement.set_keyspace("system"); - statement.bind(0, Integer(it->first)); - - Result result = session_.execute( - statement, false); // No bind variables exist so statement will return error - EXPECT_EQ(server_names[it->second], result.server_name()); - } -} - -/** - * Attempt connection to DBaaS SNI single endpoint docker image manually setting auth. - * - * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper - * automatic cloud configuration with address resolution where the authentication is not available. - * - * @jira_ticket CPP-787 - * @test_category dbaas:auth - * @since 2.14.0 - * @expected_result Successful address resolution and connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ResolveAndConnectWithoutCredsInBundle) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1_no_creds().c_str())); - cluster.with_credentials("cassandra", "cassandra"); - connect(cluster); -} - -/** - * Attempt connection to DBaaS SNI single endpoint docker image leaving auth unset. - * - * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper - * automatic cloud configuration with address resolution where the authentication is not set. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @since 2.14.0 - * @expected_result Failed to establish a connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidWithoutCreds) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1_no_creds().c_str())); - try { - connect(cluster); - EXPECT_TRUE(false) << "Connection established"; - } catch (Session::Exception& se) { - EXPECT_EQ(CASS_ERROR_SERVER_BAD_CREDENTIALS, se.error_code()); - } -} - -/** - * Attempt connection to DBaaS SNI single endpoint docker image using invalid metadata server. - * - * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid metadata - * server. The connection should not succeed as no resolution will be possible. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @since 2.14.0 - * @expected_result Failed to establish a connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidMetadataServer) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1_unreachable().c_str())); - try { - connect(cluster); - EXPECT_TRUE(false) << "Connection established"; - } catch (Session::Exception& se) { - EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code()); - } -} - -/** - * Attempt connection to DBaaS SNI single endpoint docker image using invalid certificate. - * - * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid certificate. - * The connection should not succeed as no resolution will be possible. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @since 2.14.0 - * @expected_result Failed to establish a connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidCertificate) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS, - cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1_no_cert().c_str())); - try { - connect(cluster); - EXPECT_TRUE(false) << "Connection established"; - } catch (Session::Exception& se) { - EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code()); - } -} - -/** - * Attempt connection to DBaaS SNI single endpoint docker image using invalid CA. - * - * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid CA. The - * connection should not succeed as no resolution will be possible. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @since 2.14.0 - * @expected_result Failed to establish a connection. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidCertificateAuthority) { - CHECK_FAILURE; - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1_invalid_ca().c_str())); - try { - connect(cluster); - EXPECT_TRUE(false) << "Connection established"; - } catch (Session::Exception& se) { - EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code()); - } -} - -/** - * Perform query with nodes down against the DBaaS SNI single endpoint docker image. - * - * This test will perform a connection and execute a simple statement query against the - * system.local table to ensure query execution to a DBaaS SNI single endpoint while validating the - * results. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @test_category queries - * @since 2.14.0 - * @expected_result Simple statement is executed and validated while node(s) are down. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, QueryWithNodesDown) { - CHECK_FAILURE; - - ServerNames server_names = get_server_names(); - - Cluster cluster = default_cluster(false); - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - EXPECT_TRUE(stop_node(1)); - for (int i = 0; i < 8; ++i) { - EXPECT_NE(server_names[1], session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).server_name()); - } - - EXPECT_TRUE(stop_node(3)); - for (int i = 0; i < 8; ++i) { - EXPECT_EQ(server_names[2], session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).server_name()); - } - - EXPECT_TRUE(start_cluster()); -} - -/** - * Ensure reconnection occurs during full outage. - * - * This test will perform a connection, full outage will occur and the the cluster will be restarted - * while executing a simple statement query against the system.local table to ensure reconnection - * after full outage. - * - * @jira_ticket CPP-787 - * @test_category dbaas - * @test_category queries - * @since 2.14.0 - * @expected_result Simple statement is executed and validated after full outage. - */ -CASSANDRA_INTEGRATION_TEST_F(DbaasTests, FullOutage) { - CHECK_FAILURE; - - ServerNames server_names = get_server_names(); - - Cluster cluster = default_cluster(false).with_constant_reconnect(10); // Quick reconnect - ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster.get(), creds_v1().c_str())); - connect(cluster); - - EXPECT_TRUE(stop_cluster()); - - Statement statement(SELECT_ALL_SYSTEM_LOCAL_CQL); - EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, session_.execute(statement, false).error_code()); - - EXPECT_TRUE(start_cluster()); - EXPECT_EQ(CASS_OK, session_.execute(statement).error_code()); -} diff --git a/tests/src/unit/tests/test_cloud_secure_connect_config.cpp b/tests/src/unit/tests/test_cloud_secure_connect_config.cpp deleted file mode 100644 index 9c3fc6012..000000000 --- a/tests/src/unit/tests/test_cloud_secure_connect_config.cpp +++ /dev/null @@ -1,690 +0,0 @@ -/* - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "driver_config.hpp" - -#ifdef HAVE_ZLIB -#include "http_test.hpp" - -#include "cloud_secure_connection_config.hpp" -#include "cluster_config.hpp" -#include "cluster_connector.hpp" -#include "cluster_metadata_resolver.hpp" -#include "config.hpp" -#include "dse_auth.hpp" -#include "http_client.hpp" -#include "json.hpp" -#include "string.hpp" - -#include "zip.h" - -#include -#include - -#define CONFIGURATION_FILE "config.json" -#define CERTIFICATE_AUTHORITY_FILE "ca.crt" -#define CERTIFICATE_FILE "cert" -#define KEY_FILE "key" - -#define CREDS_V1_ZIP_FILE "creds-v1.zip" - -#ifdef _WIN32 -#define PATH_SEPARATOR '\\' -#else -#define PATH_SEPARATOR '/' -#endif - -#define SNI_LOCAL_DC "dc1" -#define SNI_HOST HTTP_MOCK_HOSTNAME -#define SNI_PORT 30002 -#define SNI_HOST_AND_PORT HTTP_MOCK_HOSTNAME ":30002" -#define SNI_HOST_ID_1 "276b1694-64c4-4ba8-afb4-e33915a02f1e" -#define SNI_HOST_ID_2 "8c29f723-5c1c-4ffd-a4ef-8c683a7fc02b" -#define SNI_HOST_ID_3 "fb91d3ff-47cb-447d-b31d-c5721ca8d7ab" -#define METADATA_SERVICE_PORT 30443 - -using datastax::String; -using datastax::internal::core::AddressVec; -using datastax::internal::core::CloudSecureConnectionConfig; -using datastax::internal::core::ClusterConfig; -using datastax::internal::core::ClusterMetadataResolver; -using datastax::internal::core::ClusterSettings; -using datastax::internal::core::Config; -using datastax::internal::core::HttpClient; -using datastax::internal::core::SslContext; -using datastax::internal::core::SslContextFactory; -using datastax::internal::enterprise::DsePlainTextAuthProvider; -using datastax::internal::json::StringBuffer; -using datastax::internal::json::Writer; - -using mockssandra::Ssl; - -class CloudSecureConnectionConfigTest : public HttpTest { -public: - const String& ca_cert() const { return ca_cert_; } - void set_invalid_ca_cert() { ca_cert_ = "!!!!!INVALID!!!!!"; } - const String& ca_key() const { return ca_key_; } - const String& cert() const { return cert_; } - void set_invalid_cert() { cert_ = "!!!!!INVALID!!!!!"; } - const String& key() const { return key_; } - void set_invalid_key() { key_ = "!!!!!INVALID!!!!!"; } - - void SetUp() { - HttpTest::SetUp(); - - char tmp[260] = { 0 }; // Note: 260 is the maximum path on Windows - size_t tmp_length = 260; - uv_os_tmpdir(tmp, &tmp_length); - - tmp_zip_file_ = String(tmp, tmp_length) + PATH_SEPARATOR + CREDS_V1_ZIP_FILE; - - ca_key_ = Ssl::generate_key(); - ca_cert_ = Ssl::generate_cert(ca_key_, "CA"); - key_ = Ssl::generate_key(); - cert_ = Ssl::generate_cert(key_, "", ca_cert_, ca_key_); - } - - const String& creds_zip_file() const { return tmp_zip_file_; } - - void create_zip_file(const String& config, bool is_configuration = true, bool is_ca = true, - bool is_cert = true, bool is_key = true) { - zipFile zip_file = zipOpen64(tmp_zip_file_.c_str(), 0); - - if (is_configuration && add_zip_file_entry(zip_file, CONFIGURATION_FILE)) { - zipWriteInFileInZip(zip_file, config.c_str(), config.length()); - zipCloseFileInZip(zip_file); - } - if (is_ca && add_zip_file_entry(zip_file, CERTIFICATE_AUTHORITY_FILE)) { - zipWriteInFileInZip(zip_file, ca_cert_.c_str(), ca_cert_.length()); - zipCloseFileInZip(zip_file); - } - if (is_cert && add_zip_file_entry(zip_file, CERTIFICATE_FILE)) { - zipWriteInFileInZip(zip_file, cert_.c_str(), cert_.length()); - zipCloseFileInZip(zip_file); - } - if (is_key && add_zip_file_entry(zip_file, KEY_FILE)) { - zipWriteInFileInZip(zip_file, key_.c_str(), key_.length()); - zipCloseFileInZip(zip_file); - } - - zipClose(zip_file, NULL); - } - - static void full_config_credsv1(StringBuffer& buffer, String host = "cloud.datastax.com", - int port = 1443) { - Writer writer(buffer); - writer.StartObject(); - writer.Key("username"); - writer.String("DataStax"); - writer.Key("password"); - writer.String("Astra"); - writer.Key("host"); - writer.String(host.c_str()); - writer.Key("port"); - writer.Int(port); - writer.EndObject(); - } - -private: - bool add_zip_file_entry(zipFile zip_file, const String& zip_filename) { - zip_fileinfo file_info; - memset(&file_info, 0, sizeof(file_info)); - time_t tmp; - time(&tmp); - struct tm* time_info = localtime(&tmp); - file_info.tmz_date.tm_sec = time_info->tm_sec; - file_info.tmz_date.tm_min = time_info->tm_min; - file_info.tmz_date.tm_hour = time_info->tm_hour; - file_info.tmz_date.tm_mday = time_info->tm_mday; - file_info.tmz_date.tm_mon = time_info->tm_mon; - file_info.tmz_date.tm_year = time_info->tm_year; - - int rc = zipOpenNewFileInZip(zip_file, zip_filename.c_str(), &file_info, NULL, 0, NULL, 0, NULL, - Z_DEFLATED, Z_DEFAULT_COMPRESSION); - return rc == ZIP_OK; - } - -private: - String tmp_zip_file_; - String ca_cert_; - String ca_key_; - String cert_; - String key_; -}; - -TEST_F(CloudSecureConnectionConfigTest, CredsV1) { - Config config; - CloudSecureConnectionConfig cloud_config; - - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - EXPECT_TRUE(cloud_config.load(creds_zip_file(), &config)); - EXPECT_EQ("DataStax", cloud_config.username()); - EXPECT_EQ("Astra", cloud_config.password()); - EXPECT_EQ("cloud.datastax.com", cloud_config.host()); - EXPECT_EQ(1443, cloud_config.port()); - EXPECT_EQ(ca_cert(), cloud_config.ca_cert()); - EXPECT_EQ(cert(), cloud_config.cert()); - EXPECT_EQ(key(), cloud_config.key()); - - EXPECT_TRUE(config.ssl_context()); - EXPECT_TRUE(dynamic_cast(config.auth_provider().get()) != NULL); -} - -TEST_F(CloudSecureConnectionConfigTest, CredsV1WithoutCreds) { - Config config; - CloudSecureConnectionConfig cloud_config; - - StringBuffer buffer; - Writer writer(buffer); - writer.StartObject(); - writer.Key("host"); - writer.String("bigdata.datastax.com"); - writer.Key("port"); - writer.Int(2443); - writer.EndObject(); - create_zip_file(buffer.GetString()); - - EXPECT_TRUE(cloud_config.load(creds_zip_file(), &config)); - EXPECT_EQ("", cloud_config.username()); - EXPECT_EQ("", cloud_config.password()); - EXPECT_EQ("bigdata.datastax.com", cloud_config.host()); - EXPECT_EQ(2443, cloud_config.port()); - EXPECT_EQ(ca_cert(), cloud_config.ca_cert()); - EXPECT_EQ(cert(), cloud_config.cert()); - EXPECT_EQ(key(), cloud_config.key()); - - EXPECT_TRUE(config.ssl_context()); - EXPECT_TRUE(dynamic_cast(config.auth_provider().get()) == - NULL); // Not configured -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingHost) { - CloudSecureConnectionConfig config; - - StringBuffer buffer; - Writer writer(buffer); - writer.StartObject(); - writer.Key("username"); - writer.String("DataStax"); - writer.Key("password"); - writer.String("Astra"); - writer.Key("port"); - writer.Int(1443); - writer.EndObject(); - create_zip_file(buffer.GetString()); - - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingPort) { - CloudSecureConnectionConfig config; - - StringBuffer buffer; - Writer writer(buffer); - writer.StartObject(); - writer.Key("username"); - writer.String("DataStax"); - writer.Key("password"); - writer.String("Astra"); - writer.Key("host"); - writer.String("cloud.datastax.com"); - writer.EndObject(); - create_zip_file(buffer.GetString()); - - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsMissingZipFile) { - CloudSecureConnectionConfig config; - - EXPECT_FALSE(config.load("invalid.zip")); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1MissingConfigJson) { - CloudSecureConnectionConfig config; - - create_zip_file("", false); - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1MissingCA) { - CloudSecureConnectionConfig config; - - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString(), true, false); - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1MissingCert) { - CloudSecureConnectionConfig config; - - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString(), true, true, false); - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1MissingKey) { - CloudSecureConnectionConfig config; - - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString(), true, true, false); - create_zip_file(buffer.GetString(), true, true, true, false); - EXPECT_FALSE(config.load(creds_zip_file())); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1SslCaCert) { - Config config; - CloudSecureConnectionConfig cloud_config; - - StringBuffer buffer; - full_config_credsv1(buffer); - set_invalid_ca_cert(); - create_zip_file(buffer.GetString()); - - EXPECT_FALSE(cloud_config.load(creds_zip_file(), &config)); - EXPECT_FALSE(config.ssl_context()); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1SslCert) { - Config config; - CloudSecureConnectionConfig cloud_config; - - StringBuffer buffer; - full_config_credsv1(buffer); - set_invalid_cert(); - create_zip_file(buffer.GetString()); - - EXPECT_FALSE(cloud_config.load(creds_zip_file(), &config)); - EXPECT_FALSE(config.ssl_context()); -} - -TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1SslKey) { - Config config; - CloudSecureConnectionConfig cloud_config; - - StringBuffer buffer; - full_config_credsv1(buffer); - set_invalid_key(); - create_zip_file(buffer.GetString()); - - EXPECT_FALSE(cloud_config.load(creds_zip_file(), &config)); - EXPECT_FALSE(config.ssl_context()); -} - -class CloudMetadataServerTest : public CloudSecureConnectionConfigTest { -public: - void SetUp() { - CloudSecureConnectionConfigTest::SetUp(); - - StringBuffer buffer; - full_config_credsv1(buffer, HTTP_MOCK_HOSTNAME, HTTP_MOCK_SERVER_PORT); - create_zip_file(buffer.GetString()); - cloud_config_.load(creds_zip_file(), &config_); - - use_ssl(ca_key(), ca_cert(), HTTP_MOCK_HOSTNAME); // Ensure HttpServer is configured to use SSL - - ClusterSettings settings(config_); - resolver_ = config_.cluster_metadata_resolver_factory()->new_instance(settings); - } - - void start_http_server(bool is_content_type = true, bool is_contact_info = true, - bool is_local_dc = true, bool is_contact_points = true, - bool is_sni_proxy_address = true, bool is_port = true) { - set_path("/metadata"); - - StringBuffer buffer; - response_v1(buffer, is_contact_info, is_local_dc, is_contact_points, is_sni_proxy_address, - is_port); - set_response_body(buffer.GetString()); - - set_content_type(is_content_type ? response_v1_content_type() : "invalid"); - - HttpTest::start_http_server(); - } - - const ClusterMetadataResolver::Ptr& resolver() const { return resolver_; } - - static void on_resolve_success(ClusterMetadataResolver* resolver, bool* flag) { - *flag = true; - EXPECT_EQ("dc1", resolver->local_dc()); - - const AddressVec& contact_points = resolver->resolved_contact_points(); - ASSERT_EQ(3u, contact_points.size()); - EXPECT_EQ(Address(SNI_HOST, SNI_PORT, SNI_HOST_ID_1), contact_points[0]); - EXPECT_EQ(Address(SNI_HOST, SNI_PORT, SNI_HOST_ID_2), contact_points[1]); - EXPECT_EQ(Address(SNI_HOST, SNI_PORT, SNI_HOST_ID_3), contact_points[2]); - } - - static void on_resolve_success_default_port(ClusterMetadataResolver* resolver, bool* flag) { - *flag = true; - EXPECT_EQ("dc1", resolver->local_dc()); - - const AddressVec& contact_points = resolver->resolved_contact_points(); - ASSERT_EQ(3u, contact_points.size()); - EXPECT_EQ(Address(SNI_HOST, METADATA_SERVICE_PORT, SNI_HOST_ID_1), contact_points[0]); - EXPECT_EQ(Address(SNI_HOST, METADATA_SERVICE_PORT, SNI_HOST_ID_2), contact_points[1]); - EXPECT_EQ(Address(SNI_HOST, METADATA_SERVICE_PORT, SNI_HOST_ID_3), contact_points[2]); - } - - static void on_resolve_failed(ClusterMetadataResolver* resolver, bool* flag) { - *flag = true; - EXPECT_EQ(0u, resolver->resolved_contact_points().size()); - } - - static void on_resolve_local_dc_failed(ClusterMetadataResolver* resolver, bool* flag) { - *flag = true; - EXPECT_EQ("", resolver->local_dc()); - EXPECT_EQ(0u, resolver->resolved_contact_points().size()); - } - -private: - static void response_v1(StringBuffer& buffer, bool is_contact_info = true, - bool is_local_dc = true, bool is_contact_points = true, - bool is_sni_proxy_address = true, bool is_port = true) { - Writer writer(buffer); - writer.StartObject(); - writer.Key("version"); - writer.Int(1); - writer.Key("region"); - writer.String("local"); - if (is_contact_info) { - writer.Key("contact_info"); - writer.StartObject(); - writer.Key("type"); - writer.String("sni_proxy"); - if (is_local_dc) { - writer.Key("local_dc"); - writer.String(SNI_LOCAL_DC); - } - if (is_contact_points) { - writer.Key("contact_points"); - writer.StartArray(); - writer.String(SNI_HOST_ID_1); - writer.String(SNI_HOST_ID_2); - writer.String(SNI_HOST_ID_3); - writer.EndArray(); - } - if (is_sni_proxy_address) { - writer.Key("sni_proxy_address"); - if (is_port) { - writer.String(SNI_HOST_AND_PORT); - } else { - writer.String(SNI_HOST); - } - } - writer.EndObject(); - } - writer.EndObject(); - } - - static const char* response_v1_content_type() { return "application/json"; } - -private: - Config config_; - CloudSecureConnectionConfig cloud_config_; - ClusterMetadataResolver::Ptr resolver_; -}; - -TEST_F(CloudMetadataServerTest, ResolveV1StandardSsl) { - start_http_server(); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_success, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveV1DefaultPortSsl) { - start_http_server(true, true, true, true, true, false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, - bind_callback(on_resolve_success_default_port, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, InvalidMetadataServer) { - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); -} - -TEST_F(CloudMetadataServerTest, ResolveV1InvalidContentTypeSsl) { - start_http_server(false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveV1MissingContactInfoSsl) { - start_http_server(true, false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveV1MissingLocalDcSsl) { - start_http_server(true, true, false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, - bind_callback(on_resolve_local_dc_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveV1MissingContactPointsSsl) { - start_http_server(true, true, true, false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveV1MissingSniProxyAddressSsl) { - start_http_server(true, true, true, true, false); - - bool is_resolved = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolved)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolved); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveInvalidJsonResponse) { - add_logging_critera("Unable to configure driver from metadata server: Metadata JSON is invalid"); - - set_path("/metadata"); - set_response_body("[]"); - set_content_type("application/json"); - HttpTest::start_http_server(); - - bool is_resolve_failed = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolve_failed)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolve_failed); - EXPECT_EQ(logging_criteria_count(), 1); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveErrorResponse) { - add_logging_critera("Unable to configure driver from metadata server: Returned error response " - "code 400: 'Invalid version'"); - - const char* response_body = "{" - "\"code\": 400," - "\"message\": \"Invalid version\"" - "}"; - - set_path("/metadata"); - set_response_body(response_body); - set_response_status_code(400); - set_content_type("application/json"); - HttpTest::start_http_server(); - - bool is_resolve_failed = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolve_failed)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolve_failed); - EXPECT_EQ(logging_criteria_count(), 1); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, ResolveInvalidJsonErrorResponse) { - add_logging_critera("Unable to configure driver from metadata server: Returned error response " - "code 400: '[]'"); - - set_path("/metadata"); - set_response_body("[]"); - set_response_status_code(400); - set_content_type("application/json"); - HttpTest::start_http_server(); - - bool is_resolve_failed = false; - AddressVec contact_points; - resolver()->resolve(loop(), contact_points, bind_callback(on_resolve_failed, &is_resolve_failed)); - uv_run(loop(), UV_RUN_DEFAULT); - EXPECT_TRUE(is_resolve_failed); - EXPECT_EQ(logging_criteria_count(), 1); - - stop_http_server(); -} - -TEST_F(CloudMetadataServerTest, CloudConfiguredInvalidContactPointsOverride) { - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - ClusterConfig cluster_config; - CassCluster* cluster = CassCluster::to(&cluster_config); - EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster, creds_zip_file().c_str())); - add_logging_critera("Contact points cannot be overridden with cloud secure connection bundle"); - EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS, - cass_cluster_set_contact_points(cluster, "some.contact.point")); - EXPECT_EQ(logging_criteria_count(), 1); -} - -TEST_F(CloudMetadataServerTest, CloudConfiguredInvalidSslContextOverride) { - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - ClusterConfig cluster_config; - CassCluster* cluster = CassCluster::to(&cluster_config); - SslContext::Ptr ssl_context(SslContextFactory::create()); - CassSsl* ssl = CassSsl::to(ssl_context.get()); - - EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster, creds_zip_file().c_str())); - add_logging_critera("SSL context cannot be overridden with cloud secure connection bundle"); - cass_cluster_set_ssl(cluster, ssl); - EXPECT_EQ(logging_criteria_count(), 1); -} - -TEST_F(CloudMetadataServerTest, CloudConfiguredFailureContactPointsExist) { - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - ClusterConfig cluster_config; - CassCluster* cluster = CassCluster::to(&cluster_config); - EXPECT_EQ(CASS_OK, cass_cluster_set_contact_points(cluster, "some.contact.point")); - add_logging_critera("Contact points must not be specified with cloud secure connection bundle"); - EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS, - cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster, creds_zip_file().c_str())); - EXPECT_EQ(logging_criteria_count(), 1); -} - -TEST_F(CloudMetadataServerTest, CloudConfiguredFailureSslContextExist) { - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - ClusterConfig cluster_config; - CassCluster* cluster = CassCluster::to(&cluster_config); - SslContext::Ptr ssl_context(SslContextFactory::create()); - CassSsl* ssl = CassSsl::to(ssl_context.get()); - - cass_cluster_set_ssl(cluster, ssl); - add_logging_critera("SSL context must not be specified with cloud secure connection bundle"); - EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS, - cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster, creds_zip_file().c_str())); - EXPECT_EQ(logging_criteria_count(), 1); -} - -TEST_F(CloudMetadataServerTest, CloudConfiguredFailureContactPointsAndSslContextExist) { - StringBuffer buffer; - full_config_credsv1(buffer); - create_zip_file(buffer.GetString()); - - ClusterConfig cluster_config; - CassCluster* cluster = CassCluster::to(&cluster_config); - SslContext::Ptr ssl_context(SslContextFactory::create()); - CassSsl* ssl = CassSsl::to(ssl_context.get()); - - EXPECT_EQ(CASS_OK, cass_cluster_set_contact_points(cluster, "some.contact.point")); - cass_cluster_set_ssl(cluster, ssl); - add_logging_critera( - "Contact points and SSL context must not be specified with cloud secure connection bundle"); - EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS, - cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init( - cluster, creds_zip_file().c_str())); - EXPECT_EQ(logging_criteria_count(), 1); -} -#endif