Skip to content

Commit 038f7a8

Browse files
authored
Avoid packaging / unpacking cycle when using local current distributions (#61592) (#62176)
- Extract distribution archives defaults into plugin - Added basic test coverage - Avoid packaging/unpackaging cycle when relying on locally build distributions - Provide DSL for setting up distribution archives - Cleanup archives build script
1 parent 71ca7f4 commit 038f7a8

15 files changed

+661
-173
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.gradle.internal
21+
22+
import org.apache.commons.compress.archivers.tar.TarArchiveEntry
23+
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
24+
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream
25+
import org.apache.tools.zip.ZipEntry
26+
import org.apache.tools.zip.ZipFile
27+
import org.elasticsearch.gradle.fixtures.AbstractGradleFuncTest
28+
import org.gradle.testkit.runner.BuildResult
29+
import org.gradle.testkit.runner.TaskOutcome
30+
31+
class InternalDistributionArchiveSetupPluginFuncTest extends AbstractGradleFuncTest {
32+
33+
def setup() {
34+
buildFile << """
35+
import org.elasticsearch.gradle.tar.SymbolicLinkPreservingTar
36+
37+
plugins {
38+
id 'elasticsearch.internal-distribution-archive-setup'
39+
}
40+
"""
41+
file('someFile.txt') << "some content"
42+
}
43+
44+
def "applies defaults to tar tasks"() {
45+
given:
46+
file('someFile.txt') << "some content"
47+
buildFile << """
48+
tasks.register('${buildTaskName}', SymbolicLinkPreservingTar) {
49+
from 'someFile.txt'
50+
}
51+
"""
52+
53+
when:
54+
def result = gradleRunner(buildTaskName).build()
55+
56+
then:
57+
file(expectedOutputArchivePath).exists()
58+
assertTarPermissionDefaults(file(expectedOutputArchivePath))
59+
assertEmptyDirTasksTriggered(result)
60+
61+
where:
62+
buildTaskName | expectedOutputArchivePath
63+
"buildDarwinTar" | "darwin-tar/build/distributions/elasticsearch.tar.gz"
64+
"buildOssDarwinTar" | "oss-darwin-tar/build/distributions/elasticsearch-oss.tar.gz"
65+
}
66+
67+
def "applies defaults to zip tasks"() {
68+
given:
69+
buildFile << """
70+
tasks.register('${buildTaskName}', Zip) {
71+
from 'someFile.txt'
72+
}
73+
"""
74+
75+
when:
76+
def result = gradleRunner(buildTaskName).build()
77+
78+
then:
79+
file(expectedOutputArchivePath).exists()
80+
assertZipPermissionDefaults(file(expectedOutputArchivePath))
81+
assertEmptyDirTasksTriggered(result)
82+
83+
where:
84+
buildTaskName | expectedOutputArchivePath
85+
"buildDarwinZip" | "darwin-zip/build/distributions/elasticsearch.zip"
86+
"buildOssDarwinZip" | "oss-darwin-zip/build/distributions/elasticsearch-oss.zip"
87+
}
88+
89+
def "registered distribution provides archives and directory variant"() {
90+
given:
91+
file('someFile.txt') << "some content"
92+
93+
settingsFile << """
94+
include ':consumer'
95+
include ':producer-tar'
96+
"""
97+
98+
buildFile << """
99+
import org.gradle.api.artifacts.type.ArtifactTypeDefinition;
100+
import org.gradle.api.internal.artifacts.ArtifactAttributes;
101+
102+
distribution_archives {
103+
producerTar {
104+
content {
105+
project.copySpec {
106+
from 'someFile.txt'
107+
}
108+
}
109+
}
110+
}
111+
112+
project('consumer') { p ->
113+
configurations {
114+
consumeArchive {}
115+
consumeDir {}
116+
}
117+
118+
dependencies {
119+
consumeDir project(path: ':producer-tar', configuration:'extracted')
120+
consumeArchive project(path: ':producer-tar', configuration:'default' )
121+
}
122+
123+
tasks.register("copyDir", Copy) {
124+
from(configurations.consumeDir)
125+
into('build/dir')
126+
}
127+
128+
tasks.register("copyArchive", Copy) {
129+
from(configurations.consumeArchive)
130+
into('build/archives')
131+
}
132+
}
133+
"""
134+
when:
135+
def result = gradleRunner("copyArchive").build()
136+
137+
then:"tar task executed and target folder contains plain tar"
138+
result.task(':buildProducerTar').outcome == TaskOutcome.SUCCESS
139+
result.task(':consumer:copyArchive').outcome == TaskOutcome.SUCCESS
140+
file("producer-tar/build/distributions/elasticsearch.tar.gz").exists()
141+
file("consumer/build/archives/elasticsearch.tar.gz").exists()
142+
143+
when:
144+
result = gradleRunner("copyDir").build()
145+
then:"plain copy task executed and target folder contains plain content"
146+
result.task(':buildProducer').outcome == TaskOutcome.SUCCESS
147+
result.task(':consumer:copyDir').outcome == TaskOutcome.SUCCESS
148+
file("producer-tar/build/install/someFile.txt").exists()
149+
file("consumer/build/dir/someFile.txt").exists()
150+
}
151+
152+
private static boolean assertTarPermissionDefaults(File tarArchive) {
153+
TarArchiveInputStream tarInput = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(tarArchive)))
154+
try {
155+
TarArchiveEntry currentEntry = tarInput.getNextTarEntry()
156+
while (currentEntry != null) {
157+
if (currentEntry.isDirectory()) {
158+
assertDefaultDirPermissions(currentEntry.getMode())
159+
} else {
160+
assertDefaultFilePermissions(currentEntry.getMode())
161+
}
162+
currentEntry = tarInput.getNextTarEntry()
163+
}
164+
return true
165+
} finally {
166+
tarInput.close()
167+
}
168+
}
169+
170+
private static boolean assertZipPermissionDefaults(File archive) {
171+
ZipFile zip = new ZipFile(archive)
172+
try {
173+
Enumeration<ZipEntry> entries = zip.getEntries()
174+
while (entries.hasMoreElements()) {
175+
ZipEntry zipEntry = entries.nextElement()
176+
if (zipEntry.isDirectory()) {
177+
assertDefaultDirPermissions(zipEntry.getUnixMode())
178+
} else {
179+
assertDefaultFilePermissions(zipEntry.getUnixMode())
180+
}
181+
}
182+
} finally {
183+
zip.close()
184+
}
185+
true
186+
}
187+
188+
private static boolean assertDefaultDirPermissions(int mode) {
189+
assert ((mode >> 6) & 07) == 7
190+
assert ((mode >> 3) & 07) == 5
191+
assert ((mode >> 0) & 07) == 5
192+
true
193+
}
194+
195+
private static boolean assertDefaultFilePermissions(int mode) {
196+
assert ((mode >> 6) & 07) == 6
197+
assert ((mode >> 3) & 07) == 4
198+
assert ((mode >> 0) & 07) == 4
199+
true
200+
}
201+
202+
private static boolean assertEmptyDirTasksTriggered(BuildResult result) {
203+
result.task(":createJvmOptionsDir").outcome == TaskOutcome.SUCCESS
204+
result.task(":createLogsDir").outcome == TaskOutcome.SUCCESS
205+
result.task(":createPluginsDir").outcome == TaskOutcome.SUCCESS
206+
true
207+
}
208+
}

