From 9e6813fb076471c36776927557c55686fcf707a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20B=C4=85czkowski?= Date: Tue, 3 Dec 2024 23:22:13 +0100 Subject: [PATCH] Allow additional `scylla.version` formats Modifies version parsing logic to try passing the `scylla.version` to the CCM when it can't be parsed as VersionNumber. If CCM manages to run `create` with it, then the usual version number is fetched from `ccm node versionfrombuild` output. To do that we introduce a static, worse version of `execute` method named `executeStatic`. It's a hacky way to keep doing version initialization in a static block. Non-static parts were removed. Logging parts were removed from its `LogOutputStream`s. Errors are written directly through `System.err`. It seems the loggers were not allowing the process to shut down properly and causing it to hang up. Exact root cause is unknown, I deemed it too time consuming to look into it. --- .../com/datastax/driver/core/CCMBridge.java | 98 ++++++++++++++++++- 1 file changed, 94 insertions(+), 4 deletions(-) diff --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 index dc9b807c023..fb6285dc17d 100644 --- a/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java +++ b/driver-core/src/test/java/com/datastax/driver/core/CCMBridge.java @@ -194,6 +194,20 @@ public class CCMBridge implements CCMAccess { static { String inputCassandraVersion = System.getProperty("cassandra.version"); String inputScyllaVersion = System.getProperty("scylla.version"); + boolean inputScyllaVersionIsAVersionNumber = true; + VersionNumber parsedScyllaVersionNumber = null; + try { + parsedScyllaVersionNumber = VersionNumber.parse(inputScyllaVersion); + } catch (IllegalArgumentException e) { + inputScyllaVersionIsAVersionNumber = false; + logger.warn( + "Failed to parse scylla.version: " + + inputScyllaVersion + + ". Trying to get it through CCM.", + e); + parsedScyllaVersionNumber = getScyllaVersionThroughCcm(inputScyllaVersion); + } + GLOBAL_SCYLLA_VERSION_NUMBER = parsedScyllaVersionNumber; String installDirectory = System.getProperty("cassandra.directory"); String branch = System.getProperty("cassandra.branch"); @@ -206,8 +220,11 @@ public class CCMBridge implements CCMAccess { installArgs.add("-v git:" + branch.trim().replaceAll("\"", "")); } else if (inputScyllaVersion != null && !inputScyllaVersion.trim().isEmpty()) { installArgs.add(" --scylla "); - installArgs.add("-v release:" + inputScyllaVersion); - + if (inputScyllaVersionIsAVersionNumber) { + installArgs.add("-v release:" + inputScyllaVersion); + } else { + installArgs.add("-v " + inputScyllaVersion); + } // Detect Scylla Enterprise - it should start with // a 4-digit year. if (inputScyllaVersion.matches("\\d{4}\\..*")) { @@ -246,8 +263,6 @@ public class CCMBridge implements CCMAccess { } ENVIRONMENT_MAP = ImmutableMap.copyOf(envMap); - GLOBAL_SCYLLA_VERSION_NUMBER = VersionNumber.parse(inputScyllaVersion); - if (isDse()) { GLOBAL_DSE_VERSION_NUMBER = VersionNumber.parse(inputCassandraVersion); GLOBAL_CASSANDRA_VERSION_NUMBER = CCMBridge.getCassandraVersion(GLOBAL_DSE_VERSION_NUMBER); @@ -792,6 +807,81 @@ public void setWorkload(int node, Workload... workload) { execute(CCM_COMMAND + " node%d setworkload %s", node, workloadStr); } + private static VersionNumber getScyllaVersionThroughCcm(String versionString) { + try { + executeStatic("ccm create get_version -n 1 --scylla --version %s", versionString); + String versionOutput = executeStatic("ccm node1 versionfrombuild"); + executeStatic("ccm remove"); + return VersionNumber.parse(versionOutput.trim()); + } catch (RuntimeException cause) { + throw new RuntimeException( + "Error during getting Scylla version through ccm commands.", cause); + } + } + + // Not recommended for general use. Static version of execute() with minor modifications. + private static String executeStatic(String command, Object... args) { + String fullCommand = String.format(command, args); + Closer closer = Closer.create(); + // 10 minutes timeout + ExecuteWatchdog watchDog = new ExecuteWatchdog(TimeUnit.MINUTES.toMillis(10)); + StringWriter sw = new StringWriter(); + final PrintWriter pw = new PrintWriter(sw); + closer.register(pw); + try { + logger.info("Executing: " + fullCommand); + CommandLine cli = CommandLine.parse(fullCommand); + Executor executor = new DefaultExecutor(); + LogOutputStream outStream = + new LogOutputStream() { + @Override + protected void processLine(String line, int logLevel) { + pw.println(line); + } + }; + LogOutputStream errStream = + new LogOutputStream() { + @Override + protected void processLine(String line, int logLevel) { + String err = "ccmerr> " + line; + System.err.println(err); + pw.println(err); + } + }; + closer.register(outStream); + closer.register(errStream); + ExecuteStreamHandler streamHandler = new PumpStreamHandler(outStream, errStream); + executor.setStreamHandler(streamHandler); + executor.setWatchdog(watchDog); + int retValue = executor.execute(cli); + if (retValue != 0) { + logger.error( + "Non-zero exit code ({}) returned from executing ccm command: {}", + retValue, + fullCommand); + pw.flush(); + throw new CCMException( + String.format( + "Non-zero exit code (%s) returned from executing ccm command: %s", + retValue, fullCommand), + sw.toString()); + } + } catch (IOException e) { + if (watchDog.killedProcess()) + logger.error("The command {} was killed after 10 minutes", fullCommand); + pw.flush(); + throw new CCMException( + String.format("The command %s failed to execute", fullCommand), sw.toString(), e); + } finally { + try { + closer.close(); + } catch (IOException e) { + Throwables.propagate(e); + } + } + return sw.toString(); + } + private String execute(String command, Object... args) { String fullCommand = String.format(command, args) + " --config-dir=" + ccmDir; Closer closer = Closer.create();