1+ diff --git a/driver-core/src/test/java/com/datastax/driver/core/CCMAccess.java b/driver-core/src/test/java/com/datastax/driver/core/CCMAccess.java
2+ index b6714f07f..8a91c6fb1 100644
3+ --- a/driver-core/src/test/java/com/datastax/driver/core/CCMAccess.java
4+ +++ b/driver-core/src/test/java/com/datastax/driver/core/CCMAccess.java
5+ @@ -54,6 +54,13 @@ public interface CCMAccess extends Closeable {
6+ */
7+ VersionNumber getDSEVersion();
8+
9+ + /**
10+ + * Returns the Scylla version of this CCM cluster if this is a Scylla cluster, otherwise null.
11+ + *
12+ + * @return The version of this CCM cluster.
13+ + */
14+ + VersionNumber getScyllaVersion();
15+ +
16+ /** @return The config directory for this CCM cluster. */
17+ File getCcmDir();
18+
119diff --git a/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java b/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
2- index 4c9ba61fc5..3d0f4fa946 100644
20+ index 4c9ba61fc..62f674a88 100644
321--- a/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
422+++ b/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java
5- @@ -206,7 +206,7 @@ public class CCMBridge implements CCMAccess {
23+ @@ -194,6 +194,7 @@ public class CCMBridge implements CCMAccess {
24+ static {
25+ String inputCassandraVersion = System.getProperty("cassandra.version");
26+ String inputScyllaVersion = System.getProperty("scylla.version");
27+ + GLOBAL_SCYLLA_VERSION_NUMBER = parseScyllaInputVersion(inputScyllaVersion);
28+
29+ String installDirectory = System.getProperty("cassandra.directory");
30+ String branch = System.getProperty("cassandra.branch");
31+ @@ -206,8 +207,11 @@ public class CCMBridge implements CCMAccess {
632 installArgs.add("-v git:" + branch.trim().replaceAll("\"", ""));
733 } else if (inputScyllaVersion != null && !inputScyllaVersion.trim().isEmpty()) {
834 installArgs.add(" --scylla ");
935- installArgs.add("-v release:" + inputScyllaVersion);
10- + installArgs.add("-v " + inputScyllaVersion);
11-
36+ -
37+ + if (isVersionNumber(inputScyllaVersion)) {
38+ + installArgs.add("-v release:" + inputScyllaVersion);
39+ + } else {
40+ + installArgs.add("-v " + inputScyllaVersion);
41+ + }
1242 // Detect Scylla Enterprise - it should start with
1343 // a 4-digit year.
14- @@ -246,7 +246,8 @@ public class CCMBridge implements CCMAccess {
44+ if (inputScyllaVersion.matches("\\d{4}\\..*")) {
45+ @@ -246,8 +250,6 @@ public class CCMBridge implements CCMAccess {
1546 }
1647 ENVIRONMENT_MAP = ImmutableMap.copyOf(envMap);
1748
1849- GLOBAL_SCYLLA_VERSION_NUMBER = VersionNumber.parse(inputScyllaVersion);
19- + GLOBAL_SCYLLA_VERSION_NUMBER =
20- + VersionNumber.parse("5.2"); // VersionNumber.parse(inputScyllaVersion);
21-
50+ -
2251 if (isDse()) {
2352 GLOBAL_DSE_VERSION_NUMBER = VersionNumber.parse(inputCassandraVersion);
53+ GLOBAL_CASSANDRA_VERSION_NUMBER = CCMBridge.getCassandraVersion(GLOBAL_DSE_VERSION_NUMBER);
54+ @@ -338,12 +340,36 @@ public class CCMBridge implements CCMAccess {
55+ return osName != null && osName.startsWith("Windows");
56+ }
57+
58+ + private static boolean isVersionNumber(String versionString) {
59+ + try {
60+ + VersionNumber.parse(versionString);
61+ + } catch (IllegalArgumentException e) {
62+ + return false;
63+ + }
64+ + return true;
65+ + }
66+ +
67+ + private static VersionNumber parseScyllaInputVersion(String versionString) {
68+ + VersionNumber parsedScyllaVersionNumber = null;
69+ + try {
70+ + parsedScyllaVersionNumber = VersionNumber.parse(versionString);
71+ + } catch (IllegalArgumentException e) {
72+ + logger.warn(
73+ + "Failed to parse scylla.version: " + versionString + ". Trying to get it through CCM.",
74+ + e);
75+ + parsedScyllaVersionNumber = getScyllaVersionThroughCcm(versionString);
76+ + }
77+ + return parsedScyllaVersionNumber;
78+ + }
79+ +
80+ private final String clusterName;
81+
82+ private final VersionNumber cassandraVersion;
83+
84+ private final VersionNumber dseVersion;
85+
86+ + private final VersionNumber scyllaVersion;
87+ +
88+ private final int storagePort;
89+
90+ private final int thriftPort;
91+ @@ -378,6 +404,7 @@ public class CCMBridge implements CCMAccess {
92+ String clusterName,
93+ VersionNumber cassandraVersion,
94+ VersionNumber dseVersion,
95+ + VersionNumber scyllaVersion,
96+ String ipPrefix,
97+ int storagePort,
98+ int thriftPort,
99+ @@ -391,6 +418,7 @@ public class CCMBridge implements CCMAccess {
100+ this.clusterName = clusterName;
101+ this.cassandraVersion = cassandraVersion;
102+ this.dseVersion = dseVersion;
103+ + this.scyllaVersion = scyllaVersion;
104+ this.ipPrefix = ipPrefix;
105+ this.storagePort = storagePort;
106+ this.thriftPort = thriftPort;
107+ @@ -465,6 +493,11 @@ public class CCMBridge implements CCMAccess {
108+ return dseVersion;
109+ }
110+
111+ + @Override
112+ + public VersionNumber getScyllaVersion() {
113+ + return scyllaVersion;
114+ + }
115+ +
116+ @Override
117+ public File getCcmDir() {
118+ return ccmDir;
119+ @@ -792,7 +825,25 @@ public class CCMBridge implements CCMAccess {
120+ execute(CCM_COMMAND + " node%d setworkload %s", node, workloadStr);
121+ }
122+
123+ - private String execute(String command, Object... args) {
124+ + private static VersionNumber getScyllaVersionThroughCcm(String versionString) {
125+ + File configDir = Files.createTempDir();
126+ + try {
127+ + execute(configDir, "ccm create get_version -n 1 --scylla --version %s", versionString);
128+ + String versionOutput = execute(configDir, "ccm node1 versionfrombuild");
129+ + return VersionNumber.parse(versionOutput.replace("ccmout> ", "").trim());
130+ + } catch (RuntimeException cause) {
131+ + throw new RuntimeException(
132+ + "Error during getting Scylla version through ccm commands.", cause);
133+ + } finally {
134+ + try {
135+ + execute(configDir, "ccm remove get_version");
136+ + } catch (Exception ignored) {
137+ + }
138+ + }
139+ + }
140+ +
141+ + private static String execute(File ccmDir, String command, Object... args) {
142+ + Logger logger = CCMBridge.logger;
143+ String fullCommand = String.format(command, args) + " --config-dir=" + ccmDir;
144+ Closer closer = Closer.create();
145+ // 10 minutes timeout
146+ @@ -856,6 +907,10 @@ public class CCMBridge implements CCMAccess {
147+ return sw.toString();
148+ }
149+
150+ + private String execute(String command, Object... args) {
151+ + return execute(this.ccmDir, command, args);
152+ + }
153+ +
154+ /**
155+ * Waits for a host to be up by pinging the TCP socket directly, without using the Java driver's
156+ * API.
157+ @@ -949,10 +1004,12 @@ public class CCMBridge implements CCMAccess {
158+ private static final Pattern RANDOM_PORT_PATTERN = Pattern.compile(RANDOM_PORT);
159+
160+ private String ipPrefix = TestUtils.IP_PREFIX;
161+ + private String providedClusterName = null;
162+ int[] nodes = {1};
163+ private int[] jmxPorts = {};
164+ private boolean start = true;
165+ private boolean dse = isDse();
166+ + private boolean scylla = GLOBAL_SCYLLA_VERSION_NUMBER != null;
167+ private boolean startSniProxy = false;
168+ private VersionNumber version = null;
169+ private final Set<String> createOptions = new LinkedHashSet<String>();
170+ @@ -991,6 +1048,15 @@ public class CCMBridge implements CCMAccess {
171+ return this;
172+ }
173+
174+ + /**
175+ + * Builder takes care of naming and numbering clusters on its own. Use if you really need a
176+ + * specific name
177+ + */
178+ + public Builder withClusterName(String clusterName) {
179+ + this.providedClusterName = clusterName;
180+ + return this;
181+ + }
182+ +
183+ /** Enables SSL encryption. */
184+ public Builder withSSL() {
185+ cassandraConfiguration.put("client_encryption_options.enabled", "true");
186+ @@ -1035,8 +1101,8 @@ public class CCMBridge implements CCMAccess {
187+ }
188+
189+ /**
190+ - * The Cassandra or DSE version to use. If not specified the globally configured version is used
191+ - * instead.
192+ + * The Cassandra or DSE or Scylla version to use. If not specified the globally configured
193+ + * version is used instead.
194+ */
195+ public Builder withVersion(VersionNumber version) {
196+ this.version = version;
197+ @@ -1049,6 +1115,12 @@ public class CCMBridge implements CCMAccess {
198+ return this;
199+ }
200+
201+ + /** Indicates whether or not this cluster is meant to be a Scylla cluster. */
202+ + public Builder withScylla(boolean scylla) {
203+ + this.scylla = scylla;
204+ + return this;
205+ + }
206+ +
207+ /**
208+ * Free-form options that will be added at the end of the {@code ccm create} command (defaults
209+ * to {@link #CASSANDRA_INSTALL_ARGS} if this is never called).
210+ @@ -1115,19 +1187,30 @@ public class CCMBridge implements CCMAccess {
211+ // be careful NOT to alter internal state (hashCode/equals) during build!
212+ String clusterName = TestUtils.generateIdentifier("ccm_");
213+
214+ + if (providedClusterName != null) clusterName = providedClusterName;
215+ +
216+ VersionNumber dseVersion;
217+ VersionNumber cassandraVersion;
218+ + VersionNumber scyllaVersion;
219+ boolean versionConfigured = this.version != null;
220+ // No version was explicitly provided, fallback on global config.
221+ if (!versionConfigured) {
222+ + scyllaVersion = GLOBAL_SCYLLA_VERSION_NUMBER;
223+ dseVersion = GLOBAL_DSE_VERSION_NUMBER;
224+ cassandraVersion = GLOBAL_CASSANDRA_VERSION_NUMBER;
225+ } else if (dse) {
226+ // given version is the DSE version, base cassandra version on DSE version.
227+ + scyllaVersion = null;
228+ dseVersion = this.version;
229+ cassandraVersion = getCassandraVersion(dseVersion);
230+ + } else if (scylla) {
231+ + scyllaVersion = this.version;
232+ + dseVersion = null;
233+ + // Versions from 5.1 to 6.2.0 seem to report release_version 3.0.8 in system_local
234+ + cassandraVersion = VersionNumber.parse("3.0.8");
235+ } else {
236+ // given version is cassandra version.
237+ + scyllaVersion = null;
238+ dseVersion = null;
239+ cassandraVersion = this.version;
240+ }
241+ @@ -1182,6 +1265,7 @@ public class CCMBridge implements CCMAccess {
242+ clusterName,
243+ cassandraVersion,
244+ dseVersion,
245+ + scyllaVersion,
246+ ipPrefix,
247+ storagePort,
248+ thriftPort,
249+ @@ -1391,6 +1475,7 @@ public class CCMBridge implements CCMAccess {
250+
251+ if (ipPrefix != builder.ipPrefix) return false;
252+ if (dse != builder.dse) return false;
253+ + if (scylla != builder.scylla) return false;
254+ if (!Arrays.equals(nodes, builder.nodes)) return false;
255+ if (version != null ? !version.equals(builder.version) : builder.version != null)
256+ return false;
257+ diff --git a/driver-core/src/test/java/com/datastax/driver/core/CCMCache.java b/driver-core/src/test/java/com/datastax/driver/core/CCMCache.java
258+ index 332907355..254e2cae1 100644
259+ --- a/driver-core/src/test/java/com/datastax/driver/core/CCMCache.java
260+ +++ b/driver-core/src/test/java/com/datastax/driver/core/CCMCache.java
261+ @@ -64,6 +64,11 @@ public class CCMCache {
262+ return ccm.getDSEVersion();
263+ }
264+
265+ + @Override
266+ + public VersionNumber getScyllaVersion() {
267+ + return ccm.getScyllaVersion();
268+ + }
269+ +
270+ @Override
271+ public File getCcmDir() {
272+ return ccm.getCcmDir();
273+ diff --git a/driver-core/src/test/java/com/datastax/driver/core/CCMTestsSupport.java b/driver-core/src/test/java/com/datastax/driver/core/CCMTestsSupport.java
274+ index 6200fc233..a02efd416 100644
275+ --- a/driver-core/src/test/java/com/datastax/driver/core/CCMTestsSupport.java
276+ +++ b/driver-core/src/test/java/com/datastax/driver/core/CCMTestsSupport.java
277+ @@ -109,6 +109,11 @@ public class CCMTestsSupport {
278+ return delegate.getDSEVersion();
279+ }
280+
281+ + @Override
282+ + public VersionNumber getScyllaVersion() {
283+ + return delegate.getScyllaVersion();
284+ + }
285+ +
286+ @Override
287+ public InetSocketAddress addressOfNode(int n) {
288+ return delegate.addressOfNode(n);
24289diff --git a/driver-core/src/test/java/com/datastax/driver/core/ClusterStressTest.java b/driver-core/src/test/java/com/datastax/driver/core/ClusterStressTest.java
25- index 74befba297..e5867e5067 100644
290+ index 74befba29..e5867e506 100644
26291--- a/driver-core/src/test/java/com/datastax/driver/core/ClusterStressTest.java
27292+++ b/driver-core/src/test/java/com/datastax/driver/core/ClusterStressTest.java
28293@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
@@ -34,7 +299,7 @@ index 74befba297..e5867e5067 100644
34299
35300 private static final Logger logger = LoggerFactory.getLogger(ClusterStressTest.class);
36301diff --git a/driver-core/src/test/java/com/datastax/driver/core/HostConnectionPoolTest.java b/driver-core/src/test/java/com/datastax/driver/core/HostConnectionPoolTest.java
37- index f44e64dca0..aed5e394c6 100644
302+ index f44e64dca..aed5e394c 100644
38303--- a/driver-core/src/test/java/com/datastax/driver/core/HostConnectionPoolTest.java
39304+++ b/driver-core/src/test/java/com/datastax/driver/core/HostConnectionPoolTest.java
40305@@ -597,7 +597,7 @@ public class HostConnectionPoolTest extends ScassandraTestBase.PerClassCluster {
@@ -175,7 +440,7 @@ index f44e64dca0..aed5e394c6 100644
175440 Uninterruptibles.getUninterruptibly(request.requestInitialized, 10, TimeUnit.SECONDS);
176441 request.simulateSuccessResponse();
177442diff --git a/driver-core/src/test/java/com/datastax/driver/core/LoadBalancingPolicyBootstrapTest.java b/driver-core/src/test/java/com/datastax/driver/core/LoadBalancingPolicyBootstrapTest.java
178- index 09f8bf8edb..4f01cb47a6 100644
443+ index 09f8bf8ed..4f01cb47a 100644
179444--- a/driver-core/src/test/java/com/datastax/driver/core/LoadBalancingPolicyBootstrapTest.java
180445+++ b/driver-core/src/test/java/com/datastax/driver/core/LoadBalancingPolicyBootstrapTest.java
181446@@ -104,8 +104,16 @@ public class LoadBalancingPolicyBootstrapTest extends CCMTestsSupport {
@@ -197,7 +462,7 @@ index 09f8bf8edb..4f01cb47a6 100644
197462 try {
198463 cluster.init();
199464diff --git a/driver-core/src/test/java/com/datastax/driver/core/NettyOptionsTest.java b/driver-core/src/test/java/com/datastax/driver/core/NettyOptionsTest.java
200- index fbdf187a70..dd1c345929 100644
465+ index fbdf187a7..dd1c34592 100644
201466--- a/driver-core/src/test/java/com/datastax/driver/core/NettyOptionsTest.java
202467+++ b/driver-core/src/test/java/com/datastax/driver/core/NettyOptionsTest.java
203468@@ -45,7 +45,9 @@ import org.mockito.stubbing.Answer;
@@ -212,7 +477,7 @@ index fbdf187a70..dd1c345929 100644
212477
213478 @Test(groups = "short")
214479diff --git a/driver-core/src/test/java/com/datastax/driver/core/ReconnectionTest.java b/driver-core/src/test/java/com/datastax/driver/core/ReconnectionTest.java
215- index b3d6be0f3c..30fafa5273 100644
480+ index b3d6be0f3..30fafa527 100644
216481--- a/driver-core/src/test/java/com/datastax/driver/core/ReconnectionTest.java
217482+++ b/driver-core/src/test/java/com/datastax/driver/core/ReconnectionTest.java
218483@@ -24,6 +24,7 @@ package com.datastax.driver.core;
@@ -236,7 +501,7 @@ index b3d6be0f3c..30fafa5273 100644
236501 public void should_use_connection_from_reconnection_in_pool() {
237502 TogglabePolicy loadBalancingPolicy = new TogglabePolicy(new RoundRobinPolicy());
238503diff --git a/driver-core/src/test/java/com/datastax/driver/core/SessionLeakTest.java b/driver-core/src/test/java/com/datastax/driver/core/SessionLeakTest.java
239- index aa0f573795..6eaa74e318 100644
504+ index aa0f57379..6eaa74e31 100644
240505--- a/driver-core/src/test/java/com/datastax/driver/core/SessionLeakTest.java
241506+++ b/driver-core/src/test/java/com/datastax/driver/core/SessionLeakTest.java
242507@@ -19,6 +19,7 @@ import static com.datastax.driver.core.Assertions.assertThat;
@@ -260,7 +525,7 @@ index aa0f573795..6eaa74e318 100644
260525
261526 SocketChannelMonitor channelMonitor;
262527diff --git a/driver-core/src/test/java/com/datastax/driver/core/SessionStressTest.java b/driver-core/src/test/java/com/datastax/driver/core/SessionStressTest.java
263- index ea75f84544..ea70e9081a 100644
528+ index ea75f8454..ea70e9081 100644
264529--- a/driver-core/src/test/java/com/datastax/driver/core/SessionStressTest.java
265530+++ b/driver-core/src/test/java/com/datastax/driver/core/SessionStressTest.java
266531@@ -38,7 +38,9 @@ import org.slf4j.LoggerFactory;
@@ -274,3 +539,24 @@ index ea75f84544..ea70e9081a 100644
274539 public class SessionStressTest extends CCMTestsSupport {
275540
276541 private static final Logger logger = LoggerFactory.getLogger(SessionStressTest.class);
542+ diff --git a/driver-core/src/test/java/com/datastax/driver/core/StateListenerTest.java b/driver-core/src/test/java/com/datastax/driver/core/StateListenerTest.java
543+ index eb77f2bf2..ab574d8a4 100644
544+ --- a/driver-core/src/test/java/com/datastax/driver/core/StateListenerTest.java
545+ +++ b/driver-core/src/test/java/com/datastax/driver/core/StateListenerTest.java
546+ @@ -52,7 +52,15 @@ public class StateListenerTest extends CCMTestsSupport {
547+ ccm().start(1);
548+ listener.waitForEvent();
549+
550+ - listener.setExpectedEvent(REMOVE);
551+ + // Different expectation for Scylla versions since 6.0.0 and 2024.2, both included
552+ + VersionNumber scyllaVer = ccm().getScyllaVersion();
553+ + if (scyllaVer != null
554+ + && ((scyllaVer.getMajor() >= 6 && scyllaVer.getMajor() <= 9)
555+ + || (scyllaVer.getMajor() >= 2024 && scyllaVer.getMinor() >= 2))) {
556+ + listener.setExpectedEvent(DOWN);
557+ + } else {
558+ + listener.setExpectedEvent(REMOVE);
559+ + }
560+ ccm().decommission(2);
561+ listener.waitForEvent();
562+ }
0 commit comments