buildSrc/src/integTest/groovy/org/elasticsearch/gradle/internal/InternalDistributionDownloadPluginFuncTest.groovy

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
7272
def result = gradleRunner("setupDistro", '-g', testProjectDir.newFolder('GUH').path).build()
7373

7474
then:
75-
result.task(":distribution:archives:linux-tar:buildTar").outcome == TaskOutcome.SUCCESS
75+
result.task(":distribution:archives:linux-tar:buildExploded").outcome == TaskOutcome.SUCCESS
7676
result.task(":setupDistro").outcome == TaskOutcome.SUCCESS
7777
assertExtractedDistroIsCreated(distroVersion, "build/distro", 'current-marker.txt')
7878
}
@@ -183,14 +183,29 @@ class InternalDistributionDownloadPluginFuncTest extends AbstractGradleFuncTest
183183
def bwcSubProjectFolder = testProjectDir.newFolder("distribution", "archives", "linux-tar")
184184
new File(bwcSubProjectFolder, 'current-marker.txt') << "current"
185185
new File(bwcSubProjectFolder, 'build.gradle') << """
186+
import org.gradle.api.internal.artifacts.ArtifactAttributes;
187+
186188
apply plugin:'distribution'
187-
tasks.register("buildTar", Tar) {
189+
190+
def buildTar = tasks.register("buildTar", Tar) {
188191
from('current-marker.txt')
189192
archiveExtension = "tar.gz"
190193
compression = Compression.GZIP
191194
}
195+
def buildExploded = tasks.register("buildExploded", Copy) {
196+
from('current-marker.txt')
197+
into("build/local")
198+
}
199+
configurations {
200+
extracted {
201+
attributes {
202+
attribute(ArtifactAttributes.ARTIFACT_FORMAT, "directory")
203+
}
204+
}
205+
}
192206
artifacts {
193207
it.add("default", buildTar)
208+
it.add("extracted", buildExploded)
194209
}
195210
"""
196211
buildFile << """
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.gradle;
21+
22+
public interface DistributionDependency {
23+
static DistributionDependency of(String dependencyNotation) {
24+
return new StringBasedDistributionDependency(dependencyNotation);
25+
}
26+
27+
Object getDefaultNotation();
28+
29+
Object getExtractedNotation();
30+
31+
class StringBasedDistributionDependency implements DistributionDependency {
32+
private final String notation;
33+
34+
public StringBasedDistributionDependency(String notation) {
35+
this.notation = notation;
36+
}
37+
38+
@Override
39+
public Object getDefaultNotation() {
40+
return notation;
41+
}
42+
43+
@Override
44+
public Object getExtractedNotation() {
45+
return notation;
46+
}
47+
}
48+
}

buildSrc/src/main/java/org/elasticsearch/gradle/DistributionDownloadPlugin.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
/**
4646
* A plugin to manage getting and extracting distributions of Elasticsearch.
47-
*
47+
* <p>
4848
* The plugin provides hooks to register custom distribution resolutions.
4949
* This plugin resolves distributions from the Elastic downloads service if
5050
* no registered resolution strategy can resolve to a distribution.
@@ -122,24 +122,24 @@ void setupDistributions(Project project) {
122122
distribution.finalizeValues();
123123
DependencyHandler dependencies = project.getDependencies();
124124
// for the distribution as a file, just depend on the artifact directly
125-
Object resolvedDependency = resolveDependencyNotation(project, distribution);
126-
dependencies.add(distribution.configuration.getName(), resolvedDependency);
125+
DistributionDependency distributionDependency = resolveDependencyNotation(project, distribution);
126+
dependencies.add(distribution.configuration.getName(), distributionDependency.getDefaultNotation());
127127
// no extraction allowed for rpm, deb or docker
128128
if (distribution.getType().shouldExtract()) {
129129
// The extracted configuration depends on the artifact directly but has
130130
// an artifact transform registered to resolve it as an unpacked folder.
131-
dependencies.add(distribution.getExtracted().getName(), resolvedDependency);
131+
dependencies.add(distribution.getExtracted().getName(), distributionDependency.getExtractedNotation());
132132
}
133133
}
134134
}
135135

136-
private Object resolveDependencyNotation(Project p, ElasticsearchDistribution distribution) {
136+
private DistributionDependency resolveDependencyNotation(Project p, ElasticsearchDistribution distribution) {
137137
return distributionsResolutionStrategiesContainer.stream()
138138
.sorted(Comparator.comparingInt(DistributionResolution::getPriority))
139139
.map(r -> r.getResolver().resolve(p, distribution))
140140
.filter(d -> d != null)
141141
.findFirst()
142-
.orElseGet(() -> dependencyNotation(distribution));
142+
.orElseGet(() -> DistributionDependency.of(dependencyNotation(distribution)));
143143
}
144144

145145
private static void addIvyRepo(Project project, String name, String url, String group) {
@@ -177,7 +177,7 @@ private static void setupDownloadServiceRepo(Project project) {
177177
* Maven coordinates point to either the integ-test-zip coordinates on maven central, or a set of artificial
178178
* coordinates that resolve to the Elastic download service through an ivy repository.
179179
*/
180-
private Object dependencyNotation(ElasticsearchDistribution distribution) {
180+
private String dependencyNotation(ElasticsearchDistribution distribution) {
181181
if (distribution.getType() == Type.INTEG_TEST_ZIP) {
182182
return "org.elasticsearch.distribution.integ-test-zip:elasticsearch:" + distribution.getVersion() + "@zip";
183183
}

buildSrc/src/main/java/org/elasticsearch/gradle/DistributionResolution.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ public int getPriority() {
5151
}
5252

5353
public interface Resolver {
54-
Object resolve(Project project, ElasticsearchDistribution distribution);
54+
DistributionDependency resolve(Project project, ElasticsearchDistribution distribution);
5555
}
5656
}

buildSrc/src/main/java/org/elasticsearch/gradle/ElasticsearchDistribution.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ public Architecture getArchitecture() {
193193

194194
@Override
195195
public String toString() {
196+
return getName() + "_" + getType() + "_" + getVersion();
197+
}
198+
199+
public String getFilepath() {
196200
return configuration.getSingleFile().toString();
197201
}
198202

0 commit comments

Comments
 (0)