Revert "Simplify distribution download and extraction (7.x backport) (#61184)"

This reverts commit 8b7a0a1f64.
This commit is contained in:
Mark Vieira 2020-08-17 08:54:08 -07:00
parent a3f357c8a5
commit d26a3e144e
No known key found for this signature in database
GPG Key ID: CA947EF7E6D4B105
20 changed files with 568 additions and 376 deletions

View File

@ -92,7 +92,7 @@ dependencies {
api 'commons-codec:commons-codec:1.12'
api 'org.apache.commons:commons-compress:1.19'
api 'org.apache.ant:ant:1.10.8'
api 'com.netflix.nebula:gradle-extra-configurations-plugin:3.0.3'
api 'com.netflix.nebula:nebula-publishing-plugin:4.4.4'
api 'com.netflix.nebula:gradle-info-plugin:7.1.3'

View File

@ -1,179 +0,0 @@
/*
* 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
import com.github.tomakehurst.wiremock.WireMockServer
import org.elasticsearch.gradle.fixtures.AbstractGradleFuncTest
import org.elasticsearch.gradle.fixtures.WiremockFixture
import org.elasticsearch.gradle.transform.SymbolicLinkPreservingUntarTransform
import org.gradle.testkit.runner.TaskOutcome
import spock.lang.Unroll
class DistributionDownloadPluginFuncTest extends AbstractGradleFuncTest {
@Unroll
def "#distType version can be resolved"() {
given:
def mockRepoUrl = urlPath(version, platform)
def mockedContent = filebytes(mockRepoUrl)
buildFile << applyPluginAndSetupDistro(version, platform)
when:
def result = WiremockFixture.withWireMock(mockRepoUrl, mockedContent) { server ->
buildFile << repositoryMockSetup(server)
gradleRunner('setupDistro').build()
}
then:
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertExtractedDistroCreated("build/distro")
where:
version | platform | distType
VersionProperties.getElasticsearch() | "linux" | "current"
"8.1.0-SNAPSHOT" | "linux" | "bwc"
"7.0.0" | "windows" | "released"
}
def "transformed versions are kept across builds"() {
given:
def version = VersionProperties.getElasticsearch()
def mockRepoUrl = urlPath(version)
def mockedContent = filebytes(mockRepoUrl)
buildFile << applyPluginAndSetupDistro(version, 'linux')
buildFile << """
apply plugin:'base'
"""
when:
def result = WiremockFixture.withWireMock(mockRepoUrl, mockedContent) { server ->
buildFile << repositoryMockSetup(server)
gradleRunner('clean', 'setupDistro').build()
gradleRunner('clean', 'setupDistro', '-i').build()
}
then:
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertOutputContains(result.output, "Skipping ${SymbolicLinkPreservingUntarTransform.class.simpleName}")
}
def "transforms are reused across projects"() {
given:
def version = VersionProperties.getElasticsearch()
def mockRepoUrl = urlPath(version)
def mockedContent = filebytes(mockRepoUrl)
3.times {
settingsFile << """
include ':sub-$it'
"""
}
buildFile.text = """
import org.elasticsearch.gradle.Architecture
plugins {
id 'elasticsearch.distribution-download'
}
subprojects {
apply plugin: 'elasticsearch.distribution-download'
${setupTestDistro(version, 'linux')}
${setupDistroTask()}
}
"""
when:
def result = WiremockFixture.withWireMock(mockRepoUrl, mockedContent) { server ->
buildFile << repositoryMockSetup(server)
gradleRunner('setupDistro', '-i', '-g', testProjectDir.newFolder().toString()).build()
}
then:
result.tasks.size() == 3
result.output.count("Unpacking elasticsearch-${version}-linux-x86_64.tar.gz using SymbolicLinkPreservingUntarTransform.") == 1
}
private boolean assertExtractedDistroCreated(String relativePath) {
File distroExtracted = new File(testProjectDir.root, relativePath)
assert distroExtracted.exists()
assert distroExtracted.isDirectory()
assert new File(distroExtracted, "elasticsearch-1.2.3/bin/elasticsearch").exists()
true
}
private static String urlPath(String version, String platform = 'linux') {
String fileType = platform == "linux" ? "tar.gz" : "zip"
"/downloads/elasticsearch/elasticsearch-${version}-${platform}-x86_64.$fileType"
}
private static String repositoryMockSetup(WireMockServer server) {
"""allprojects{ p ->
p.repositories.all { repo ->
repo.setUrl('${server.baseUrl()}')
}
}"""
}
private static byte[] filebytes(String urlPath) throws IOException {
String suffix = urlPath.endsWith("zip") ? "zip" : "tar.gz";
return DistributionDownloadPluginFuncTest.getResourceAsStream("fake_elasticsearch." + suffix).getBytes()
}
private static String applyPluginAndSetupDistro(String version, String platform) {
"""
import org.elasticsearch.gradle.Architecture
plugins {
id 'elasticsearch.distribution-download'
}
${setupTestDistro(version, platform)}
${setupDistroTask()}
"""
}
private static String setupTestDistro(String version, String platform) {
return """
elasticsearch_distributions {
test_distro {
version = "$version"
type = "archive"
platform = "$platform"
architecture = Architecture.current();
}
}
"""
}
private static String setupDistroTask() {
return """
tasks.register("setupDistro", Sync) {
from(elasticsearch_distributions.test_distro.extracted)
into("build/distro")
}
"""
}
}

View File

@ -38,7 +38,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
"""
when:
def result = gradleRunner("tasks").buildAndFail()
def result = gradleRunner("createExtractedTestDistro").buildAndFail()
then:
assertOutputContains(result.output, "Plugin 'elasticsearch.internal-distribution-download' is not supported. " +
@ -61,19 +61,18 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
architecture = Architecture.current();
}
}
tasks.register("setupDistro", Sync) {
from(elasticsearch_distributions.test_distro.extracted)
into("build/distro")
tasks.register("createExtractedTestDistro") {
dependsOn elasticsearch_distributions.test_distro.extracted
}
"""
when:
def result = gradleRunner("setupDistro", '-g', testProjectDir.newFolder('GUH').path).build()
def result = gradleRunner("createExtractedTestDistro").build()
then:
result.task(":distribution:archives:linux-tar:buildTar").outcome == TaskOutcome.SUCCESS
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, "build/distro", 'current-marker.txt')
result.task(":extractElasticsearchLinux$distroVersion").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, 'current-marker.txt')
}
def "resolves bwc versions from source"() {
@ -92,18 +91,16 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
architecture = Architecture.current();
}
}
tasks.register("setupDistro", Sync) {
from(elasticsearch_distributions.test_distro.extracted)
into("build/distro")
tasks.register("createExtractedTestDistro") {
dependsOn elasticsearch_distributions.test_distro.extracted
}
"""
when:
def result = gradleRunner("setupDistro").build()
def result = gradleRunner("createExtractedTestDistro").build()
then:
result.task(":distribution:bwc:minor:buildBwcTask").outcome == TaskOutcome.SUCCESS
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, "build/distro", 'bwc-marker.txt')
result.task(":extractElasticsearchLinux8.1.0").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion,'bwc-marker.txt')
}
def "fails on resolving bwc versions with no bundled jdk"() {
@ -198,8 +195,8 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
}
boolean assertExtractedDistroIsCreated(String version, String relativeDistroPath, String markerFileName) {
File extractedFolder = new File(testProjectDir.root, relativeDistroPath)
boolean assertExtractedDistroIsCreated(String version, String markerFileName) {
File extractedFolder = new File(testProjectDir.root, "build/elasticsearch-distros/extracted_elasticsearch_${version}_archive_linux_default")
assert extractedFolder.exists()
assert new File(extractedFolder, markerFileName).exists()
true

View File

@ -95,7 +95,7 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
given:
def mockRepoUrl = urlPath(jdkVendor, jdkVersion, platform)
def mockedContent = filebytes(jdkVendor, platform)
3.times {
10.times {
settingsFile << """
include ':sub-$it'
"""
@ -132,8 +132,8 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
}
then:
result.tasks.size() == 3
result.output.count("Unpacking linux-12.0.2-x64.tar.gz using ${SymbolicLinkPreservingUntarTransform.simpleName}.") == 1
result.tasks.size() == 10
result.output.count("Unpacking linux-12.0.2-x64.tar.gz using SymbolicLinkPreservingUntarTransform.") == 1
where:
platform | jdkVendor | jdkVersion | expectedJavaBin
@ -149,7 +149,7 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
plugins {
id 'elasticsearch.jdk-download'
}
apply plugin: 'base'
apply plugin: 'elasticsearch.jdk-download'
jdks {
@ -175,9 +175,9 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
def commonGradleUserHome = testProjectDir.newFolder().toString()
// initial run
gradleRunner('clean', 'getJdk', '-g', commonGradleUserHome).build()
gradleRunner('getJdk', '-g', commonGradleUserHome).build()
// run against up-to-date transformations
gradleRunner('clean', 'getJdk', '-i', '-g', commonGradleUserHome).build()
gradleRunner('getJdk', '-i', '-g', commonGradleUserHome).build()
}
then:

View File

@ -0,0 +1,188 @@
/*
* 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;
import com.github.tomakehurst.wiremock.WireMockServer;
import org.elasticsearch.gradle.test.GradleIntegrationTestCase;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.head;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
public class DistributionDownloadPluginIT extends GradleIntegrationTestCase {
// TODO: check reuse of root task across projects MOVE TO UNIT TEST
// TODO: future: check integ-test-zip to maven, snapshots to snapshot service for external project
public void testCurrentExternal() throws Exception {
checkService(
VersionProperties.getElasticsearch(),
"archive",
"linux",
null,
null,
"/downloads/elasticsearch/elasticsearch-" + VersionProperties.getElasticsearch() + "-linux-x86_64.tar.gz",
"tests.internal",
"false"
);
}
public void testBwcExternal() throws Exception {
checkService(
"8.1.0-SNAPSHOT",
"archive",
"linux",
null,
null,
"/downloads/elasticsearch/elasticsearch-8.1.0-SNAPSHOT-linux-x86_64.tar.gz",
"tests.internal",
"false",
"tests.current_version",
"9.0.0"
);
}
public void testReleased() throws Exception {
checkService("7.0.0", "archive", "windows", null, null, "/downloads/elasticsearch/elasticsearch-7.0.0-windows-x86_64.zip");
checkService("6.5.0", "archive", "windows", null, null, "/downloads/elasticsearch/elasticsearch-6.5.0.zip");
}
public void testReleasedExternal() throws Exception {
checkService(
"7.0.0",
"archive",
"windows",
null,
null,
"/downloads/elasticsearch/elasticsearch-7.0.0-windows-x86_64.zip",
"tests.internal",
"false"
);
checkService(
"6.5.0",
"archive",
"windows",
null,
null,
"/downloads/elasticsearch/elasticsearch-6.5.0.zip",
"tests.internal",
"false"
);
}
private void checkService(
String version,
String type,
String platform,
String flavor,
Boolean bundledJdk,
String urlPath,
String... sysProps
) throws IOException {
String suffix = urlPath.endsWith("zip") ? "zip" : "tar.gz";
String sourceFile = "src/testKit/distribution-download/distribution/files/fake_elasticsearch." + suffix;
WireMockServer wireMock = new WireMockServer(0);
try {
final byte[] filebytes;
try (InputStream stream = Files.newInputStream(Paths.get(sourceFile))) {
filebytes = stream.readAllBytes();
}
wireMock.stubFor(head(urlEqualTo(urlPath)).willReturn(aResponse().withStatus(200)));
wireMock.stubFor(get(urlEqualTo(urlPath)).willReturn(aResponse().withStatus(200).withBody(filebytes)));
wireMock.start();
List<String> allSysProps = new ArrayList<>();
allSysProps.addAll(Arrays.asList(sysProps));
allSysProps.add("tests.download_service");
allSysProps.add(wireMock.baseUrl());
assertExtractedDistro(version, type, platform, flavor, bundledJdk, allSysProps.toArray(new String[0]));
} catch (Exception e) {
// for debugging
System.err.println("missed requests: " + wireMock.findUnmatchedRequests().getRequests());
throw e;
} finally {
wireMock.stop();
}
}
private void assertFileDistro(String version, String type, String platform, String flavor, Boolean bundledJdk, String... sysProps)
throws IOException {
List<String> finalSysProps = new ArrayList<>();
addDistroSysProps(finalSysProps, version, type, platform, flavor, bundledJdk);
finalSysProps.addAll(Arrays.asList(sysProps));
runBuild(":subproj:assertDistroFile", finalSysProps.toArray(new String[0]));
}
private void assertExtractedDistro(String version, String type, String platform, String flavor, Boolean bundledJdk, String... sysProps)
throws IOException {
List<String> finalSysProps = new ArrayList<>();
addDistroSysProps(finalSysProps, version, type, platform, flavor, bundledJdk);
finalSysProps.addAll(Arrays.asList(sysProps));
runBuild(":subproj:assertDistroExtracted", finalSysProps.toArray(new String[0]));
}
private BuildResult runBuild(String taskname, String... sysProps) throws IOException {
assert sysProps.length % 2 == 0;
List<String> args = new ArrayList<>();
args.add(taskname);
for (int i = 0; i < sysProps.length; i += 2) {
args.add("-D" + sysProps[i] + "=" + sysProps[i + 1]);
}
args.add("-i");
GradleRunner runner = getGradleRunner("distribution-download").withArguments(args);
BuildResult result = runner.build();
System.out.println(result.getOutput());
return result;
}
private void addDistroSysProps(List<String> sysProps, String version, String type, String platform, String flavor, Boolean bundledJdk) {
if (version != null) {
sysProps.add("tests.distro.version");
sysProps.add(version);
}
if (type != null) {
sysProps.add("tests.distro.type");
sysProps.add(type);
}
if (platform != null) {
sysProps.add("tests.distro.platform");
sysProps.add(platform);
}
if (flavor != null) {
sysProps.add("tests.distro.flavor");
sysProps.add(flavor);
}
if (bundledJdk != null) {
sysProps.add("tests.distro.bundledJdk");
sysProps.add(bundledJdk.toString());
}
}
}

View File

@ -24,22 +24,31 @@ import org.elasticsearch.gradle.ElasticsearchDistribution.Platform;
import org.elasticsearch.gradle.ElasticsearchDistribution.Type;
import org.elasticsearch.gradle.docker.DockerSupportPlugin;
import org.elasticsearch.gradle.docker.DockerSupportService;
import org.elasticsearch.gradle.transform.SymbolicLinkPreservingUntarTransform;
import org.elasticsearch.gradle.transform.UnzipTransform;
import org.elasticsearch.gradle.util.GradleUtils;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.UnknownTaskException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
import org.gradle.api.credentials.HttpHeaderCredentials;
import org.gradle.api.internal.artifacts.ArtifactAttributes;
import org.gradle.api.file.FileTree;
import org.gradle.api.file.RelativePath;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Sync;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.authentication.http.HttpHeaderAuthentication;
import java.io.File;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import static org.elasticsearch.gradle.util.GradleUtils.projectDependency;
import static org.elasticsearch.gradle.util.Util.capitalize;
/**
* A plugin to manage getting and extracting distributions of Elasticsearch.
@ -68,17 +77,6 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
DockerSupportPlugin.DOCKER_SUPPORT_SERVICE_NAME
);
project.getDependencies().registerTransform(UnzipTransform.class, transformSpec -> {
transformSpec.getFrom().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.ZIP_TYPE);
transformSpec.getTo().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
});
ArtifactTypeDefinition tarArtifactTypeDefinition = project.getDependencies().getArtifactTypes().maybeCreate("tar.gz");
project.getDependencies().registerTransform(SymbolicLinkPreservingUntarTransform.class, transformSpec -> {
transformSpec.getFrom().attribute(ArtifactAttributes.ARTIFACT_FORMAT, tarArtifactTypeDefinition.getName());
transformSpec.getTo().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
});
setupResolutionsContainer(project);
setupDistributionContainer(project, dockerSupport);
setupDownloadServiceRepo(project);
@ -89,7 +87,6 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
distributionsContainer = project.container(ElasticsearchDistribution.class, name -> {
Configuration fileConfiguration = project.getConfigurations().create("es_distro_file_" + name);
Configuration extractedConfiguration = project.getConfigurations().create("es_distro_extracted_" + name);
extractedConfiguration.getAttributes().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
return new ElasticsearchDistribution(name, project.getObjects(), dockerSupport, fileConfiguration, extractedConfiguration);
});
project.getExtensions().add(CONTAINER_NAME, distributionsContainer);
@ -118,15 +115,21 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
void setupDistributions(Project project) {
for (ElasticsearchDistribution distribution : distributionsContainer) {
distribution.finalizeValues();
DependencyHandler dependencies = project.getDependencies();
// for the distribution as a file, just depend on the artifact directly
Object resolvedDependency = resolveDependencyNotation(project, distribution);
dependencies.add(distribution.configuration.getName(), resolvedDependency);
dependencies.add(distribution.configuration.getName(), resolveDependencyNotation(project, distribution));
// no extraction allowed for rpm, deb or docker
if (distribution.getType().shouldExtract()) {
// The extracted configuration depends on the artifact directly but has
// an artifact transform registered to resolve it as an unpacked folder.
dependencies.add(distribution.getExtracted().getName(), resolvedDependency);
// for the distribution extracted, add a root level task that does the extraction, and depend on that
// extracted configuration as an artifact consisting of the extracted distribution directory
dependencies.add(
distribution.getExtracted().configuration.getName(),
projectDependency(project, ":", configName("extracted_elasticsearch", distribution))
);
// ensure a root level download task exists
setupRootDownload(project.getRootProject(), distribution);
}
}
}
@ -140,6 +143,59 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
.orElseGet(() -> dependencyNotation(distribution));
}
private void setupRootDownload(Project rootProject, ElasticsearchDistribution distribution) {
String extractTaskName = extractTaskName(distribution);
// NOTE: this is *horrendous*, but seems to be the only way to check for the existence of a registered task
try {
rootProject.getTasks().named(extractTaskName);
// already setup this version
return;
} catch (UnknownTaskException e) {
// fall through: register the task
}
setupDownloadServiceRepo(rootProject);
final ConfigurationContainer configurations = rootProject.getConfigurations();
String downloadConfigName = configName("elasticsearch", distribution);
String extractedConfigName = "extracted_" + downloadConfigName;
final Configuration downloadConfig = configurations.create(downloadConfigName);
configurations.create(extractedConfigName);
rootProject.getDependencies().add(downloadConfigName, resolveDependencyNotation(rootProject, distribution));
// add task for extraction, delaying resolving config until runtime
if (distribution.getType() == Type.ARCHIVE || distribution.getType() == Type.INTEG_TEST_ZIP) {
Supplier<File> archiveGetter = downloadConfig::getSingleFile;
String extractDir = rootProject.getBuildDir().toPath().resolve("elasticsearch-distros").resolve(extractedConfigName).toString();
TaskProvider<Sync> extractTask = rootProject.getTasks().register(extractTaskName, Sync.class, syncTask -> {
syncTask.dependsOn(downloadConfig);
syncTask.into(extractDir);
syncTask.from((Callable<FileTree>) () -> {
File archiveFile = archiveGetter.get();
String archivePath = archiveFile.toString();
if (archivePath.endsWith(".zip")) {
return rootProject.zipTree(archiveFile);
} else if (archivePath.endsWith(".tar.gz")) {
return rootProject.tarTree(rootProject.getResources().gzip(archiveFile));
}
throw new IllegalStateException("unexpected file extension on [" + archivePath + "]");
});
// Workaround for https://github.com/elastic/elasticsearch/issues/49417
syncTask.eachFile(details -> {
String[] segments = details.getRelativePath().getSegments();
if (segments[0].equals(".")) {
details.setRelativePath(new RelativePath(true, Arrays.copyOfRange(segments, 1, segments.length)));
}
});
});
rootProject.getArtifacts()
.add(
extractedConfigName,
rootProject.getLayout().getProjectDirectory().dir(extractDir),
artifact -> artifact.builtBy(extractTask)
);
}
}
private static void addIvyRepo(Project project, String name, String url, String group) {
IvyArtifactRepository ivyRepo = project.getRepositories().ivy(repo -> {
repo.setName(name);
@ -207,4 +263,35 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
String group = distribution.getVersion().endsWith("-SNAPSHOT") ? FAKE_SNAPSHOT_IVY_GROUP : FAKE_IVY_GROUP;
return group + ":elasticsearch" + flavor + ":" + distribution.getVersion() + classifier + "@" + extension;
}
private static String configName(String prefix, ElasticsearchDistribution distribution) {
return String.format(
"%s_%s_%s_%s%s%s",
prefix,
distribution.getVersion(),
distribution.getType(),
distribution.getPlatform() == null ? "" : distribution.getPlatform() + "_",
distribution.getFlavor(),
distribution.getBundledJdk() ? "" : "_nojdk"
);
}
private static String extractTaskName(ElasticsearchDistribution distribution) {
String taskName = "extractElasticsearch";
if (distribution.getType() != Type.INTEG_TEST_ZIP) {
if (distribution.getFlavor() == Flavor.OSS) {
taskName += "Oss";
}
if (distribution.getBundledJdk() == false) {
taskName += "NoJdk";
}
}
if (distribution.getType() == Type.ARCHIVE) {
taskName += capitalize(distribution.getPlatform().toString());
} else if (distribution.getType() != Type.INTEG_TEST_ZIP) {
taskName += capitalize(distribution.getType().toString());
}
taskName += distribution.getVersion();
return taskName;
}
}

View File

@ -87,10 +87,36 @@ public class ElasticsearchDistribution implements Buildable, Iterable<File> {
.onMac(() -> Platform.DARWIN)
.supply();
public static final class Extracted implements Buildable, Iterable<File> {
// pkg private so plugin can configure
final Configuration configuration;
private Extracted(Configuration configuration) {
this.configuration = configuration;
}
@Override
public Iterator<File> iterator() {
return configuration.iterator();
}
@Override
public TaskDependency getBuildDependencies() {
return configuration.getBuildDependencies();
}
@Override
public String toString() {
return configuration.getSingleFile().toString();
}
}
private final String name;
private final Provider<DockerSupportService> dockerSupport;
// pkg private so plugin can configure
final Configuration configuration;
private final Extracted extracted;
private final Property<Architecture> architecture;
private final Property<String> version;
@ -99,7 +125,6 @@ public class ElasticsearchDistribution implements Buildable, Iterable<File> {
private final Property<Flavor> flavor;
private final Property<Boolean> bundledJdk;
private final Property<Boolean> failIfUnavailable;
private final Configuration extracted;
ElasticsearchDistribution(
String name,
@ -119,7 +144,7 @@ public class ElasticsearchDistribution implements Buildable, Iterable<File> {
this.flavor = objectFactory.property(Flavor.class);
this.bundledJdk = objectFactory.property(Boolean.class);
this.failIfUnavailable = objectFactory.property(Boolean.class).convention(true);
this.extracted = extractedConfiguration;
this.extracted = new Extracted(extractedConfiguration);
}
public String getName() {
@ -188,7 +213,7 @@ public class ElasticsearchDistribution implements Buildable, Iterable<File> {
return configuration.getSingleFile().toString();
}
public Configuration getExtracted() {
public Extracted getExtracted() {
switch (getType()) {
case DEB:
case DOCKER:

View File

@ -29,7 +29,6 @@ import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.dsl.RepositoryHandler;
import org.gradle.api.artifacts.repositories.IvyArtifactRepository;
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
import org.gradle.api.attributes.Attribute;
import org.gradle.api.internal.artifacts.ArtifactAttributes;
public class JdkDownloadPlugin implements Plugin<Project> {
@ -39,39 +38,25 @@ public class JdkDownloadPlugin implements Plugin<Project> {
private static final String REPO_NAME_PREFIX = "jdk_repo_";
private static final String EXTENSION_NAME = "jdks";
public static final String JDK_TRIMMED_PREFIX = "jdk-?\\d.*";
@Override
public void apply(Project project) {
Attribute<Boolean> jdkAttribute = Attribute.of("jdk", Boolean.class);
project.getDependencies().getAttributesSchema().attribute(jdkAttribute);
project.getDependencies().getArtifactTypes().maybeCreate(ArtifactTypeDefinition.ZIP_TYPE);
project.getDependencies().registerTransform(UnzipTransform.class, transformSpec -> {
transformSpec.getFrom()
.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.ZIP_TYPE)
.attribute(jdkAttribute, true);
transformSpec.getTo()
.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE)
.attribute(jdkAttribute, true);
transformSpec.parameters(parameters -> parameters.setTrimmedPrefixPattern(JDK_TRIMMED_PREFIX));
transformSpec.getFrom().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.ZIP_TYPE);
transformSpec.getTo().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
});
ArtifactTypeDefinition tarArtifactTypeDefinition = project.getDependencies().getArtifactTypes().maybeCreate("tar.gz");
project.getDependencies().registerTransform(SymbolicLinkPreservingUntarTransform.class, transformSpec -> {
transformSpec.getFrom()
.attribute(ArtifactAttributes.ARTIFACT_FORMAT, tarArtifactTypeDefinition.getName())
.attribute(jdkAttribute, true);
transformSpec.getTo()
.attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE)
.attribute(jdkAttribute, true);
transformSpec.parameters(parameters -> parameters.setTrimmedPrefixPattern(JDK_TRIMMED_PREFIX));
transformSpec.getFrom().attribute(ArtifactAttributes.ARTIFACT_FORMAT, tarArtifactTypeDefinition.getName());
transformSpec.getTo().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
});
NamedDomainObjectContainer<Jdk> jdksContainer = project.container(Jdk.class, name -> {
Configuration configuration = project.getConfigurations().create("jdk_" + name);
configuration.setCanBeConsumed(false);
configuration.getAttributes().attribute(ArtifactAttributes.ARTIFACT_FORMAT, ArtifactTypeDefinition.DIRECTORY_TYPE);
configuration.getAttributes().attribute(jdkAttribute, true);
Jdk jdk = new Jdk(name, configuration, project.getObjects());
configuration.defaultDependencies(dependencies -> {
jdk.finalizeValues();

View File

@ -1182,7 +1182,7 @@ public class ElasticsearchNode implements TestClusterConfiguration {
}
private Path getExtractedDistributionDir() {
return distributions.get(currentDistro).getExtracted().getSingleFile().toPath();
return Paths.get(distributions.get(currentDistro).getExtracted().toString());
}
private List<File> getInstalledFileSet(Action<? super PatternFilterable> filter) {

View File

@ -31,22 +31,22 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;
import static org.elasticsearch.gradle.util.PermissionUtils.chmod;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
public abstract class SymbolicLinkPreservingUntarTransform implements UnpackTransform {
public void unpack(File tarFile, File targetDir) throws IOException {
Logging.getLogger(SymbolicLinkPreservingUntarTransform.class)
.info("Unpacking " + tarFile.getName() + " using " + SymbolicLinkPreservingUntarTransform.class.getSimpleName() + ".");
Function<String, Path> pathModifier = pathResolver();
TarArchiveInputStream tar = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(tarFile)));
final Path destinationPath = targetDir.toPath();
TarArchiveEntry entry = tar.getNextTarEntry();
while (entry != null) {
final Path relativePath = pathModifier.apply(entry.getName());
final Path relativePath = UnpackTransform.trimArchiveExtractPath(entry.getName());
if (relativePath == null) {
entry = tar.getNextTarEntry();
continue;
@ -70,10 +70,41 @@ public abstract class SymbolicLinkPreservingUntarTransform implements UnpackTran
}
if (entry.isSymbolicLink() == false) {
// check if the underlying file system supports POSIX permissions
chmod(destination, entry.getMode());
final PosixFileAttributeView view = Files.getFileAttributeView(destination, PosixFileAttributeView.class);
if (view != null) {
final Set<PosixFilePermission> permissions = PosixFilePermissions.fromString(
permissions((entry.getMode() >> 6) & 07) + permissions((entry.getMode() >> 3) & 07) + permissions(
(entry.getMode() >> 0) & 07
)
);
Files.setPosixFilePermissions(destination, permissions);
}
}
entry = tar.getNextTarEntry();
}
}
private static String permissions(final int permissions) {
if (permissions < 0 || permissions > 7) {
throw new IllegalArgumentException("permissions [" + permissions + "] out of range");
}
final StringBuilder sb = new StringBuilder(3);
if ((permissions & 4) == 4) {
sb.append('r');
} else {
sb.append('-');
}
if ((permissions & 2) == 2) {
sb.append('w');
} else {
sb.append('-');
}
if ((permissions & 1) == 1) {
sb.append('x');
} else {
sb.append('-');
}
return sb.toString();
}
}

View File

@ -25,28 +25,16 @@ import org.gradle.api.artifacts.transform.TransformOutputs;
import org.gradle.api.artifacts.transform.TransformParameters;
import org.gradle.api.file.FileSystemLocation;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.internal.UncheckedException;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Function;
public interface UnpackTransform extends TransformAction<UnpackTransform.Parameters> {
interface Parameters extends TransformParameters {
@Input
@Optional
String getTrimmedPrefixPattern();
void setTrimmedPrefixPattern(String pattern);
}
public interface UnpackTransform extends TransformAction<TransformParameters.None> {
@PathSensitive(PathSensitivity.NAME_ONLY)
@InputArtifact
@ -55,9 +43,9 @@ public interface UnpackTransform extends TransformAction<UnpackTransform.Paramet
@Override
default void transform(TransformOutputs outputs) {
File archiveFile = getArchiveFile().get().getAsFile();
File extractedDir = outputs.dir(archiveFile.getName());
File unzipDir = outputs.dir(archiveFile.getName());
try {
unpack(archiveFile, extractedDir);
unpack(archiveFile, unzipDir);
} catch (IOException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
@ -65,17 +53,8 @@ public interface UnpackTransform extends TransformAction<UnpackTransform.Paramet
void unpack(File archiveFile, File targetDir) throws IOException;
default Function<String, Path> pathResolver() {
String trimmedPrefixPattern = getParameters().getTrimmedPrefixPattern();
return trimmedPrefixPattern != null
? (i) -> trimArchiveExtractPath(trimmedPrefixPattern, i)
: (i) -> FileSystems.getDefault().getPath(i);
}
/*
* We want to be able to trim off certain prefixes when transforming archives.
*
* E.g We want to remove up to the and including the jdk-.* relative paths. That is a JDK archive is structured as:
* We want to remove up to the and including the jdk-.* relative paths. That is a JDK archive is structured as:
* jdk-12.0.1/
* jdk-12.0.1/Contents
* ...
@ -87,11 +66,11 @@ public interface UnpackTransform extends TransformAction<UnpackTransform.Paramet
*
* so we account for this and search the path components until we find the jdk-12.0.1, and strip the leading components.
*/
static Path trimArchiveExtractPath(String ignoredPattern, String relativePath) {
static Path trimArchiveExtractPath(String relativePath) {
final Path entryName = Paths.get(relativePath);
int index = 0;
for (; index < entryName.getNameCount(); index++) {
if (entryName.getName(index).toString().matches(ignoredPattern)) {
if (entryName.getName(index).toString().matches("jdk-?\\d.*")) {
break;
}
}
@ -102,4 +81,5 @@ public interface UnpackTransform extends TransformAction<UnpackTransform.Paramet
// finally remove the top-level directories from the output path
return entryName.subpath(index + 1, entryName.getNameCount());
}
}

View File

@ -20,48 +20,35 @@
package org.elasticsearch.gradle.transform;
import org.apache.commons.io.IOUtils;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.gradle.api.logging.Logging;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.function.Function;
import static org.elasticsearch.gradle.util.PermissionUtils.chmod;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public abstract class UnzipTransform implements UnpackTransform {
public void unpack(File zipFile, File targetDir) throws IOException {
Logging.getLogger(UnzipTransform.class)
.info("Unpacking " + zipFile.getName() + " using " + UnzipTransform.class.getSimpleName() + ".");
Function<String, Path> pathModifier = pathResolver();
ZipFile zip = new ZipFile(zipFile);
try {
Enumeration<ZipEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipEntry zipEntry = entries.nextElement();
Path child = pathModifier.apply(zipEntry.getName());
if (child == null) {
try (ZipInputStream inputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)))) {
ZipEntry entry;
while ((entry = inputStream.getNextEntry()) != null) {
if (entry.isDirectory()) {
continue;
}
Path outputPath = targetDir.toPath().resolve(child);
if (zipEntry.isDirectory()) {
outputPath.toFile().mkdirs();
chmod(outputPath, zipEntry.getUnixMode());
continue;
String child = UnpackTransform.trimArchiveExtractPath(entry.getName()).toString();
File outFile = new File(targetDir, child);
outFile.getParentFile().mkdirs();
try (FileOutputStream outputStream = new FileOutputStream(outFile)) {
IOUtils.copyLarge(inputStream, outputStream);
}
try (FileOutputStream outputStream = new FileOutputStream(outputPath.toFile())) {
IOUtils.copyLarge(zip.getInputStream(zipEntry), outputStream);
}
chmod(outputPath, zipEntry.getUnixMode());
}
} finally {
zip.close();
}
}
}

View File

@ -1,69 +0,0 @@
/*
* 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.util;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
public class PermissionUtils {
public static void chmod(Path path, int mode) throws IOException {
final PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
if (view != null && (mode != 0)) {
final Set<PosixFilePermission> permissions = permissionsFromInt(mode);
Files.setPosixFilePermissions(path, permissions);
}
}
private static Set<PosixFilePermission> permissionsFromInt(int mode) {
return PosixFilePermissions.fromString(
permissions((mode >> 6) & 07) + permissions((mode >> 3) & 07) + permissions((mode >> 0) & 07)
);
}
private static String permissions(final int permissions) {
if (permissions < 0 || permissions > 7) {
throw new IllegalArgumentException("permissions [" + permissions + "] out of range");
}
final StringBuilder sb = new StringBuilder(3);
if ((permissions & 4) == 4) {
sb.append('r');
} else {
sb.append('-');
}
if ((permissions & 2) == 2) {
sb.append('w');
} else {
sb.append('-');
}
if ((permissions & 1) == 1) {
sb.append('x');
} else {
sb.append('-');
}
return sb.toString();
}
}

View File

@ -0,0 +1,53 @@
import org.elasticsearch.gradle.BwcVersions
import org.elasticsearch.gradle.Version
import org.elasticsearch.gradle.info.BuildParams
/*
* 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.
*/
plugins {
id 'elasticsearch.global-build-info'
}
boolean internal = Boolean.parseBoolean(System.getProperty("tests.internal", "true"))
BuildParams.init { it.setIsInternal(internal) }
project.gradle.projectsEvaluated {
// wire the download service url to wiremock
String fakeDownloadService = System.getProperty('tests.download_service')
if (fakeDownloadService != null) {
IvyArtifactRepository repository = (IvyArtifactRepository) rootProject.repositories.getByName("elasticsearch-downloads")
repository.setUrl(fakeDownloadService)
repository = (IvyArtifactRepository) project('subproj').repositories.getByName("elasticsearch-downloads")
repository.setUrl(fakeDownloadService)
if (internal == false) {
repository = (IvyArtifactRepository) rootProject.repositories.getByName("elasticsearch-snapshots")
repository.setUrl(fakeDownloadService)
repository = (IvyArtifactRepository) project('subproj').repositories.getByName("elasticsearch-snapshots")
repository.setUrl(fakeDownloadService)
}
}
}
if (internal) {
Version currentVersion = Version.fromString("9.0.0")
BwcVersions versions = new BwcVersions(new TreeSet<>(
Arrays.asList(Version.fromString("8.0.0"), Version.fromString("8.0.1"), Version.fromString("8.1.0"), currentVersion)),
currentVersion)
BuildParams.init { it.setBwcVersions(versions) }
}

View File

@ -0,0 +1,34 @@
String distroConfig = System.getProperty('tests.local_distro.config')
if (distroConfig != null) {
// setup the test distribution as an artifact of this project
String distroType = System.getProperty('tests.distro.type')
def buildDistro
File buildFile
if (['rpm', 'deb'].contains(distroType)) {
buildDistro = project.tasks.register("build" + distroType.capitalize(), Copy) {
from 'files'
into 'build/files'
include 'fake_elasticsearch.tar.gz'
// this shouldn't be extracted so we just rename the file so it is a dummy package
rename { filename -> filename.replace('tar.gz', distroType) }
}
buildFile = project.file("build/files/fake_elasticsearch." + distroType)
} else {
String distroPlatform = System.getProperty('tests.distro.platform')
String extension = "tar.gz"
if (distroType == 'archive' && distroPlatform == 'windows' || distroType == 'integ-test-zip') {
extension = "zip"
}
// copy file as is
buildDistro = project.tasks.register("buildArchive", Copy) {
from 'files'
include "fake_elasticsearch.${extension}"
into 'build/files'
}
buildFile = project.file("build/files/fake_elasticsearch.${extension}")
}
configurations.create(distroConfig)
artifacts.add(distroConfig, [file: buildFile, builtBy: buildDistro])
}

View File

@ -0,0 +1,8 @@
include 'subproj'
String distroProject = System.getProperty('tests.local_distro.project')
System.out.println("local distro project: " + distroProject);
if (distroProject != null) {
include distroProject
project(distroProject).projectDir = new File(rootDir, 'distribution')
}

View File

@ -0,0 +1,65 @@
import org.elasticsearch.gradle.Architecture
plugins {
id 'elasticsearch.distribution-download'
}
String distroVersion = System.getProperty('tests.distro.version')
String distroType = System.getProperty('tests.distro.type')
String distroPlatform = System.getProperty('tests.distro.platform')
String distroFlavor = System.getProperty('tests.distro.flavor')
String distroBundledJdk = System.getProperty('tests.distro.bundledJdk')
String distroArch = System.getProperty('tests.distro.arch');
elasticsearch_distributions {
test_distro {
if (distroVersion != null) {
version = distroVersion
}
if (distroType != null) {
type = distroType
}
if (distroPlatform != null) {
platform = distroPlatform
}
if (distroFlavor != null) {
flavor = distroFlavor
}
if (distroBundledJdk != null) {
bundledJdk = Boolean.parseBoolean(distroBundledJdk)
}
if (distroArch != null) {
architecture = Architecture.valueOf(distroArch);
} else {
architecture = Architecture.current();
}
}
}
tasks.register("assertDistroFile") {
dependsOn elasticsearch_distributions.test_distro
doLast {
File distroFile = new File(elasticsearch_distributions.test_distro.toString())
if (distroFile.exists() == false) {
throw new GradleException("distro file does not exist: ${distroFile}")
}
if (distroFile.isFile() == false) {
throw new GradleException("distro file is not a regular file: ${distroFile}")
}
}
}
if (['rpm', 'deb'].contains(distroType) == false) {
tasks.register("assertDistroExtracted") {
dependsOn elasticsearch_distributions.test_distro.extracted, assertDistroFile
doLast {
File distroExtracted = new File(elasticsearch_distributions.test_distro.extracted.toString())
if (distroExtracted.exists() == false) {
throw new GradleException("extracted does not exist: ${distroExtracted}")
}
if (distroExtracted.isDirectory() == false) {
throw new GradleException("extracted distro is not a directory: ${distroExtracted}")
}
}
}
}

View File

@ -316,7 +316,7 @@ BuildParams.bwcVersions.forPreviousUnreleased { BwcVersions.UnreleasedVersionInf
File artifactFile = e.value
String artifactFileName = artifactFile.name
String artifactName = artifactFileName.contains('oss') ? 'elasticsearch-oss' : 'elasticsearch'
String suffix = artifactFileName.endsWith("tar.gz") ? "tar.gz" : artifactFileName[-3..-1]
String suffix = artifactFile.toString()[-3..-1]
int archIndex = artifactFileName.indexOf('x86_64')
String classifier = ''
if (archIndex != -1) {