forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[7.6] Smarter copying of the rest specs and tests (elastic#52114) (el…
…astic#52856) This PR addresses the unnecessary copying of the rest specs and allows for better semantics for which specs and tests are copied. By default the rest specs will get copied if the project applies `elasticsearch.standalone-rest-test` or `esplugin` and the project has rest tests or you configure the custom extension `restResources`. This PR also removes the need for dozens of places where the x-pack specs were copied by supporting copying of the x-pack rest specs too. The plugin/task introduced here can also copy the rest tests to the local project through a similar configuration. The new plugin/task allows a user to minimize the surface area of which rest specs are copied. Per project can be configured to include only a subset of the specs (or tests). Configuring a project to only copy the specs when actually needed should help with build cache hit rates since we can better define what is actually in use. However, project level optimizations for build cache hit rates are not included with this PR. Also, with this PR you can no longer use the includePackaged flag on integTest task. The following items are included in this PR: * new plugin: `elasticsearch.rest-resources` * new tasks: CopyRestApiTask and CopyRestTestsTask - performs the copy * new extension 'restResources' ``` restResources { restApi { includeCore 'foo' , 'bar' //will include the core specs that start with foo and bar includeXpack 'baz' //will include x-pack specs that start with baz } restTests { includeCore 'foo', 'bar' //will include the core tests that start with foo and bar includeXpack 'baz' //will include the x-pack tests that start with baz } } ```
- Loading branch information
1 parent
89860c8
commit 5e1ea07
Showing
44 changed files
with
726 additions
and
327 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
195 changes: 195 additions & 0 deletions
195
buildSrc/src/main/java/org/elasticsearch/gradle/test/rest/CopyRestApiTask.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
/* | ||
* Licensed to Elasticsearch under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
package org.elasticsearch.gradle.test.rest; | ||
|
||
import org.elasticsearch.gradle.VersionProperties; | ||
import org.elasticsearch.gradle.info.BuildParams; | ||
import org.elasticsearch.gradle.tool.Boilerplate; | ||
import org.gradle.api.DefaultTask; | ||
import org.gradle.api.Project; | ||
import org.gradle.api.artifacts.Configuration; | ||
import org.gradle.api.file.ConfigurableFileCollection; | ||
import org.gradle.api.file.FileTree; | ||
import org.gradle.api.provider.ListProperty; | ||
import org.gradle.api.tasks.Input; | ||
import org.gradle.api.tasks.InputFiles; | ||
import org.gradle.api.tasks.OutputDirectory; | ||
import org.gradle.api.tasks.SkipWhenEmpty; | ||
import org.gradle.api.tasks.SourceSet; | ||
import org.gradle.api.tasks.TaskAction; | ||
import org.gradle.api.tasks.util.PatternFilterable; | ||
import org.gradle.api.tasks.util.PatternSet; | ||
import org.gradle.internal.Factory; | ||
|
||
import javax.inject.Inject; | ||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
/** | ||
* Copies the files needed for the Rest YAML specs to the current projects test resources output directory. | ||
* This is intended to be be used from {@link RestResourcesPlugin} since the plugin wires up the needed | ||
* configurations and custom extensions. | ||
* @see RestResourcesPlugin | ||
*/ | ||
public class CopyRestApiTask extends DefaultTask { | ||
private static final String COPY_TO = "rest-api-spec/api"; | ||
final ListProperty<String> includeCore = getProject().getObjects().listProperty(String.class); | ||
final ListProperty<String> includeXpack = getProject().getObjects().listProperty(String.class); | ||
|
||
Configuration coreConfig; | ||
Configuration xpackConfig; | ||
|
||
private final PatternFilterable corePatternSet; | ||
private final PatternFilterable xpackPatternSet; | ||
|
||
public CopyRestApiTask() { | ||
corePatternSet = getPatternSetFactory().create(); | ||
xpackPatternSet = getPatternSetFactory().create(); | ||
} | ||
|
||
@Inject | ||
protected Factory<PatternSet> getPatternSetFactory() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Input | ||
public ListProperty<String> getIncludeCore() { | ||
return includeCore; | ||
} | ||
|
||
@Input | ||
public ListProperty<String> getIncludeXpack() { | ||
return includeXpack; | ||
} | ||
|
||
@SkipWhenEmpty | ||
@InputFiles | ||
public FileTree getInputDir() { | ||
xpackPatternSet.setIncludes(includeXpack.get().stream().map(prefix -> prefix + "*/**").collect(Collectors.toList())); | ||
ConfigurableFileCollection fileCollection = getProject().files(xpackConfig.getAsFileTree().matching(xpackPatternSet)); | ||
if (BuildParams.isInternal()) { | ||
corePatternSet.setIncludes(includeCore.get().stream().map(prefix -> prefix + "*/**").collect(Collectors.toList())); | ||
fileCollection.plus(coreConfig.getAsFileTree().matching(corePatternSet)); | ||
} else { | ||
fileCollection.plus(coreConfig); | ||
} | ||
// if project has rest tests or the includes are explicitly configured execute the task, else NO-SOURCE due to the null input | ||
return projectHasYamlRestTests() || includeCore.get().isEmpty() == false || includeXpack.get().isEmpty() == false | ||
? fileCollection.getAsFileTree() | ||
: null; | ||
} | ||
|
||
@OutputDirectory | ||
public File getOutputDir() { | ||
return new File(getTestSourceSet().getOutput().getResourcesDir(), COPY_TO); | ||
} | ||
|
||
@TaskAction | ||
void copy() { | ||
Project project = getProject(); | ||
// always copy the core specs if the task executes | ||
if (BuildParams.isInternal()) { | ||
getLogger().debug("Rest specs for project [{}] will be copied to the test resources.", project.getPath()); | ||
project.copy(c -> { | ||
c.from(coreConfig.getSingleFile()); | ||
c.into(getOutputDir()); | ||
c.include(corePatternSet.getIncludes()); | ||
}); | ||
} else { | ||
getLogger().debug( | ||
"Rest specs for project [{}] will be copied to the test resources from the published jar (version: [{}]).", | ||
project.getPath(), | ||
VersionProperties.getElasticsearch() | ||
); | ||
project.copy(c -> { | ||
c.from(project.zipTree(coreConfig.getSingleFile())); | ||
c.into(getTestSourceSet().getOutput().getResourcesDir()); // this ends up as the same dir as outputDir | ||
c.include(includeCore.get().stream().map(prefix -> COPY_TO + "/" + prefix + "*/**").collect(Collectors.toList())); | ||
}); | ||
} | ||
// only copy x-pack specs if explicitly instructed | ||
if (includeXpack.get().isEmpty() == false) { | ||
getLogger().debug("X-pack rest specs for project [{}] will be copied to the test resources.", project.getPath()); | ||
project.copy(c -> { | ||
c.from(xpackConfig.getSingleFile()); | ||
c.into(getOutputDir()); | ||
c.include(xpackPatternSet.getIncludes()); | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
* Returns true if any files with a .yml extension exist the test resources rest-api-spec/test directory (from source or output dir) | ||
*/ | ||
private boolean projectHasYamlRestTests() { | ||
File testSourceResourceDir = getTestSourceResourceDir(); | ||
File testOutputResourceDir = getTestOutputResourceDir(); // check output for cases where tests are copied programmatically | ||
|
||
if (testSourceResourceDir == null && testOutputResourceDir == null) { | ||
return false; | ||
} | ||
try { | ||
if (testSourceResourceDir != null) { | ||
return new File(testSourceResourceDir, "rest-api-spec/test").exists() == false | ||
|| Files.walk(testSourceResourceDir.toPath().resolve("rest-api-spec/test")) | ||
.anyMatch(p -> p.getFileName().toString().endsWith("yml")); | ||
} | ||
if (testOutputResourceDir != null) { | ||
return new File(testOutputResourceDir, "rest-api-spec/test").exists() == false | ||
|| Files.walk(testOutputResourceDir.toPath().resolve("rest-api-spec/test")) | ||
.anyMatch(p -> p.getFileName().toString().endsWith("yml")); | ||
} | ||
} catch (IOException e) { | ||
throw new IllegalStateException(String.format("Error determining if this project [%s] has rest tests.", getProject()), e); | ||
} | ||
return false; | ||
} | ||
|
||
private File getTestSourceResourceDir() { | ||
SourceSet testSources = getTestSourceSet(); | ||
if (testSources == null) { | ||
return null; | ||
} | ||
Set<File> resourceDir = testSources.getResources() | ||
.getSrcDirs() | ||
.stream() | ||
.filter(f -> f.isDirectory() && f.getParentFile().getName().equals("test") && f.getName().equals("resources")) | ||
.collect(Collectors.toSet()); | ||
assert resourceDir.size() <= 1; | ||
if (resourceDir.size() == 0) { | ||
return null; | ||
} | ||
return resourceDir.iterator().next(); | ||
} | ||
|
||
private File getTestOutputResourceDir() { | ||
SourceSet testSources = getTestSourceSet(); | ||
if (testSources == null) { | ||
return null; | ||
} | ||
return testSources.getOutput().getResourcesDir(); | ||
} | ||
|
||
private SourceSet getTestSourceSet() { | ||
return Boilerplate.getJavaSourceSets(getProject()).findByName("test"); | ||
} | ||
} |
Oops, something went wrong.