diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java index e3c21e0f5bae..755d49e5e0b5 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/Builder.java @@ -105,8 +105,8 @@ public void build(BuildRequest request) throws DockerEngineException, IOExceptio assertStackIdsMatch(runImage, builderImage); BuildOwner buildOwner = BuildOwner.fromEnv(builderImage.getConfig().getEnv()); Buildpacks buildpacks = getBuildpacks(request, imageFetcher, builderMetadata); - EphemeralBuilder ephemeralBuilder = new EphemeralBuilder(buildOwner, builderImage, builderMetadata, - request.getCreator(), request.getEnv(), buildpacks); + 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); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java index 7d3abe5e87ea..1f221327da82 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/main/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilder.java @@ -34,6 +34,8 @@ */ class EphemeralBuilder { + static final String BUILDER_FOR_LABEL_NAME = "org.springframework.boot.builderFor"; + private final BuildOwner buildOwner; private final BuilderMetadata builderMetadata; @@ -45,21 +47,24 @@ class EphemeralBuilder { /** * Create a new {@link EphemeralBuilder} instance. * @param buildOwner the build owner - * @param builderImage the image + * @param builderImage the base builder image + * @param targetImage the image being built * @param builderMetadata the builder metadata * @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, BuilderMetadata builderMetadata, Creator creator, - Map env, Buildpacks buildpacks) throws IOException { + EphemeralBuilder(BuildOwner buildOwner, Image builderImage, ImageReference targetImage, + BuilderMetadata builderMetadata, Creator creator, Map env, Buildpacks buildpacks) + throws IOException { ImageReference 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) -> { update.withUpdatedConfig(this.builderMetadata::attachTo); + update.withUpdatedConfig((config) -> config.withLabel(BUILDER_FOR_LABEL_NAME, targetImage.toString())); update.withTag(name); if (env != null && !env.isEmpty()) { update.withNewLayer(getEnvLayer(env)); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java index b0b5b3e792bf..9ba9ab9e821e 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-buildpack-platform/src/test/java/org/springframework/boot/buildpack/platform/build/EphemeralBuilderTests.java @@ -48,6 +48,7 @@ import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; /** * Tests for {@link EphemeralBuilder}. @@ -64,6 +65,8 @@ class EphemeralBuilderTests extends AbstractJsonTests { private Image image; + private ImageReference targetImage; + private BuilderMetadata metadata; private Map env; @@ -75,6 +78,7 @@ class EphemeralBuilderTests extends AbstractJsonTests { @BeforeEach void setup() throws Exception { this.image = Image.of(getContent("image.json")); + this.targetImage = ImageReference.of("my-image:latest"); this.metadata = BuilderMetadata.fromImage(this.image); this.env = new HashMap<>(); this.env.put("spring", "boot"); @@ -83,18 +87,18 @@ void setup() throws Exception { @Test void getNameHasRandomName() throws Exception { - EphemeralBuilder b1 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); - EphemeralBuilder b2 = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); + 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, + this.creator, this.env, this.buildpacks); assertThat(b1.getName().toString()).startsWith("pack.local/builder/").endsWith(":latest"); assertThat(b1.getName().toString()).isNotEqualTo(b2.getName().toString()); } @Test void getArchiveHasCreatedByConfig() throws Exception { - EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, + this.creator, this.env, this.buildpacks); ImageConfig config = builder.getArchive().getImageConfig(); BuilderMetadata ephemeralMetadata = BuilderMetadata.fromImageConfig(config); assertThat(ephemeralMetadata.getCreatedBy().getName()).isEqualTo("Spring Boot"); @@ -103,16 +107,16 @@ void getArchiveHasCreatedByConfig() throws Exception { @Test void getArchiveHasTag() throws Exception { - EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, + this.creator, this.env, this.buildpacks); ImageReference tag = builder.getArchive().getTag(); assertThat(tag.toString()).startsWith("pack.local/builder/").endsWith(":latest"); } @Test void getArchiveHasFixedCreateDate() throws Exception { - EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, + this.creator, this.env, this.buildpacks); Instant createInstant = builder.getArchive().getCreateDate(); OffsetDateTime createDateTime = OffsetDateTime.ofInstant(createInstant, ZoneId.of("UTC")); assertThat(createDateTime.getYear()).isEqualTo(1980); @@ -125,13 +129,22 @@ void getArchiveHasFixedCreateDate() throws Exception { @Test void getArchiveContainsEnvLayer() throws Exception { - EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, this.env, - this.buildpacks); + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, + this.creator, this.env, this.buildpacks); File directory = unpack(getLayer(builder.getArchive(), 0), "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(""); } + @Test + 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(); + assertThat(config.getLabels()) + .contains(entry(EphemeralBuilder.BUILDER_FOR_LABEL_NAME, this.targetImage.toString())); + } + @Test void getArchiveContainsBuildpackLayers() throws Exception { List buildpackList = new ArrayList<>(); @@ -139,8 +152,8 @@ void getArchiveContainsBuildpackLayers() throws Exception { buildpackList.add(new TestBuildpack("example/buildpack2", "0.0.2")); buildpackList.add(new TestBuildpack("example/buildpack3", "0.0.3")); this.buildpacks = Buildpacks.of(buildpackList); - EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.metadata, this.creator, null, - this.buildpacks); + EphemeralBuilder builder = new EphemeralBuilder(this.owner, this.image, this.targetImage, this.metadata, + this.creator, null, this.buildpacks); assertBuildpackLayerContent(builder, 0, "/cnb/buildpacks/example_buildpack1/0.0.1/buildpack.toml"); assertBuildpackLayerContent(builder, 1, "/cnb/buildpacks/example_buildpack2/0.0.2/buildpack.toml"); assertBuildpackLayerContent(builder, 2, "/cnb/buildpacks/example_buildpack3/0.0.3/buildpack.toml");