Skip to content

Commit

Permalink
Simplify distribution download and extraction (7.x backport) (#61184)
Browse files Browse the repository at this point in the history
We leverage artifact transforms now when downloading and unpacking elasticsearch distributions.

This has the benefit of

- handcrafted extract tasks on the root project are not required. 
- the general tight coupling to the root project has been removed.
- the overall required configurations required to handle a distribution have been reduced
- ElasticsearchDistribution has been simplified by making Extracted an ordinary Configuration
downloaded and unpacked external distributions are reused in later builds by been cached
in the gradle user home.

DistributionDownloadPlugin functional tests have been extended and ported
to DistributionDownloadPluginFuncTest.

* Fix java8 compliant Path calculation
  • Loading branch information
breskeby authored Aug 17, 2020
1 parent 1ffc983 commit 8b7a0a1
Show file tree
Hide file tree
Showing 20 changed files with 376 additions and 568 deletions.
2 changes: 1 addition & 1 deletion buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* 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")
}
"""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
"""

when:
def result = gradleRunner("createExtractedTestDistro").buildAndFail()
def result = gradleRunner("tasks").buildAndFail()

then:
assertOutputContains(result.output, "Plugin 'elasticsearch.internal-distribution-download' is not supported. " +
Expand All @@ -61,18 +61,19 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
architecture = Architecture.current();
}
}
tasks.register("createExtractedTestDistro") {
dependsOn elasticsearch_distributions.test_distro.extracted
tasks.register("setupDistro", Sync) {
from(elasticsearch_distributions.test_distro.extracted)
into("build/distro")
}
"""

when:
def result = gradleRunner("createExtractedTestDistro").build()
def result = gradleRunner("setupDistro", '-g', testProjectDir.newFolder('GUH').path).build()

then:
result.task(":distribution:archives:linux-tar:buildTar").outcome == TaskOutcome.SUCCESS
result.task(":extractElasticsearchLinux$distroVersion").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, 'current-marker.txt')
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, "build/distro", 'current-marker.txt')
}

def "resolves bwc versions from source"() {
Expand All @@ -91,16 +92,18 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
architecture = Architecture.current();
}
}
tasks.register("createExtractedTestDistro") {
dependsOn elasticsearch_distributions.test_distro.extracted
tasks.register("setupDistro", Sync) {
from(elasticsearch_distributions.test_distro.extracted)
into("build/distro")
}
"""
when:
def result = gradleRunner("createExtractedTestDistro").build()

def result = gradleRunner("setupDistro").build()
then:
result.task(":distribution:bwc:minor:buildBwcTask").outcome == TaskOutcome.SUCCESS
result.task(":extractElasticsearchLinux8.1.0").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion,'bwc-marker.txt')
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
assertExtractedDistroIsCreated(distroVersion, "build/distro", 'bwc-marker.txt')
}

def "fails on resolving bwc versions with no bundled jdk"() {
Expand Down Expand Up @@ -195,8 +198,8 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest

}

boolean assertExtractedDistroIsCreated(String version, String markerFileName) {
File extractedFolder = new File(testProjectDir.root, "build/elasticsearch-distros/extracted_elasticsearch_${version}_archive_linux_default")
boolean assertExtractedDistroIsCreated(String version, String relativeDistroPath, String markerFileName) {
File extractedFolder = new File(testProjectDir.root, relativeDistroPath)
assert extractedFolder.exists()
assert new File(extractedFolder, markerFileName).exists()
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
given:
def mockRepoUrl = urlPath(jdkVendor, jdkVersion, platform)
def mockedContent = filebytes(jdkVendor, platform)
10.times {
3.times {
settingsFile << """
include ':sub-$it'
"""
Expand Down Expand Up @@ -132,8 +132,8 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
}

then:
result.tasks.size() == 10
result.output.count("Unpacking linux-12.0.2-x64.tar.gz using SymbolicLinkPreservingUntarTransform.") == 1
result.tasks.size() == 3
result.output.count("Unpacking linux-12.0.2-x64.tar.gz using ${SymbolicLinkPreservingUntarTransform.simpleName}.") == 1

where:
platform | jdkVendor | jdkVersion | expectedJavaBin
Expand All @@ -149,7 +149,7 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {
plugins {
id 'elasticsearch.jdk-download'
}
apply plugin: 'base'
apply plugin: 'elasticsearch.jdk-download'
jdks {
Expand All @@ -175,9 +175,9 @@ class JdkDownloadPluginFuncTest extends AbstractGradleFuncTest {

def commonGradleUserHome = testProjectDir.newFolder().toString()
// initial run
gradleRunner('getJdk', '-g', commonGradleUserHome).build()
gradleRunner('clean', 'getJdk', '-g', commonGradleUserHome).build()
// run against up-to-date transformations
gradleRunner('getJdk', '-i', '-g', commonGradleUserHome).build()
gradleRunner('clean', 'getJdk', '-i', '-g', commonGradleUserHome).build()
}

then:
Expand Down
Loading

0 comments on commit 8b7a0a1

Please sign in to comment.