Skip to content

Commit 6de917f

Browse files
committed
test_scylla_cloud: add new tests for using cloud config bundle
* those test are using CCM sni_proxy code
1 parent 5d529e1 commit 6de917f

File tree

4 files changed

+105
-21
lines changed

4 files changed

+105
-21
lines changed

.github/workflows/integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ jobs:
1919

2020
- name: Test with pytest
2121
run: |
22-
./ci/run_integration_test.sh tests/integration/standard/test_authentication.py tests/integration/standard/test_cluster.py tests/integration/standard/test_concurrent.py tests/integration/standard/test_connection.py tests/integration/standard/test_control_connection.py tests/integration/standard/test_custom_payload.py tests/integration/standard/test_custom_protocol_handler.py tests/integration/standard/test_cython_protocol_handlers.py
22+
./ci/run_integration_test.sh tests/integration/standard/test_authentication.py tests/integration/standard/test_cluster.py tests/integration/standard/test_concurrent.py tests/integration/standard/test_connection.py tests/integration/standard/test_control_connection.py tests/integration/standard/test_custom_payload.py tests/integration/standard/test_custom_protocol_handler.py tests/integration/standard/test_cython_protocol_handlers.py tests/integration/standard/test_scylla_cloud.py
2323
# can't run this, cause only 2 cpus on github actions: tests/integration/standard/test_shard_aware.py

