Make distro test plugin apply to the top level project (#45406) (#45481)

The distro test plugin was originally designed to be applied within each
subproject, per operating system we run in a VM with vagrant. However,
for efficiency, and also ease of having a single task to run in CI when
launching within individual OS VMs, having the "destructive" tasks in a
single place is more convenient. This commit reworks the distro test
plugin to be applied to the qa/vagrant project, which now creates only
the wrapper tasks in each of the subprojects for each vagrant VM.
This commit is contained in:
Ryan Ernst 2019-08-13 08:19:18 -07:00 committed by GitHub
parent 7f550f2b29
commit 884f26a1dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 104 additions and 110 deletions

View File

@ -30,7 +30,6 @@ import org.elasticsearch.gradle.Jdk;
import org.elasticsearch.gradle.JdkDownloadPlugin;
import org.elasticsearch.gradle.Version;
import org.elasticsearch.gradle.VersionProperties;
import org.elasticsearch.gradle.tool.Boilerplate;
import org.elasticsearch.gradle.vagrant.BatsProgressLogger;
import org.elasticsearch.gradle.vagrant.VagrantBasePlugin;
import org.elasticsearch.gradle.vagrant.VagrantExtension;
@ -41,7 +40,6 @@ import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.Directory;
import org.gradle.api.plugins.ExtraPropertiesExtension;
import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Copy;
import org.gradle.api.tasks.TaskInputs;
@ -52,8 +50,12 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
@ -66,34 +68,47 @@ public class DistroTestPlugin implements Plugin<Project> {
// all distributions used by distro tests. this is temporary until tests are per distribution
private static final String PACKAGING_DISTRIBUTION = "packaging";
private static final String COPY_PACKAGING_TASK = "copyPackagingArchives";
private static final String COPY_DISTRIBUTIONS_TASK = "copyDistributions";
private static final String IN_VM_SYSPROP = "tests.inVM";
private static Version upgradeVersion;
private Provider<Directory> archivesDir;
private TaskProvider<Copy> copyPackagingArchives;
private Jdk gradleJdk;
@Override
public void apply(Project project) {
project.getPluginManager().apply(JdkDownloadPlugin.class);
project.getPluginManager().apply(DistributionDownloadPlugin.class);
project.getPluginManager().apply(VagrantBasePlugin.class);
project.getPluginManager().apply(JavaPlugin.class);
configureVM(project);
project.getPluginManager().apply(BuildPlugin.class);
if (upgradeVersion == null) {
// just read this once, since it is the same for all projects. this is safe because gradle configuration is single threaded
upgradeVersion = getUpgradeVersion(project);
}
// TODO: it would be useful to also have the SYSTEM_JAVA_HOME setup in the root project, so that running from GCP only needs
// a java for gradle to run, and the tests are self sufficient and consistent with the java they use
// setup task to run inside VM
configureDistributions(project);
configureCopyPackagingTask(project);
configureDistroTest(project);
configureBatsTest(project, "oss");
configureBatsTest(project, "default");
Version upgradeVersion = getUpgradeVersion(project);
Provider<Directory> distributionsDir = project.getLayout().getBuildDirectory().dir("packaging/distributions");
configureDistributions(project, upgradeVersion);
TaskProvider<Copy> copyDistributionsTask = configureCopyDistributionsTask(project, upgradeVersion, distributionsDir);
Map<String, TaskProvider<?>> distroTests = new HashMap<>();
Map<String, TaskProvider<?>> batsTests = new HashMap<>();
distroTests.put("distribution", configureDistroTest(project, distributionsDir, copyDistributionsTask));
batsTests.put("bats oss", configureBatsTest(project, "oss", distributionsDir, copyDistributionsTask));
batsTests.put("bats default", configureBatsTest(project, "default", distributionsDir, copyDistributionsTask));
project.subprojects(vmProject -> {
vmProject.getPluginManager().apply(VagrantBasePlugin.class);
vmProject.getPluginManager().apply(JdkDownloadPlugin.class);
List<Object> vmDependencies = new ArrayList<>(configureVM(vmProject));
// a hack to ensure the parent task has already been run. this will not be necessary once tests are per distribution
// which will eliminate the copy distributions task altogether
vmDependencies.add(copyDistributionsTask);
vmDependencies.add(project.getConfigurations().getByName("testRuntimeClasspath"));
distroTests.forEach((desc, task) -> configureVMWrapperTask(vmProject, desc, task.getName(), vmDependencies));
VagrantExtension vagrant = vmProject.getExtensions().getByType(VagrantExtension.class);
batsTests.forEach((desc, task) -> {
configureVMWrapperTask(vmProject, desc, task.getName(), vmDependencies).configure(t -> {
t.setProgressHandler(new BatsProgressLogger(project.getLogger()));
t.onlyIf(spec -> vagrant.isWindowsVM() == false); // bats doesn't run on windows
});
});
});
}
private static Jdk createJdk(NamedDomainObjectContainer<Jdk> jdksContainer, String name, String version, String platform) {
@ -125,20 +140,22 @@ public class DistroTestPlugin implements Plugin<Project> {
return indexCompatVersions.get(new Random(seed).nextInt(indexCompatVersions.size()));
}
private void configureVM(Project project) {
private static List<Object> configureVM(Project project) {
String box = project.getName();
// setup jdks used by the distro tests, and by gradle executing
NamedDomainObjectContainer<Jdk> jdksContainer = JdkDownloadPlugin.getContainer(project);
String platform = box.contains("windows") ? "windows" : "linux";
this.gradleJdk = createJdk(jdksContainer, "gradle", GRADLE_JDK_VERSION, platform);
Jdk gradleJdk = createJdk(jdksContainer, "gradle", GRADLE_JDK_VERSION, platform);
// setup VM used by these tests
VagrantExtension vagrant = project.getExtensions().getByType(VagrantExtension.class);
vagrant.setBox(box);
vagrant.vmEnv("PATH", convertPath(project, vagrant, gradleJdk, "/bin:$PATH", "\\bin;$Env:PATH"));
vagrant.setIsWindowsVM(box.contains("windows"));
return Arrays.asList(gradleJdk);
}
private static Object convertPath(Project project, VagrantExtension vagrant, Jdk jdk,
@ -154,10 +171,11 @@ public class DistroTestPlugin implements Plugin<Project> {
};
}
private void configureCopyPackagingTask(Project project) {
this.archivesDir = project.getParent().getLayout().getBuildDirectory().dir("packaging/archives");
private static TaskProvider<Copy> configureCopyDistributionsTask(Project project, Version upgradeVersion,
Provider<Directory> archivesDir) {
// temporary, until we have tasks per distribution
this.copyPackagingArchives = Boilerplate.maybeRegister(project.getParent().getTasks(), COPY_PACKAGING_TASK, Copy.class,
return project.getTasks().register(COPY_DISTRIBUTIONS_TASK, Copy.class,
t -> {
t.into(archivesDir);
t.from(project.getConfigurations().getByName(PACKAGING_DISTRIBUTION));
@ -195,64 +213,50 @@ public class DistroTestPlugin implements Plugin<Project> {
});
}
private void configureDistroTest(Project project) {
BuildPlugin.configureCompile(project);
BuildPlugin.configureRepositories(project);
BuildPlugin.configureTestTasks(project);
BuildPlugin.configureInputNormalization(project);
TaskProvider<Test> destructiveTest = project.getTasks().register("destructiveDistroTest", Test.class,
t -> {
t.setMaxParallelForks(1);
t.setWorkingDir(archivesDir.get());
if (System.getProperty(IN_VM_SYSPROP) == null) {
t.dependsOn(copyPackagingArchives, gradleJdk);
}
});
// setup outer task to run
project.getTasks().register("distroTest", GradleDistroTestTask.class,
private TaskProvider<GradleDistroTestTask> configureVMWrapperTask(Project project, String type, String destructiveTaskPath,
List<Object> dependsOn) {
int taskNameStart = destructiveTaskPath.lastIndexOf(':') + "destructive".length() + 1;
String taskname = destructiveTaskPath.substring(taskNameStart);
taskname = taskname.substring(0, 1).toLowerCase(Locale.ROOT) + taskname.substring(1);
return project.getTasks().register(taskname, GradleDistroTestTask.class,
t -> {
t.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
t.setDescription("Runs distribution tests within vagrant");
t.setTaskName(project.getPath() + ":" + destructiveTest.getName());
t.setDescription("Runs " + type + " tests within vagrant");
t.setTaskName(destructiveTaskPath);
t.extraArg("-D'" + IN_VM_SYSPROP + "'");
t.dependsOn(copyPackagingArchives, gradleJdk);
t.dependsOn(dependsOn);
});
}
private void configureBatsTest(Project project, String type) {
// destructive task to run inside
TaskProvider<BatsTestTask> destructiveTest = project.getTasks().register("destructiveBatsTest." + type, BatsTestTask.class,
private TaskProvider<?> configureDistroTest(Project project, Provider<Directory> distributionsDir,
TaskProvider<Copy> copyPackagingArchives) {
// TODO: don't run with security manager...
return project.getTasks().register("destructiveDistroTest", Test.class,
t -> {
// this is hacky for shared source, but bats are a temporary thing we are removing, so it is not worth
// the overhead of a real project dependency
Directory batsDir = project.getParent().getLayout().getProjectDirectory().dir("bats");
t.setTestsDir(batsDir.dir(type));
t.setUtilsDir(batsDir.dir("utils"));
t.setArchivesDir(archivesDir.get());
t.setPackageName("elasticsearch" + (type.equals("oss") ? "-oss" : ""));
t.setMaxParallelForks(1);
t.setWorkingDir(distributionsDir);
if (System.getProperty(IN_VM_SYSPROP) == null) {
t.dependsOn(copyPackagingArchives, gradleJdk);
t.dependsOn(copyPackagingArchives);
}
});
}
VagrantExtension vagrant = project.getExtensions().getByType(VagrantExtension.class);
// setup outer task to run
project.getTasks().register("batsTest." + type, GradleDistroTestTask.class,
private TaskProvider<?> configureBatsTest(Project project, String type, Provider<Directory> distributionsDir,
TaskProvider<Copy> copyPackagingArchives) {
return project.getTasks().register("destructiveBatsTest." + type, BatsTestTask.class,
t -> {
t.setGroup(JavaBasePlugin.VERIFICATION_GROUP);
t.setDescription("Runs bats tests within vagrant");
t.setTaskName(project.getPath() + ":" + destructiveTest.getName());
t.setProgressHandler(new BatsProgressLogger(project.getLogger()));
t.extraArg("-D'" + IN_VM_SYSPROP + "'");
t.dependsOn(copyPackagingArchives, gradleJdk);
t.onlyIf(spec -> vagrant.isWindowsVM() == false); // bats doesn't run on windows
Directory batsDir = project.getLayout().getProjectDirectory().dir("bats");
t.setTestsDir(batsDir.dir(type));
t.setUtilsDir(batsDir.dir("utils"));
t.setDistributionsDir(distributionsDir);
t.setPackageName("elasticsearch" + (type.equals("oss") ? "-oss" : ""));
if (System.getProperty(IN_VM_SYSPROP) == null) {
t.dependsOn(copyPackagingArchives);
}
});
}
private void configureDistributions(Project project) {
private void configureDistributions(Project project, Version upgradeVersion) {
NamedDomainObjectContainer<ElasticsearchDistribution> distributions = DistributionDownloadPlugin.getContainer(project);
for (Type type : Arrays.asList(Type.DEB, Type.RPM)) {

View File

@ -21,6 +21,8 @@ package org.elasticsearch.gradle.test;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputDirectory;
import org.gradle.api.tasks.TaskAction;
@ -31,36 +33,42 @@ import java.util.stream.Collectors;
public class BatsTestTask extends DefaultTask {
private Directory testsDir;
private Directory utilsDir;
private Directory archivesDir;
private final DirectoryProperty testsDir;
private final DirectoryProperty utilsDir;
private final DirectoryProperty distributionsDir;
private String packageName;
public BatsTestTask() {
this.testsDir = getProject().getObjects().directoryProperty();
this.utilsDir = getProject().getObjects().directoryProperty();
this.distributionsDir = getProject().getObjects().directoryProperty();
}
@InputDirectory
public Directory getTestsDir() {
public Provider<Directory> getTestsDir() {
return testsDir;
}
public void setTestsDir(Directory testsDir) {
this.testsDir = testsDir;
this.testsDir.set(testsDir);
}
@InputDirectory
public Directory getUtilsDir() {
public Provider<Directory> getUtilsDir() {
return utilsDir;
}
public void setUtilsDir(Directory utilsDir) {
this.utilsDir = utilsDir;
this.utilsDir.set(utilsDir);
}
@InputDirectory
public Directory getArchivesDir() {
return archivesDir;
public Provider<Directory> getDistributionsDir() {
return distributionsDir;
}
public void setArchivesDir(Directory archivesDir) {
this.archivesDir = archivesDir;
public void setDistributionsDir(Provider<Directory> distributionsDir) {
this.distributionsDir.set(distributionsDir);
}
@Input
@ -81,10 +89,10 @@ public class BatsTestTask extends DefaultTask {
.filter(f -> f.getName().endsWith(".bats"))
.sorted().collect(Collectors.toList()));
getProject().exec(spec -> {
spec.setWorkingDir(archivesDir.getAsFile());
spec.setWorkingDir(distributionsDir.getAsFile());
spec.environment(System.getenv());
spec.environment("BATS_TESTS", testsDir.getAsFile().toString());
spec.environment("BATS_UTILS", utilsDir.getAsFile().toString());
spec.environment("BATS_TESTS", testsDir.getAsFile().get().toString());
spec.environment("BATS_UTILS", utilsDir.getAsFile().get().toString());
spec.environment("PACKAGE_NAME", packageName);
spec.setCommandLine(command);
});

View File

@ -18,7 +18,7 @@
*/
plugins {
id 'elasticsearch.build'
id 'elasticsearch.distro-test'
}
dependencies {
@ -35,14 +35,7 @@ dependencies {
compile project(':libs:elasticsearch-core')
}
configurations.create('testClasses')
String classesDir = project.file(project.sourceSets.main.output.classesDirs.singleFile).toString()
artifacts.add('testClasses', project.layout.projectDirectory.dir(classesDir)) {
builtBy tasks.named('testClasses')
}
forbiddenApisMain {
forbiddenApisTest {
replaceSignatureFiles 'jdk-signatures'
}
@ -69,24 +62,14 @@ tasks.thirdPartyAudit.ignoreMissingClasses (
'javax.servlet.ServletContextListener'
)
boolean sample = project.properties.get('vagrant.boxes') != 'all'
tasks.register('destructivePackagingTest') {
dependsOn 'destructiveDistroTest', 'destructiveBatsTest.oss', 'destructiveBatsTest.default'
}
subprojects { Project platformProject ->
apply plugin: 'elasticsearch.distro-test'
apply plugin: 'java'
configurations.create('testClasses')
dependencies {
testClasses project(path: ':qa:vagrant', configuration: 'testClasses')
testRuntime project(path: ':qa:vagrant', configuration: 'runtime')
}
tasks.named('destructiveDistroTest') {
testClassesDirs += project.files(configurations.testClasses.singleFile)
}
// TODO: remove this property lookup once CI is switched to use an explicit task for the sample tests
boolean allBoxes = project.properties.get('vagrant.boxes', '') == 'all'
boolean allBoxes = (project.findProperty('vagrant.boxes') ?: '') == 'all'
if (allBoxes || ['centos-7', 'ubuntu-1604'].contains(platformProject.name)) {
tasks.register('packagingTest') {
dependsOn 'distroTest', 'batsTest.oss', 'batsTest.default'
@ -96,7 +79,6 @@ subprojects { Project platformProject ->
vagrant {
hostEnv 'VAGRANT_PROJECT_DIR', platformProject.projectDir.absolutePath
}
}
configurations {
@ -115,7 +97,7 @@ for (Project subproj : project.rootProject.subprojects) {
}
plugins = plugins.toSorted()
copyPackagingArchives {
tasks.named('copyDistributions') {
from configurations.allPlugins
doLast {
// TODO: this was copied from the old way bats tests get the plugins list. we should pass