Skip to content

Commit 67e310e

Browse files
authored
Improve Docker image caching and testing (#78552)
Firstly: we tag our Docker images with various pieces of information, including a timestamp for when the image was built. However, this makes it impossible completely cache the image. When developing the Docker images, it's very tedious to completely rebuild an image for every single change. Therefore, provided we're not building a proper release build, we fix the build time to midnight so that the Docker build cache is usable. Secondly: the `DockerBuildTask` outputs a marker file to indicate that an image has been built, but that isn't enough for a meaningful up-to-date check by Gradle. Improve this by fetching the newly-built image's hash, and writing that to the output file. Thirdly: improve the Docker tests to make them more ergonomic, and also disable `ingest.geoip.downloader.enabled` by default. Fourthly: add missing test coverage for sourcing settings from env vars.
1 parent bfba7fa commit 67e310e

File tree

6 files changed

+195
-146
lines changed

6 files changed

+195
-146
lines changed

build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/docker/DockerBuildTask.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,17 @@
2929
import org.gradle.workers.WorkParameters;
3030
import org.gradle.workers.WorkerExecutor;
3131

32-
import javax.inject.Inject;
32+
import java.io.ByteArrayOutputStream;
3333
import java.io.IOException;
34+
import java.nio.file.Files;
3435
import java.util.Arrays;
36+
import java.util.List;
37+
import javax.inject.Inject;
3538

39+
/**
40+
* This task wraps up the details of building a Docker image, including adding a pull
41+
* mechanism that can retry, and emitting the image SHA as a task output.
42+
*/
3643
public class DockerBuildTask extends DefaultTask {
3744
private static final Logger LOGGER = Logging.getLogger(DockerBuildTask.class);
3845

@@ -167,6 +174,8 @@ public void execute() {
167174
parameters.getBaseImages().get().forEach(this::pullBaseImage);
168175
}
169176

177+
final List<String> tags = parameters.getTags().get();
178+
170179
LoggedExec.exec(execOperations, spec -> {
171180
spec.executable("docker");
172181

@@ -176,17 +185,32 @@ public void execute() {
176185
spec.args("--no-cache");
177186
}
178187

179-
parameters.getTags().get().forEach(tag -> spec.args("--tag", tag));
188+
tags.forEach(tag -> spec.args("--tag", tag));
180189

181190
parameters.getBuildArgs().get().forEach((k, v) -> spec.args("--build-arg", k + "=" + v));
182191
});
183192

193+
// Fetch the Docker image's hash, and write it to desk as the task's output. Doing this allows us
194+
// to do proper up-to-date checks in Gradle.
184195
try {
185-
parameters.getMarkerFile().getAsFile().get().createNewFile();
196+
final String checksum = getImageChecksum(tags.get(0));
197+
Files.writeString(parameters.getMarkerFile().getAsFile().get().toPath(), checksum + "\n");
186198
} catch (IOException e) {
187-
throw new RuntimeException("Failed to create marker file", e);
199+
throw new RuntimeException("Failed to write marker file", e);
188200
}
189201
}
202+
203+
private String getImageChecksum(String imageTag) {
204+
final ByteArrayOutputStream stdout = new ByteArrayOutputStream();
205+
206+
execOperations.exec(spec -> {
207+
spec.setCommandLine("docker", "inspect", "--format", "{{ .Id }}", imageTag);
208+
spec.setStandardOutput(stdout);
209+
spec.setIgnoreExitValue(false);
210+
});
211+
212+
return stdout.toString().trim();
213+
}
190214
}
191215

192216
interface Parameters extends WorkParameters {

distribution/docker/build.gradle

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.elasticsearch.gradle.internal.docker.TransformLog4jConfigFilter
99
import org.elasticsearch.gradle.internal.info.BuildParams
1010

1111
import java.nio.file.Path
12+
import java.time.temporal.ChronoUnit
1213

1314
apply plugin: 'elasticsearch.standalone-rest-test'
1415
apply plugin: 'elasticsearch.test.fixtures'
@@ -81,10 +82,17 @@ dependencies {
8182
ext.expansions = { Architecture architecture, DockerBase base ->
8283
def (major,minor) = VersionProperties.elasticsearch.split("\\.")
8384

85+
// We tag our Docker images with various pieces of information, including a timestamp
86+
// for when the image was built. However, this makes it impossible completely cache
87+
// the image. When developing the Docker images, it's very tedious to completely rebuild
88+
// an image for every single change. Therefore, outside of CI, we fix the
89+
// build time to midnight so that the Docker build cache is usable.
90+
def buildDate = BuildParams.isCi() ? BuildParams.buildDate : BuildParams.buildDate.truncatedTo(ChronoUnit.DAYS)
91+
8492
return [
8593
'base_image' : base.image,
8694
'bin_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'bin',
87-
'build_date' : BuildParams.buildDate,
95+
'build_date' : buildDate,
8896
'config_dir' : base == DockerBase.IRON_BANK ? 'scripts' : 'config',
8997
'git_revision' : BuildParams.gitRevision,
9098
'license' : base == DockerBase.IRON_BANK ? 'Elastic License 1.0' : 'Elastic-License-2.0',
@@ -341,6 +349,7 @@ void addBuildDockerImageTask(Architecture architecture, DockerBase base) {
341349

342350
dockerContext.fileProvider(transformTask.map { Sync task -> task.getDestinationDir() })
343351

352+
noCache = BuildParams.isCi
344353
tags = generateTags(base)
345354

346355
if (base == DockerBase.IRON_BANK) {
@@ -402,11 +411,11 @@ void addBuildEssDockerImageTask(Architecture architecture) {
402411
tasks.register(taskName("build", architecture, base, "DockerImage"), DockerBuildTask) {
403412

404413
TaskProvider<DockerBuildTask> buildCloudTask = tasks.named(taskName("build", architecture, DockerBase.CLOUD, "DockerImage"))
405-
dependsOn(buildCloudTask)
406-
dependsOn(buildContextTask)
414+
inputs.files(buildCloudTask)
407415

408416
dockerContext.fileProvider(buildContextTask.map { it.getDestinationDir() })
409417

418+
noCache = BuildParams.isCi
410419
baseImages = []
411420
tags = generateTags(base)
412421

0 commit comments

Comments
 (0)