Skip to content

Commit 2455295

Browse files
committed
Testsclusters use seprate configurations per version (elastic#41504)
* artifact config tests WIP * wip version configuration * Tests for older versions * use separate configurations per version * checkstyle * PR review
1 parent d54a921 commit 2455295

File tree

7 files changed

+157
-75
lines changed

7 files changed

+157
-75
lines changed

buildSrc/src/main/java/org/elasticsearch/gradle/Distribution.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,26 @@
2020

2121
public enum Distribution {
2222

23-
INTEG_TEST("elasticsearch", "integ-test-zip"),
24-
DEFAULT("elasticsearch", "elasticsearch"),
25-
OSS("elasticsearch-oss", "elasticsearch-oss");
23+
INTEG_TEST("elasticsearch"),
24+
DEFAULT("elasticsearch"),
25+
OSS("elasticsearch-oss");
2626

2727
private final String artifactName;
28-
private final String group;
2928

30-
Distribution(String name, String group) {
29+
Distribution(String name) {
3130
this.artifactName = name;
32-
this.group = group;
3331
}
3432

3533
public String getArtifactName() {
3634
return artifactName;
3735
}
3836

3937
public String getGroup() {
40-
return "org.elasticsearch.distribution." + group;
38+
if (this.equals(INTEG_TEST)) {
39+
return "org.elasticsearch.distribution.integ-test-zip";
40+
} else {
41+
return "org.elasticsearch.distribution." + name().toLowerCase();
42+
}
4143
}
4244

4345
public String getFileExtension() {

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ public boolean isProcessAlive() {
279279
}
280280

281281
void eachVersionedDistribution(BiConsumer<String, Distribution> consumer) {
282-
nodes.forEach(each -> consumer.accept(each.getVersion(), each.getDistribution()));
282+
nodes.forEach(each -> {
283+
consumer.accept(each.getVersion(), each.getDistribution());
284+
});
283285
}
284286

285287
public ElasticsearchNode singleNode() {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ public synchronized void start() {
276276

277277
Path distroArtifact = artifactsExtractDir
278278
.resolve(distribution.getGroup())
279-
.resolve(distribution.getArtifactName() + "-" + getVersion());
279+
.resolve("elasticsearch-" + getVersion());
280280

281281
if (Files.exists(distroArtifact) == false) {
282282
throw new TestClustersException("Can not start " + this + ", missing: " + distroArtifact);

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

+91-64
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@
2020

2121
import groovy.lang.Closure;
2222
import org.elasticsearch.gradle.BwcVersions;
23-
import org.elasticsearch.gradle.Distribution;
2423
import org.elasticsearch.gradle.Version;
24+
import org.elasticsearch.gradle.tool.Boilerplate;
2525
import org.gradle.api.Action;
2626
import org.gradle.api.NamedDomainObjectContainer;
2727
import org.gradle.api.Plugin;
2828
import org.gradle.api.Project;
2929
import org.gradle.api.Task;
3030
import org.gradle.api.artifacts.Configuration;
31+
import org.gradle.api.artifacts.repositories.MavenArtifactRepository;
32+
import org.gradle.api.credentials.HttpHeaderCredentials;
3133
import org.gradle.api.execution.TaskActionListener;
3234
import org.gradle.api.execution.TaskExecutionListener;
33-
import org.gradle.api.file.FileCollection;
3435
import org.gradle.api.file.FileTree;
3536
import org.gradle.api.logging.Logger;
3637
import org.gradle.api.logging.Logging;
@@ -46,7 +47,6 @@
4647
import java.util.List;
4748
import java.util.Map;
4849
import java.util.Set;
49-
import java.util.concurrent.Callable;
5050
import java.util.concurrent.ExecutorService;
5151
import java.util.concurrent.Executors;
5252
import java.util.concurrent.TimeUnit;
@@ -56,7 +56,7 @@ public class TestClustersPlugin implements Plugin<Project> {
5656

5757
private static final String LIST_TASK_NAME = "listTestClusters";
5858
private static final String NODE_EXTENSION_NAME = "testClusters";
59-
private static final String HELPER_CONFIGURATION_NAME = "testclusters";
59+
private static final String HELPER_CONFIGURATION_PREFIX = "testclusters";
6060
private static final String SYNC_ARTIFACTS_TASK_NAME = "syncTestClustersArtifacts";
6161
private static final int EXECUTOR_SHUTDOWN_TIMEOUT = 1;
6262
private static final TimeUnit EXECUTOR_SHUTDOWN_TIMEOUT_UNIT = TimeUnit.MINUTES;
@@ -69,6 +69,10 @@ public class TestClustersPlugin implements Plugin<Project> {
6969
private final Thread shutdownHook = new Thread(this::shutDownAllClusters);
7070
private ExecutorService executorService = Executors.newSingleThreadExecutor();
7171

72+
public static String getHelperConfigurationName(String version) {
73+
return HELPER_CONFIGURATION_PREFIX + "-" + version;
74+
}
75+
7276
@Override
7377
public void apply(Project project) {
7478
Project rootProject = project.getRootProject();
@@ -82,47 +86,6 @@ public void apply(Project project) {
8286
// create DSL for tasks to mark clusters these use
8387
createUseClusterTaskExtension(project, container);
8488

85-
if (rootProject.getConfigurations().findByName(HELPER_CONFIGURATION_NAME) == null) {
86-
// We use a single configuration on the root project to resolve all testcluster dependencies ( like distros )
87-
// at once, only once without the need to repeat it for each project. This pays off assuming that most
88-
// projects use the same dependencies.
89-
Configuration helperConfiguration = project.getRootProject().getConfigurations().create(HELPER_CONFIGURATION_NAME);
90-
helperConfiguration.setDescription(
91-
"Internal helper configuration used by cluster configuration to download " +
92-
"ES distributions and plugins."
93-
);
94-
95-
// We have a single task to sync the helper configuration to "artifacts dir"
96-
// the clusters will look for artifacts there based on the naming conventions.
97-
// Tasks that use a cluster will add this as a dependency automatically so it's guaranteed to run early in
98-
// the build.
99-
rootProject.getTasks().create(SYNC_ARTIFACTS_TASK_NAME, sync -> {
100-
sync.getInputs().files((Callable<FileCollection>) helperConfiguration::getAsFileTree);
101-
sync.getOutputs().dir(new File(project.getRootProject().getBuildDir(), "testclusters/extract"));
102-
// NOTE: Gradle doesn't allow a lambda here ( fails at runtime )
103-
sync.doLast(new Action<Task>() {
104-
@Override
105-
public void execute(Task task) {
106-
project.sync(spec ->
107-
helperConfiguration.getResolvedConfiguration().getResolvedArtifacts().forEach(resolvedArtifact -> {
108-
final FileTree files;
109-
File file = resolvedArtifact.getFile();
110-
if (file.getName().endsWith(".zip")) {
111-
files = project.zipTree(file);
112-
} else if (file.getName().endsWith("tar.gz")) {
113-
files = project.tarTree(file);
114-
} else {
115-
throw new IllegalArgumentException("Can't extract " + file + " unknown file extension");
116-
}
117-
spec.from(files).into(new File(project.getRootProject().getBuildDir(), "testclusters/extract") + "/" +
118-
resolvedArtifact.getModuleVersion().getId().getGroup()
119-
);
120-
}));
121-
}
122-
});
123-
});
124-
}
125-
12689
// When we know what tasks will run, we claim the clusters of those task to differentiate between clusters
12790
// that are defined in the build script and the ones that will actually be used in this invocation of gradle
12891
// we use this information to determine when the last task that required the cluster executed so that we can
@@ -143,6 +106,10 @@ public void execute(Task task) {
143106
autoConfigureClusterDependencies(project, rootProject, container);
144107
}
145108

109+
private static File getExtractDir(Project project) {
110+
return new File(project.getRootProject().getBuildDir(), "testclusters/extract/");
111+
}
112+
146113
private NamedDomainObjectContainer<ElasticsearchCluster> createTestClustersContainerExtension(Project project) {
147114
// Create an extensions that allows describing clusters
148115
NamedDomainObjectContainer<ElasticsearchCluster> container = project.container(
@@ -290,12 +257,59 @@ private static void autoConfigureClusterDependencies(
290257
Project rootProject,
291258
NamedDomainObjectContainer<ElasticsearchCluster> container
292259
) {
260+
// Download integ test distribution from maven central
261+
MavenArtifactRepository mavenCentral = project.getRepositories().mavenCentral();
262+
mavenCentral.content(spec -> {
263+
spec.includeGroupByRegex("org\\.elasticsearch\\.distribution\\..*");
264+
});
265+
266+
// Other distributions from the download service
267+
project.getRepositories().add(
268+
project.getRepositories().ivy(spec -> {
269+
spec.setUrl("https://artifacts.elastic.co/downloads");
270+
spec.patternLayout(p -> p.artifact("elasticsearch/[module]-[revision](-[classifier]).[ext]"));
271+
HttpHeaderCredentials headerConfig = spec.getCredentials(HttpHeaderCredentials.class);
272+
headerConfig.setName("X-Elastic-No-KPI");
273+
headerConfig.setValue("1");
274+
spec.content(c-> c.includeGroupByRegex("org\\.elasticsearch\\.distribution\\..*"));
275+
})
276+
);
277+
278+
// We have a single task to sync the helper configuration to "artifacts dir"
279+
// the clusters will look for artifacts there based on the naming conventions.
280+
// Tasks that use a cluster will add this as a dependency automatically so it's guaranteed to run early in
281+
// the build.
282+
Task sync = Boilerplate.maybeCreate(rootProject.getTasks(), SYNC_ARTIFACTS_TASK_NAME, onCreate -> {
283+
onCreate.getOutputs().dir(getExtractDir(rootProject));
284+
// NOTE: Gradle doesn't allow a lambda here ( fails at runtime )
285+
onCreate.doFirst(new Action<Task>() {
286+
@Override
287+
public void execute(Task task) {
288+
// Clean up the extract dir first to make sure we have no stale files from older
289+
// previous builds of the same distribution
290+
project.delete(getExtractDir(rootProject));
291+
}
292+
});
293+
});
294+
293295
// When the project evaluated we know of all tasks that use clusters.
294296
// Each of these have to depend on the artifacts being synced.
295297
// We need afterEvaluate here despite the fact that container is a domain object, we can't implement this with
296298
// all because fields can change after the fact.
297299
project.afterEvaluate(ip -> container.forEach(esCluster ->
298300
esCluster.eachVersionedDistribution((version, distribution) -> {
301+
Configuration helperConfiguration = Boilerplate.maybeCreate(
302+
rootProject.getConfigurations(),
303+
getHelperConfigurationName(version),
304+
onCreate ->
305+
// We use a single configuration on the root project to resolve all testcluster dependencies ( like distros )
306+
// at once, only once without the need to repeat it for each project. This pays off assuming that most
307+
// projects use the same dependencies.
308+
onCreate.setDescription(
309+
"Internal helper configuration used by cluster configuration to download " +
310+
"ES distributions and plugins for " + version
311+
)
312+
);
299313
BwcVersions.UnreleasedVersionInfo unreleasedInfo;
300314
final List<Version> unreleased;
301315
{
@@ -320,29 +334,42 @@ private static void autoConfigureClusterDependencies(
320334
projectNotation.put("path", unreleasedInfo.gradleProjectPath);
321335
projectNotation.put("configuration", distribution.getLiveConfiguration());
322336
rootProject.getDependencies().add(
323-
HELPER_CONFIGURATION_NAME,
337+
helperConfiguration.getName(),
324338
project.getDependencies().project(projectNotation)
325339
);
326340
} else {
327-
if (distribution.equals(Distribution.INTEG_TEST)) {
328-
rootProject.getDependencies().add(
329-
HELPER_CONFIGURATION_NAME, "org.elasticsearch.distribution.integ-test-zip:elasticsearch:" + version
330-
);
331-
} else {
332-
// declare dependencies to be downloaded from the download service.
333-
// The BuildPlugin sets up the right repo for this to work
334-
// TODO: move the repo definition in this plugin when ClusterFormationTasks is removed
335-
String dependency = String.format(
336-
"%s:%s:%s:%s@%s",
337-
distribution.getGroup(),
338-
distribution.getArtifactName(),
339-
version,
340-
distribution.getClassifier(),
341-
distribution.getFileExtension()
342-
);
343-
rootProject.getDependencies().add(HELPER_CONFIGURATION_NAME, dependency);
344-
}
341+
rootProject.getDependencies().add(
342+
helperConfiguration.getName(),
343+
distribution.getGroup() + ":" +
344+
distribution.getArtifactName() + ":" +
345+
version +
346+
(distribution.getClassifier().isEmpty() ? "" : ":" + distribution.getClassifier()) + "@" +
347+
distribution.getFileExtension());
348+
345349
}
350+
351+
sync.getInputs().files(helperConfiguration);
352+
// NOTE: Gradle doesn't allow a lambda here ( fails at runtime )
353+
sync.doLast(new Action<Task>() {
354+
@Override
355+
public void execute(Task task) {
356+
project.copy(spec ->
357+
helperConfiguration.getResolvedConfiguration().getResolvedArtifacts().forEach(resolvedArtifact -> {
358+
final FileTree files;
359+
File file = resolvedArtifact.getFile();
360+
if (file.getName().endsWith(".zip")) {
361+
files = project.zipTree(file);
362+
} else if (file.getName().endsWith("tar.gz")) {
363+
files = project.tarTree(file);
364+
} else {
365+
throw new IllegalArgumentException("Can't extract " + file + " unknown file extension");
366+
}
367+
368+
spec.from(files, s -> s.into(resolvedArtifact.getModuleVersion().getId().getGroup()));
369+
spec.into(getExtractDir(project));
370+
}));
371+
}
372+
});
346373
})));
347374
}
348375

buildSrc/src/main/java/org/elasticsearch/gradle/tool/Boilerplate.java

+19
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,33 @@
1818
*/
1919
package org.elasticsearch.gradle.tool;
2020

21+
import org.gradle.api.Action;
22+
import org.gradle.api.NamedDomainObjectContainer;
2123
import org.gradle.api.Project;
2224
import org.gradle.api.plugins.JavaPluginConvention;
2325
import org.gradle.api.tasks.SourceSetContainer;
2426

27+
import java.util.Optional;
28+
2529
public abstract class Boilerplate {
2630

2731
public static SourceSetContainer getJavaSourceSets(Project project) {
2832
return project.getConvention().getPlugin(JavaPluginConvention.class).getSourceSets();
2933
}
3034

35+
public static <T> T maybeCreate(NamedDomainObjectContainer<T> collection, String name) {
36+
return Optional.ofNullable(collection.findByName(name))
37+
.orElse(collection.create(name));
38+
39+
}
40+
public static <T> T maybeCreate(NamedDomainObjectContainer<T> collection, String name, Action<T> action) {
41+
return Optional.ofNullable(collection.findByName(name))
42+
.orElseGet(() -> {
43+
T result = collection.create(name);
44+
action.execute(result);
45+
return result;
46+
});
47+
48+
}
49+
3150
}

buildSrc/src/test/java/org/elasticsearch/gradle/testclusters/TestClustersPluginIT.java

+8
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ public void testMultiProject() {
103103
);
104104
}
105105

106+
public void testReleased() {
107+
BuildResult result = getTestClustersRunner("testReleased").build();
108+
assertTaskSuccessful(result, ":testReleased");
109+
assertStartedAndStoppedOnce(result, "releasedVersionDefault-1");
110+
assertStartedAndStoppedOnce(result, "releasedVersionOSS-1");
111+
assertStartedAndStoppedOnce(result, "releasedVersionIntegTest-1");
112+
}
113+
106114
public void testIncremental() {
107115
BuildResult result = getTestClustersRunner("clean", ":user1").build();
108116
assertTaskSuccessful(result, ":user1");

buildSrc/src/testKit/testclusters/build.gradle

+26-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ allprojects { all ->
1717
url "https://s3.amazonaws.com/download.elasticsearch.org/lucenesnapshots/" + luceneSnapshotRevision
1818
}
1919
}
20-
21-
jcenter()
2220
}
2321

2422
if (project == rootProject || project.name == "alpha" || project.name == "bravo") {
@@ -58,6 +56,21 @@ testClusters {
5856
javaHome = file(System.getProperty('java.home'))
5957
numberOfNodes = 3
6058
}
59+
releasedVersionDefault {
60+
version = "7.0.0"
61+
distribution = 'DEFAULT'
62+
javaHome = file(System.getProperty('java.home'))
63+
}
64+
releasedVersionOSS {
65+
version = "7.0.0"
66+
distribution = 'OSS'
67+
javaHome = file(System.getProperty('java.home'))
68+
}
69+
releasedVersionIntegTest {
70+
version = "7.0.0"
71+
distribution = 'INTEG_TEST'
72+
javaHome = file(System.getProperty('java.home'))
73+
}
6174
}
6275

6376
task multiNode {
@@ -67,6 +80,17 @@ task multiNode {
6780
}
6881
}
6982

83+
task testReleased {
84+
useCluster testClusters.releasedVersionDefault
85+
useCluster testClusters.releasedVersionOSS
86+
useCluster testClusters.releasedVersionIntegTest
87+
doFirst {
88+
println "$path: Cluster running @ ${testClusters.releasedVersionDefault.httpSocketURI}"
89+
println "$path: Cluster running @ ${testClusters.releasedVersionOSS.httpSocketURI}"
90+
println "$path: Cluster running @ ${testClusters.releasedVersionIntegTest.httpSocketURI}"
91+
}
92+
}
93+
7094
task printLog {
7195
useCluster testClusters.myTestCluster
7296
doFirst {

0 commit comments

Comments
 (0)