Commit 12c640e1 authored by Scott Frederick's avatar Scott Frederick

Upgrade default image builder version

This commit upgrades the default CNB builder image from
cloudfoundry/cnb:0.0.43-bionic to cloudfoundry/cnb:0.0.53-bionic.
It also adds integration tests for the Maven and Gradle plugins
to verify both versions are supported.
parent d0706265
......@@ -31,11 +31,12 @@ import org.springframework.util.Assert;
* A build request to be handled by the {@link Builder}.
*
* @author Phillip Webb
* @author Scott Frederick
* @since 2.3.0
*/
public class BuildRequest {
private static final ImageReference DEFAULT_BUILDER = ImageReference.of("cloudfoundry/cnb:0.0.43-bionic");
private static final ImageReference DEFAULT_BUILDER = ImageReference.of("cloudfoundry/cnb:0.0.53-bionic");
private final ImageReference name;
......
......@@ -54,7 +54,7 @@ public class BuildRequestTests {
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(jarFile);
assertThat(request.getName().toString()).isEqualTo("docker.io/library/my-app:0.0.1");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.53-bionic");
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
......@@ -65,7 +65,7 @@ public class BuildRequestTests {
writeTestJarFile(jarFile);
BuildRequest request = BuildRequest.forJarFile(ImageReference.of("test-app"), jarFile);
assertThat(request.getName().toString()).isEqualTo("docker.io/library/test-app:latest");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.53-bionic");
assertThat(request.getApplicationContent(Owner.ROOT)).satisfies(this::hasExpectedJarContent);
assertThat(request.getEnv()).isEmpty();
}
......
......@@ -50,6 +50,7 @@ import static org.mockito.Mockito.verify;
* Tests for {@link Builder}.
*
* @author Phillip Webb
* @author Scott Frederick
*/
class BuilderTests {
......@@ -71,7 +72,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.43-bionic")), any()))
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.53-bionic")), any()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:full-cnb")), any()))
.willAnswer(withPulledImage(runImage));
......@@ -96,7 +97,7 @@ class BuilderTests {
DockerApi docker = mockDockerApi();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image-with-bad-stack.json");
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.43-bionic")), any()))
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.53-bionic")), any()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:full-cnb")), any()))
.willAnswer(withPulledImage(runImage));
......@@ -112,7 +113,7 @@ class BuilderTests {
DockerApi docker = mockDockerApiLifecycleError();
Image builderImage = loadImage("image.json");
Image runImage = loadImage("run-image.json");
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.43-bionic")), any()))
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/cnb:0.0.53-bionic")), any()))
.willAnswer(withPulledImage(builderImage));
given(docker.image().pull(eq(ImageReference.of("docker.io/cloudfoundry/run:full-cnb")), any()))
.willAnswer(withPulledImage(runImage));
......
......@@ -21,7 +21,7 @@ The following table summarizes the available properties and their default values
| `builder`
| Name of the Builder image to use.
| `cloudfoundry/cnb:0.0.43-bionic`
| `cloudfoundry/cnb:0.0.53-bionic`
| `imageName`
| {spring-boot-api}/buildpack/platform/docker/type/ImageReference.html#of-java.lang.String-[Image name] for the generated image.
......
......@@ -45,6 +45,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Integration tests for {@link BootBuildImage}.
*
* @author Andy Wilkinson
* @author Scott Frederick
*/
@ExtendWith(GradleCompatibilityExtension.class)
@DisabledIfDockerUnavailable
......@@ -53,11 +54,12 @@ class BootBuildImageIntegrationTests {
GradleBuild gradleBuild;
@TestTemplate
void bootBuildImageBuildsImage() throws IOException {
void buildsImageWithDefaultBuilder() throws IOException {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.build("bootBuildImage");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("cloudfoundry/cnb:0.0.53-bionic");
ImageReference imageReference = ImageReference.of(ImageName.of(this.gradleBuild.getProjectDir().getName()));
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
......@@ -67,6 +69,31 @@ class BootBuildImageIntegrationTests {
}
}
@TestTemplate
void buildsImageWithV1Builder() throws IOException {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.build("bootBuildImage");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("cloudfoundry/cnb:0.0.43-bionic");
ImageReference imageReference = ImageReference.of(ImageName.of(this.gradleBuild.getProjectDir().getName()));
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
}
finally {
new DockerApi().image().remove(imageReference, false);
}
}
@TestTemplate
void failsWithBuilderError() {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
assertThat(result.getOutput()).contains("Builder lifecycle 'builder' failed with status code");
}
private void writeMainClass() {
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
examplePackage.mkdirs();
......
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
bootBuildImage {
builder = "cloudfoundry/cnb:0.0.43-bionic"
}
plugins {
id 'java'
id 'org.springframework.boot' version '{version}'
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
bootBuildImage {
environment = ["BP_JAVA_VERSION" : "13.9.9"]
}
......@@ -47,7 +47,7 @@ The following table summarizes the available properties and their default values
| `builder`
| Name of the Builder image to use.
| `cloudfoundry/cnb:0.0.43-bionic`
| `cloudfoundry/cnb:0.0.53-bionic`
| `name`
| {spring-boot-api}/buildpack/platform/docker/type/ImageReference.html#of-java.lang.String-[Image name] for the generated image.
......
......@@ -39,6 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Integration tests for the Maven plugin's image support.
*
* @author Stephane Nicoll
* @author Scott Frederick
*/
@ExtendWith(MavenBuildExtension.class)
@DisabledIfDockerUnavailable
......@@ -51,7 +52,7 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests {
assertThat(jar).isFile();
File original = new File(project, "target/build-image-0.0.1.BUILD-SNAPSHOT.jar.original");
assertThat(original).doesNotExist();
assertThat(buildLog(project)).contains("Building image")
assertThat(buildLog(project)).contains("Building image").contains("cloudfoundry/cnb:0.0.53-bionic")
.contains("docker.io/library/build-image:0.0.1.BUILD-SNAPSHOT")
.contains("Successfully built image");
ImageReference imageReference = ImageReference.of(ImageName.of("build-image"), "0.0.1.BUILD-SNAPSHOT");
......@@ -65,7 +66,7 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests {
}
@TestTemplate
void whenBuildImageIsInvokedWihCustomImageName(MavenBuild mavenBuild) {
void whenBuildImageIsInvokedWithCustomImageName(MavenBuild mavenBuild) {
mavenBuild.project("build-image-custom-name").goals("package").execute((project) -> {
File jar = new File(project, "target/build-image-custom-name-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar).isFile();
......@@ -83,6 +84,30 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests {
});
}
@TestTemplate
void whenBuildImageIsInvokedWithV1BuilderImage(MavenBuild mavenBuild) {
mavenBuild.project("build-image-v1-builder").goals("package").execute((project) -> {
assertThat(buildLog(project)).contains("Building image").contains("cloudfoundry/cnb:0.0.43-bionic")
.contains("docker.io/library/build-image-v1-builder:0.0.1.BUILD-SNAPSHOT")
.contains("Successfully built image");
ImageReference imageReference = ImageReference
.of("docker.io/library/build-image-v1-builder:0.0.1.BUILD-SNAPSHOT");
try (GenericContainer<?> container = new GenericContainer<>(imageReference.toString())) {
container.waitingFor(Wait.forLogMessage("Launched\\n", 1)).start();
}
finally {
removeImage(imageReference);
}
});
}
@TestTemplate
void failsWhenBuilderFails(MavenBuild mavenBuild) {
mavenBuild.project("build-image-builder-error").goals("package")
.executeAndFail((project) -> assertThat(buildLog(project)).contains("Building image")
.contains("Builder lifecycle 'builder' failed with status code"));
}
private void writeLongNameResource(File project) {
StringBuilder name = new StringBuilder();
new Random().ints('a', 'z' + 1).limit(128).forEach((i) -> name.append((char) i));
......
......@@ -43,7 +43,6 @@ import java.util.function.Consumer;
import org.apache.maven.shared.invoker.DefaultInvocationRequest;
import org.apache.maven.shared.invoker.DefaultInvoker;
import org.apache.maven.shared.invoker.InvocationOutputHandler;
import org.apache.maven.shared.invoker.InvocationRequest;
import org.apache.maven.shared.invoker.InvocationResult;
import org.apache.maven.shared.invoker.Invoker;
......@@ -56,6 +55,7 @@ import static org.assertj.core.api.Assertions.contentOf;
* Helper class for executing a Maven build.
*
* @author Andy Wilkinson
* @author Scott Frederick
*
*/
class MavenBuild {
......@@ -117,6 +117,14 @@ class MavenBuild {
}
void execute(Consumer<File> callback) {
execute(callback, 0);
}
void executeAndFail(Consumer<File> callback) {
execute(callback, 1);
}
private void execute(Consumer<File> callback, int expectedExitCode) {
Invoker invoker = new DefaultInvoker();
invoker.setMavenHome(this.home);
InvocationRequest request = new DefaultInvocationRequest();
......@@ -170,18 +178,13 @@ class MavenBuild {
}
File buildLogFile = new File(target, "build.log");
try (PrintWriter buildLog = new PrintWriter(new FileWriter(buildLogFile))) {
request.setOutputHandler(new InvocationOutputHandler() {
@Override
public void consumeLine(String line) {
buildLog.println(line);
buildLog.flush();
}
request.setOutputHandler((line) -> {
buildLog.println(line);
buildLog.flush();
});
try {
InvocationResult result = invoker.execute(request);
assertThat(result.getExitCode()).as(contentOf(buildLogFile)).isEqualTo(0);
assertThat(result.getExitCode()).as(contentOf(buildLogFile)).isEqualTo(expectedExitCode);
}
catch (MavenInvocationException ex) {
throw new RuntimeException(ex);
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>build-image-builder-error</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
<configuration>
<image>
<env>
<BP_JAVA_VERSION>13.9.9</BP_JAVA_VERSION>
</env>
</image>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2012-2020 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.test;
public class SampleApplication {
public static void main(String[] args) throws Exception {
System.out.println("Launched");
synchronized(args) {
args.wait(); // Prevent exit
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot.maven.it</groupId>
<artifactId>build-image-v1-builder</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>@java.version@</maven.compiler.source>
<maven.compiler.target>@java.version@</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<groupId>@project.groupId@</groupId>
<artifactId>@project.artifactId@</artifactId>
<version>@project.version@</version>
<executions>
<execution>
<goals>
<goal>build-image</goal>
</goals>
<configuration>
<image>
<builder>cloudfoundry/cnb:0.0.43-bionic</builder>
</image>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 2012-2020 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.test;
public class SampleApplication {
public static void main(String[] args) throws Exception {
System.out.println("Launched");
synchronized(args) {
args.wait(); // Prevent exit
}
}
}
......@@ -57,7 +57,7 @@ class ImageTests {
void getBuildRequestWhenNoCustomizationsUsesDefaults() {
BuildRequest request = new Image().getBuildRequest(createArtifact(), mockAplicationContent());
assertThat(request.getName().toString()).isEqualTo("docker.io/library/my-app:0.0.1-SNAPSHOT");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.43-bionic");
assertThat(request.getBuilder().toString()).isEqualTo("docker.io/cloudfoundry/cnb:0.0.53-bionic");
assertThat(request.getEnv()).isEmpty();
assertThat(request.isCleanCache()).isFalse();
assertThat(request.isVerboseLogging()).isFalse();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment