Merge branch '2.7.x'

This commit is contained in:
Scott Frederick 2021-12-14 15:51:18 -06:00
commit 54c4ec18c6
3 changed files with 204 additions and 91 deletions

View File

@ -18,13 +18,13 @@ package org.springframework.boot.image.assertions;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
import com.github.dockerjava.api.model.ContainerConfig; import com.github.dockerjava.api.model.ContainerConfig;
import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.AbstractListAssert; import org.assertj.core.api.AbstractListAssert;
import org.assertj.core.api.AbstractMapAssert;
import org.assertj.core.api.AbstractObjectAssert; import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.AbstractStringAssert;
import org.assertj.core.api.AssertionsForClassTypes;
import org.assertj.core.api.ListAssert; import org.assertj.core.api.ListAssert;
import org.assertj.core.api.ObjectAssert; import org.assertj.core.api.ObjectAssert;
@ -45,16 +45,16 @@ public class ContainerConfigAssert extends AbstractAssert<ContainerConfigAssert,
super(containerConfig, ContainerConfigAssert.class); super(containerConfig, ContainerConfigAssert.class);
} }
public BuildMetadataAssert buildMetadata() { public void buildMetadata(Consumer<BuildMetadataAssert> assertConsumer) {
return new BuildMetadataAssert(jsonLabel(BUILD_METADATA_LABEL)); assertConsumer.accept(new BuildMetadataAssert(jsonLabel(BUILD_METADATA_LABEL)));
} }
public LifecycleMetadataAssert lifecycleMetadata() { public void lifecycleMetadata(Consumer<LifecycleMetadataAssert> assertConsumer) {
return new LifecycleMetadataAssert(jsonLabel(LIFECYCLE_METADATA_LABEL)); assertConsumer.accept(new LifecycleMetadataAssert(jsonLabel(LIFECYCLE_METADATA_LABEL)));
} }
public AbstractStringAssert<?> label(String label) { public void labels(Consumer<LabelsAssert> assertConsumer) {
return AssertionsForClassTypes.assertThat(getLabel(label)); assertConsumer.accept(new LabelsAssert(this.actual.getLabels()));
} }
private JsonContentAssert jsonLabel(String label) { private JsonContentAssert jsonLabel(String label) {
@ -72,6 +72,17 @@ public class ContainerConfigAssert extends AbstractAssert<ContainerConfigAssert,
return labels.get(label); return labels.get(label);
} }
/**
* Asserts for labels on an image.
*/
public static class LabelsAssert extends AbstractMapAssert<LabelsAssert, Map<String, String>, String, String> {
protected LabelsAssert(Map<String, String> labels) {
super(labels, LabelsAssert.class);
}
}
/** /**
* Asserts for the JSON content in the {@code io.buildpacks.build.metadata} label. * Asserts for the JSON content in the {@code io.buildpacks.build.metadata} label.
* *
@ -116,6 +127,10 @@ public class ContainerConfigAssert extends AbstractAssert<ContainerConfigAssert,
return this.actual.extractingJsonPathArrayValue("$.app").extracting("sha"); return this.actual.extractingJsonPathArrayValue("$.app").extracting("sha");
} }
public AbstractObjectAssert<?, Object> sbomLayerSha() {
return this.actual.extractingJsonPathValue("$.sbom.sha");
}
} }
} }

View File

@ -22,9 +22,11 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.assertj.core.api.AbstractAssert; import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.assertj.core.api.ListAssert; import org.assertj.core.api.ListAssert;
@ -32,6 +34,7 @@ import org.assertj.core.api.ListAssert;
import org.springframework.boot.buildpack.platform.docker.DockerApi; import org.springframework.boot.buildpack.platform.docker.DockerApi;
import org.springframework.boot.buildpack.platform.docker.type.ImageReference; import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.boot.buildpack.platform.docker.type.Layer; import org.springframework.boot.buildpack.platform.docker.type.Layer;
import org.springframework.boot.test.json.JsonContentAssert;
/** /**
* AssertJ {@link org.assertj.core.api.Assert} for Docker image contents. * AssertJ {@link org.assertj.core.api.Assert} for Docker image contents.
@ -47,11 +50,11 @@ public class ImageAssert extends AbstractAssert<ImageAssert, ImageReference> {
getLayers(); getLayers();
} }
public LayerContentAssert hasLayer(String layerDigest) { public void layer(String layerDigest, Consumer<LayerContentAssert> assertConsumer) {
if (!this.layers.containsKey(layerDigest)) { if (!this.layers.containsKey(layerDigest)) {
failWithMessage("Layer with digest '%s' not found in image", layerDigest); failWithMessage("Layer with digest '%s' not found in image", layerDigest);
} }
return new LayerContentAssert(this.layers.get(layerDigest)); assertConsumer.accept(new LayerContentAssert(this.layers.get(layerDigest)));
} }
private void getLayers() throws IOException { private void getLayers() throws IOException {
@ -70,22 +73,52 @@ public class ImageAssert extends AbstractAssert<ImageAssert, ImageReference> {
super(layer, LayerContentAssert.class); super(layer, LayerContentAssert.class);
} }
public ListAssert<String> entries() throws IOException { public ListAssert<String> entries() {
List<String> entryNames = new ArrayList<>(); List<String> entryNames = new ArrayList<>();
ByteArrayOutputStream out = new ByteArrayOutputStream(); try {
this.actual.writeTo(out); ByteArrayOutputStream out = new ByteArrayOutputStream();
try (TarArchiveInputStream in = new TarArchiveInputStream(new ByteArrayInputStream(out.toByteArray()))) { this.actual.writeTo(out);
TarArchiveEntry entry = in.getNextTarEntry(); try (TarArchiveInputStream in = new TarArchiveInputStream(
while (entry != null) { new ByteArrayInputStream(out.toByteArray()))) {
if (!entry.isDirectory()) { TarArchiveEntry entry = in.getNextTarEntry();
entryNames.add(entry.getName().replaceFirst("^/workspace/", "")); while (entry != null) {
if (!entry.isDirectory()) {
entryNames.add(entry.getName().replaceFirst("^/workspace/", ""));
}
entry = in.getNextTarEntry();
} }
entry = in.getNextTarEntry();
} }
} }
catch (IOException ex) {
failWithMessage("IOException while reading image layer archive: '%s'", ex.getMessage());
}
return Assertions.assertThat(entryNames); return Assertions.assertThat(entryNames);
} }
public void jsonEntry(String name, Consumer<JsonContentAssert> assertConsumer) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
this.actual.writeTo(out);
try (TarArchiveInputStream in = new TarArchiveInputStream(
new ByteArrayInputStream(out.toByteArray()))) {
TarArchiveEntry entry = in.getNextTarEntry();
while (entry != null) {
if (entry.getName().equals(name)) {
ByteArrayOutputStream entryOut = new ByteArrayOutputStream();
IOUtils.copy(in, entryOut);
assertConsumer.accept(new JsonContentAssert(LayerContentAssert.class, entryOut.toString()));
return;
}
entry = in.getNextTarEntry();
}
}
failWithMessage("Expected JSON entry '%s' in layer with digest '%s'", name, this.actual.getId());
}
catch (IOException ex) {
failWithMessage("IOException while reading image layer archive: '%s'", ex.getMessage());
}
}
} }
} }

View File

@ -49,6 +49,7 @@ import org.springframework.boot.testsupport.gradle.testkit.GradleBuildExtension;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/** /**
* Integration tests for the Paketo builder and buildpacks. * Integration tests for the Paketo builder and buildpacks.
@ -80,14 +81,16 @@ class PaketoBuilderTests {
container.waitingFor(Wait.forHttp("/test")).start(); container.waitingFor(Wait.forHttp("/test")).start();
ContainerConfig config = container.getContainerInfo().getConfig(); ContainerConfig config = container.getContainerInfo().getConfig();
assertLabelsMatchManifestAttributes(config); assertLabelsMatchManifestAttributes(config);
ImageAssertions.assertThat(config).buildMetadata().buildpacks().contains( ImageAssertions.assertThat(config).buildMetadata((metadata) -> {
"paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", metadata.buildpacks().contains("paketo-buildpacks/ca-certificates",
"paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); "paketo-buildpacks/bellsoft-liberica", "paketo-buildpacks/executable-jar",
ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot");
.containsExactly("java", Collections.singletonList("org.springframework.boot.loader.JarLauncher")); metadata.processOfType("web").extracting("command", "args").containsExactly("java",
ImageAssertions.assertThat(config).buildMetadata().processOfType("executable-jar") Collections.singletonList("org.springframework.boot.loader.JarLauncher"));
.extracting("command", "args") metadata.processOfType("executable-jar").extracting("command", "args").containsExactly("java",
.containsExactly("java", Collections.singletonList("org.springframework.boot.loader.JarLauncher")); Collections.singletonList("org.springframework.boot.loader.JarLauncher"));
});
assertImageHasSbomLayer(imageReference, config, "executable-jar");
assertImageLayersMatchLayersIndex(imageReference, config); assertImageLayersMatchLayersIndex(imageReference, config);
} }
finally { finally {
@ -143,17 +146,22 @@ class PaketoBuilderTests {
try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) { try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) {
container.waitingFor(Wait.forHttp("/test")).start(); container.waitingFor(Wait.forHttp("/test")).start();
ContainerConfig config = container.getContainerInfo().getConfig(); ContainerConfig config = container.getContainerInfo().getConfig();
ImageAssertions.assertThat(config).buildMetadata().buildpacks().contains( ImageAssertions.assertThat(config).buildMetadata((metadata) -> {
"paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", metadata.buildpacks().contains("paketo-buildpacks/ca-certificates",
"paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); "paketo-buildpacks/bellsoft-liberica", "paketo-buildpacks/dist-zip",
ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") "paketo-buildpacks/spring-boot");
.containsExactly("/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList()); metadata.processOfType("web").extracting("command", "args").containsExactly(
ImageAssertions.assertThat(config).buildMetadata().processOfType("dist-zip").extracting("command", "args") "/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList());
.containsExactly("/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList()); metadata.processOfType("dist-zip").extracting("command", "args").containsExactly(
DigestCapturingCondition digests = new DigestCapturingCondition(); "/workspace/" + projectName + "-boot/bin/" + projectName, Collections.emptyList());
ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); });
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries().contains( assertImageHasSbomLayer(imageReference, config, "dist-zip");
projectName + "-boot/bin/" + projectName, projectName + "-boot/lib/" + projectName + ".jar"); DigestCapturingCondition digest = new DigestCapturingCondition();
ImageAssertions.assertThat(config)
.lifecycleMetadata((metadata) -> metadata.appLayerShas().haveExactly(1, digest));
ImageAssertions.assertThat(imageReference).layer(digest.getDigest(),
(layer) -> layer.entries().contains(projectName + "-boot/bin/" + projectName,
projectName + "-boot/lib/" + projectName + ".jar"));
} }
finally { finally {
removeImage(imageReference); removeImage(imageReference);
@ -171,20 +179,24 @@ class PaketoBuilderTests {
try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) { try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) {
container.waitingFor(Wait.forHttp("/test")).start(); container.waitingFor(Wait.forHttp("/test")).start();
ContainerConfig config = container.getContainerInfo().getConfig(); ContainerConfig config = container.getContainerInfo().getConfig();
ImageAssertions.assertThat(config).buildMetadata().buildpacks().contains( ImageAssertions.assertThat(config).buildMetadata((metadata) -> {
"paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", metadata.buildpacks().contains("paketo-buildpacks/ca-certificates",
"paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); "paketo-buildpacks/bellsoft-liberica", "paketo-buildpacks/dist-zip",
ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") "paketo-buildpacks/spring-boot");
.containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList()); metadata.processOfType("web").extracting("command", "args")
ImageAssertions.assertThat(config).buildMetadata().processOfType("dist-zip").extracting("command", "args") .containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList());
.containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList()); metadata.processOfType("dist-zip").extracting("command", "args")
DigestCapturingCondition digests = new DigestCapturingCondition(); .containsExactly("/workspace/" + projectName + "/bin/" + projectName, Collections.emptyList());
ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); });
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() assertImageHasSbomLayer(imageReference, config, "dist-zip");
DigestCapturingCondition digest = new DigestCapturingCondition();
ImageAssertions.assertThat(config)
.lifecycleMetadata((metadata) -> metadata.appLayerShas().haveExactly(1, digest));
ImageAssertions.assertThat(imageReference).layer(digest.getDigest(), (layer) -> layer.entries()
.contains(projectName + "/bin/" + projectName, projectName + "/lib/" + projectName + "-plain.jar") .contains(projectName + "/bin/" + projectName, projectName + "/lib/" + projectName + "-plain.jar")
.anyMatch((s) -> s.startsWith(projectName + "/lib/spring-boot-")) .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-boot-"))
.anyMatch((s) -> s.startsWith(projectName + "/lib/spring-core-")) .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-core-"))
.anyMatch((s) -> s.startsWith(projectName + "/lib/spring-web-")); .anyMatch((s) -> s.startsWith(projectName + "/lib/spring-web-")));
} }
finally { finally {
removeImage(imageReference); removeImage(imageReference);
@ -203,14 +215,16 @@ class PaketoBuilderTests {
container.waitingFor(Wait.forHttp("/test")).start(); container.waitingFor(Wait.forHttp("/test")).start();
ContainerConfig config = container.getContainerInfo().getConfig(); ContainerConfig config = container.getContainerInfo().getConfig();
assertLabelsMatchManifestAttributes(config); assertLabelsMatchManifestAttributes(config);
ImageAssertions.assertThat(config).buildMetadata().buildpacks().contains( ImageAssertions.assertThat(config).buildMetadata((metadata) -> {
"paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", metadata.buildpacks().contains("paketo-buildpacks/ca-certificates",
"paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); "paketo-buildpacks/bellsoft-liberica", "paketo-buildpacks/executable-jar",
ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot");
.containsExactly("java", Collections.singletonList("org.springframework.boot.loader.WarLauncher")); metadata.processOfType("web").extracting("command", "args").containsExactly("java",
ImageAssertions.assertThat(config).buildMetadata().processOfType("executable-jar") Collections.singletonList("org.springframework.boot.loader.WarLauncher"));
.extracting("command", "args") metadata.processOfType("executable-jar").extracting("command", "args").containsExactly("java",
.containsExactly("java", Collections.singletonList("org.springframework.boot.loader.WarLauncher")); Collections.singletonList("org.springframework.boot.loader.WarLauncher"));
});
assertImageHasSbomLayer(imageReference, config, "executable-jar");
assertImageLayersMatchLayersIndex(imageReference, config); assertImageLayersMatchLayersIndex(imageReference, config);
} }
finally { finally {
@ -229,21 +243,26 @@ class PaketoBuilderTests {
try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) { try (GenericContainer<?> container = new GenericContainer<>(imageName).withExposedPorts(8080)) {
container.waitingFor(Wait.forHttp("/test")).start(); container.waitingFor(Wait.forHttp("/test")).start();
ContainerConfig config = container.getContainerInfo().getConfig(); ContainerConfig config = container.getContainerInfo().getConfig();
ImageAssertions.assertThat(config).buildMetadata().buildpacks().contains( ImageAssertions.assertThat(config).buildMetadata((metadata) -> {
"paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", metadata.buildpacks().contains("paketo-buildpacks/ca-certificates",
"paketo-buildpacks/apache-tomcat", "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot"); "paketo-buildpacks/bellsoft-liberica", "paketo-buildpacks/apache-tomcat",
ImageAssertions.assertThat(config).buildMetadata().processOfType("web").extracting("command", "args") "paketo-buildpacks/dist-zip", "paketo-buildpacks/spring-boot");
.containsExactly("bash", Arrays.asList("catalina.sh", "run")); metadata.processOfType("web").extracting("command", "args").containsExactly("bash",
ImageAssertions.assertThat(config).buildMetadata().processOfType("tomcat").extracting("command", "args") Arrays.asList("catalina.sh", "run"));
.containsExactly("bash", Arrays.asList("catalina.sh", "run")); metadata.processOfType("tomcat").extracting("command", "args").containsExactly("bash",
DigestCapturingCondition digests = new DigestCapturingCondition(); Arrays.asList("catalina.sh", "run"));
ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(1, digests); });
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() assertImageHasSbomLayer(imageReference, config, "apache-tomcat");
.contains("WEB-INF/classes/example/ExampleApplication.class", DigestCapturingCondition digest = new DigestCapturingCondition();
"WEB-INF/classes/example/HelloController.class", "META-INF/MANIFEST.MF") ImageAssertions.assertThat(config)
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-boot-")) .lifecycleMetadata((metadata) -> metadata.appLayerShas().haveExactly(1, digest));
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-core-")) ImageAssertions.assertThat(imageReference).layer(digest.getDigest(),
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-web-")); (layer) -> layer.entries()
.contains("WEB-INF/classes/example/ExampleApplication.class",
"WEB-INF/classes/example/HelloController.class", "META-INF/MANIFEST.MF")
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-boot-"))
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-core-"))
.anyMatch((s) -> s.startsWith("WEB-INF/lib/spring-web-")));
} }
finally { finally {
removeImage(imageReference); removeImage(imageReference);
@ -318,29 +337,54 @@ class PaketoBuilderTests {
private void assertLabelsMatchManifestAttributes(ContainerConfig config) throws IOException { private void assertLabelsMatchManifestAttributes(ContainerConfig config) throws IOException {
JarFile jarFile = new JarFile(projectArchiveFile()); JarFile jarFile = new JarFile(projectArchiveFile());
Attributes attributes = jarFile.getManifest().getMainAttributes(); Attributes attributes = jarFile.getManifest().getMainAttributes();
ImageAssertions.assertThat(config).label("org.springframework.boot.version") ImageAssertions.assertThat(config).labels((labels) -> {
.isEqualTo(attributes.getValue("Spring-Boot-Version")); labels.contains(entry("org.springframework.boot.version", attributes.getValue("Spring-Boot-Version")));
ImageAssertions.assertThat(config).label("org.opencontainers.image.title") labels.contains(
.isEqualTo(attributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE)); entry("org.opencontainers.image.title", attributes.getValue(Attributes.Name.IMPLEMENTATION_TITLE)));
ImageAssertions.assertThat(config).label("org.opencontainers.image.version") labels.contains(entry("org.opencontainers.image.version",
.isEqualTo(attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION)); attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION)));
});
}
private void assertImageHasSbomLayer(ImageReference imageReference, ContainerConfig config, String buildpack)
throws IOException {
DigestCapturingCondition digest = new DigestCapturingCondition();
ImageAssertions.assertThat(config).lifecycleMetadata((metadata) -> metadata.sbomLayerSha().has(digest));
ImageAssertions.assertThat(imageReference).layer(digest.getDigest(), (layer) -> {
layer.entries().contains("/layers/sbom/launch/paketo-buildpacks_bellsoft-liberica/jre/sbom.syft.json",
"/layers/sbom/launch/paketo-buildpacks_" + buildpack + "/sbom.syft.json",
"/layers/sbom/launch/paketo-buildpacks_" + buildpack + "/sbom.cdx.json");
layer.jsonEntry("/layers/sbom/launch/paketo-buildpacks_bellsoft-liberica/jre/sbom.syft.json", (json) -> {
json.extractingJsonPathStringValue("$.Artifacts[0].Name").isEqualTo("BellSoft Liberica JRE");
json.extractingJsonPathStringValue("$.Artifacts[0].Version").startsWith(javaMajorVersion());
});
layer.jsonEntry("/layers/sbom/launch/paketo-buildpacks_" + buildpack + "/sbom.syft.json",
(json) -> json.extractingJsonPathArrayValue("$.artifacts.[*].name").contains("spring-beans",
"spring-boot", "spring-boot-autoconfigure", "spring-context", "spring-core",
"spring-expression", "spring-jcl", "spring-web", "spring-webmvc"));
layer.jsonEntry("/layers/sbom/launch/paketo-buildpacks_" + buildpack + "/sbom.cdx.json",
(json) -> json.extractingJsonPathArrayValue("$.components.[*].name").contains("spring-beans",
"spring-boot", "spring-boot-autoconfigure", "spring-context", "spring-core",
"spring-expression", "spring-jcl", "spring-web", "spring-webmvc"));
});
} }
private void assertImageLayersMatchLayersIndex(ImageReference imageReference, ContainerConfig config) private void assertImageLayersMatchLayersIndex(ImageReference imageReference, ContainerConfig config)
throws IOException { throws IOException {
DigestCapturingCondition digests = new DigestCapturingCondition(); DigestsCapturingCondition digests = new DigestsCapturingCondition();
ImageAssertions.assertThat(config).lifecycleMetadata().appLayerShas().haveExactly(5, digests); ImageAssertions.assertThat(config)
.lifecycleMetadata((metadata) -> metadata.appLayerShas().haveExactly(5, digests));
LayersIndex layersIndex = LayersIndex.fromArchiveFile(projectArchiveFile()); LayersIndex layersIndex = LayersIndex.fromArchiveFile(projectArchiveFile());
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(0)).entries() ImageAssertions.assertThat(imageReference).layer(digests.getDigest(0), (layer) -> layer.entries()
.allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("dependencies"))); .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("dependencies"))));
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(1)).entries() ImageAssertions.assertThat(imageReference).layer(digests.getDigest(1), (layer) -> layer.entries()
.allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("spring-boot-loader"))); .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("spring-boot-loader"))));
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(2)).entries() ImageAssertions.assertThat(imageReference).layer(digests.getDigest(2), (layer) -> layer.entries()
.allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("snapshot-dependencies"))); .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("snapshot-dependencies"))));
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(3)).entries() ImageAssertions.assertThat(imageReference).layer(digests.getDigest(3), (layer) -> layer.entries()
.allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("application"))); .allMatch((entry) -> startsWithOneOf(entry, layersIndex.getLayer("application"))));
ImageAssertions.assertThat(imageReference).hasLayer(digests.getDigest(4)).entries() ImageAssertions.assertThat(imageReference).layer(digests.getDigest(4),
.allMatch((entry) -> entry.contains("lib/spring-cloud-bindings-")); (layer) -> layer.entries().allMatch((entry) -> entry.contains("lib/spring-cloud-bindings-")));
} }
private File projectArchiveFile() { private File projectArchiveFile() {
@ -376,12 +420,33 @@ class PaketoBuilderTests {
private static class DigestCapturingCondition extends Condition<Object> { private static class DigestCapturingCondition extends Condition<Object> {
private static List<String> digests; private static String digest = null;
DigestCapturingCondition() { DigestCapturingCondition() {
super(predicate(), "a value starting with 'sha256:'"); super(predicate(), "a value starting with 'sha256:'");
} }
private static Predicate<Object> predicate() {
return (sha) -> {
digest = sha.toString();
return sha.toString().startsWith("sha256:");
};
}
String getDigest() {
return digest;
}
}
private static class DigestsCapturingCondition extends Condition<Object> {
private static List<String> digests;
DigestsCapturingCondition() {
super(predicate(), "a value starting with 'sha256:'");
}
private static Predicate<Object> predicate() { private static Predicate<Object> predicate() {
digests = new ArrayList<>(); digests = new ArrayList<>();
return (sha) -> { return (sha) -> {