Skip to content

Commit c1a1047

Browse files
authored
Consolidate docker availability build logic (#52548)
1 parent f83d75d commit c1a1047

File tree

17 files changed

+650
-524
lines changed

17 files changed

+650
-524
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import static org.elasticsearch.gradle.tool.Boilerplate.maybeConfigure
3434

3535
plugins {
3636
id 'lifecycle-base'
37+
id 'elasticsearch.docker-support'
3738
id 'elasticsearch.global-build-info'
3839
id "com.diffplug.gradle.spotless" version "3.24.2" apply false
3940
}

buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import org.elasticsearch.gradle.precommit.PrecommitTasks
3232
import org.elasticsearch.gradle.test.ErrorReportingTestListener
3333
import org.elasticsearch.gradle.testclusters.ElasticsearchCluster
3434
import org.elasticsearch.gradle.testclusters.TestClustersPlugin
35-
import org.elasticsearch.gradle.testclusters.TestDistribution
3635
import org.elasticsearch.gradle.tool.Boilerplate
3736
import org.gradle.api.Action
3837
import org.gradle.api.GradleException
@@ -83,8 +82,6 @@ import java.nio.charset.StandardCharsets
8382
import java.nio.file.Files
8483

8584
import static org.elasticsearch.gradle.tool.Boilerplate.maybeConfigure
86-
import static org.elasticsearch.gradle.tool.DockerUtils.assertDockerIsAvailable
87-
import static org.elasticsearch.gradle.tool.DockerUtils.getDockerPath
8885

8986
/**
9087
* Encapsulates build configuration for elasticsearch projects.
@@ -186,51 +183,6 @@ class BuildPlugin implements Plugin<Project> {
186183
}
187184
}
188185

189-
static void requireDocker(final Task task) {
190-
final Project rootProject = task.project.rootProject
191-
ExtraPropertiesExtension ext = rootProject.extensions.getByType(ExtraPropertiesExtension)
192-
193-
if (rootProject.hasProperty('requiresDocker') == false) {
194-
/*
195-
* This is our first time encountering a task that requires Docker. We will add an extension that will let us track the tasks
196-
* that register as requiring Docker. We will add a delayed execution that when the task graph is ready if any such tasks are
197-
* in the task graph, then we check two things:
198-
* - the Docker binary is available
199-
* - we can execute a Docker command that requires privileges
200-
*
201-
* If either of these fail, we fail the build.
202-
*/
203-
204-
// check if the Docker binary exists and record its path
205-
final String dockerBinary = getDockerPath().orElse(null)
206-
207-
final boolean buildDocker
208-
final String buildDockerProperty = System.getProperty("build.docker")
209-
if (buildDockerProperty == null) {
210-
buildDocker = dockerBinary != null
211-
} else if (buildDockerProperty == "true") {
212-
buildDocker = true
213-
} else if (buildDockerProperty == "false") {
214-
buildDocker = false
215-
} else {
216-
throw new IllegalArgumentException(
217-
"expected build.docker to be unset or one of \"true\" or \"false\" but was [" + buildDockerProperty + "]")
218-
}
219-
220-
ext.set('buildDocker', buildDocker)
221-
ext.set('requiresDocker', [])
222-
rootProject.gradle.taskGraph.whenReady { TaskExecutionGraph taskGraph ->
223-
final List<String> tasks = taskGraph.allTasks.intersect(ext.get('requiresDocker') as List<Task>).collect { " ${it.path}".toString()}
224-
225-
if (tasks.isEmpty() == false) {
226-
assertDockerIsAvailable(task.project, tasks)
227-
}
228-
}
229-
}
230-
231-
(ext.get('requiresDocker') as List<Task>).add(task)
232-
}
233-
234186
/** Add a check before gradle execution phase which ensures java home for the given java version is set. */
235187
static void requireJavaHome(Task task, int version) {
236188
// use root project for global accounting

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
import org.elasticsearch.gradle.ElasticsearchDistribution.Flavor;
2323
import org.elasticsearch.gradle.ElasticsearchDistribution.Platform;
2424
import org.elasticsearch.gradle.ElasticsearchDistribution.Type;
25+
import org.elasticsearch.gradle.docker.DockerSupportPlugin;
26+
import org.elasticsearch.gradle.docker.DockerSupportService;
2527
import org.elasticsearch.gradle.info.BuildParams;
2628
import org.elasticsearch.gradle.info.GlobalBuildInfoPlugin;
29+
import org.elasticsearch.gradle.tool.Boilerplate;
2730
import org.gradle.api.GradleException;
2831
import org.gradle.api.NamedDomainObjectContainer;
2932
import org.gradle.api.Plugin;
@@ -37,6 +40,7 @@
3740
import org.gradle.api.credentials.HttpHeaderCredentials;
3841
import org.gradle.api.file.FileTree;
3942
import org.gradle.api.plugins.ExtraPropertiesExtension;
43+
import org.gradle.api.provider.Provider;
4044
import org.gradle.api.tasks.Sync;
4145
import org.gradle.api.tasks.TaskProvider;
4246
import org.gradle.authentication.http.HttpHeaderAuthentication;
@@ -71,11 +75,17 @@ public class DistributionDownloadPlugin implements Plugin<Project> {
7175
public void apply(Project project) {
7276
// this is needed for isInternal
7377
project.getRootProject().getPluginManager().apply(GlobalBuildInfoPlugin.class);
78+
project.getRootProject().getPluginManager().apply(DockerSupportPlugin.class);
79+
80+
Provider<DockerSupportService> dockerSupport = Boilerplate.getBuildService(
81+
project.getGradle().getSharedServices(),
82+
DockerSupportPlugin.DOCKER_SUPPORT_SERVICE_NAME
83+
);
7484

7585
distributionsContainer = project.container(ElasticsearchDistribution.class, name -> {
7686
Configuration fileConfiguration = project.getConfigurations().create("es_distro_file_" + name);
7787
Configuration extractedConfiguration = project.getConfigurations().create("es_distro_extracted_" + name);
78-
return new ElasticsearchDistribution(name, project.getObjects(), fileConfiguration, extractedConfiguration);
88+
return new ElasticsearchDistribution(name, project.getObjects(), dockerSupport, fileConfiguration, extractedConfiguration);
7989
});
8090
project.getExtensions().add(CONTAINER_NAME, distributionsContainer);
8191

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

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919

2020
package org.elasticsearch.gradle;
2121

22+
import org.elasticsearch.gradle.docker.DockerSupportService;
2223
import org.gradle.api.Buildable;
2324
import org.gradle.api.artifacts.Configuration;
2425
import org.gradle.api.model.ObjectFactory;
2526
import org.gradle.api.provider.Property;
27+
import org.gradle.api.provider.Provider;
2628
import org.gradle.api.tasks.TaskDependency;
2729

2830
import java.io.File;
31+
import java.util.Collections;
2932
import java.util.Iterator;
3033
import java.util.Locale;
3134

@@ -110,6 +113,7 @@ public String toString() {
110113
}
111114

112115
private final String name;
116+
private final Provider<DockerSupportService> dockerSupport;
113117
// pkg private so plugin can configure
114118
final Configuration configuration;
115119
private final Extracted extracted;
@@ -119,21 +123,25 @@ public String toString() {
119123
private final Property<Platform> platform;
120124
private final Property<Flavor> flavor;
121125
private final Property<Boolean> bundledJdk;
126+
private final Property<Boolean> failIfUnavailable;
122127

123128
ElasticsearchDistribution(
124129
String name,
125130
ObjectFactory objectFactory,
131+
Provider<DockerSupportService> dockerSupport,
126132
Configuration fileConfiguration,
127133
Configuration extractedConfiguration
128134
) {
129135
this.name = name;
136+
this.dockerSupport = dockerSupport;
130137
this.configuration = fileConfiguration;
131138
this.version = objectFactory.property(String.class).convention(VersionProperties.getElasticsearch());
132139
this.type = objectFactory.property(Type.class);
133140
this.type.convention(Type.ARCHIVE);
134141
this.platform = objectFactory.property(Platform.class);
135142
this.flavor = objectFactory.property(Flavor.class);
136143
this.bundledJdk = objectFactory.property(Boolean.class);
144+
this.failIfUnavailable = objectFactory.property(Boolean.class).convention(true);
137145
this.extracted = new Extracted(extractedConfiguration);
138146
}
139147

@@ -182,6 +190,14 @@ public void setBundledJdk(Boolean bundledJdk) {
182190
this.bundledJdk.set(bundledJdk);
183191
}
184192

193+
public boolean getFailIfUnavailable() {
194+
return this.failIfUnavailable.get();
195+
}
196+
197+
public void setFailIfUnavailable(boolean failIfUnavailable) {
198+
this.failIfUnavailable.set(failIfUnavailable);
199+
}
200+
185201
@Override
186202
public String toString() {
187203
return configuration.getSingleFile().toString();
@@ -203,6 +219,13 @@ public Extracted getExtracted() {
203219

204220
@Override
205221
public TaskDependency getBuildDependencies() {
222+
// For non-required Docker distributions, skip building the distribution is Docker is unavailable
223+
if (getType() == Type.DOCKER
224+
&& getFailIfUnavailable() == false
225+
&& dockerSupport.get().getDockerAvailability().isAvailable == false) {
226+
return task -> Collections.emptySet();
227+
}
228+
206229
return configuration.getBuildDependencies();
207230
}
208231

@@ -222,7 +245,7 @@ void finalizeValues() {
222245
if (getType() == Type.INTEG_TEST_ZIP) {
223246
if (platform.getOrNull() != null) {
224247
throw new IllegalArgumentException(
225-
"platform not allowed for elasticsearch distribution [" + name + "] of type [integ_test_zip]"
248+
"platform cannot be set on elasticsearch distribution [" + name + "] of type [integ_test_zip]"
226249
);
227250
}
228251
if (flavor.getOrNull() != null) {
@@ -232,12 +255,18 @@ void finalizeValues() {
232255
}
233256
if (bundledJdk.getOrNull() != null) {
234257
throw new IllegalArgumentException(
235-
"bundledJdk not allowed for elasticsearch distribution [" + name + "] of type [integ_test_zip]"
258+
"bundledJdk cannot be set on elasticsearch distribution [" + name + "] of type [integ_test_zip]"
236259
);
237260
}
238261
return;
239262
}
240263

264+
if (getType() != Type.DOCKER && failIfUnavailable.get() == false) {
265+
throw new IllegalArgumentException(
266+
"failIfUnavailable cannot be 'false' on elasticsearch distribution [" + name + "] of type [" + getType() + "]"
267+
);
268+
}
269+
241270
if (getType() == Type.ARCHIVE) {
242271
// defaults for archive, set here instead of via convention so integ-test-zip can verify they are not set
243272
if (platform.isPresent() == false) {
@@ -246,7 +275,12 @@ void finalizeValues() {
246275
} else { // rpm, deb or docker
247276
if (platform.isPresent()) {
248277
throw new IllegalArgumentException(
249-
"platform not allowed for elasticsearch distribution [" + name + "] of type [" + getType() + "]"
278+
"platform cannot be set on elasticsearch distribution [" + name + "] of type [" + getType() + "]"
279+
);
280+
}
281+
if (getType() == Type.DOCKER && bundledJdk.isPresent()) {
282+
throw new IllegalArgumentException(
283+
"bundledJdk cannot be set on elasticsearch distribution [" + name + "] of type [docker]"
250284
);
251285
}
252286
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.elasticsearch.gradle.docker;
2+
3+
import org.gradle.api.Plugin;
4+
import org.gradle.api.Project;
5+
import org.gradle.api.Task;
6+
import org.gradle.api.plugins.ExtraPropertiesExtension;
7+
import org.gradle.api.provider.Provider;
8+
9+
import java.io.File;
10+
import java.util.List;
11+
import java.util.stream.Collectors;
12+
13+
/**
14+
* Plugin providing {@link DockerSupportService} for detecting Docker installations and determining requirements for Docker-based
15+
* Elasticsearch build tasks.
16+
* <p>
17+
* Additionally registers a task graph listener used to assert a compatible Docker installation exists when task requiring Docker are
18+
* scheduled for execution. Tasks may declare a Docker requirement via an extra property. If a compatible Docker installation is not
19+
* available on the build system an exception will be thrown prior to task execution.
20+
*
21+
* <pre>
22+
* task myDockerTask {
23+
* ext.requiresDocker = true
24+
* }
25+
* </pre>
26+
*/
27+
public class DockerSupportPlugin implements Plugin<Project> {
28+
public static final String DOCKER_SUPPORT_SERVICE_NAME = "dockerSupportService";
29+
public static final String DOCKER_ON_LINUX_EXCLUSIONS_FILE = ".ci/dockerOnLinuxExclusions";
30+
public static final String REQUIRES_DOCKER_ATTRIBUTE = "requiresDocker";
31+
32+
@Override
33+
public void apply(Project project) {
34+
if (project != project.getRootProject()) {
35+
throw new IllegalStateException(this.getClass().getName() + " can only be applied to the root project.");
36+
}
37+
38+
Provider<DockerSupportService> dockerSupportServiceProvider = project.getGradle()
39+
.getSharedServices()
40+
.registerIfAbsent(
41+
DOCKER_SUPPORT_SERVICE_NAME,
42+
DockerSupportService.class,
43+
spec -> spec.parameters(
44+
params -> { params.setExclusionsFile(new File(project.getRootDir(), DOCKER_ON_LINUX_EXCLUSIONS_FILE)); }
45+
)
46+
);
47+
48+
// Ensure that if any tasks declare they require docker, we assert an available Docker installation exists
49+
project.getGradle().getTaskGraph().whenReady(graph -> {
50+
List<String> dockerTasks = graph.getAllTasks().stream().filter(task -> {
51+
ExtraPropertiesExtension ext = task.getExtensions().getExtraProperties();
52+
return ext.has(REQUIRES_DOCKER_ATTRIBUTE) && (boolean) ext.get(REQUIRES_DOCKER_ATTRIBUTE);
53+
}).map(Task::getPath).collect(Collectors.toList());
54+
55+
if (dockerTasks.isEmpty() == false) {
56+
dockerSupportServiceProvider.get().failIfDockerUnavailable(dockerTasks);
57+
}
58+
});
59+
}
60+
61+
}

0 commit comments

Comments
 (0)