Restrict which tasks can use testclusters (#45198)
* Restrict which tasks can use testclusters This PR fixes a problem between the interaction of test-clusters and build cache. Before this any task could have used a cluster without tracking it as input. With this change a new interface is introduced to track the tasks that can use clusters and we do consider the cluster as input for all of them.
This commit is contained in:
parent
5ddeb488a6
commit
634a070430
|
@ -20,6 +20,7 @@ package org.elasticsearch.gradle.test
|
||||||
|
|
||||||
import org.elasticsearch.gradle.VersionProperties
|
import org.elasticsearch.gradle.VersionProperties
|
||||||
import org.elasticsearch.gradle.testclusters.ElasticsearchCluster
|
import org.elasticsearch.gradle.testclusters.ElasticsearchCluster
|
||||||
|
import org.elasticsearch.gradle.testclusters.RestTestRunnerTask
|
||||||
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
|
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
|
||||||
import org.elasticsearch.gradle.tool.ClasspathUtils
|
import org.elasticsearch.gradle.tool.ClasspathUtils
|
||||||
import org.gradle.api.DefaultTask
|
import org.gradle.api.DefaultTask
|
||||||
|
@ -49,8 +50,6 @@ class RestIntegTestTask extends DefaultTask {
|
||||||
|
|
||||||
protected Test runner
|
protected Test runner
|
||||||
|
|
||||||
protected Task clusterInit
|
|
||||||
|
|
||||||
/** Info about nodes in the integ test cluster. Note this is *not* available until runtime. */
|
/** Info about nodes in the integ test cluster. Note this is *not* available until runtime. */
|
||||||
List<NodeInfo> nodes
|
List<NodeInfo> nodes
|
||||||
|
|
||||||
|
@ -61,8 +60,6 @@ class RestIntegTestTask extends DefaultTask {
|
||||||
RestIntegTestTask() {
|
RestIntegTestTask() {
|
||||||
runner = project.tasks.create("${name}Runner", RestTestRunnerTask.class)
|
runner = project.tasks.create("${name}Runner", RestTestRunnerTask.class)
|
||||||
super.dependsOn(runner)
|
super.dependsOn(runner)
|
||||||
clusterInit = project.tasks.create(name: "${name}Cluster#init", dependsOn: project.testClasses)
|
|
||||||
runner.dependsOn(clusterInit)
|
|
||||||
boolean usesTestclusters = project.plugins.hasPlugin(TestClustersPlugin.class)
|
boolean usesTestclusters = project.plugins.hasPlugin(TestClustersPlugin.class)
|
||||||
if (usesTestclusters == false) {
|
if (usesTestclusters == false) {
|
||||||
clusterConfig = project.extensions.create("${name}Cluster", ClusterConfiguration.class, project)
|
clusterConfig = project.extensions.create("${name}Cluster", ClusterConfiguration.class, project)
|
||||||
|
@ -75,8 +72,6 @@ class RestIntegTestTask extends DefaultTask {
|
||||||
runner.useCluster project.testClusters."$name"
|
runner.useCluster project.testClusters."$name"
|
||||||
}
|
}
|
||||||
|
|
||||||
// override/add more for rest tests
|
|
||||||
runner.maxParallelForks = 1
|
|
||||||
runner.include('**/*IT.class')
|
runner.include('**/*IT.class')
|
||||||
runner.systemProperty('tests.rest.load_packaged', 'false')
|
runner.systemProperty('tests.rest.load_packaged', 'false')
|
||||||
|
|
||||||
|
@ -134,7 +129,6 @@ class RestIntegTestTask extends DefaultTask {
|
||||||
project.gradle.projectsEvaluated {
|
project.gradle.projectsEvaluated {
|
||||||
if (enabled == false) {
|
if (enabled == false) {
|
||||||
runner.enabled = false
|
runner.enabled = false
|
||||||
clusterInit.enabled = false
|
|
||||||
return // no need to add cluster formation tasks if the task won't run!
|
return // no need to add cluster formation tasks if the task won't run!
|
||||||
}
|
}
|
||||||
if (usesTestclusters == false) {
|
if (usesTestclusters == false) {
|
||||||
|
@ -185,11 +179,6 @@ class RestIntegTestTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Task mustRunAfter(Object... tasks) {
|
|
||||||
clusterInit.mustRunAfter(tasks)
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runner(Closure configure) {
|
public void runner(Closure configure) {
|
||||||
project.tasks.getByName("${name}Runner").configure(configure)
|
project.tasks.getByName("${name}Runner").configure(configure)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.elasticsearch.gradle.testclusters;
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
public class DefaultTestClustersTask extends DefaultTask implements TestClustersAware {
|
||||||
|
|
||||||
|
private Collection<ElasticsearchCluster> clusters = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ElasticsearchCluster> getClusters() {
|
||||||
|
return clusters;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -117,6 +117,10 @@ public class ElasticsearchCluster implements TestClusterConfiguration, Named {
|
||||||
return clusterName;
|
return clusterName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVersion(String version) {
|
public void setVersion(String version) {
|
||||||
nodes.all(each -> each.setVersion(version));
|
nodes.all(each -> each.setVersion(version));
|
||||||
|
|
|
@ -216,6 +216,9 @@ public class ElasticsearchNode implements TestClusterConfiguration {
|
||||||
public void plugin(URI plugin) {
|
public void plugin(URI plugin) {
|
||||||
requireNonNull(plugin, "Plugin name can't be null");
|
requireNonNull(plugin, "Plugin name can't be null");
|
||||||
checkFrozen();
|
checkFrozen();
|
||||||
|
if (plugins.contains(plugin)) {
|
||||||
|
throw new TestClustersException("Plugin already configured for installation " + plugin);
|
||||||
|
}
|
||||||
this.plugins.add(plugin);
|
this.plugins.add(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package org.elasticsearch.gradle.test;
|
package org.elasticsearch.gradle.testclusters;
|
||||||
|
|
||||||
import org.elasticsearch.gradle.testclusters.ElasticsearchCluster;
|
|
||||||
import org.gradle.api.tasks.CacheableTask;
|
import org.gradle.api.tasks.CacheableTask;
|
||||||
import org.gradle.api.tasks.Nested;
|
import org.gradle.api.tasks.Nested;
|
||||||
import org.gradle.api.tasks.testing.Test;
|
import org.gradle.api.tasks.testing.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import static org.elasticsearch.gradle.testclusters.TestDistribution.INTEG_TEST;
|
import static org.elasticsearch.gradle.testclusters.TestDistribution.INTEG_TEST;
|
||||||
|
|
||||||
|
@ -16,9 +15,9 @@ import static org.elasticsearch.gradle.testclusters.TestDistribution.INTEG_TEST;
|
||||||
* {@link Nested} inputs.
|
* {@link Nested} inputs.
|
||||||
*/
|
*/
|
||||||
@CacheableTask
|
@CacheableTask
|
||||||
public class RestTestRunnerTask extends Test {
|
public class RestTestRunnerTask extends Test implements TestClustersAware {
|
||||||
|
|
||||||
private Collection<ElasticsearchCluster> clusters = new ArrayList<>();
|
private Collection<ElasticsearchCluster> clusters = new HashSet<>();
|
||||||
|
|
||||||
public RestTestRunnerTask() {
|
public RestTestRunnerTask() {
|
||||||
super();
|
super();
|
||||||
|
@ -26,12 +25,15 @@ public class RestTestRunnerTask extends Test {
|
||||||
task -> clusters.stream().flatMap(c -> c.getNodes().stream()).anyMatch(n -> n.getTestDistribution() != INTEG_TEST));
|
task -> clusters.stream().flatMap(c -> c.getNodes().stream()).anyMatch(n -> n.getTestDistribution() != INTEG_TEST));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxParallelForks() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
|
@Override
|
||||||
public Collection<ElasticsearchCluster> getClusters() {
|
public Collection<ElasticsearchCluster> getClusters() {
|
||||||
return clusters;
|
return clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCluster(ElasticsearchCluster cluster) {
|
|
||||||
this.clusters.add(cluster);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.elasticsearch.gradle.testclusters;
|
||||||
|
|
||||||
|
import org.gradle.api.Task;
|
||||||
|
import org.gradle.api.tasks.Nested;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
interface TestClustersAware extends Task {
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
Collection<ElasticsearchCluster> getClusters();
|
||||||
|
|
||||||
|
default void useCluster(ElasticsearchCluster cluster) {
|
||||||
|
if (cluster.getPath().equals(getProject().getPath()) == false) {
|
||||||
|
throw new TestClustersException(
|
||||||
|
"Task " + getPath() + " can't use test cluster from" +
|
||||||
|
" another project " + cluster
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ElasticsearchNode node : cluster.getNodes()) {
|
||||||
|
this.dependsOn(node.getDistribution().getExtracted());
|
||||||
|
}
|
||||||
|
getClusters().add(cluster);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,32 +18,22 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.gradle.testclusters;
|
package org.elasticsearch.gradle.testclusters;
|
||||||
|
|
||||||
import groovy.lang.Closure;
|
|
||||||
import org.elasticsearch.gradle.DistributionDownloadPlugin;
|
import org.elasticsearch.gradle.DistributionDownloadPlugin;
|
||||||
import org.elasticsearch.gradle.ElasticsearchDistribution;
|
import org.elasticsearch.gradle.ElasticsearchDistribution;
|
||||||
import org.elasticsearch.gradle.ReaperPlugin;
|
import org.elasticsearch.gradle.ReaperPlugin;
|
||||||
import org.elasticsearch.gradle.ReaperService;
|
import org.elasticsearch.gradle.ReaperService;
|
||||||
import org.elasticsearch.gradle.test.RestTestRunnerTask;
|
|
||||||
import org.gradle.api.NamedDomainObjectContainer;
|
import org.gradle.api.NamedDomainObjectContainer;
|
||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.Task;
|
import org.gradle.api.Task;
|
||||||
import org.gradle.api.execution.TaskActionListener;
|
import org.gradle.api.execution.TaskActionListener;
|
||||||
import org.gradle.api.execution.TaskExecutionListener;
|
import org.gradle.api.execution.TaskExecutionListener;
|
||||||
|
import org.gradle.api.invocation.Gradle;
|
||||||
import org.gradle.api.logging.Logger;
|
import org.gradle.api.logging.Logger;
|
||||||
import org.gradle.api.logging.Logging;
|
import org.gradle.api.logging.Logging;
|
||||||
import org.gradle.api.plugins.ExtraPropertiesExtension;
|
|
||||||
import org.gradle.api.tasks.TaskState;
|
import org.gradle.api.tasks.TaskState;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class TestClustersPlugin implements Plugin<Project> {
|
public class TestClustersPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@ -51,12 +41,6 @@ public class TestClustersPlugin implements Plugin<Project> {
|
||||||
public static final String EXTENSION_NAME = "testClusters";
|
public static final String EXTENSION_NAME = "testClusters";
|
||||||
|
|
||||||
private static final Logger logger = Logging.getLogger(TestClustersPlugin.class);
|
private static final Logger logger = Logging.getLogger(TestClustersPlugin.class);
|
||||||
private static final String TESTCLUSTERS_INSPECT_FAILURE = "testclusters.inspect.failure";
|
|
||||||
|
|
||||||
private final Map<Task, List<ElasticsearchCluster>> usedClusters = new HashMap<>();
|
|
||||||
private final Map<ElasticsearchCluster, Integer> claimsInventory = new HashMap<>();
|
|
||||||
private final Set<ElasticsearchCluster> runningClusters = new HashSet<>();
|
|
||||||
private final Boolean allowClusterToSurvive = Boolean.valueOf(System.getProperty(TESTCLUSTERS_INSPECT_FAILURE, "false"));
|
|
||||||
|
|
||||||
private ReaperService reaper;
|
private ReaperService reaper;
|
||||||
|
|
||||||
|
@ -73,20 +57,22 @@ public class TestClustersPlugin implements Plugin<Project> {
|
||||||
// provide a task to be able to list defined clusters.
|
// provide a task to be able to list defined clusters.
|
||||||
createListClustersTask(project, container);
|
createListClustersTask(project, container);
|
||||||
|
|
||||||
// create DSL for tasks to mark clusters these use
|
if (project.getRootProject().getExtensions().findByType(TestClustersRegistry.class) == null) {
|
||||||
createUseClusterTaskExtension(project, container);
|
TestClustersRegistry registry = project.getRootProject().getExtensions()
|
||||||
|
.create("testClusters", TestClustersRegistry.class);
|
||||||
|
|
||||||
// When we know what tasks will run, we claim the clusters of those task to differentiate between clusters
|
// When we know what tasks will run, we claim the clusters of those task to differentiate between clusters
|
||||||
// that are defined in the build script and the ones that will actually be used in this invocation of gradle
|
// that are defined in the build script and the ones that will actually be used in this invocation of gradle
|
||||||
// we use this information to determine when the last task that required the cluster executed so that we can
|
// we use this information to determine when the last task that required the cluster executed so that we can
|
||||||
// terminate the cluster right away and free up resources.
|
// terminate the cluster right away and free up resources.
|
||||||
configureClaimClustersHook(project);
|
configureClaimClustersHook(project.getGradle(), registry);
|
||||||
|
|
||||||
// Before each task, we determine if a cluster needs to be started for that task.
|
// Before each task, we determine if a cluster needs to be started for that task.
|
||||||
configureStartClustersHook(project);
|
configureStartClustersHook(project.getGradle(), registry);
|
||||||
|
|
||||||
// After each task we determine if there are clusters that are no longer needed.
|
// After each task we determine if there are clusters that are no longer needed.
|
||||||
configureStopClustersHook(project);
|
configureStopClustersHook(project.getGradle(), registry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private NamedDomainObjectContainer<ElasticsearchCluster> createTestClustersContainerExtension(Project project) {
|
private NamedDomainObjectContainer<ElasticsearchCluster> createTestClustersContainerExtension(Project project) {
|
||||||
|
@ -120,78 +106,28 @@ public class TestClustersPlugin implements Plugin<Project> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createUseClusterTaskExtension(Project project, NamedDomainObjectContainer<ElasticsearchCluster> container) {
|
private static void configureClaimClustersHook(Gradle gradle, TestClustersRegistry registry) {
|
||||||
// register an extension for all current and future tasks, so that any task can declare that it wants to use a
|
|
||||||
// specific cluster.
|
|
||||||
project.getTasks().configureEach((Task task) ->
|
|
||||||
task.getExtensions().findByType(ExtraPropertiesExtension.class)
|
|
||||||
.set(
|
|
||||||
"useCluster",
|
|
||||||
new Closure<Void>(project, task) {
|
|
||||||
public void doCall(ElasticsearchCluster cluster) {
|
|
||||||
if (container.contains(cluster) == false) {
|
|
||||||
throw new TestClustersException(
|
|
||||||
"Task " + task.getPath() + " can't use test cluster from" +
|
|
||||||
" another project " + cluster
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Object thisObject = this.getThisObject();
|
|
||||||
if (thisObject instanceof Task == false) {
|
|
||||||
throw new AssertionError("Expected " + thisObject + " to be an instance of " +
|
|
||||||
"Task, but got: " + thisObject.getClass());
|
|
||||||
}
|
|
||||||
usedClusters.computeIfAbsent(task, k -> new ArrayList<>()).add(cluster);
|
|
||||||
for (ElasticsearchNode node : cluster.getNodes()) {
|
|
||||||
((Task) thisObject).dependsOn(node.getDistribution().getExtracted());
|
|
||||||
}
|
|
||||||
if (thisObject instanceof RestTestRunnerTask) {
|
|
||||||
((RestTestRunnerTask) thisObject).testCluster(cluster);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureClaimClustersHook(Project project) {
|
|
||||||
// Once we know all the tasks that need to execute, we claim all the clusters that belong to those and count the
|
// Once we know all the tasks that need to execute, we claim all the clusters that belong to those and count the
|
||||||
// claims so we'll know when it's safe to stop them.
|
// claims so we'll know when it's safe to stop them.
|
||||||
project.getGradle().getTaskGraph().whenReady(taskExecutionGraph -> {
|
gradle.getTaskGraph().whenReady(taskExecutionGraph -> {
|
||||||
Set<String> forExecution = taskExecutionGraph.getAllTasks().stream()
|
taskExecutionGraph.getAllTasks().stream()
|
||||||
.map(Task::getPath)
|
.filter(task -> task instanceof TestClustersAware)
|
||||||
.collect(Collectors.toSet());
|
.map(task -> (TestClustersAware) task)
|
||||||
|
.flatMap(task -> task.getClusters().stream())
|
||||||
usedClusters.forEach((task, listOfClusters) ->
|
.forEach(registry::claimCluster);
|
||||||
listOfClusters.forEach(elasticsearchCluster -> {
|
|
||||||
if (forExecution.contains(task.getPath())) {
|
|
||||||
elasticsearchCluster.freeze();
|
|
||||||
claimsInventory.put(elasticsearchCluster, claimsInventory.getOrDefault(elasticsearchCluster, 0) + 1);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
if (claimsInventory.isEmpty() == false) {
|
|
||||||
logger.info("Claims inventory: {}", claimsInventory);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureStartClustersHook(Project project) {
|
private static void configureStartClustersHook(Gradle gradle, TestClustersRegistry registry) {
|
||||||
project.getGradle().addListener(
|
gradle.addListener(
|
||||||
new TaskActionListener() {
|
new TaskActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeActions(Task task) {
|
public void beforeActions(Task task) {
|
||||||
|
if (task instanceof TestClustersAware == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// we only start the cluster before the actions, so we'll not start it if the task is up-to-date
|
// we only start the cluster before the actions, so we'll not start it if the task is up-to-date
|
||||||
List<ElasticsearchCluster> neededButNotRunning = usedClusters.getOrDefault(
|
((TestClustersAware) task).getClusters().forEach(registry::maybeStartCluster);
|
||||||
task,
|
|
||||||
Collections.emptyList()
|
|
||||||
)
|
|
||||||
.stream()
|
|
||||||
.filter(cluster -> runningClusters.contains(cluster) == false)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
neededButNotRunning
|
|
||||||
.forEach(elasticsearchCluster -> {
|
|
||||||
elasticsearchCluster.start();
|
|
||||||
runningClusters.add(elasticsearchCluster);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void afterActions(Task task) {}
|
public void afterActions(Task task) {}
|
||||||
|
@ -199,43 +135,18 @@ public class TestClustersPlugin implements Plugin<Project> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void configureStopClustersHook(Project project) {
|
private static void configureStopClustersHook(Gradle gradle, TestClustersRegistry registry) {
|
||||||
project.getGradle().addListener(
|
gradle.addListener(
|
||||||
new TaskExecutionListener() {
|
new TaskExecutionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void afterExecute(Task task, TaskState state) {
|
public void afterExecute(Task task, TaskState state) {
|
||||||
// always unclaim the cluster, even if _this_ task is up-to-date, as others might not have been
|
if (task instanceof TestClustersAware == false) {
|
||||||
// and caused the cluster to start.
|
|
||||||
List<ElasticsearchCluster> clustersUsedByTask = usedClusters.getOrDefault(
|
|
||||||
task,
|
|
||||||
Collections.emptyList()
|
|
||||||
);
|
|
||||||
if (clustersUsedByTask.isEmpty()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.info("Clusters were used, stopping and releasing permits");
|
// always unclaim the cluster, even if _this_ task is up-to-date, as others might not have been
|
||||||
final int permitsToRelease;
|
// and caused the cluster to start.
|
||||||
if (state.getFailure() != null) {
|
((TestClustersAware) task).getClusters()
|
||||||
// If the task fails, and other tasks use this cluster, the other task will likely never be
|
.forEach(cluster -> registry.stopCluster(cluster, state.getFailure() != null));
|
||||||
// executed at all, so we will never be called again to un-claim and terminate it.
|
|
||||||
clustersUsedByTask.forEach(cluster -> stopCluster(cluster, true));
|
|
||||||
permitsToRelease = clustersUsedByTask.stream()
|
|
||||||
.map(cluster -> cluster.getNumberOfNodes())
|
|
||||||
.reduce(Integer::sum).get();
|
|
||||||
} else {
|
|
||||||
clustersUsedByTask.forEach(
|
|
||||||
cluster -> claimsInventory.put(cluster, claimsInventory.getOrDefault(cluster, 0) - 1)
|
|
||||||
);
|
|
||||||
List<ElasticsearchCluster> stoppingClusers = claimsInventory.entrySet().stream()
|
|
||||||
.filter(entry -> entry.getValue() == 0)
|
|
||||||
.filter(entry -> runningClusters.contains(entry.getKey()))
|
|
||||||
.map(Map.Entry::getKey)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
stoppingClusers.forEach(cluster -> {
|
|
||||||
stopCluster(cluster, false);
|
|
||||||
runningClusters.remove(cluster);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void beforeExecute(Task task) {}
|
public void beforeExecute(Task task) {}
|
||||||
|
@ -243,25 +154,5 @@ public class TestClustersPlugin implements Plugin<Project> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopCluster(ElasticsearchCluster cluster, boolean taskFailed) {
|
|
||||||
if (allowClusterToSurvive) {
|
|
||||||
logger.info("Not stopping clusters, disabled by property");
|
|
||||||
if (taskFailed) {
|
|
||||||
// task failed or this is the last one to stop
|
|
||||||
for (int i=1 ; ; i += i) {
|
|
||||||
logger.lifecycle(
|
|
||||||
"No more test clusters left to run, going to sleep because {} was set," +
|
|
||||||
" interrupt (^C) to stop clusters.", TESTCLUSTERS_INSPECT_FAILURE
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000 * i);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cluster.stop(taskFailed);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.elasticsearch.gradle.testclusters;
|
||||||
|
|
||||||
|
import org.gradle.api.logging.Logger;
|
||||||
|
import org.gradle.api.logging.Logging;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class TestClustersRegistry {
|
||||||
|
private static final Logger logger = Logging.getLogger(TestClustersRegistry.class);
|
||||||
|
private static final String TESTCLUSTERS_INSPECT_FAILURE = "testclusters.inspect.failure";
|
||||||
|
private final Boolean allowClusterToSurvive = Boolean.valueOf(System.getProperty(TESTCLUSTERS_INSPECT_FAILURE, "false"));
|
||||||
|
private final Map<ElasticsearchCluster, Integer> claimsInventory = new HashMap<>();
|
||||||
|
private final Set<ElasticsearchCluster> runningClusters = new HashSet<>();
|
||||||
|
|
||||||
|
public void claimCluster(ElasticsearchCluster cluster) {
|
||||||
|
cluster.freeze();
|
||||||
|
claimsInventory.put(cluster, claimsInventory.getOrDefault(cluster, 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void maybeStartCluster(ElasticsearchCluster cluster) {
|
||||||
|
if (runningClusters.contains(cluster)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runningClusters.add(cluster);
|
||||||
|
cluster.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopCluster(ElasticsearchCluster cluster, boolean taskFailed) {
|
||||||
|
if (taskFailed) {
|
||||||
|
// If the task fails, and other tasks use this cluster, the other task will likely never be
|
||||||
|
// executed at all, so we will never be called again to un-claim and terminate it.
|
||||||
|
if (allowClusterToSurvive) {
|
||||||
|
logger.info("Not stopping clusters, disabled by property");
|
||||||
|
// task failed or this is the last one to stop
|
||||||
|
for (int i = 1; ; i += i) {
|
||||||
|
logger.lifecycle(
|
||||||
|
"No more test clusters left to run, going to sleep because {} was set," +
|
||||||
|
" interrupt (^C) to stop clusters.", TESTCLUSTERS_INSPECT_FAILURE
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000 * i);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cluster.stop(false);
|
||||||
|
runningClusters.remove(cluster);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int currentClaims = claimsInventory.getOrDefault(cluster, 0) - 1;
|
||||||
|
claimsInventory.put(cluster, currentClaims);
|
||||||
|
|
||||||
|
if (currentClaims <= 0 && runningClusters.contains(cluster)) {
|
||||||
|
cluster.stop(false);
|
||||||
|
runningClusters.remove(cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ import java.util.function.Function;
|
||||||
public class LoggedExec extends Exec {
|
public class LoggedExec extends Exec {
|
||||||
|
|
||||||
private Consumer<Logger> outputLogger;
|
private Consumer<Logger> outputLogger;
|
||||||
|
|
||||||
public LoggedExec() {
|
public LoggedExec() {
|
||||||
|
|
||||||
if (getLogger().isInfoEnabled() == false) {
|
if (getLogger().isInfoEnabled() == false) {
|
||||||
|
|
|
@ -39,8 +39,8 @@ testClusters.'remote-cluster' {
|
||||||
}
|
}
|
||||||
|
|
||||||
task mixedClusterTest(type: RestIntegTestTask) {
|
task mixedClusterTest(type: RestIntegTestTask) {
|
||||||
useCluster testClusters.'remote-cluster'
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters.'remote-cluster'
|
||||||
dependsOn 'remote-cluster'
|
dependsOn 'remote-cluster'
|
||||||
systemProperty 'tests.rest.suite', 'multi_cluster'
|
systemProperty 'tests.rest.suite', 'multi_cluster'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import org.elasticsearch.gradle.LoggedExec
|
import org.elasticsearch.gradle.LoggedExec
|
||||||
import org.elasticsearch.gradle.VersionProperties
|
import org.elasticsearch.gradle.VersionProperties
|
||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
import org.elasticsearch.gradle.testclusters.DefaultTestClustersTask
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
@ -87,7 +88,7 @@ task deploy(type: Copy) {
|
||||||
into "${wildflyInstall}/standalone/deployments"
|
into "${wildflyInstall}/standalone/deployments"
|
||||||
}
|
}
|
||||||
|
|
||||||
task writeElasticsearchProperties {
|
task writeElasticsearchProperties(type: DefaultTestClustersTask) {
|
||||||
onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) }
|
onlyIf { !Os.isFamily(Os.FAMILY_WINDOWS) }
|
||||||
useCluster testClusters.integTest
|
useCluster testClusters.integTest
|
||||||
dependsOn deploy
|
dependsOn deploy
|
||||||
|
|
|
@ -38,8 +38,8 @@ task writeJavaPolicy {
|
||||||
|
|
||||||
task "follow-cluster"(type: RestIntegTestTask) {
|
task "follow-cluster"(type: RestIntegTestTask) {
|
||||||
dependsOn 'writeJavaPolicy', "leader-cluster"
|
dependsOn 'writeJavaPolicy', "leader-cluster"
|
||||||
useCluster testClusters."leader-cluster"
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters."leader-cluster"
|
||||||
systemProperty 'java.security.policy', "file://${policyFile}"
|
systemProperty 'java.security.policy', "file://${policyFile}"
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
||||||
|
|
|
@ -24,8 +24,8 @@ testClusters."leader-cluster" {
|
||||||
|
|
||||||
task "middle-cluster"(type: RestIntegTestTask) {
|
task "middle-cluster"(type: RestIntegTestTask) {
|
||||||
dependsOn "leader-cluster"
|
dependsOn "leader-cluster"
|
||||||
useCluster testClusters."leader-cluster"
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters."leader-cluster"
|
||||||
systemProperty 'tests.target_cluster', 'middle'
|
systemProperty 'tests.target_cluster', 'middle'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host',
|
nonInputProperties.systemProperty 'tests.leader_host',
|
||||||
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
||||||
|
@ -40,9 +40,9 @@ testClusters."middle-cluster" {
|
||||||
|
|
||||||
task 'follow-cluster'(type: RestIntegTestTask) {
|
task 'follow-cluster'(type: RestIntegTestTask) {
|
||||||
dependsOn "leader-cluster", "middle-cluster"
|
dependsOn "leader-cluster", "middle-cluster"
|
||||||
useCluster testClusters."leader-cluster"
|
|
||||||
useCluster testClusters."middle-cluster"
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters."leader-cluster"
|
||||||
|
useCluster testClusters."middle-cluster"
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host',
|
nonInputProperties.systemProperty 'tests.leader_host',
|
||||||
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
||||||
|
|
|
@ -21,8 +21,8 @@ testClusters.'leader-cluster' {
|
||||||
|
|
||||||
task 'follow-cluster'(type: RestIntegTestTask) {
|
task 'follow-cluster'(type: RestIntegTestTask) {
|
||||||
dependsOn 'leader-cluster'
|
dependsOn 'leader-cluster'
|
||||||
useCluster testClusters.'leader-cluster'
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters.'leader-cluster'
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host',
|
nonInputProperties.systemProperty 'tests.leader_host',
|
||||||
{ "${testClusters.'follow-cluster'.getAllHttpSocketURI().get(0)}" }
|
{ "${testClusters.'follow-cluster'.getAllHttpSocketURI().get(0)}" }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import org.elasticsearch.gradle.test.RestIntegTestTask
|
import org.elasticsearch.gradle.test.RestIntegTestTask
|
||||||
|
import org.elasticsearch.gradle.testclusters.RestTestRunnerTask
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.testclusters'
|
apply plugin: 'elasticsearch.testclusters'
|
||||||
apply plugin: 'elasticsearch.standalone-test'
|
apply plugin: 'elasticsearch.standalone-test'
|
||||||
|
@ -20,8 +21,8 @@ testClusters.'leader-cluster' {
|
||||||
|
|
||||||
task 'follow-cluster'(type: RestIntegTestTask) {
|
task 'follow-cluster'(type: RestIntegTestTask) {
|
||||||
dependsOn 'leader-cluster'
|
dependsOn 'leader-cluster'
|
||||||
useCluster testClusters.'leader-cluster'
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters.'leader-cluster'
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host',
|
nonInputProperties.systemProperty 'tests.leader_host',
|
||||||
"${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
"${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
||||||
|
@ -36,12 +37,11 @@ testClusters.'follow-cluster' {
|
||||||
nameCustomization = { 'follow' }
|
nameCustomization = { 'follow' }
|
||||||
}
|
}
|
||||||
|
|
||||||
task followClusterRestartTest(type: Test) {
|
task followClusterRestartTest(type: RestTestRunnerTask) {
|
||||||
dependsOn tasks.'follow-cluster'
|
dependsOn tasks.'follow-cluster'
|
||||||
useCluster testClusters.'leader-cluster'
|
useCluster testClusters.'leader-cluster'
|
||||||
useCluster testClusters.'follow-cluster'
|
useCluster testClusters.'follow-cluster'
|
||||||
|
|
||||||
maxParallelForks = 1
|
|
||||||
systemProperty 'tests.rest.load_packaged', 'false'
|
systemProperty 'tests.rest.load_packaged', 'false'
|
||||||
systemProperty 'tests.target_cluster', 'follow-restart'
|
systemProperty 'tests.target_cluster', 'follow-restart'
|
||||||
doFirst {
|
doFirst {
|
||||||
|
@ -49,7 +49,6 @@ task followClusterRestartTest(type: Test) {
|
||||||
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
||||||
nonInputProperties.systemProperty 'tests.rest.cluster', "${-> testClusters.'follow-cluster'.getAllHttpSocketURI().join(",")}"
|
nonInputProperties.systemProperty 'tests.rest.cluster', "${-> testClusters.'follow-cluster'.getAllHttpSocketURI().join(",")}"
|
||||||
}
|
}
|
||||||
outputs.doNotCacheIf "Caching of REST tests not implemented yet", { false }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
check.dependsOn followClusterRestartTest
|
check.dependsOn followClusterRestartTest
|
||||||
|
|
|
@ -28,8 +28,8 @@ testClusters.'leader-cluster' {
|
||||||
|
|
||||||
task 'follow-cluster'(type: RestIntegTestTask) {
|
task 'follow-cluster'(type: RestIntegTestTask) {
|
||||||
dependsOn 'leader-cluster'
|
dependsOn 'leader-cluster'
|
||||||
useCluster testClusters.'leader-cluster'
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters.'leader-cluster'
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
nonInputProperties.systemProperty 'tests.leader_host', "${-> testClusters.'leader-cluster'.getAllHttpSocketURI().get(0)}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,8 @@ testClusters.'leader-cluster' {
|
||||||
|
|
||||||
task 'follow-cluster'(type: RestIntegTestTask) {
|
task 'follow-cluster'(type: RestIntegTestTask) {
|
||||||
dependsOn 'leader-cluster'
|
dependsOn 'leader-cluster'
|
||||||
useCluster testClusters.'leader-cluster'
|
|
||||||
runner {
|
runner {
|
||||||
|
useCluster testClusters.'leader-cluster'
|
||||||
systemProperty 'tests.target_cluster', 'follow'
|
systemProperty 'tests.target_cluster', 'follow'
|
||||||
nonInputProperties.systemProperty 'tests.leader_host',
|
nonInputProperties.systemProperty 'tests.leader_host',
|
||||||
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
"${-> testClusters."leader-cluster".getAllHttpSocketURI().get(0)}"
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import org.elasticsearch.gradle.testclusters.RestTestRunnerTask
|
||||||
|
|
||||||
apply plugin: 'elasticsearch.testclusters'
|
apply plugin: 'elasticsearch.testclusters'
|
||||||
apply plugin: 'elasticsearch.standalone-rest-test'
|
apply plugin: 'elasticsearch.standalone-rest-test'
|
||||||
apply plugin: 'elasticsearch.rest-test'
|
apply plugin: 'elasticsearch.rest-test'
|
||||||
|
@ -24,13 +26,11 @@ testClusters.integTest {
|
||||||
setting 'xpack.security.enabled', 'false'
|
setting 'xpack.security.enabled', 'false'
|
||||||
}
|
}
|
||||||
|
|
||||||
task integTestSecurity(type: Test) {
|
task integTestSecurity(type: RestTestRunnerTask) {
|
||||||
description = "Run tests against a cluster that has security"
|
description = "Run tests against a cluster that has security"
|
||||||
useCluster testClusters.integTest
|
useCluster testClusters.integTest
|
||||||
dependsOn integTest
|
dependsOn integTest
|
||||||
systemProperty 'tests.has_security', 'true'
|
systemProperty 'tests.has_security', 'true'
|
||||||
maxParallelForks = 1
|
|
||||||
outputs.cacheIf "Caching of REST tests not implemented yet", { false }
|
|
||||||
|
|
||||||
doFirst {
|
doFirst {
|
||||||
testClusters.integTest {
|
testClusters.integTest {
|
||||||
|
|
Loading…
Reference in New Issue