cassandra/cluster.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,14 +1160,15 @@ def __init__(self,
11601160
self.connection_class = connection_class
11611161

11621162
if scylla_cloud is not None:
1163-
if contact_points is not _NOT_SET or endpoint_factory or ssl_context or ssl_options:
1164-
raise ValueError("contact_points, endpoint_factory, ssl_context, and ssl_options "
1163+
if contact_points is not _NOT_SET or ssl_context or ssl_options:
1164+
raise ValueError("contact_points, ssl_context, and ssl_options "
11651165
"cannot be specified with a scylla cloud configuration")
11661166

11671167
uses_twisted = TwistedConnection and issubclass(self.connection_class, TwistedConnection)
11681168
uses_eventlet = EventletConnection and issubclass(self.connection_class, EventletConnection)
11691169

1170-
scylla_cloud_config = CloudConfiguration.create(scylla_cloud, pyopenssl=uses_twisted or uses_eventlet)
1170+
scylla_cloud_config = CloudConfiguration.create(scylla_cloud, pyopenssl=uses_twisted or uses_eventlet,
1171+
endpoint_factory=endpoint_factory)
11711172
ssl_context = scylla_cloud_config.ssl_context
11721173
endpoint_factory = scylla_cloud_config.endpoint_factory
11731174
contact_points = scylla_cloud_config.contact_points

cassandra/scylla/cloud.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class CloudConfiguration:
5959
ssl_context: SSLContext
6060
skip_tls_verify: bool
6161

62-
def __init__(self, configuration_file, pyopenssl=False):
62+
def __init__(self, configuration_file, pyopenssl=False, endpoint_factory=None):
6363
cloud_config = yaml.safe_load(open(configuration_file))
6464

6565
self.current_context = cloud_config['contexts'][cloud_config['currentContext']]
@@ -69,9 +69,13 @@ def __init__(self, configuration_file, pyopenssl=False):
6969
self.skip_tls_verify = self.auth_info.get('insecureSkipTLSVerify', False)
7070
self.ssl_context = self.create_pyopenssl_context() if pyopenssl else self.create_ssl_context()
7171

72-
proxy_address, port, node_domain = self.get_server(self.data_centers[self.current_context['datacenterName']],
73-
keys_order=['testServer', 'server'])
74-
self.endpoint_factory = SniEndPointFactory(proxy_address, port=int(port), node_domain=node_domain)
72+
proxy_address, port, node_domain = self.get_server(self.data_centers[self.current_context['datacenterName']])
73+
74+
if not endpoint_factory:
75+
endpoint_factory = SniEndPointFactory(proxy_address, port=int(port), node_domain=node_domain)
76+
else:
77+
assert isinstance(endpoint_factory, SniEndPointFactory)
78+
self.endpoint_factory = endpoint_factory
7579

7680
username, password = self.auth_info.get('username'), self.auth_info.get('password')
7781
if username and password:
@@ -86,17 +90,14 @@ def contact_points(self):
8690
_contact_points.append(self.endpoint_factory.create_from_sni(address))
8791
return _contact_points
8892

89-
def get_server(self, data_center, keys_order=None):
90-
keys_order = keys_order or ['server']
91-
for key in keys_order:
92-
address = data_center.get(key, '')
93-
if not address:
94-
continue
95-
address = address.split(":")
96-
port = nth(address, 1, default=443)
97-
address = nth(address, 0)
98-
node_domain = data_center.get('nodeDomain')
99-
return address, port, node_domain
93+
def get_server(self, data_center):
94+
address = data_center.get('server')
95+
address = address.split(":")
96+
port = nth(address, 1, default=443)
97+
address = nth(address, 0)
98+
node_domain = data_center.get('nodeDomain')
99+
assert address and port and node_domain, "server or nodeDomain are missing"
100+
return address, port, node_domain
100101

101102
def create_ssl_context(self):
102103
ssl_context = ssl.SSLContext(protocol=ssl.PROTOCOL_SSLv23)
@@ -138,5 +139,5 @@ def create_pyopenssl_context(self):
138139
return ssl_context
139140

140141
@classmethod
141-
def create(cls, configuration_file, pyopenssl=False):
142-
return cls(configuration_file, pyopenssl)
142+
def create(cls, configuration_file, pyopenssl=False, endpoint_factory=None):
143+
return cls(configuration_file, pyopenssl=pyopenssl, endpoint_factory=endpoint_factory)
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import os.path
2+
from unittest import TestCase
3+
from ccmlib.utils.ssl_utils import generate_ssl_stores
4+
from ccmlib.utils.sni_proxy import refresh_certs, get_cluster_info, start_sni_proxy, create_cloud_config
5+
6+
from tests.integration import use_cluster
7+
from cassandra.cluster import Cluster, TwistedConnection
8+
from cassandra.connection import SniEndPointFactory
9+
from cassandra.io.asyncorereactor import AsyncoreConnection
10+
from cassandra.io.libevreactor import LibevConnection
11+
from cassandra.io.geventreactor import GeventConnection
12+
from cassandra.io.eventletreactor import EventletConnection
13+
from cassandra.io.asyncioreactor import AsyncioConnection
14+
15+
supported_connection_classes = [AsyncoreConnection, LibevConnection, TwistedConnection]
16+
# need to run them with specific configuration like `gevent.monkey.patch_all()` or under async functions
17+
unsupported_connection_classes = [GeventConnection, AsyncioConnection, EventletConnection]
18+
19+
20+
class ScyllaCloudConfigTests(TestCase):
21+
def start_cluster_with_proxy(self):
22+
ccm_cluster = self.ccm_cluster
23+
generate_ssl_stores(ccm_cluster.get_path())
24+
ssl_port = 9142
25+
sni_port = 443
26+
ccm_cluster.set_configuration_options(dict(
27+
client_encryption_options=
28+
dict(require_client_auth=True,
29+
truststore=os.path.join(ccm_cluster.get_path(), 'ccm_node.cer'),
30+
certificate=os.path.join(ccm_cluster.get_path(), 'ccm_node.pem'),
31+
keyfile=os.path.join(ccm_cluster.get_path(), 'ccm_node.key'),
32+
enabled=True),
33+
native_transport_port_ssl=ssl_port))
34+
35+
ccm_cluster._update_config()
36+
37+
ccm_cluster.start(wait_for_binary_proto=True)
38+
39+
nodes_info = get_cluster_info(ccm_cluster, port=ssl_port)
40+
refresh_certs(ccm_cluster, nodes_info)
41+
42+
docker_id, listen_address, listen_port = \
43+
start_sni_proxy(ccm_cluster.get_path(), nodes_info=nodes_info, listen_port=sni_port)
44+
ccm_cluster.sni_proxy_docker_id = docker_id
45+
ccm_cluster.sni_proxy_listen_port = listen_port
46+
ccm_cluster._update_config()
47+
48+
config_data_yaml, config_path_yaml = create_cloud_config(ccm_cluster.get_path(), listen_port)
49+
50+
endpoint_factory = SniEndPointFactory(listen_address, port=int(listen_port),
51+
node_domain="cluster-id.scylla.com")
52+
53+
return config_data_yaml, config_path_yaml, endpoint_factory
54+
55+
def test_1_node_cluster(self):
56+
self.ccm_cluster = use_cluster("sni_proxy", [1], start=False)
57+
config_data_yaml, config_path_yaml, endpoint_factory = self.start_cluster_with_proxy()
58+
59+
for config in [config_path_yaml, config_data_yaml]:
60+
for connection_class in supported_connection_classes:
61+
cluster = Cluster(scylla_cloud=config, connection_class=connection_class,
62+
endpoint_factory=endpoint_factory)
63+
with cluster.connect() as session:
64+
res = session.execute("SELECT * FROM system.local")
65+
assert res.all()
66+
67+
assert len(cluster.metadata._hosts) == 1
68+
assert len(cluster.metadata._host_id_by_endpoint) == 1
69+
70+
def test_3_node_cluster(self):
71+
self.ccm_cluster = use_cluster("sni_proxy", [3], start=False)
72+
config_data_yaml, config_path_yaml, endpoint_factory = self.start_cluster_with_proxy()
73+
74+
for config in [config_path_yaml, config_data_yaml]:
75+
for connection_class in supported_connection_classes:
76+
cluster = Cluster(scylla_cloud=config, connection_class=connection_class,
77+
endpoint_factory=endpoint_factory)
78+
with cluster.connect() as session:
79+
res = session.execute("SELECT * FROM system.local")
80+
assert res.all()
81+
assert len(cluster.metadata._hosts) == 3
82+
assert len(cluster.metadata._host_id_by_endpoint) == 3

0 commit comments

Comments
 (0)