commit
89a89a11a7
|
|
@ -31,6 +31,7 @@ import org.springframework.boot.buildpack.platform.docker.configuration.Resolved
|
|||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||
|
|
@ -115,16 +116,10 @@ public class Builder {
|
|||
Buildpacks buildpacks = getBuildpacks(request, imageFetcher, builderMetadata, buildpackLayersMetadata);
|
||||
EphemeralBuilder ephemeralBuilder = new EphemeralBuilder(buildOwner, builderImage, request.getName(),
|
||||
builderMetadata, request.getCreator(), request.getEnv(), buildpacks);
|
||||
this.docker.image().load(ephemeralBuilder.getArchive(), UpdateListener.none());
|
||||
try {
|
||||
executeLifecycle(request, ephemeralBuilder);
|
||||
tagImage(request.getName(), request.getTags());
|
||||
if (request.isPublish()) {
|
||||
pushImages(request.getName(), request.getTags());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.docker.image().remove(ephemeralBuilder.getName(), true);
|
||||
executeLifecycle(request, ephemeralBuilder);
|
||||
tagImage(request.getName(), request.getTags());
|
||||
if (request.isPublish()) {
|
||||
pushImages(request.getName(), request.getTags());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -170,13 +165,25 @@ public class Builder {
|
|||
}
|
||||
|
||||
private void executeLifecycle(BuildRequest request, EphemeralBuilder builder) throws IOException {
|
||||
ResolvedDockerHost dockerHost = null;
|
||||
if (this.dockerConfiguration != null && this.dockerConfiguration.isBindHostToBuilder()) {
|
||||
dockerHost = ResolvedDockerHost.from(this.dockerConfiguration.getHost());
|
||||
try (Lifecycle lifecycle = new Lifecycle(this.log, this.docker, getDockerHost(), request, builder)) {
|
||||
executeLifecycle(builder, lifecycle);
|
||||
}
|
||||
try (Lifecycle lifecycle = new Lifecycle(this.log, this.docker, dockerHost, request, builder)) {
|
||||
}
|
||||
|
||||
private void executeLifecycle(EphemeralBuilder builder, Lifecycle lifecycle) throws IOException {
|
||||
ImageArchive archive = builder.getArchive(lifecycle.getApplicationDirectory());
|
||||
this.docker.image().load(archive, UpdateListener.none());
|
||||
try {
|
||||
lifecycle.execute();
|
||||
}
|
||||
finally {
|
||||
this.docker.image().remove(builder.getName(), true);
|
||||
}
|
||||
}
|
||||
|
||||
private ResolvedDockerHost getDockerHost() {
|
||||
boolean bindHostToBuilder = this.dockerConfiguration != null && this.dockerConfiguration.isBindHostToBuilder();
|
||||
return (bindHostToBuilder) ? ResolvedDockerHost.from(this.dockerConfiguration.getHost()) : null;
|
||||
}
|
||||
|
||||
private void tagImage(ImageReference sourceReference, List<ImageReference> tags) throws IOException {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -21,11 +21,14 @@ import java.util.Map;
|
|||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive.Update;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Layer;
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A short-lived builder that is created for each {@link Lifecycle} run.
|
||||
|
|
@ -37,13 +40,17 @@ class EphemeralBuilder {
|
|||
|
||||
static final String BUILDER_FOR_LABEL_NAME = "org.springframework.boot.builderFor";
|
||||
|
||||
private ImageReference name;
|
||||
|
||||
private final BuildOwner buildOwner;
|
||||
|
||||
private final Creator creator;
|
||||
|
||||
private final BuilderMetadata builderMetadata;
|
||||
|
||||
private final ImageArchive archive;
|
||||
private final Image builderImage;
|
||||
|
||||
private final Creator creator;
|
||||
private final IOConsumer<Update> archiveUpdate;
|
||||
|
||||
/**
|
||||
* Create a new {@link EphemeralBuilder} instance.
|
||||
|
|
@ -54,26 +61,25 @@ class EphemeralBuilder {
|
|||
* @param creator the builder creator
|
||||
* @param env the builder env
|
||||
* @param buildpacks an optional set of buildpacks to apply
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
EphemeralBuilder(BuildOwner buildOwner, Image builderImage, ImageReference targetImage,
|
||||
BuilderMetadata builderMetadata, Creator creator, Map<String, String> env, Buildpacks buildpacks)
|
||||
throws IOException {
|
||||
ImageReference name = ImageReference.random("pack.local/builder/").inTaggedForm();
|
||||
BuilderMetadata builderMetadata, Creator creator, Map<String, String> env, Buildpacks buildpacks) {
|
||||
this.name = ImageReference.random("pack.local/builder/").inTaggedForm();
|
||||
this.buildOwner = buildOwner;
|
||||
this.creator = creator;
|
||||
this.builderMetadata = builderMetadata.copy(this::updateMetadata);
|
||||
this.archive = ImageArchive.from(builderImage, (update) -> {
|
||||
this.builderImage = builderImage;
|
||||
this.archiveUpdate = (update) -> {
|
||||
update.withUpdatedConfig(this.builderMetadata::attachTo);
|
||||
update.withUpdatedConfig((config) -> config.withLabel(BUILDER_FOR_LABEL_NAME, targetImage.toString()));
|
||||
update.withTag(name);
|
||||
update.withTag(this.name);
|
||||
if (!CollectionUtils.isEmpty(env)) {
|
||||
update.withNewLayer(getEnvLayer(env));
|
||||
}
|
||||
if (buildpacks != null) {
|
||||
buildpacks.apply(update::withNewLayer);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private void updateMetadata(BuilderMetadata.Update update) {
|
||||
|
|
@ -95,7 +101,7 @@ class EphemeralBuilder {
|
|||
* @return the ephemeral builder name
|
||||
*/
|
||||
ImageReference getName() {
|
||||
return this.archive.getTag();
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -116,15 +122,26 @@ class EphemeralBuilder {
|
|||
|
||||
/**
|
||||
* Return the contents of ephemeral builder for passing to Docker.
|
||||
* @param applicationDirectory the application directory
|
||||
* @return the ephemeral builder archive
|
||||
* @throws IOException on IO error
|
||||
*/
|
||||
ImageArchive getArchive() {
|
||||
return this.archive;
|
||||
ImageArchive getArchive(String applicationDirectory) throws IOException {
|
||||
return ImageArchive.from(this.builderImage, (update) -> {
|
||||
this.archiveUpdate.accept(update);
|
||||
if (StringUtils.hasLength(applicationDirectory)) {
|
||||
update.withNewLayer(applicationDirectoryLayer(applicationDirectory));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Layer applicationDirectoryLayer(String applicationDirectory) throws IOException {
|
||||
return Layer.of((layout) -> layout.directory(applicationDirectory, this.buildOwner));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.archive.getTag().toString();
|
||||
return this.name.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2024 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -117,6 +117,10 @@ class Lifecycle implements Closeable {
|
|||
this.securityOptions = getSecurityOptions(request);
|
||||
}
|
||||
|
||||
String getApplicationDirectory() {
|
||||
return this.applicationDirectory;
|
||||
}
|
||||
|
||||
private Cache getBuildCache(BuildRequest request) {
|
||||
if (request.getBuildCache() != null) {
|
||||
return request.getBuildCache();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -88,7 +88,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getNameHasRandomName() throws Exception {
|
||||
void getNameHasRandomName() {
|
||||
EphemeralBuilder b1 = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
EphemeralBuilder b2 = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
|
|
@ -101,7 +101,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
void getArchiveHasCreatedByConfig() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
ImageConfig config = builder.getArchive().getImageConfig();
|
||||
ImageConfig config = builder.getArchive(null).getImageConfig();
|
||||
BuilderMetadata ephemeralMetadata = BuilderMetadata.fromImageConfig(config);
|
||||
assertThat(ephemeralMetadata.getCreatedBy().getName()).isEqualTo("Spring Boot");
|
||||
assertThat(ephemeralMetadata.getCreatedBy().getVersion()).isEqualTo("dev");
|
||||
|
|
@ -111,7 +111,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
void getArchiveHasTag() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
ImageReference tag = builder.getArchive().getTag();
|
||||
ImageReference tag = builder.getArchive(null).getTag();
|
||||
assertThat(tag.toString()).startsWith("pack.local/builder/").endsWith(":latest");
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +119,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
void getArchiveHasFixedCreatedDate() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
Instant createInstant = builder.getArchive().getCreateDate();
|
||||
Instant createInstant = builder.getArchive(null).getCreateDate();
|
||||
OffsetDateTime createDateTime = OffsetDateTime.ofInstant(createInstant, ZoneId.of("UTC"));
|
||||
assertThat(createDateTime.getYear()).isEqualTo(1980);
|
||||
assertThat(createDateTime.getMonthValue()).isOne();
|
||||
|
|
@ -133,7 +133,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
void getArchiveContainsEnvLayer() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
File directory = unpack(getLayer(builder.getArchive(), EXISTING_IMAGE_LAYER_COUNT), "env");
|
||||
File directory = unpack(getLayer(builder.getArchive(null), EXISTING_IMAGE_LAYER_COUNT), "env");
|
||||
assertThat(new File(directory, "platform/env/spring")).usingCharset(StandardCharsets.UTF_8).hasContent("boot");
|
||||
assertThat(new File(directory, "platform/env/empty")).usingCharset(StandardCharsets.UTF_8).hasContent("");
|
||||
}
|
||||
|
|
@ -142,7 +142,7 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
void getArchiveHasBuilderForLabel() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
ImageConfig config = builder.getArchive().getImageConfig();
|
||||
ImageConfig config = builder.getArchive(null).getImageConfig();
|
||||
assertThat(config.getLabels())
|
||||
.contains(entry(EphemeralBuilder.BUILDER_FOR_LABEL_NAME, this.targetImage.toString()));
|
||||
}
|
||||
|
|
@ -162,13 +162,21 @@ class EphemeralBuilderTests extends AbstractJsonTests {
|
|||
"/cnb/buildpacks/example_buildpack2/0.0.2/buildpack.toml");
|
||||
assertBuildpackLayerContent(builder, EXISTING_IMAGE_LAYER_COUNT + 2,
|
||||
"/cnb/buildpacks/example_buildpack3/0.0.3/buildpack.toml");
|
||||
File orderDirectory = unpack(getLayer(builder.getArchive(), EXISTING_IMAGE_LAYER_COUNT + 3), "order");
|
||||
File orderDirectory = unpack(getLayer(builder.getArchive(null), EXISTING_IMAGE_LAYER_COUNT + 3), "order");
|
||||
assertThat(new File(orderDirectory, "cnb/order.toml")).usingCharset(StandardCharsets.UTF_8)
|
||||
.hasContent(content("order.toml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getArchiveHasApplicationDirectoryLayer() throws Exception {
|
||||
EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata,
|
||||
this.creator, this.env, this.buildpacks);
|
||||
File directory = unpack(getLayer(builder.getArchive("/myapp"), EXISTING_IMAGE_LAYER_COUNT + 1), "appdir");
|
||||
assertThat(new File(directory, "myapp")).isDirectory();
|
||||
}
|
||||
|
||||
private void assertBuildpackLayerContent(EphemeralBuilder builder, int index, String s) throws Exception {
|
||||
File buildpackDirectory = unpack(getLayer(builder.getArchive(), index), "buildpack");
|
||||
File buildpackDirectory = unpack(getLayer(builder.getArchive(null), index), "buildpack");
|
||||
assertThat(new File(buildpackDirectory, s)).usingCharset(StandardCharsets.UTF_8).hasContent("[test]");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue