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