From 1cadc8dae0e4b6eae8e0dad52b62da9f9b7a27d8 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 20 Mar 2025 11:21:39 +1100 Subject: [PATCH] [Test] Allow configuring configDir for the Java test cluster (#125094) For creating and deleting projects in multi-project tests, we need create and delete settings and secrets files on the fly. This PR adds such feature to the Java test cluster with an option to specify the config directory. (cherry picked from commit a1b0ed104bc23c9f80c61cb3016a1402cbb2fee9) --- .../local/AbstractLocalClusterFactory.java | 15 ++++++++++----- .../local/AbstractLocalClusterSpecBuilder.java | 3 ++- .../cluster/local/AbstractLocalSpecBuilder.java | 12 ++++++++++++ .../cluster/local/DefaultLocalClusterHandle.java | 10 +++++----- .../test/cluster/local/LocalClusterSpec.java | 13 +++++++++++-- .../test/cluster/local/LocalSpecBuilder.java | 7 +++++++ 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterFactory.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterFactory.java index 680acdccf2f39..2c4b86b0d721b 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterFactory.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterFactory.java @@ -54,6 +54,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -146,7 +147,7 @@ public Node( this.repoDir = baseWorkingDir.resolve("repo"); this.dataDir = workingDir.resolve("data"); this.logsDir = workingDir.resolve("logs"); - this.configDir = workingDir.resolve("config"); + this.configDir = Optional.ofNullable(spec.getConfigDir()).orElse(workingDir.resolve("config")); this.tempDir = workingDir.resolve("tmp"); // elasticsearch temporary directory this.debugPort = DefaultLocalClusterHandle.NEXT_DEBUG_PORT.getAndIncrement(); } @@ -294,6 +295,10 @@ Path getWorkingDir() { return workingDir; } + Path getConfigDir() { + return configDir; + } + public void waitUntilReady() { try { Retry.retryUntilTrue(NODE_UP_TIMEOUT, Duration.ofMillis(500), () -> { @@ -426,7 +431,7 @@ private void writeConfiguration() { try (Stream configFiles = Files.walk(distributionDir.resolve("config"))) { for (Path file : configFiles.toList()) { Path relativePath = distributionDir.resolve("config").relativize(file); - Path dest = configDir.resolve(relativePath); + Path dest = configDir.resolve(relativePath.toFile().getPath()); if (Files.exists(dest) == false) { Files.createDirectories(dest.getParent()); Files.copy(file, dest); @@ -640,7 +645,7 @@ private void configureSecurity() { if (operators.isEmpty() == false) { // TODO: Support service accounts here final String operatorUsersFileName = "operator_users.yml"; - final Path destination = workingDir.resolve("config").resolve(operatorUsersFileName); + final Path destination = configDir.resolve(operatorUsersFileName); if (Files.exists(destination)) { throw new IllegalStateException( "Operator users file [" @@ -667,7 +672,7 @@ private void configureSecurity() { } private void writeRolesFile() { - Path destination = workingDir.resolve("config").resolve("roles.yml"); + Path destination = configDir.resolve("roles.yml"); spec.getRolesFiles().forEach(rolesFile -> { try ( Writer writer = Files.newBufferedWriter(destination, StandardOpenOption.APPEND); @@ -857,7 +862,7 @@ private void startElasticsearch() { private Map getEnvironmentVariables() { Map environment = new HashMap<>(spec.resolveEnvironment()); - environment.put("ES_PATH_CONF", workingDir.resolve("config").toString()); + environment.put("ES_PATH_CONF", configDir.toString()); environment.put("ES_TMPDIR", workingDir.resolve("tmp").toString()); // Windows requires this as it defaults to `c:\windows` despite ES_TMPDIR environment.put("TMP", workingDir.resolve("tmp").toString()); diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterSpecBuilder.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterSpecBuilder.java index 7f1a384ebb43d..73c5afdec5b9f 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterSpecBuilder.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalClusterSpecBuilder.java @@ -212,7 +212,8 @@ private LocalNodeSpec build(LocalClusterSpec cluster, int nodeIndex) { getExtraConfigFiles(), getSystemPropertyProviders(), getSystemProperties(), - getJvmArgs() + getJvmArgs(), + Optional.ofNullable(getConfigDirSupplier()).map(Supplier::get).orElse(null) ); } } diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalSpecBuilder.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalSpecBuilder.java index 78f3d9e66159b..82c5b1a54c13c 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalSpecBuilder.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/AbstractLocalSpecBuilder.java @@ -17,6 +17,7 @@ import org.elasticsearch.test.cluster.util.Version; import org.elasticsearch.test.cluster.util.resource.Resource; +import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; @@ -47,6 +48,7 @@ public abstract class AbstractLocalSpecBuilder> im private DistributionType distributionType; private Version version; private String keystorePassword; + private Supplier configDirSupplier; protected AbstractLocalSpecBuilder(AbstractLocalSpecBuilder parent) { this.parent = parent; @@ -270,6 +272,16 @@ public String getKeystorePassword() { return inherit(() -> parent.getKeystorePassword(), keystorePassword); } + @Override + public T withConfigDir(Supplier configDirSupplier) { + this.configDirSupplier = configDirSupplier; + return cast(this); + } + + public Supplier getConfigDirSupplier() { + return inherit(() -> parent.getConfigDirSupplier(), configDirSupplier); + } + @Override public T version(Version version) { this.version = version; diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterHandle.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterHandle.java index 4331728aedb1e..f2e2245e7321e 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterHandle.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/DefaultLocalClusterHandle.java @@ -237,15 +237,15 @@ private WaitForHttpResource configureWaitForReady() throws MalformedURLException private void configureWaitSecurity(WaitForHttpResource wait, Node node) { String caFile = node.getSpec().getSetting("xpack.security.http.ssl.certificate_authorities", null); if (caFile != null) { - wait.setCertificateAuthorities(node.getWorkingDir().resolve("config").resolve(caFile).toFile()); + wait.setCertificateAuthorities(node.getConfigDir().resolve(caFile).toFile()); } String sslCertFile = node.getSpec().getSetting("xpack.security.http.ssl.certificate", null); if (sslCertFile != null) { - wait.setCertificateAuthorities(node.getWorkingDir().resolve("config").resolve(sslCertFile).toFile()); + wait.setCertificateAuthorities(node.getConfigDir().resolve(sslCertFile).toFile()); } String sslKeystoreFile = node.getSpec().getSetting("xpack.security.http.ssl.keystore.path", null); if (sslKeystoreFile != null && caFile == null) { // Can not set both trust stores and CA - wait.setTrustStoreFile(node.getWorkingDir().resolve("config").resolve(sslKeystoreFile).toFile()); + wait.setTrustStoreFile(node.getConfigDir().resolve(sslKeystoreFile).toFile()); } String keystorePassword = node.getSpec().getSetting("xpack.security.http.ssl.keystore.secure_password", null); if (keystorePassword != null) { @@ -254,7 +254,7 @@ private void configureWaitSecurity(WaitForHttpResource wait, Node node) { } private boolean isSecurityAutoConfigured(Node node) { - Path configFile = node.getWorkingDir().resolve("config").resolve("elasticsearch.yml"); + Path configFile = node.getConfigDir().resolve("elasticsearch.yml"); try (Stream lines = Files.lines(configFile)) { return lines.anyMatch(l -> l.contains("BEGIN SECURITY AUTO CONFIGURATION")); } catch (IOException e) { @@ -273,7 +273,7 @@ private void writeUnicastHostsFile() { LOGGER.info("Skipping writing unicast hosts file for node {}", node.getName()); return; } - Path hostsFile = node.getWorkingDir().resolve("config").resolve("unicast_hosts.txt"); + Path hostsFile = node.getConfigDir().resolve("unicast_hosts.txt"); LOGGER.info("Writing unicast hosts file {} for node {}", hostsFile, node.getName()); Files.writeString(hostsFile, transportUris); } catch (IOException e) { diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalClusterSpec.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalClusterSpec.java index ed5c0c5d1bbc0..cccf2a95234c9 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalClusterSpec.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalClusterSpec.java @@ -19,6 +19,7 @@ import org.elasticsearch.test.cluster.util.Version; import org.elasticsearch.test.cluster.util.resource.Resource; +import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -103,6 +104,7 @@ public static class LocalNodeSpec { private final List systemPropertyProviders; private final Map systemProperties; private final List jvmArgs; + private final Path configDir; private Version version; public LocalNodeSpec( @@ -124,7 +126,8 @@ public LocalNodeSpec( Map extraConfigFiles, List systemPropertyProviders, Map systemProperties, - List jvmArgs + List jvmArgs, + Path configDir ) { this.cluster = cluster; this.name = name; @@ -145,6 +148,7 @@ public LocalNodeSpec( this.systemPropertyProviders = systemPropertyProviders; this.systemProperties = systemProperties; this.jvmArgs = jvmArgs; + this.configDir = configDir; } void setVersion(Version version) { @@ -203,6 +207,10 @@ public List getJvmArgs() { return jvmArgs; } + public Path getConfigDir() { + return configDir; + } + public boolean isSecurityEnabled() { return Boolean.parseBoolean(getSetting("xpack.security.enabled", getVersion().onOrAfter("8.0.0") ? "true" : "false")); } @@ -339,7 +347,8 @@ private LocalNodeSpec getFilteredSpec(SettingsProvider filteredProvider, Setting n.extraConfigFiles, n.systemPropertyProviders, n.systemProperties, - n.jvmArgs + n.jvmArgs, + n.configDir ) ) .toList(); diff --git a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalSpecBuilder.java b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalSpecBuilder.java index 52846bcc2b6fd..53d283224b26f 100644 --- a/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalSpecBuilder.java +++ b/test/test-clusters/src/main/java/org/elasticsearch/test/cluster/local/LocalSpecBuilder.java @@ -18,6 +18,7 @@ import org.elasticsearch.test.cluster.util.Version; import org.elasticsearch.test.cluster.util.resource.Resource; +import java.nio.file.Path; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -160,4 +161,10 @@ interface LocalSpecBuilder> { * Adds an additional command line argument to node JVM arguments. */ T jvmArg(String arg); + + /** + * Register a supplier to provide the config directory. The default config directory + * is used when the supplier is null or the return value of the supplier is null. + */ + T withConfigDir(Supplier configDirSupplier); }