|
@@ -18,6 +18,7 @@
|
|
|
*/
|
|
|
package org.elasticsearch.gradle.testclusters;
|
|
|
|
|
|
+import org.elasticsearch.gradle.DistributionDownloadPlugin;
|
|
|
import org.elasticsearch.gradle.ElasticsearchDistribution;
|
|
|
import org.elasticsearch.gradle.FileSupplier;
|
|
|
import org.elasticsearch.gradle.LazyPropertyList;
|
|
@@ -31,8 +32,8 @@ import org.elasticsearch.gradle.VersionProperties;
|
|
|
import org.elasticsearch.gradle.http.WaitForHttpResource;
|
|
|
import org.gradle.api.Action;
|
|
|
import org.gradle.api.Named;
|
|
|
+import org.gradle.api.NamedDomainObjectContainer;
|
|
|
import org.gradle.api.Project;
|
|
|
-import org.gradle.api.file.FileCollection;
|
|
|
import org.gradle.api.logging.Logger;
|
|
|
import org.gradle.api.logging.Logging;
|
|
|
import org.gradle.api.tasks.Classpath;
|
|
@@ -71,6 +72,7 @@ import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
import java.util.Set;
|
|
|
+import java.util.TreeSet;
|
|
|
import java.util.concurrent.ExecutionException;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.concurrent.TimeoutException;
|
|
@@ -135,23 +137,23 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
private final Path esStdoutFile;
|
|
|
private final Path esStderrFile;
|
|
|
private final Path tmpDir;
|
|
|
+ private final Path distroDir;
|
|
|
|
|
|
- private String version;
|
|
|
+ private int currentDistro = 0;
|
|
|
private TestDistribution testDistribution;
|
|
|
- private ElasticsearchDistribution distribution;
|
|
|
+ private List<ElasticsearchDistribution> distributions = new ArrayList<>();
|
|
|
private File javaHome;
|
|
|
private volatile Process esProcess;
|
|
|
private Function<String, String> nameCustomization = Function.identity();
|
|
|
private boolean isWorkingDirConfigured = false;
|
|
|
|
|
|
- ElasticsearchNode(String path, String name, Project project, ReaperService reaper, File workingDirBase,
|
|
|
- ElasticsearchDistribution distribution) {
|
|
|
+ ElasticsearchNode(String path, String name, Project project, ReaperService reaper, File workingDirBase) {
|
|
|
this.path = path;
|
|
|
this.name = name;
|
|
|
this.project = project;
|
|
|
this.reaper = reaper;
|
|
|
- this.workingDir = workingDirBase.toPath().resolve(safeName(name)).toAbsolutePath();
|
|
|
- this.distribution = distribution;
|
|
|
+ workingDir = workingDirBase.toPath().resolve(safeName(name)).toAbsolutePath();
|
|
|
+ distroDir = workingDir.resolve("distro");
|
|
|
confPathRepo = workingDir.resolve("repo");
|
|
|
configFile = workingDir.resolve("config/elasticsearch.yml");
|
|
|
confPathData = workingDir.resolve("data");
|
|
@@ -173,15 +175,31 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
|
|
|
@Internal
|
|
|
public Version getVersion() {
|
|
|
- return distribution.getVersion();
|
|
|
+ return distributions.get(currentDistro).getVersion();
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void setVersion(String version) {
|
|
|
requireNonNull(version, "null version passed when configuring test cluster `" + this + "`");
|
|
|
+ String distroName = "testclusters" + path.replace(":", "-") + "-" + this.name + "-" + version + "-";
|
|
|
+ NamedDomainObjectContainer<ElasticsearchDistribution> container = DistributionDownloadPlugin.getContainer(project);
|
|
|
+ if (container.findByName(distroName) == null){
|
|
|
+ container.create(distroName);
|
|
|
+ }
|
|
|
+ ElasticsearchDistribution distro = container.getByName(distroName);
|
|
|
+ distro.setVersion(version);
|
|
|
+ setDistributionType(distro, testDistribution);
|
|
|
+ distributions.add(distro);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setVersions(List<String> versions) {
|
|
|
+ requireNonNull(versions, "null version list passed when configuring test cluster `" + this + "`");
|
|
|
checkFrozen();
|
|
|
- this.version = version;
|
|
|
- this.distribution.setVersion(version);
|
|
|
+ distributions.clear();
|
|
|
+ for (String version : versions) {
|
|
|
+ setVersion(version);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Internal
|
|
@@ -191,8 +209,8 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
|
|
|
// package private just so test clusters plugin can access to wire up task dependencies
|
|
|
@Internal
|
|
|
- ElasticsearchDistribution getDistribution() {
|
|
|
- return distribution;
|
|
|
+ List<ElasticsearchDistribution> getDistributions() {
|
|
|
+ return distributions;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -200,14 +218,20 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
requireNonNull(testDistribution, "null distribution passed when configuring test cluster `" + this + "`");
|
|
|
checkFrozen();
|
|
|
this.testDistribution = testDistribution;
|
|
|
+ for (ElasticsearchDistribution distribution : distributions) {
|
|
|
+ setDistributionType(distribution, testDistribution);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void setDistributionType(ElasticsearchDistribution distribution, TestDistribution testDistribution) {
|
|
|
if (testDistribution == TestDistribution.INTEG_TEST) {
|
|
|
- this.distribution.setType(ElasticsearchDistribution.Type.INTEG_TEST_ZIP);
|
|
|
+ distribution.setType(ElasticsearchDistribution.Type.INTEG_TEST_ZIP);
|
|
|
} else {
|
|
|
- this.distribution.setType(ElasticsearchDistribution.Type.ARCHIVE);
|
|
|
+ distribution.setType(ElasticsearchDistribution.Type.ARCHIVE);
|
|
|
if (testDistribution == TestDistribution.DEFAULT) {
|
|
|
- this.distribution.setFlavor(ElasticsearchDistribution.Flavor.DEFAULT);
|
|
|
+ distribution.setFlavor(ElasticsearchDistribution.Flavor.DEFAULT);
|
|
|
} else {
|
|
|
- this.distribution.setFlavor(ElasticsearchDistribution.Flavor.OSS);
|
|
|
+ distribution.setFlavor(ElasticsearchDistribution.Flavor.OSS);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -317,8 +341,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
|
|
|
@Override
|
|
|
public void freeze() {
|
|
|
- requireNonNull(distribution, "null distribution passed when configuring test cluster `" + this + "`");
|
|
|
- requireNonNull(getVersion(), "null version passed when configuring test cluster `" + this + "`");
|
|
|
+ requireNonNull(distributions, "null distribution passed when configuring test cluster `" + this + "`");
|
|
|
requireNonNull(javaHome, "null javaHome passed when configuring test cluster `" + this + "`");
|
|
|
LOGGER.info("Locking configuration of `{}`", this);
|
|
|
configurationFrozen.set(true);
|
|
@@ -361,10 +384,13 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
try {
|
|
|
if (isWorkingDirConfigured == false) {
|
|
|
logToProcessStdout("Configuring working directory: " + workingDir);
|
|
|
- // Only configure working dir once so we don't lose data on restarts
|
|
|
+ // make sure we always start fresh
|
|
|
+ if (Files.exists(workingDir)) {
|
|
|
+ project.delete(workingDir);
|
|
|
+ }
|
|
|
isWorkingDirConfigured = true;
|
|
|
- createWorkingDir(getExtractedDistributionDir());
|
|
|
}
|
|
|
+ createWorkingDir(getExtractedDistributionDir());
|
|
|
} catch (IOException e) {
|
|
|
throw new UncheckedIOException("Failed to create working directory for " + this, e);
|
|
|
}
|
|
@@ -446,6 +472,18 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
start();
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void goToNextVersion() {
|
|
|
+ if (currentDistro + 1 >= distributions.size()) {
|
|
|
+ throw new TestClustersException("Ran out of versions to go to for " + this);
|
|
|
+ }
|
|
|
+ LOGGER.info("Switch version from {} to {} for {}",
|
|
|
+ getVersion(), distributions.get(currentDistro + 1).getVersion(), this
|
|
|
+ );
|
|
|
+ currentDistro += 1;
|
|
|
+ restart();
|
|
|
+ }
|
|
|
+
|
|
|
private boolean isSettingMissingOrTrue(String name) {
|
|
|
return Boolean.valueOf(settings.getOrDefault(name, "false").toString());
|
|
|
}
|
|
@@ -474,8 +512,9 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
if (testDistribution == TestDistribution.INTEG_TEST) {
|
|
|
logToProcessStdout("Installing " + modules.size() + "modules");
|
|
|
for (File module : modules) {
|
|
|
- Path destination = workingDir.resolve("modules").resolve(module.getName().replace(".zip", "")
|
|
|
- .replace("-" + version, ""));
|
|
|
+ Path destination = distroDir.resolve("modules").resolve(module.getName().replace(".zip", "")
|
|
|
+ .replace("-" + getVersion(), "")
|
|
|
+ .replace("-SNAPSHOT", ""));
|
|
|
|
|
|
// only install modules that are not already bundled with the integ-test distribution
|
|
|
if (Files.exists(destination) == false) {
|
|
@@ -492,7 +531,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- LOGGER.info("Not installing " + modules.size() + "(s) since the " + distribution + " distribution already " +
|
|
|
+ LOGGER.info("Not installing " + modules.size() + "(s) since the " + distributions + " distribution already " +
|
|
|
"has them");
|
|
|
}
|
|
|
}
|
|
@@ -533,8 +572,8 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
|
|
|
private void runElaticsearchBinScriptWithInput(String input, String tool, String... args) {
|
|
|
if (
|
|
|
- Files.exists(workingDir.resolve("bin").resolve(tool)) == false &&
|
|
|
- Files.exists(workingDir.resolve("bin").resolve(tool + ".bat")) == false
|
|
|
+ Files.exists(distroDir.resolve("bin").resolve(tool)) == false &&
|
|
|
+ Files.exists(distroDir.resolve("bin").resolve(tool + ".bat")) == false
|
|
|
) {
|
|
|
throw new TestClustersException("Can't run bin script: `" + tool + "` does not exist. " +
|
|
|
"Is this the distribution you expect it to be ?");
|
|
@@ -542,7 +581,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
try (InputStream byteArrayInputStream = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) {
|
|
|
LoggedExec.exec(project, spec -> {
|
|
|
spec.setEnvironment(getESEnvironment());
|
|
|
- spec.workingDir(workingDir);
|
|
|
+ spec.workingDir(distroDir);
|
|
|
spec.executable(
|
|
|
OS.conditionalString()
|
|
|
.onUnix(() -> "./bin/" + tool)
|
|
@@ -620,8 +659,8 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
final ProcessBuilder processBuilder = new ProcessBuilder();
|
|
|
|
|
|
List<String> command = OS.<List<String>>conditional()
|
|
|
- .onUnix(() -> Arrays.asList("./bin/elasticsearch"))
|
|
|
- .onWindows(() -> Arrays.asList("cmd", "/c", "bin\\elasticsearch.bat"))
|
|
|
+ .onUnix(() -> Arrays.asList(distroDir.getFileName().resolve("./bin/elasticsearch").toString()))
|
|
|
+ .onWindows(() -> Arrays.asList("cmd", "/c", distroDir.getFileName().resolve("bin\\elasticsearch.bat").toString()))
|
|
|
.supply();
|
|
|
processBuilder.command(command);
|
|
|
processBuilder.directory(workingDir.toFile());
|
|
@@ -821,7 +860,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
}
|
|
|
|
|
|
private void createWorkingDir(Path distroExtractDir) throws IOException {
|
|
|
- syncWithLinks(distroExtractDir, workingDir);
|
|
|
+ syncWithLinks(distroExtractDir, distroDir);
|
|
|
Files.createDirectories(configFile.getParent());
|
|
|
Files.createDirectories(confPathRepo);
|
|
|
Files.createDirectories(confPathData);
|
|
@@ -844,7 +883,14 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
|
|
|
try (Stream<Path> stream = Files.walk(sourceRoot)) {
|
|
|
stream.forEach(source -> {
|
|
|
- Path destination = destinationRoot.resolve(sourceRoot.relativize(source));
|
|
|
+ Path relativeDestination = sourceRoot.relativize(source);
|
|
|
+ if (relativeDestination.getNameCount() <= 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // Throw away the first name as the archives have everything in a single top level folder we are not interested in
|
|
|
+ relativeDestination = relativeDestination.subpath(1, relativeDestination.getNameCount());
|
|
|
+
|
|
|
+ Path destination = destinationRoot.resolve(relativeDestination);
|
|
|
if (Files.isDirectory(source)) {
|
|
|
try {
|
|
|
Files.createDirectories(destination);
|
|
@@ -920,9 +966,6 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
.forEach(defaultConfig::remove);
|
|
|
|
|
|
try {
|
|
|
- // We create hard links for the distribution, so we need to remove the config file before writing it
|
|
|
- // to prevent the changes to reflect across all copies.
|
|
|
- Files.delete(configFile);
|
|
|
Files.write(
|
|
|
configFile,
|
|
|
Stream.concat(
|
|
@@ -931,8 +974,21 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
)
|
|
|
.map(entry -> entry.getKey() + ": " + entry.getValue())
|
|
|
.collect(Collectors.joining("\n"))
|
|
|
- .getBytes(StandardCharsets.UTF_8)
|
|
|
+ .getBytes(StandardCharsets.UTF_8),
|
|
|
+ StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE
|
|
|
);
|
|
|
+
|
|
|
+ final List<Path> configFiles;
|
|
|
+ try (Stream<Path> stream = Files.list(distroDir.resolve("config"))) {
|
|
|
+ configFiles = stream.collect(Collectors.toList());
|
|
|
+ }
|
|
|
+ logToProcessStdout("Copying additional config files from distro " + configFiles);
|
|
|
+ for (Path file : configFiles) {
|
|
|
+ Path dest = configFile.getParent().resolve(file.getFileName());
|
|
|
+ if (Files.exists(dest) == false) {
|
|
|
+ Files.copy(file, dest);
|
|
|
+ }
|
|
|
+ }
|
|
|
} catch (IOException e) {
|
|
|
throw new UncheckedIOException("Could not write config file: " + configFile, e);
|
|
|
}
|
|
@@ -972,7 +1028,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
}
|
|
|
|
|
|
private Path getExtractedDistributionDir() {
|
|
|
- return Paths.get(distribution.getExtracted().toString()).resolve("elasticsearch-" + version);
|
|
|
+ return Paths.get(distributions.get(currentDistro).getExtracted().toString());
|
|
|
}
|
|
|
|
|
|
private List<File> getInstalledFileSet(Action<? super PatternFilterable> filter) {
|
|
@@ -1007,19 +1063,26 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
|
|
}
|
|
|
|
|
|
@Classpath
|
|
|
- private List<File> getDistributionClasspath() {
|
|
|
- ArrayList<File> files = new ArrayList<>(project.fileTree(getExtractedDistributionDir())
|
|
|
- .matching(filter -> filter.include("**/*.jar"))
|
|
|
- .getFiles());
|
|
|
- files.sort(Comparator.comparing(File::getName));
|
|
|
-
|
|
|
- return files;
|
|
|
+ private Set<File> getDistributionClasspath() {
|
|
|
+ return getDistributionFiles(filter -> filter.include("**/*.jar"));
|
|
|
}
|
|
|
|
|
|
@InputFiles
|
|
|
@PathSensitive(PathSensitivity.RELATIVE)
|
|
|
- private FileCollection getDistributionFiles() {
|
|
|
- return project.fileTree(getExtractedDistributionDir()).minus(project.files(getDistributionClasspath()));
|
|
|
+ private Set<File> getDistributionFiles() {
|
|
|
+ return getDistributionFiles(filter -> filter.exclude("**/*.jar"));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Set<File> getDistributionFiles(Action<PatternFilterable> patternFilter) {
|
|
|
+ Set<File> files = new TreeSet<>();
|
|
|
+ for (ElasticsearchDistribution distribution : distributions) {
|
|
|
+ files.addAll(
|
|
|
+ project.fileTree(Paths.get(distribution.getExtracted().toString()))
|
|
|
+ .matching(patternFilter)
|
|
|
+ .getFiles()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return files;
|
|
|
}
|
|
|
|
|
|
@Nested
|