1 /*
2   Copyright (c) DataStax, Inc.
3 
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7 
8   http://www.apache.org/licenses/LICENSE-2.0
9 
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15 */
16 
17 #include "integration.hpp"
18 
19 #include "process.hpp"
20 
21 #define PROXY_CREDS_V1_INVALID_CA_FILENAME "creds-v1-invalid-ca.zip"
22 #define PROXY_CREDS_V1_UNREACHABLE_FILENAME "creds-v1-unreachable.zip"
23 #define PROXY_CREDS_V1_NO_CERT_FILENAME "creds-v1-wo-cert.zip"
24 #define PROXY_CREDS_V1_NO_CREDS_FILENAME "creds-v1-wo-creds.zip"
25 #define PROXY_CREDS_V1_FILENAME "creds-v1.zip"
26 
27 #ifdef WIN32
28 #define PROXY_RUN_SCRIPT "run.ps1"
29 #define PROXY_CREDS_BUNDLES "certs\\bundles\\"
30 #else
31 #define PROXY_RUN_SCRIPT "run.sh"
32 #define PROXY_CREDS_BUNDLES "certs/bundles/"
33 #endif
34 
35 using test::Utils;
36 using utils::Process;
37 
38 /**
39  * Database as a service integration tests
40  */
41 class DbaasTests : public Integration {
42 public:
43   typedef std::map<int, std::string> ServerNames;
44   typedef std::pair<int, std::string> ServerPair;
45 
SetUpTestCase()46   static void SetUpTestCase() {
47     char* proxy_path = getenv("PROXY_PATH");
48     if (proxy_path) {
49       proxy_path_ = proxy_path;
50     } else {
51       proxy_path_ = Utils::home_directory() + Utils::PATH_SEPARATOR + "proxy";
52     }
53     proxy_path_ += Utils::PATH_SEPARATOR;
54     proxy_run_script_ = proxy_path_ + PROXY_RUN_SCRIPT;
55 
56     // Allow the proxy to start itself or use a currently running proxy
57     if (file_exists(proxy_run_script_)) {
58       if (!start_proxy()) {
59         FAIL() << "Unable to start SNI single endpoint proxy service. Check PROXY_PATH environment "
60                   "variable"
61 #ifdef WIN32
62                << " or ensure proper ExecutionPolicy is set (e.g. Set-ExecutionPolicy -Scope "
63                   "CurrentUser Unrestricted); see "
64                   "https:/go.microsoft.com/fwlink/?LinkID=135170"
65 #endif
66                << ".";
67       }
68     } else {
69       if (!is_proxy_running()) {
70         FAIL()
71             << "SNI single endpoint proxy is not available. Start container before executing test.";
72       }
73     }
74 
75     if (!file_exists(proxy_cred_bundles_path_)) {
76       proxy_cred_bundles_path_ = proxy_path_ + proxy_cred_bundles_path_;
77     }
78     if (!file_exists(creds_v1_invalid_ca()) || !file_exists(creds_v1_unreachable()) ||
79         !file_exists(creds_v1_no_cert()) || !file_exists(creds_v1_no_creds()) ||
80         !file_exists(creds_v1())) {
81       FAIL() << "Unable to locate SNI single endpoint credential bundles. Check PROXY_PATH "
82                 "environment variable.";
83     }
84   }
85 
SetUp()86   void SetUp() {
87     // Ensure CCM and session are not created for these tests
88     is_ccm_requested_ = false;
89     is_session_requested_ = false;
90     is_schema_metadata_ = true; // Needed for prepared statements
91     Integration::SetUp();
92   }
93 
TearDownTestCase()94   static void TearDownTestCase() {
95     if (!Options::keep_clusters()) {
96       stop_proxy();
97     }
98   }
99 
creds_v1_invalid_ca()100   static std::string creds_v1_invalid_ca() {
101     return proxy_cred_bundles_path_ + PROXY_CREDS_V1_INVALID_CA_FILENAME;
102   }
103 
creds_v1_unreachable()104   static std::string creds_v1_unreachable() {
105     return proxy_cred_bundles_path_ + PROXY_CREDS_V1_UNREACHABLE_FILENAME;
106   }
107 
creds_v1_no_cert()108   static std::string creds_v1_no_cert() {
109     return proxy_cred_bundles_path_ + PROXY_CREDS_V1_NO_CERT_FILENAME;
110   }
111 
creds_v1_no_creds()112   static std::string creds_v1_no_creds() {
113     return proxy_cred_bundles_path_ + PROXY_CREDS_V1_NO_CREDS_FILENAME;
114   }
115 
creds_v1()116   static std::string creds_v1() { return proxy_cred_bundles_path_ + PROXY_CREDS_V1_FILENAME; }
117 
get_node_id(const std::string & rpc_address)118   int get_node_id(const std::string& rpc_address) {
119     std::vector<std::string> octects = explode(rpc_address, '.');
120     std::stringstream ss(octects[octects.size() - 1]);
121     int node = 0;
122     if ((ss >> node).fail()) {
123       EXPECT_TRUE(false) << "Unable to parse node number from rpc_address";
124     }
125     return node;
126   }
127 
128   /**
129    * Vector of server names sorted by node number (e.g. last octet in real IP address)
130    */
get_server_names()131   ServerNames get_server_names() {
132     ServerNames map;
133     {
134       Cluster cluster = default_cluster(false)
135                             .with_randomized_contact_points(false)
136                             .with_load_balance_round_robin();
137       EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
138                              cluster.get(), creds_v1().c_str()));
139       Session session = cluster.connect();
140       for (int i = 0; i < 3; ++i) {
141         Row row = session.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).first_row();
142         int node = get_node_id(row.column_by_name<Inet>("rpc_address").str());
143         map.insert(ServerPair(node, row.column_by_name<Uuid>("host_id").str()));
144       }
145     }
146     return map;
147   }
148 
start_cluster()149   bool start_cluster() {
150     Process::Args args;
151     args.push_back("start");
152     args.push_back("--root");
153     args.push_back("--wait-for-binary-proto");
154     args.push_back("--jvm_arg=-Ddse.product_type=DATASTAX_APOLLO");
155     return ccm_execute(args);
156   }
157 
stop_cluster()158   bool stop_cluster() {
159     Process::Args args;
160     args.push_back("stop");
161     return ccm_execute(args);
162   }
163 
start_node(unsigned int node)164   bool start_node(unsigned int node) {
165     Process::Args args;
166     args.push_back(node_name(node));
167     args.push_back("start");
168     args.push_back("--root");
169     args.push_back("--wait-for-binary-proto");
170     args.push_back("--jvm_arg=-Ddse.product_type=DATASTAX_APOLLO");
171     return ccm_execute(args);
172   }
173 
stop_node(unsigned int node,bool is_kill=false)174   bool stop_node(unsigned int node, bool is_kill = false) {
175     Process::Args args;
176     args.push_back(node_name(node));
177     args.push_back("stop");
178     if (is_kill) {
179       args.push_back("--not-gently");
180     }
181     return ccm_execute(args);
182   }
183 
184 private:
node_name(int node)185   std::string node_name(int node) {
186     std::stringstream node_name;
187     node_name << "node" << node;
188     return node_name.str();
189   }
190 
ccm_execute(Process::Args args)191   bool ccm_execute(Process::Args args) {
192     Process::Args command;
193     command.push_back("docker");
194     command.push_back("exec");
195     command.push_back(get_proxy_id());
196     command.push_back("ccm");
197     command.insert(command.end(), args.begin(), args.end());
198     Process::Result result = Process::execute(command);
199     return result.exit_status == 0;
200   }
201 
202 private:
get_proxy_id()203   static std::string get_proxy_id() {
204     if (proxy_id_.empty()) {
205       Process::Args command;
206       command.push_back("docker");
207       command.push_back("ps");
208       command.push_back("-aqf");
209       command.push_back("ancestor=single_endpoint");
210       Process::Result result = Process::execute(command);
211       proxy_id_ = Utils::trim(result.standard_output);
212     }
213     return proxy_id_;
214   }
215 
is_proxy_running()216   static bool is_proxy_running() { return !get_proxy_id().empty(); }
217 
start_proxy()218   static bool start_proxy() {
219     if (is_proxy_running()) return true;
220 
221     Process::Args command;
222 #ifdef WIN32
223     command.push_back("powershell");
224 #endif
225     command.push_back(proxy_run_script_);
226     Process::Result result = Process::execute(command);
227     return result.exit_status == 0;
228   }
229 
stop_proxy()230   static bool stop_proxy() {
231     Process::Args command;
232     command.push_back("docker");
233     command.push_back("kill");
234     command.push_back(get_proxy_id());
235     Process::Result result = Process::execute(command);
236     return result.exit_status == 0;
237   }
238 
239 private:
240   static std::string proxy_path_;
241   static std::string proxy_cred_bundles_path_;
242   static std::string proxy_run_script_;
243   static std::string proxy_id_;
244 };
245 
246 std::string DbaasTests::proxy_path_;
247 std::string DbaasTests::proxy_cred_bundles_path_ = PROXY_CREDS_BUNDLES;
248 std::string DbaasTests::proxy_run_script_ = PROXY_RUN_SCRIPT;
249 std::string DbaasTests::proxy_id_;
250 
251 /**
252  * Perform connection to DBaaS SNI single endpoint docker image.
253  *
254  * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper
255  * automatic cloud configuration with address resolution.
256  *
257  * @jira_ticket CPP-787
258  * @test_category dbaas
259  * @since 2.14.0
260  * @expected_result Successful address resolution and connection.
261  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,ResolveAndConnect)262 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ResolveAndConnect) {
263   CHECK_FAILURE;
264 
265   Cluster cluster = default_cluster(false);
266   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
267                          cluster.get(), creds_v1().c_str()));
268   connect(cluster);
269 }
270 
271 /**
272  * Perform query using a simple statement against the DBaaS SNI single endpoint docker image.
273  *
274  * This test will perform a connection and execute a simple statement query against the
275  * system.local table to ensure query execution to a DBaaS SNI single endpoint while validating the
276  * results.
277  *
278  * @jira_ticket CPP-787
279  * @test_category dbaas
280  * @test_category queries
281  * @since 2.14.0
282  * @expected_result Simple statement is executed and nodes are validated.
283  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,QueryEachNode)284 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, QueryEachNode) {
285   CHECK_FAILURE;
286 
287   Cluster cluster = default_cluster(false).with_load_balance_round_robin();
288   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
289                          cluster.get(), creds_v1().c_str()));
290   connect(cluster);
291 
292   ServerNames server_names;
293   for (int i = 0; i < 3; ++i) {
294     Result result = session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL);
295     Uuid expected_host_id = Uuid(result.server_name());
296     Row row = result.first_row();
297 
298     Uuid host_id = row.column_by_name<Uuid>("host_id");
299     int node = get_node_id(row.column_by_name<Inet>("rpc_address").str());
300     EXPECT_NE(0, node);
301     EXPECT_EQ(expected_host_id, host_id);
302     server_names.insert(ServerPair(node, host_id.str()));
303   }
304 
305   EXPECT_EQ(3u, server_names.size()); // Ensure all three nodes were queried
306 }
307 
308 /**
309  * Create function and aggregate definitions and ensure the schema metadata is reflected when
310  * execute against the DBaaS SNI single endpoint docker image.
311  *
312  * This test will perform a connection and execute create function/aggregate queries to ensure
313  * schema metadata using a DBaaS SNI single endpoint is handled properly.
314  *
315  * @jira_ticket CPP-815
316  * @test_category dbaas
317  * @test_category queries:schema_metadata:udf
318  * @since 2.14.0
319  * @expected_result Function/Aggregate definitions schema metadata are validated.
320  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,SchemaMetadata)321 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, SchemaMetadata) {
322   CHECK_FAILURE;
323 
324   Cluster cluster = default_cluster(false);
325   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
326                          cluster.get(), creds_v1().c_str()));
327   connect(cluster);
328 
329   // clang-format off
330   session_.execute("CREATE OR REPLACE FUNCTION avg_state(state tuple<int, bigint>, val int) "
331                    "CALLED ON NULL INPUT RETURNS tuple<int, bigint> "
332                    "LANGUAGE java AS "
333                      "'if (val != null) {"
334                        "state.setInt(0, state.getInt(0) + 1);"
335                        "state.setLong(1, state.getLong(1) + val.intValue());"
336                      "};"
337                      "return state;'"
338                    ";");
339   session_.execute("CREATE OR REPLACE FUNCTION avg_final (state tuple<int, bigint>) "
340                    "CALLED ON NULL INPUT RETURNS double "
341                    "LANGUAGE java AS "
342                      "'double r = 0;"
343                      "if (state.getInt(0) == 0) return null;"
344                      "r = state.getLong(1);"
345                      "r /= state.getInt(0);"
346                      "return Double.valueOf(r);'"
347                    ";");
348   session_.execute("CREATE OR REPLACE AGGREGATE average(int) "
349                    "SFUNC avg_state STYPE tuple<int, bigint> FINALFUNC avg_final "
350                    "INITCOND(0, 0);");
351   // clang-format on
352 
353   const CassSchemaMeta* schema_meta = cass_session_get_schema_meta(session_.get());
354   ASSERT_TRUE(schema_meta != NULL);
355   const CassKeyspaceMeta* keyspace_meta =
356       cass_schema_meta_keyspace_by_name(schema_meta, default_keyspace().c_str());
357   ASSERT_TRUE(keyspace_meta != NULL);
358 
359   { // Function `avg_state`
360     const char* data = NULL;
361     size_t length = 0;
362     const CassDataType* datatype = NULL;
363 
364     const CassFunctionMeta* function_meta =
365         cass_keyspace_meta_function_by_name(keyspace_meta, "avg_state", "tuple<int,bigint>,int");
366     ASSERT_TRUE(function_meta != NULL);
367     cass_function_meta_name(function_meta, &data, &length);
368     EXPECT_EQ("avg_state", std::string(data, length));
369     cass_function_meta_full_name(function_meta, &data, &length);
370     EXPECT_EQ("avg_state(tuple<int,bigint>,int)", std::string(data, length));
371     cass_function_meta_body(function_meta, &data, &length);
372     EXPECT_EQ("if (val != null) {state.setInt(0, state.getInt(0) + 1);state.setLong(1, "
373               "state.getLong(1) + val.intValue());};return state;",
374               std::string(data, length));
375     cass_function_meta_language(function_meta, &data, &length);
376     EXPECT_EQ("java", std::string(data, length));
377     EXPECT_TRUE(cass_function_meta_called_on_null_input(function_meta));
378     ASSERT_EQ(2u, cass_function_meta_argument_count(function_meta));
379     cass_function_meta_argument(function_meta, 0, &data, &length, &datatype);
380     EXPECT_EQ("state", std::string(data, length));
381     EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype));
382     ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype));
383     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0)));
384     EXPECT_EQ(CASS_VALUE_TYPE_BIGINT,
385               cass_data_type_type(cass_data_type_sub_data_type(datatype, 1)));
386     cass_function_meta_argument(function_meta, 1, &data, &length, &datatype);
387     EXPECT_EQ("val", std::string(data, length));
388     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype));
389     datatype = cass_function_meta_argument_type_by_name(function_meta, "state");
390     EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype));
391     ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype));
392     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0)));
393     EXPECT_EQ(CASS_VALUE_TYPE_BIGINT,
394               cass_data_type_type(cass_data_type_sub_data_type(datatype, 1)));
395     datatype = cass_function_meta_argument_type_by_name(function_meta, "val");
396     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype));
397     datatype = cass_function_meta_return_type(function_meta);
398     EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype));
399     ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype));
400     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0)));
401     EXPECT_EQ(CASS_VALUE_TYPE_BIGINT,
402               cass_data_type_type(cass_data_type_sub_data_type(datatype, 1)));
403   }
404 
405   { // Aggregate `average`
406     const char* data = NULL;
407     size_t length = 0;
408     const CassDataType* datatype = NULL;
409 
410     const CassAggregateMeta* aggregate_meta =
411         cass_keyspace_meta_aggregate_by_name(keyspace_meta, "average", "int");
412     ASSERT_TRUE(aggregate_meta != NULL);
413     cass_aggregate_meta_name(aggregate_meta, &data, &length);
414     EXPECT_EQ("average", std::string(data, length));
415     cass_aggregate_meta_full_name(aggregate_meta, &data, &length);
416     EXPECT_EQ("average(int)", std::string(data, length));
417     ASSERT_EQ(1u, cass_aggregate_meta_argument_count(aggregate_meta));
418     datatype = cass_aggregate_meta_argument_type(aggregate_meta, 0);
419     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(datatype));
420     datatype = cass_aggregate_meta_return_type(aggregate_meta);
421     EXPECT_EQ(CASS_VALUE_TYPE_DOUBLE, cass_data_type_type(datatype));
422     datatype = cass_aggregate_meta_state_type(aggregate_meta);
423     EXPECT_EQ(CASS_VALUE_TYPE_TUPLE, cass_data_type_type(datatype));
424     ASSERT_EQ(2u, cass_data_type_sub_type_count(datatype));
425     EXPECT_EQ(CASS_VALUE_TYPE_INT, cass_data_type_type(cass_data_type_sub_data_type(datatype, 0)));
426     EXPECT_EQ(CASS_VALUE_TYPE_BIGINT,
427               cass_data_type_type(cass_data_type_sub_data_type(datatype, 1)));
428     const CassFunctionMeta* function_meta = cass_aggregate_meta_state_func(aggregate_meta);
429     cass_function_meta_name(function_meta, &data, &length);
430     EXPECT_EQ("avg_state", std::string(data, length));
431     function_meta = cass_aggregate_meta_final_func(aggregate_meta);
432     cass_function_meta_name(function_meta, &data, &length);
433     EXPECT_EQ("avg_final", std::string(data, length));
434     const CassValue* initcond = cass_aggregate_meta_init_cond(aggregate_meta);
435     EXPECT_EQ(CASS_VALUE_TYPE_VARCHAR, cass_value_type(initcond));
436     EXPECT_EQ(Text("(0, 0)"), Text(initcond));
437     ASSERT_TRUE(true);
438   }
439 
440   cass_schema_meta_free(schema_meta);
441 }
442 
443 /**
444  * Ensure guardrails are enabled when performing a query against the DBaaS SNI single endpoint
445  * docker image.
446  *
447  * This test will perform a connection and execute a simple insert statement query against the
448  * server using a valid consistency level.DBaaS SNI single endpoint while validating the
449  * insert occured.
450  *
451  * @jira_ticket CPP-813
452  * @test_category dbaas
453  * @test_category queries:guard_rails
454  * @since 2.14.0
455  * @expected_result Simple statement is executed and is validated.
456  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,ConsistencyGuardrails)457 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ConsistencyGuardrails) {
458   CHECK_FAILURE;
459 
460   Cluster cluster = default_cluster(false);
461   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
462                          cluster.get(), creds_v1().c_str()));
463   connect(cluster);
464 
465   session_.execute(
466       format_string(CASSANDRA_KEY_VALUE_TABLE_FORMAT, default_table().c_str(), "int", "int"));
467   CHECK_FAILURE;
468 
469   session_.execute(Statement(
470       format_string(CASSANDRA_KEY_VALUE_INSERT_FORMAT, default_table().c_str(), "0", "1")));
471   Result result = session_.execute(
472       Statement(format_string(CASSANDRA_SELECT_VALUE_FORMAT, default_table().c_str(), "0")));
473   EXPECT_EQ(1u, result.row_count());
474   ASSERT_EQ(1u, result.column_count());
475   ASSERT_EQ(Integer(1), result.first_row().next().as<Integer>());
476 }
477 
478 /**
479  * Ensure guardrails are enabled when performing a query against the DBaaS SNI single endpoint
480  * docker image.
481  *
482  * This test will perform a connection and execute a simple statement query against the
483  * server using an invalid consistency level.DBaaS SNI single endpoint while validating the
484  * error.
485  *
486  * @jira_ticket CPP-813
487  * @test_category dbaas
488  * @test_category queries:guard_rails
489  * @since 2.14.0
490  * @expected_result Simple statement is executed and guard rail error is validated.
491  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,ConsistencyGuardrailsInvalid)492 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ConsistencyGuardrailsInvalid) {
493   CHECK_FAILURE;
494 
495   Cluster cluster = default_cluster(false);
496   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
497                          cluster.get(), creds_v1().c_str()));
498   connect(cluster);
499 
500   session_.execute(
501       format_string(CASSANDRA_KEY_VALUE_TABLE_FORMAT, default_table().c_str(), "int", "int"));
502   CHECK_FAILURE
503 
504   Statement statement(
505       format_string(CASSANDRA_KEY_VALUE_INSERT_FORMAT, default_table().c_str(), "0", "1"));
506   statement.set_consistency(
507       CASS_CONSISTENCY_LOCAL_ONE); // Override default DBaaS configured consistency
508   Result result = session_.execute(statement, false);
509   EXPECT_TRUE(result.error_code() != CASS_OK)
510       << "Statement execution succeeded; guardrails may not be enabled";
511   EXPECT_TRUE(contains(result.error_message(),
512                        "Provided value LOCAL_ONE is not allowed for Write Consistency Level"));
513 }
514 
515 /**
516  * Perform query ensuring token aware is enabled by default.
517  *
518  * This test will perform a connection and execute a insert query against to ensure that token
519  * aware is enabled by default when automatically configured .
520  *
521  * @jira_ticket CPP-787
522  * @test_category dbaas
523  * @test_category queries
524  * @since 2.14.0
525  * @expected_result Simple statement is executed and validated against replicas.
526  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,DcAwareTokenAwareRoutingDefault)527 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, DcAwareTokenAwareRoutingDefault) {
528   CHECK_FAILURE;
529 
530   ServerNames server_names = get_server_names();
531 
532   // Validate replicas are used during token aware routing
533   std::vector<std::pair<int, int> > replicas;
534   replicas.push_back(std::pair<int, int>(0, 2)); // query key, node id (last octet of rpc_address)
535   replicas.push_back(std::pair<int, int>(1, 2));
536   replicas.push_back(std::pair<int, int>(2, 2));
537   replicas.push_back(std::pair<int, int>(3, 1));
538   replicas.push_back(std::pair<int, int>(4, 3));
539   replicas.push_back(std::pair<int, int>(5, 2));
540 
541   Cluster cluster = default_cluster(false);
542   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
543                          cluster.get(), creds_v1().c_str()));
544   connect(cluster);
545 
546   for (std::vector<std::pair<int, int> >::iterator it = replicas.begin(), end = replicas.end();
547        it != end; ++it) {
548     Statement statement(SELECT_ALL_SYSTEM_LOCAL_CQL, 1);
549     statement.set_consistency(CASS_CONSISTENCY_ONE);
550     statement.add_key_index(0);
551     statement.set_keyspace("system");
552     statement.bind<Integer>(0, Integer(it->first));
553 
554     Result result = session_.execute(
555         statement, false); // No bind variables exist so statement will return error
556     EXPECT_EQ(server_names[it->second], result.server_name());
557   }
558 }
559 
560 /**
561  * Attempt connection to DBaaS SNI single endpoint docker image manually setting auth.
562  *
563  * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper
564  * automatic cloud configuration with address resolution where the authentication is not available.
565  *
566  * @jira_ticket CPP-787
567  * @test_category dbaas:auth
568  * @since 2.14.0
569  * @expected_result Successful address resolution and connection.
570  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,ResolveAndConnectWithoutCredsInBundle)571 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, ResolveAndConnectWithoutCredsInBundle) {
572   CHECK_FAILURE;
573 
574   Cluster cluster = default_cluster(false);
575   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
576                          cluster.get(), creds_v1_no_creds().c_str()));
577   cluster.with_credentials("cassandra", "cassandra");
578   connect(cluster);
579 }
580 
581 /**
582  * Attempt connection to DBaaS SNI single endpoint docker image leaving auth unset.
583  *
584  * This test will perform a connection to a DBaaS SNI single endpoint while ensuring proper
585  * automatic cloud configuration with address resolution where the authentication is not set.
586  *
587  * @jira_ticket CPP-787
588  * @test_category dbaas
589  * @since 2.14.0
590  * @expected_result Failed to establish a connection.
591  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,InvalidWithoutCreds)592 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidWithoutCreds) {
593   CHECK_FAILURE;
594 
595   Cluster cluster = default_cluster(false);
596   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
597                          cluster.get(), creds_v1_no_creds().c_str()));
598   try {
599     connect(cluster);
600     EXPECT_TRUE(false) << "Connection established";
601   } catch (Session::Exception& se) {
602     EXPECT_EQ(CASS_ERROR_SERVER_BAD_CREDENTIALS, se.error_code());
603   }
604 }
605 
606 /**
607  * Attempt connection to DBaaS SNI single endpoint docker image using invalid metadata server.
608  *
609  * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid metadata
610  * server. The connection should not succeed as no resolution will be possible.
611  *
612  * @jira_ticket CPP-787
613  * @test_category dbaas
614  * @since 2.14.0
615  * @expected_result Failed to establish a connection.
616  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,InvalidMetadataServer)617 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidMetadataServer) {
618   CHECK_FAILURE;
619 
620   Cluster cluster = default_cluster(false);
621   EXPECT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
622                          cluster.get(), creds_v1_unreachable().c_str()));
623   try {
624     connect(cluster);
625     EXPECT_TRUE(false) << "Connection established";
626   } catch (Session::Exception& se) {
627     EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code());
628   }
629 }
630 
631 /**
632  * Attempt connection to DBaaS SNI single endpoint docker image using invalid certificate.
633  *
634  * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid certificate.
635  * The connection should not succeed as no resolution will be possible.
636  *
637  * @jira_ticket CPP-787
638  * @test_category dbaas
639  * @since 2.14.0
640  * @expected_result Failed to establish a connection.
641  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,InvalidCertificate)642 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidCertificate) {
643   CHECK_FAILURE;
644 
645   Cluster cluster = default_cluster(false);
646   EXPECT_EQ(CASS_ERROR_LIB_BAD_PARAMS,
647             cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
648                 cluster.get(), creds_v1_no_cert().c_str()));
649   try {
650     connect(cluster);
651     EXPECT_TRUE(false) << "Connection established";
652   } catch (Session::Exception& se) {
653     EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code());
654   }
655 }
656 
657 /**
658  * Attempt connection to DBaaS SNI single endpoint docker image using invalid CA.
659  *
660  * This test will attempt a connection to a DBaaS SNI single endpoint using an invalid CA. The
661  * connection should not succeed as no resolution will be possible.
662  *
663  * @jira_ticket CPP-787
664  * @test_category dbaas
665  * @since 2.14.0
666  * @expected_result Failed to establish a connection.
667  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,InvalidCertificateAuthority)668 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, InvalidCertificateAuthority) {
669   CHECK_FAILURE;
670 
671   Cluster cluster = default_cluster(false);
672   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
673                          cluster.get(), creds_v1_invalid_ca().c_str()));
674   try {
675     connect(cluster);
676     EXPECT_TRUE(false) << "Connection established";
677   } catch (Session::Exception& se) {
678     EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, se.error_code());
679   }
680 }
681 
682 /**
683  * Perform query with nodes down against the DBaaS SNI single endpoint docker image.
684  *
685  * This test will perform a connection and execute a simple statement query against the
686  * system.local table to ensure query execution to a DBaaS SNI single endpoint while validating the
687  * results.
688  *
689  * @jira_ticket CPP-787
690  * @test_category dbaas
691  * @test_category queries
692  * @since 2.14.0
693  * @expected_result Simple statement is executed and validated while node(s) are down.
694  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,QueryWithNodesDown)695 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, QueryWithNodesDown) {
696   CHECK_FAILURE;
697 
698   ServerNames server_names = get_server_names();
699 
700   Cluster cluster = default_cluster(false);
701   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
702                          cluster.get(), creds_v1().c_str()));
703   connect(cluster);
704 
705   EXPECT_TRUE(stop_node(1));
706   for (int i = 0; i < 8; ++i) {
707     EXPECT_NE(server_names[1], session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).server_name());
708   }
709 
710   EXPECT_TRUE(stop_node(3));
711   for (int i = 0; i < 8; ++i) {
712     EXPECT_EQ(server_names[2], session_.execute(SELECT_ALL_SYSTEM_LOCAL_CQL).server_name());
713   }
714 
715   EXPECT_TRUE(start_cluster());
716 }
717 
718 /**
719  * Ensure reconnection occurs during full outage.
720  *
721  * This test will perform a connection, full outage will occur and the the cluster will be restarted
722  * while executing a simple statement query against the system.local table to ensure reconnection
723  * after full outage.
724  *
725  * @jira_ticket CPP-787
726  * @test_category dbaas
727  * @test_category queries
728  * @since 2.14.0
729  * @expected_result Simple statement is executed and validated after full outage.
730  */
CASSANDRA_INTEGRATION_TEST_F(DbaasTests,FullOutage)731 CASSANDRA_INTEGRATION_TEST_F(DbaasTests, FullOutage) {
732   CHECK_FAILURE;
733 
734   ServerNames server_names = get_server_names();
735 
736   Cluster cluster = default_cluster(false).with_constant_reconnect(10); // Quick reconnect
737   ASSERT_EQ(CASS_OK, cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(
738                          cluster.get(), creds_v1().c_str()));
739   connect(cluster);
740 
741   EXPECT_TRUE(stop_cluster());
742 
743   Statement statement(SELECT_ALL_SYSTEM_LOCAL_CQL);
744   EXPECT_EQ(CASS_ERROR_LIB_NO_HOSTS_AVAILABLE, session_.execute(statement, false).error_code());
745 
746   EXPECT_TRUE(start_cluster());
747   EXPECT_EQ(CASS_OK, session_.execute(statement).error_code());
748 }
749