Skip to content

Commit 644d017

Browse files
authored
Cloud example and fixes (#282)
* Add example for cloud (CaaS) * Fix crash when the socket times out during connection * Handle `SSL_ERROR_ZERO_RETURN` properly * Initialize the SSL library when using cloud * Fix cloud `local_dc` issue where `ControlConnection::local_dc()` is used, but is never set * Remove authentication using cloud secure connect bundle * Remove secure bundle authentication from unit tests
1 parent 2e7e57f commit 644d017

13 files changed

Lines changed: 188 additions & 50 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cloud
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
cmake_minimum_required(VERSION 2.6.4)
2+
3+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ".")
4+
set(PROJECT_EXAMPLE_NAME cloud)
5+
6+
file(GLOB EXAMPLE_SRC_FILES ${CASS_ROOT_DIR}/examples/cloud/*.c)
7+
include_directories(${INCLUDES})
8+
add_executable(${PROJECT_EXAMPLE_NAME} ${EXAMPLE_SRC_FILES})
9+
target_link_libraries(${PROJECT_EXAMPLE_NAME} ${PROJECT_LIB_NAME_TARGET} ${CASS_LIBS})
10+
add_dependencies(${PROJECT_EXAMPLE_NAME} ${PROJECT_LIB_NAME_TARGET})
11+
12+
set_property(TARGET ${PROJECT_EXAMPLE_NAME} PROPERTY FOLDER "Examples")

cpp-driver/examples/cloud/cloud.c

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*
2+
This is free and unencumbered software released into the public domain.
3+
4+
Anyone is free to copy, modify, publish, use, compile, sell, or
5+
distribute this software, either in source code form or as a compiled
6+
binary, for any purpose, commercial or non-commercial, and by any
7+
means.
8+
9+
In jurisdictions that recognize copyright laws, the author or authors
10+
of this software dedicate any and all copyright interest in the
11+
software to the public domain. We make this dedication for the benefit
12+
of the public at large and to the detriment of our heirs and
13+
successors. We intend this dedication to be an overt act of
14+
relinquishment in perpetuity of all present and future rights to this
15+
software under copyright law.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
OTHER DEALINGS IN THE SOFTWARE.
24+
25+
For more information, please refer to <http://unlicense.org/>
26+
*/
27+
28+
#include <cassandra.h>
29+
#include <stdio.h>
30+
31+
int main(int argc, char* argv[]) {
32+
/* Setup and connect to cluster */
33+
CassFuture* connect_future = NULL;
34+
CassCluster* cluster;
35+
CassSession* session;
36+
37+
const char* secure_connect_bundle;
38+
const char* username;
39+
const char* password;
40+
41+
if (argc < 4) {
42+
fprintf(stderr, "Usage: %s <secure connect bundle zip> <username> <password>\n", argv[0]);
43+
return 1;
44+
}
45+
46+
secure_connect_bundle = argv[1];
47+
username = argv[2];
48+
password = argv[3];
49+
50+
cluster = cass_cluster_new();
51+
session = cass_session_new();
52+
53+
/* Setup driver to connect to the cloud using the secure connection bundle */
54+
if (cass_cluster_set_cloud_secure_connection_bundle(cluster, secure_connect_bundle) != CASS_OK) {
55+
fprintf(stderr, "Unable to configure cloud using the secure connection bundle: %s\n",
56+
secure_connect_bundle);
57+
}
58+
59+
cass_cluster_set_credentials(cluster, username, password);
60+
61+
/* Provide the cluster object as configuration to connect the session */
62+
connect_future = cass_session_connect(session, cluster);
63+
64+
if (cass_future_error_code(connect_future) == CASS_OK) {
65+
/* Build statement and execute query */
66+
const char* query = "SELECT release_version FROM system.local";
67+
CassStatement* statement = cass_statement_new(query, 0);
68+
69+
CassFuture* result_future = cass_session_execute(session, statement);
70+
71+
if (cass_future_error_code(result_future) == CASS_OK) {
72+
/* Retrieve result set and get the first row */
73+
const CassResult* result = cass_future_get_result(result_future);
74+
const CassRow* row = cass_result_first_row(result);
75+
76+
if (row) {
77+
const CassValue* value = cass_row_get_column_by_name(row, "release_version");
78+
79+
const char* release_version;
80+
size_t release_version_length;
81+
cass_value_get_string(value, &release_version, &release_version_length);
82+
printf("release_version: '%.*s'\n", (int)release_version_length, release_version);
83+
}
84+
85+
cass_result_free(result);
86+
} else {
87+
/* Handle error */
88+
const char* message;
89+
size_t message_length;
90+
cass_future_error_message(result_future, &message, &message_length);
91+
fprintf(stderr, "Unable to run query: '%.*s'\n", (int)message_length, message);
92+
}
93+
94+
cass_statement_free(statement);
95+
cass_future_free(result_future);
96+
} else {
97+
/* Handle error */
98+
const char* message;
99+
size_t message_length;
100+
cass_future_error_message(connect_future, &message, &message_length);
101+
fprintf(stderr, "Unable to connect: '%.*s'\n", (int)message_length, message);
102+
}
103+
104+
cass_future_free(connect_future);
105+
cass_cluster_free(cluster);
106+
cass_session_free(session);
107+
108+
return 0;
109+
}

cpp-driver/gtests/src/unit/tests/test_cloud_secure_connect_config.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ using datastax::internal::core::ClusterMetadataResolver;
6161
using datastax::internal::core::ClusterSettings;
6262
using datastax::internal::core::Config;
6363
using datastax::internal::core::HttpClient;
64-
using datastax::internal::enterprise::DsePlainTextAuthProvider;
6564
using datastax::internal::json::StringBuffer;
6665
using datastax::internal::json::Writer;
6766

@@ -122,10 +121,6 @@ class CloudSecureConnectionConfigTest : public HttpTest {
122121
int port = 1443) {
123122
Writer<StringBuffer> writer(buffer);
124123
writer.StartObject();
125-
writer.Key("username");
126-
writer.String("DataStax");
127-
writer.Key("password");
128-
writer.String("Constellation");
129124
writer.Key("host");
130125
writer.String(host.c_str());
131126
writer.Key("port");
@@ -171,8 +166,6 @@ TEST_F(CloudSecureConnectionConfigTest, CredsV1) {
171166
create_zip_file(buffer.GetString());
172167

173168
EXPECT_TRUE(cloud_config.load(creds_zip_file(), &config));
174-
EXPECT_EQ("DataStax", cloud_config.username());
175-
EXPECT_EQ("Constellation", cloud_config.password());
176169
EXPECT_EQ("cloud.datastax.com", cloud_config.host());
177170
EXPECT_EQ(1443, cloud_config.port());
178171
EXPECT_EQ("database_as_a_service", cloud_config.keyspace());
@@ -181,7 +174,6 @@ TEST_F(CloudSecureConnectionConfigTest, CredsV1) {
181174
EXPECT_EQ(key(), cloud_config.key());
182175

183176
EXPECT_TRUE(config.ssl_context());
184-
EXPECT_TRUE(dynamic_cast<DsePlainTextAuthProvider*>(config.auth_provider().get()) != NULL);
185177
}
186178

187179
TEST_F(CloudSecureConnectionConfigTest, CredsV1WithoutCreds) {
@@ -201,8 +193,6 @@ TEST_F(CloudSecureConnectionConfigTest, CredsV1WithoutCreds) {
201193
create_zip_file(buffer.GetString());
202194

203195
EXPECT_TRUE(cloud_config.load(creds_zip_file(), &config));
204-
EXPECT_EQ("", cloud_config.username());
205-
EXPECT_EQ("", cloud_config.password());
206196
EXPECT_EQ("bigdata.datastax.com", cloud_config.host());
207197
EXPECT_EQ(2443, cloud_config.port());
208198
EXPECT_EQ("datastax", cloud_config.keyspace());
@@ -211,8 +201,6 @@ TEST_F(CloudSecureConnectionConfigTest, CredsV1WithoutCreds) {
211201
EXPECT_EQ(key(), cloud_config.key());
212202

213203
EXPECT_TRUE(config.ssl_context());
214-
EXPECT_TRUE(dynamic_cast<DsePlainTextAuthProvider*>(config.auth_provider().get()) ==
215-
NULL); // Not configured
216204
}
217205

218206
TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingHost) {
@@ -221,10 +209,6 @@ TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingHost) {
221209
StringBuffer buffer;
222210
Writer<StringBuffer> writer(buffer);
223211
writer.StartObject();
224-
writer.Key("username");
225-
writer.String("DataStax");
226-
writer.Key("password");
227-
writer.String("Constellation");
228212
writer.Key("port");
229213
writer.Int(1443);
230214
writer.Key("keyspace");
@@ -241,10 +225,6 @@ TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingPort) {
241225
StringBuffer buffer;
242226
Writer<StringBuffer> writer(buffer);
243227
writer.StartObject();
244-
writer.Key("username");
245-
writer.String("DataStax");
246-
writer.Key("password");
247-
writer.String("Constellation");
248228
writer.Key("host");
249229
writer.String("cloud.datastax.com");
250230
writer.Key("keyspace");
@@ -261,10 +241,6 @@ TEST_F(CloudSecureConnectionConfigTest, InvalidCredsV1ConfigMissingKeyspace) {
261241
StringBuffer buffer;
262242
Writer<StringBuffer> writer(buffer);
263243
writer.StartObject();
264-
writer.Key("username");
265-
writer.String("DataStax");
266-
writer.Key("password");
267-
writer.String("Constellation");
268244
writer.Key("host");
269245
writer.String("cloud.datastax.com");
270246
writer.Key("port");

cpp-driver/include/cassandra.h

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,8 @@ typedef enum CassErrorSource_ {
700700
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_NO_PEER_CERT, 3, "No peer certificate") \
701701
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_INVALID_PEER_CERT, 4, "Invalid peer certificate") \
702702
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_IDENTITY_MISMATCH, 5, "Certificate does not match host or IP address") \
703-
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_PROTOCOL_ERROR, 6, "Protocol error")
703+
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_PROTOCOL_ERROR, 6, "Protocol error") \
704+
XX(CASS_ERROR_SOURCE_SSL, CASS_ERROR_SSL_CLOSED, 7, "Connection closed")
704705

705706
/* @cond IGNORE */
706707
#define CASS_ERROR_MAP CASS_ERROR_MAPPING /* Deprecated */
@@ -2771,12 +2772,14 @@ cass_cluster_set_host_listener_callback(CassCluster* cluster,
27712772
*/
27722773
CASS_EXPORT CassError
27732774
cass_cluster_set_cloud_secure_connection_bundle(CassCluster* cluster,
2774-
const char*path);
2775+
const char* path);
27752776

27762777
/**
27772778
* Same as cass_cluster_set_cloud_secure_connection_bundle(), but with lengths
27782779
* for string parameters.
27792780
*
2781+
* @see cass_cluster_set_cloud_secure_connection_bundle()
2782+
*
27802783
* @param[in] cluster
27812784
* @param[in] path Absolute path to DBaaS credentials file.
27822785
* @param[in] path_length Length of path variable.
@@ -2787,6 +2790,41 @@ cass_cluster_set_cloud_secure_connection_bundle_n(CassCluster* cluster,
27872790
const char* path,
27882791
size_t path_length);
27892792

2793+
/**
2794+
* Same as cass_cluster_set_cloud_secure_connection_bundle(), but it does not
2795+
* initialize the underlying SSL library implementation. The SSL library still
2796+
* needs to be initialized, but it's up to the client application to handle
2797+
* initialization. This is similar to the function cass_ssl_new_no_lib_init(),
2798+
* and its documentation should be used as a reference to properly initialize
2799+
* the underlying SSL library.
2800+
*
2801+
* @see cass_ssl_new_no_lib_init()
2802+
* @see cass_cluster_set_cloud_secure_connection_bundle()
2803+
*
2804+
* @param[in] cluster
2805+
* @param[in] path Absolute path to DBaaS credentials file.
2806+
* @return CASS_OK if successful, otherwise error occured.
2807+
*/
2808+
CASS_EXPORT CassError
2809+
cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(CassCluster* cluster,
2810+
const char* path);
2811+
2812+
/**
2813+
* Same as cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(),
2814+
* but with lengths for string parameters.
2815+
*
2816+
* @see cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init()
2817+
*
2818+
* @param[in] cluster
2819+
* @param[in] path Absolute path to DBaaS credentials file.
2820+
* @param[in] path_length Length of path variable.
2821+
* @return CASS_OK if successful, otherwise error occured.
2822+
*/
2823+
CASS_EXPORT CassError
2824+
cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init_n(CassCluster* cluster,
2825+
const char* path,
2826+
size_t path_length);
2827+
27902828
/***********************************************************************************
27912829
*
27922830
* Session

cpp-driver/src/cloud_secure_connection_config.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -246,18 +246,6 @@ bool CloudSecureConnectionConfig::load(const String& filename, Config* config /*
246246
return false;
247247
}
248248

249-
if (document.HasMember("username") && document["username"].IsString()) {
250-
username_ = document["username"].GetString();
251-
}
252-
if (document.HasMember("password") && document["password"].IsString()) {
253-
password_ = document["password"].GetString();
254-
}
255-
256-
if (config && (!username_.empty() || !password_.empty())) {
257-
config->set_auth_provider(
258-
AuthProvider::Ptr(new enterprise::DsePlainTextAuthProvider(username_, password_, "")));
259-
}
260-
261249
if (!document.HasMember("host") || !document["host"].IsString()) {
262250
LOG_ERROR(CLOUD_ERROR "Missing host");
263251
return false;

cpp-driver/src/cloud_secure_connection_config.hpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ class CloudSecureConnectionConfig {
3030
bool load(const String& filename, Config* config = NULL);
3131
bool is_loaded() const { return is_loaded_; }
3232

33-
const String& username() const { return username_; }
34-
const String& password() const { return password_; }
3533
const String& host() const { return host_; }
3634
int port() const { return port_; }
3735
const String& keyspace() const { return keyspace_; }
@@ -42,8 +40,6 @@ class CloudSecureConnectionConfig {
4240

4341
private:
4442
bool is_loaded_;
45-
String username_;
46-
String password_;
4743
String host_;
4844
int port_;
4945
String keyspace_;

cpp-driver/src/cluster_config.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,19 @@ CassError cass_cluster_set_cloud_secure_connection_bundle(CassCluster* cluster,
485485

486486
CassError cass_cluster_set_cloud_secure_connection_bundle_n(CassCluster* cluster, const char* path,
487487
size_t path_length) {
488+
SslContextFactory::init_once();
489+
return cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init_n(cluster, path,
490+
path_length);
491+
}
492+
493+
CassError cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init(CassCluster* cluster,
494+
const char* path) {
495+
return cass_cluster_set_cloud_secure_connection_bundle_n(cluster, path, SAFE_STRLEN(path));
496+
}
497+
498+
CassError cass_cluster_set_cloud_secure_connection_bundle_no_ssl_lib_init_n(CassCluster* cluster,
499+
const char* path,
500+
size_t path_length) {
488501
if (!cluster->config().set_cloud_secure_connection_bundle(String(path, path_length))) {
489502
return CASS_ERROR_LIB_BAD_PARAMS;
490503
}

cpp-driver/src/cluster_connector.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ void ClusterConnector::on_connect(ControlConnector* connector) {
230230
for (LoadBalancingPolicy::Vec::const_iterator it = policies.begin(), end = policies.end();
231231
it != end; ++it) {
232232
LoadBalancingPolicy::Ptr policy(*it);
233-
policy->init(connected_host, hosts, random_, connector->local_dc());
233+
policy->init(connected_host, hosts, random_, local_dc_);
234234
policy->register_handles(event_loop_->loop());
235235
}
236236

cpp-driver/src/control_connector.hpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,6 @@ class ControlConnector
153153

154154
const ProtocolVersion protocol_version() const { return connector_->protocol_version(); }
155155

156-
const String& local_dc() const { return local_dc_; }
157-
158156
bool is_ok() const { return error_code_ == CONTROL_CONNECTION_OK; }
159157
bool is_canceled() const { return error_code_ == CONTROL_CONNECTION_CANCELED; }
160158
bool is_invalid_protocol() const {
@@ -220,7 +218,6 @@ class ControlConnector
220218

221219
ControlConnectionListener* listener_;
222220
Metrics* metrics_;
223-
String local_dc_;
224221
Host::Ptr host_;
225222
ControlConnectionSettings settings_;
226223
};

0 commit comments

Comments
 (0)