Skip to content

Commit bc85b22

Browse files
authored
Complete testclusters backport (#47623)
* Use versions specific distribution folders so we don't need to clean up (#46539) * Retry deleting distro dir on windows When retarting the cluster we clean up old distribution files that might still be in use by the OS. Windows closes resources of ded processes async, so we do a couple of retries to get arround it. Closes #46014 * Avoid having to delete the distro folder. * Remove the use of ClusterFormationTasks form RestTestTask (#47022) This PR removes a use-case of the ClusterFormationTasks and converts a project that flew under the radar so far. There's probably more clean-up possible here, but for now the goal is to be able to remove that code after `RunTask` is also updated. * Migrate some 7.x only projects
1 parent 1359ef7 commit bc85b22

File tree

15 files changed

+64
-186
lines changed

15 files changed

+64
-186
lines changed

buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class PluginBuildPlugin implements Plugin<Project> {
5353
@Override
5454
void apply(Project project) {
5555
project.pluginManager.apply(BuildPlugin)
56+
project.pluginManager.apply(TestClustersPlugin)
5657

5758
PluginPropertiesExtension extension = project.extensions.create(PLUGIN_EXTENSION_NAME, PluginPropertiesExtension, project)
5859
configureDependencies(project)

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestIntegTestTask.groovy

Lines changed: 7 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -26,55 +26,32 @@ import org.elasticsearch.gradle.tool.Boilerplate
2626
import org.elasticsearch.gradle.tool.ClasspathUtils
2727
import org.gradle.api.DefaultTask
2828
import org.gradle.api.Task
29-
import org.gradle.api.execution.TaskExecutionAdapter
3029
import org.gradle.api.file.FileCopyDetails
31-
import org.gradle.api.logging.Logger
32-
import org.gradle.api.logging.Logging
3330
import org.gradle.api.tasks.Copy
3431
import org.gradle.api.tasks.Input
35-
import org.gradle.api.tasks.TaskState
36-
import org.gradle.api.tasks.options.Option
3732
import org.gradle.api.tasks.testing.Test
3833
import org.gradle.plugins.ide.idea.IdeaPlugin
39-
import org.gradle.process.CommandLineArgumentProvider
40-
41-
import java.nio.charset.StandardCharsets
42-
import java.nio.file.Files
43-
import java.util.stream.Stream
44-
4534
/**
4635
* A wrapper task around setting up a cluster and running rest tests.
4736
*/
4837
class RestIntegTestTask extends DefaultTask {
4938

50-
private static final Logger LOGGER = Logging.getLogger(RestIntegTestTask)
51-
52-
protected ClusterConfiguration clusterConfig
53-
5439
protected Test runner
5540

56-
/** Info about nodes in the integ test cluster. Note this is *not* available until runtime. */
57-
List<NodeInfo> nodes
58-
5941
/** Flag indicating whether the rest tests in the rest spec should be run. */
6042
@Input
6143
Boolean includePackaged = false
6244

6345
RestIntegTestTask() {
6446
runner = project.tasks.create("${name}Runner", RestTestRunnerTask.class)
6547
super.dependsOn(runner)
66-
boolean usesTestclusters = project.plugins.hasPlugin(TestClustersPlugin.class)
67-
if (usesTestclusters == false) {
68-
clusterConfig = project.extensions.create("${name}Cluster", ClusterConfiguration.class, project)
69-
runner.outputs.doNotCacheIf("Caching is disabled when using ClusterFormationTasks", { true })
70-
} else {
71-
project.testClusters {
48+
49+
project.testClusters {
7250
"$name" {
7351
javaHome = project.file(project.ext.runtimeJavaHome)
7452
}
75-
}
76-
runner.useCluster project.testClusters."$name"
7753
}
54+
runner.useCluster project.testClusters."$name"
7855

7956
runner.include('**/*IT.class')
8057
runner.systemProperty('tests.rest.load_packaged', 'false')
@@ -83,38 +60,10 @@ class RestIntegTestTask extends DefaultTask {
8360
if (System.getProperty("tests.cluster") != null) {
8461
throw new IllegalArgumentException("tests.rest.cluster and tests.cluster must both be null or non-null")
8562
}
86-
if (usesTestclusters == true) {
87-
ElasticsearchCluster cluster = project.testClusters."${name}"
88-
runner.nonInputProperties.systemProperty('tests.rest.cluster', "${-> cluster.allHttpSocketURI.join(",") }")
89-
runner.nonInputProperties.systemProperty('tests.cluster', "${-> cluster.transportPortURI }")
90-
} else {
91-
// we pass all nodes to the rest cluster to allow the clients to round-robin between them
92-
// this is more realistic than just talking to a single node
93-
runner.nonInputProperties.systemProperty('tests.rest.cluster', "${-> nodes.collect { it.httpUri() }.join(",")}")
94-
runner.nonInputProperties.systemProperty('tests.config.dir', "${-> nodes[0].pathConf}")
95-
// TODO: our "client" qa tests currently use the rest-test plugin. instead they should have their own plugin
96-
// that sets up the test cluster and passes this transport uri instead of http uri. Until then, we pass
97-
// both as separate sysprops
98-
runner.nonInputProperties.systemProperty('tests.cluster', "${-> nodes[0].transportUri()}")
99-
100-
// dump errors and warnings from cluster log on failure
101-
TaskExecutionAdapter logDumpListener = new TaskExecutionAdapter() {
102-
@Override
103-
void afterExecute(Task task, TaskState state) {
104-
if (task == runner && state.failure != null) {
105-
for (NodeInfo nodeInfo : nodes) {
106-
printLogExcerpt(nodeInfo)
107-
}
108-
}
109-
}
110-
}
111-
runner.doFirst {
112-
project.gradle.addListener(logDumpListener)
113-
}
114-
runner.doLast {
115-
project.gradle.removeListener(logDumpListener)
116-
}
117-
}
63+
ElasticsearchCluster cluster = project.testClusters."${name}"
64+
runner.nonInputProperties.systemProperty('tests.rest.cluster', "${-> cluster.allHttpSocketURI.join(",")}")
65+
runner.nonInputProperties.systemProperty('tests.cluster', "${-> cluster.transportPortURI}")
66+
runner.nonInputProperties.systemProperty('tests.clustername', "${-> cluster.getName()}")
11867
} else {
11968
if (System.getProperty("tests.cluster") == null) {
12069
throw new IllegalArgumentException("tests.rest.cluster and tests.cluster must both be null or non-null")
@@ -135,13 +84,6 @@ class RestIntegTestTask extends DefaultTask {
13584
runner.enabled = false
13685
return // no need to add cluster formation tasks if the task won't run!
13786
}
138-
if (usesTestclusters == false) {
139-
// only create the cluster if needed as otherwise an external cluster to use was specified
140-
if (System.getProperty("tests.rest.cluster") == null) {
141-
nodes = ClusterFormationTasks.setup(project, "${name}Cluster", runner, clusterConfig)
142-
}
143-
super.dependsOn(runner.finalizedBy)
144-
}
14587
}
14688
}
14789

@@ -150,18 +92,6 @@ class RestIntegTestTask extends DefaultTask {
15092
includePackaged = include
15193
}
15294

153-
@Option(
154-
option = "debug-jvm",
155-
description = "Enable debugging configuration, to allow attaching a debugger to elasticsearch."
156-
)
157-
public void setDebug(boolean enabled) {
158-
clusterConfig.debug = enabled;
159-
}
160-
161-
public List<NodeInfo> getNodes() {
162-
return nodes
163-
}
164-
16595
@Override
16696
public Task dependsOn(Object... dependencies) {
16797
runner.dependsOn(dependencies)
@@ -187,44 +117,6 @@ class RestIntegTestTask extends DefaultTask {
187117
project.tasks.getByName("${name}Runner").configure(configure)
188118
}
189119

190-
/** Print out an excerpt of the log from the given node. */
191-
protected static void printLogExcerpt(NodeInfo nodeInfo) {
192-
File logFile = new File(nodeInfo.homeDir, "logs/${nodeInfo.clusterName}.log")
193-
LOGGER.lifecycle("\nCluster ${nodeInfo.clusterName} - node ${nodeInfo.nodeNum} log excerpt:")
194-
LOGGER.lifecycle("(full log at ${logFile})")
195-
LOGGER.lifecycle('-----------------------------------------')
196-
Stream<String> stream = Files.lines(logFile.toPath(), StandardCharsets.UTF_8)
197-
try {
198-
boolean inStartup = true
199-
boolean inExcerpt = false
200-
int linesSkipped = 0
201-
for (String line : stream) {
202-
if (line.startsWith("[")) {
203-
inExcerpt = false // clear with the next log message
204-
}
205-
if (line =~ /(\[WARN *\])|(\[ERROR *\])/) {
206-
inExcerpt = true // show warnings and errors
207-
}
208-
if (inStartup || inExcerpt) {
209-
if (linesSkipped != 0) {
210-
LOGGER.lifecycle("... SKIPPED ${linesSkipped} LINES ...")
211-
}
212-
LOGGER.lifecycle(line)
213-
linesSkipped = 0
214-
} else {
215-
++linesSkipped
216-
}
217-
if (line =~ /recovered \[\d+\] indices into cluster_state/) {
218-
inStartup = false
219-
}
220-
}
221-
} finally {
222-
stream.close()
223-
}
224-
LOGGER.lifecycle('=========================================')
225-
226-
}
227-
228120
Copy createCopyRestSpecTask() {
229121
Boilerplate.maybeCreate(project.configurations, 'restSpec') {
230122
project.dependencies.add(

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/RestTestPlugin.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.elasticsearch.gradle.test
2020

2121
import org.elasticsearch.gradle.BuildPlugin
22+
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
2223
import org.gradle.api.InvalidUserDataException
2324
import org.gradle.api.Plugin
2425
import org.gradle.api.Project
@@ -43,6 +44,7 @@ public class RestTestPlugin implements Plugin<Project> {
4344
+ 'elasticsearch.standalone-rest-test')
4445
}
4546

47+
project.pluginManager.apply(TestClustersPlugin)
4648
RestIntegTestTask integTest = project.tasks.create('integTest', RestIntegTestTask.class)
4749
integTest.description = 'Runs rest tests against an elasticsearch cluster.'
4850
integTest.group = JavaBasePlugin.VERIFICATION_GROUP

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/StandaloneRestTestPlugin.groovy

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020

2121
package org.elasticsearch.gradle.test
2222

23-
2423
import groovy.transform.CompileStatic
2524
import org.elasticsearch.gradle.BuildPlugin
2625
import org.elasticsearch.gradle.ExportElasticsearchBuildResourcesTask
27-
import org.elasticsearch.gradle.VersionProperties
2826
import org.elasticsearch.gradle.info.GlobalBuildInfoPlugin
2927
import org.elasticsearch.gradle.precommit.PrecommitTasks
28+
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
3029
import org.gradle.api.InvalidUserDataException
3130
import org.gradle.api.JavaVersion
3231
import org.gradle.api.Plugin
@@ -42,7 +41,6 @@ import org.gradle.api.tasks.compile.JavaCompile
4241
import org.gradle.api.tasks.testing.Test
4342
import org.gradle.plugins.ide.eclipse.model.EclipseModel
4443
import org.gradle.plugins.ide.idea.model.IdeaModel
45-
4644
/**
4745
* Configures the build to compile tests against Elasticsearch's test framework
4846
* and run REST tests. Use BuildPlugin if you want to build main code as well
@@ -60,6 +58,7 @@ class StandaloneRestTestPlugin implements Plugin<Project> {
6058
}
6159
project.rootProject.pluginManager.apply(GlobalBuildInfoPlugin)
6260
project.pluginManager.apply(JavaBasePlugin)
61+
project.pluginManager.apply(TestClustersPlugin)
6362

6463
project.getTasks().create("buildResources", ExportElasticsearchBuildResourcesTask)
6564
BuildPlugin.configureRepositories(project)

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/TestWithDependenciesPlugin.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ class TestWithDependenciesPlugin implements Plugin<Project> {
5656

5757
private static addPluginResources(Project project, Project pluginProject) {
5858
String outputDir = "${project.buildDir}/generated-resources/${pluginProject.name}"
59-
String taskName = ClusterFormationTasks.pluginTaskName("copy", pluginProject.name, "Metadata")
59+
String camelName = pluginProject.name.replaceAll(/-(\w)/) { _, c -> c.toUpperCase(Locale.ROOT) }
60+
String taskName = "copy" + camelName[0].toUpperCase(Locale.ROOT) + camelName.substring(1) + "Metadata"
6061
Copy copyPluginMetadata = project.tasks.create(taskName, Copy.class)
6162
copyPluginMetadata.into(outputDir)
6263
copyPluginMetadata.from(pluginProject.tasks.pluginProperties)

buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchCluster.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ private void commonNodeConfig(ElasticsearchNode node, String nodeNames, Elastics
295295
.filter(name -> name.startsWith("discovery.zen."))
296296
.collect(Collectors.toList())
297297
.forEach(node.defaultConfig::remove);
298-
if (nodeNames != null) {
298+
if (nodeNames != null &&
299+
node.settings.getOrDefault("discovery.type", "anything").equals("single-node") == false
300+
) {
299301
node.defaultConfig.put("cluster.initial_master_nodes", "[" + nodeNames + "]");
300302
}
301303
node.defaultConfig.put("discovery.seed_providers", "file");

buildSrc/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ public class ElasticsearchNode implements TestClusterConfiguration {
140140
private final Path esStdoutFile;
141141
private final Path esStderrFile;
142142
private final Path tmpDir;
143-
private final Path distroDir;
144143

145144
private int currentDistro = 0;
146145
private TestDistribution testDistribution;
@@ -156,7 +155,6 @@ public class ElasticsearchNode implements TestClusterConfiguration {
156155
this.project = project;
157156
this.reaper = reaper;
158157
workingDir = workingDirBase.toPath().resolve(safeName(name)).toAbsolutePath();
159-
distroDir = workingDir.resolve("distro");
160158
confPathRepo = workingDir.resolve("repo");
161159
configFile = workingDir.resolve("config/elasticsearch.yml");
162160
confPathData = workingDir.resolve("data");
@@ -183,6 +181,10 @@ public Version getVersion() {
183181
return distributions.get(currentDistro).getVersion();
184182
}
185183

184+
public Path getDistroDir() {
185+
return workingDir.resolve("distro").resolve(getVersion() + "-" + testDistribution);
186+
}
187+
186188
@Override
187189
public void setVersion(String version) {
188190
requireNonNull(version, "null version passed when configuring test cluster `" + this + "`");
@@ -530,7 +532,7 @@ private void installModules() {
530532
if (testDistribution == TestDistribution.INTEG_TEST) {
531533
logToProcessStdout("Installing " + modules.size() + "modules");
532534
for (File module : modules) {
533-
Path destination = distroDir.resolve("modules").resolve(module.getName().replace(".zip", "")
535+
Path destination = getDistroDir().resolve("modules").resolve(module.getName().replace(".zip", "")
534536
.replace("-" + getVersion(), "")
535537
.replace("-SNAPSHOT", ""));
536538

@@ -590,16 +592,16 @@ public void user(Map<String, String> userSpec) {
590592

591593
private void runElaticsearchBinScriptWithInput(String input, String tool, String... args) {
592594
if (
593-
Files.exists(distroDir.resolve("bin").resolve(tool)) == false &&
594-
Files.exists(distroDir.resolve("bin").resolve(tool + ".bat")) == false
595+
Files.exists(getDistroDir().resolve("bin").resolve(tool)) == false &&
596+
Files.exists(getDistroDir().resolve("bin").resolve(tool + ".bat")) == false
595597
) {
596598
throw new TestClustersException("Can't run bin script: `" + tool + "` does not exist. " +
597599
"Is this the distribution you expect it to be ?");
598600
}
599601
try (InputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) {
600602
LoggedExec.exec(project, spec -> {
601603
spec.setEnvironment(getESEnvironment());
602-
spec.workingDir(distroDir);
604+
spec.workingDir(getDistroDir());
603605
spec.executable(
604606
OS.conditionalString()
605607
.onUnix(() -> "./bin/" + tool)
@@ -684,8 +686,8 @@ private void startElasticsearchProcess() {
684686
final ProcessBuilder processBuilder = new ProcessBuilder();
685687

686688
List<String> command = OS.<List<String>>conditional()
687-
.onUnix(() -> Arrays.asList(distroDir.getFileName().resolve("./bin/elasticsearch").toString()))
688-
.onWindows(() -> Arrays.asList("cmd", "/c", distroDir.getFileName().resolve("bin\\elasticsearch.bat").toString()))
689+
.onUnix(() -> Arrays.asList(workingDir.relativize(getDistroDir()).resolve("./bin/elasticsearch").toString()))
690+
.onWindows(() -> Arrays.asList("cmd", "/c", workingDir.relativize(getDistroDir()).resolve("bin\\elasticsearch.bat").toString()))
689691
.supply();
690692
processBuilder.command(command);
691693
processBuilder.directory(workingDir.toFile());
@@ -915,7 +917,9 @@ private void waitForProcessToExit(ProcessHandle processHandle) {
915917
}
916918

917919
private void createWorkingDir(Path distroExtractDir) throws IOException {
918-
syncWithLinks(distroExtractDir, distroDir);
920+
if (Files.exists(getDistroDir()) == false) {
921+
syncWithLinks(distroExtractDir, getDistroDir());
922+
}
919923
// Start configuration from scratch in case of a restart
920924
project.delete(configFile.getParent());
921925
Files.createDirectories(configFile.getParent());
@@ -934,10 +938,7 @@ private void createWorkingDir(Path distroExtractDir) throws IOException {
934938
* @param destinationRoot destination to link to
935939
*/
936940
private void syncWithLinks(Path sourceRoot, Path destinationRoot) {
937-
if (Files.exists(destinationRoot)) {
938-
project.delete(destinationRoot);
939-
}
940-
941+
assert Files.exists(destinationRoot) == false;
941942
try (Stream<Path> stream = Files.walk(sourceRoot)) {
942943
stream.forEach(source -> {
943944
Path relativeDestination = sourceRoot.relativize(source);
@@ -1040,7 +1041,7 @@ private void createConfiguration() {
10401041
);
10411042

10421043
final List<Path> configFiles;
1043-
try (Stream<Path> stream = Files.list(distroDir.resolve("config"))) {
1044+
try (Stream<Path> stream = Files.list(getDistroDir().resolve("config"))) {
10441045
configFiles = stream.collect(Collectors.toList());
10451046
}
10461047
logToProcessStdout("Copying additional config files from distro " + configFiles);

distribution/archives/build.gradle

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -310,17 +310,13 @@ configure(subprojects.findAll { it.name == 'integ-test-zip' }) {
310310
group = "org.elasticsearch.distribution.integ-test-zip"
311311

312312
integTest {
313-
includePackaged = true
314-
}
315-
316-
integTestCluster {
317313
dependsOn assemble
318-
distribution = project.name
319-
}
320-
integTestRunner {
321-
if (Os.isFamily(Os.FAMILY_WINDOWS) && System.getProperty('tests.timeoutSuite') == null) {
322-
// override the suite timeout to 30 mins for windows, because it has the most inefficient filesystem known to man
323-
systemProperty 'tests.timeoutSuite', '1800000!'
314+
includePackaged = true
315+
runner {
316+
if (Os.isFamily(Os.FAMILY_WINDOWS) && System.getProperty('tests.timeoutSuite') == null) {
317+
// override the suite timeout to 30 mins for windows, because it has the most inefficient filesystem known to man
318+
systemProperty 'tests.timeoutSuite', '1800000!'
319+
}
324320
}
325321
}
326322

distribution/archives/integ-test-zip/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
integTestRunner {
20+
integTest.runner {
2121
/*
2222
* There are two unique things going on here:
2323
* 1. These tests can be run against an external cluster with
@@ -27,7 +27,7 @@ integTestRunner {
2727
*/
2828
if (System.getProperty("tests.rest.cluster") == null) {
2929
nonInputProperties.systemProperty 'tests.logfile',
30-
"${ -> integTest.nodes[0].homeDir}/logs/${ -> integTest.nodes[0].clusterName }_server.json"
30+
"${ -> testClusters.integTest.singleNode().getServerLog()}"
3131
} else {
3232
systemProperty 'tests.logfile', '--external--'
3333
}

0 commit comments

Comments
 (0)