Add support for building OCI images using the Gradle plugin
Closes gh-19831
This commit is contained in:
@@ -35,8 +35,9 @@ import org.springframework.util.StreamUtils;
|
||||
* Adapter class to convert a ZIP file to a {@link TarArchive}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.3.0
|
||||
*/
|
||||
class ZipFileTarArchive implements TarArchive {
|
||||
public class ZipFileTarArchive implements TarArchive {
|
||||
|
||||
static final long NORMALIZED_MOD_TIME = TarArchive.NORMALIZED_TIME.toEpochMilli();
|
||||
|
||||
@@ -44,7 +45,13 @@ class ZipFileTarArchive implements TarArchive {
|
||||
|
||||
private final Owner owner;
|
||||
|
||||
ZipFileTarArchive(File zip, Owner owner) {
|
||||
/**
|
||||
* Creates an archive from the contents of the given {@code zip}. Each entry in the
|
||||
* archive will be owned by the given {@code owner}.
|
||||
* @param zip the zip to use as a source
|
||||
* @param owner the owner of the tar entries
|
||||
*/
|
||||
public ZipFileTarArchive(File zip, Owner owner) {
|
||||
Assert.notNull(zip, "Zip must not be null");
|
||||
Assert.notNull(owner, "Owner must not be null");
|
||||
this.zip = zip;
|
||||
|
||||
@@ -30,6 +30,7 @@ dependencies {
|
||||
|
||||
asciidoctorExtensions("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.3.0.RELEASE")
|
||||
|
||||
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-buildpack-platform"))
|
||||
implementation(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-tools"))
|
||||
implementation("io.spring.gradle:dependency-management-plugin")
|
||||
implementation("org.apache.commons:commons-compress")
|
||||
@@ -38,9 +39,11 @@ dependencies {
|
||||
optional(platform(project(":spring-boot-project:spring-boot-dependencies")))
|
||||
optional("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
|
||||
testImplementation("org.assertj:assertj-core")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testImplementation("org.mockito:mockito-core")
|
||||
testImplementation("org.testcontainers:testcontainers")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
|
||||
@@ -26,6 +26,7 @@ Andy Wilkinson
|
||||
:api-documentation: {spring-boot-docs}/gradle-plugin/api
|
||||
:spring-boot-reference: {spring-boot-docs}/reference/htmlsingle
|
||||
:build-info-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/buildinfo/BuildInfo.html
|
||||
:boot-build-image-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/bundling/BootBuildImage.html
|
||||
:boot-jar-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/bundling/BootJar.html
|
||||
:boot-war-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/bundling/BootWar.html
|
||||
:boot-run-javadoc: {api-documentation}/org/springframework/boot/gradle/tasks/run/BootRun.html
|
||||
|
||||
@@ -298,3 +298,12 @@ The jar will then be split into layer folders which may include:
|
||||
* `resources`
|
||||
* `snapshots-dependencies`
|
||||
* `dependencies`
|
||||
|
||||
|
||||
|
||||
[[packaging-oci-images]]
|
||||
== Packaging OCI images
|
||||
|
||||
The plugin can create OCI images from executable jars using a https://buildpacks.io[buildpack].
|
||||
Images can be built using the `bootBuildImage` task and a local Docker installation.
|
||||
The task is automatically created when the `java` plugin is applied and is an instance of {boot-build-image-javadoc}[`BootBuildImage`].
|
||||
|
||||
@@ -15,10 +15,11 @@ When Gradle's {java-plugin}[`java` plugin] is applied to a project, the Spring B
|
||||
The jar will contain everything on the runtime classpath of the main source set; classes are packaged in `BOOT-INF/classes` and jars are packaged in `BOOT-INF/lib`
|
||||
2. Configures the `assemble` task to depend on the `bootJar` task.
|
||||
3. Disables the `jar` task.
|
||||
4. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run your application.
|
||||
5. Creates a configuration named `bootArchives` that contains the artifact produced by the `bootJar` task.
|
||||
6. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`.
|
||||
7. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument.
|
||||
4. Creates a {boot-build-image-javadoc}[`BootBuildImage`] task named `bootBuildImage` that will create a OCI image using a https://buildpacks.io[buildpack].
|
||||
5. Creates a {boot-run-javadoc}[`BootRun`] task named `bootRun` that can be used to run your application.
|
||||
6. Creates a configuration named `bootArchives` that contains the artifact produced by the `bootJar` task.
|
||||
7. Configures any `JavaCompile` tasks with no configured encoding to use `UTF-8`.
|
||||
8. Configures any `JavaCompile` tasks to use the `-parameters` compiler argument.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage;
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootJar;
|
||||
import org.springframework.boot.gradle.tasks.run.BootRun;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -65,6 +66,7 @@ final class JavaPluginAction implements PluginApplicationAction {
|
||||
disableJarTask(project);
|
||||
configureBuildTask(project);
|
||||
BootJar bootJar = configureBootJarTask(project);
|
||||
configureBootBuildImageTask(project, bootJar);
|
||||
configureArtifactPublication(bootJar);
|
||||
configureBootRunTask(project);
|
||||
configureUtf8Encoding(project);
|
||||
@@ -94,6 +96,14 @@ final class JavaPluginAction implements PluginApplicationAction {
|
||||
return bootJar;
|
||||
}
|
||||
|
||||
private void configureBootBuildImageTask(Project project, BootJar bootJar) {
|
||||
BootBuildImage buildImage = project.getTasks().create(SpringBootPlugin.BOOT_BUILD_IMAGE_TASK_NAME,
|
||||
BootBuildImage.class);
|
||||
buildImage.setDescription("Builds an OCI image of the application using the output of the bootJar task");
|
||||
buildImage.setGroup(BasePlugin.BUILD_GROUP);
|
||||
buildImage.from(bootJar);
|
||||
}
|
||||
|
||||
private void configureArtifactPublication(BootJar bootJar) {
|
||||
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar);
|
||||
this.singlePublishedArtifact.addCandidate(artifact);
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.gradle.api.artifacts.ResolvableDependencies;
|
||||
import org.gradle.util.GradleVersion;
|
||||
|
||||
import org.springframework.boot.gradle.dsl.SpringBootExtension;
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootBuildImage;
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootJar;
|
||||
import org.springframework.boot.gradle.tasks.bundling.BootWar;
|
||||
|
||||
@@ -68,6 +69,12 @@ public class SpringBootPlugin implements Plugin<Project> {
|
||||
*/
|
||||
public static final String BOOT_WAR_TASK_NAME = "bootWar";
|
||||
|
||||
/**
|
||||
* The name of the default {@link BootBuildImage} task.
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public static final String BOOT_BUILD_IMAGE_TASK_NAME = "bootBuildImage";
|
||||
|
||||
/**
|
||||
* The coordinates {@code (group:name:version)} of the
|
||||
* {@code spring-boot-dependencies} bom.
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.springframework.boot.gradle.tasks.bundling;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.buildpack.platform.build.Builder;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerException;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.ZipFileTarArchive;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link Task} for bundling an application into an OCI image using a
|
||||
* <a href="https://buildpacks.io">buildpack</a>.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 2.3.0
|
||||
*/
|
||||
public class BootBuildImage extends DefaultTask {
|
||||
|
||||
private Supplier<File> jar;
|
||||
|
||||
private String imageName;
|
||||
|
||||
private String builder;
|
||||
|
||||
private Map<String, String> environment = new HashMap<String, String>();
|
||||
|
||||
private boolean cleanCache;
|
||||
|
||||
private boolean verboseLogging;
|
||||
|
||||
/**
|
||||
* Configures this task to create an image from the given {@code bootJar} task. This
|
||||
* task is also configured to depend upon the given task.
|
||||
* @param bootJar the fat jar from which the image should be created.
|
||||
*/
|
||||
public void from(BootJar bootJar) {
|
||||
dependsOn(bootJar);
|
||||
this.jar = () -> bootJar.getArchiveFile().get().getAsFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this task to create an image from the given jar file.
|
||||
* @param jar the jar from which the image should be created.
|
||||
*/
|
||||
public void from(File jar) {
|
||||
this.jar = () -> jar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the image that will be built. When {@code null}, the name will
|
||||
* be derived from the {@link Project Project's} {@link Project#getName() name} and
|
||||
* {@link Project#getVersion version}.
|
||||
* @return name of the image
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
public String getImageName() {
|
||||
return this.imageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the image that will be built.
|
||||
* @param imageName name of the image
|
||||
*/
|
||||
public void setImageName(String imageName) {
|
||||
this.imageName = imageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the builder that will be used to build the image. When {@code null}, the
|
||||
* default builder will be used.
|
||||
* @return the builder
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
public String getBuilder() {
|
||||
return this.builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the builder that will be used to build the image.
|
||||
* @param builder the builder
|
||||
*/
|
||||
public void setBuilder(String builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment that will be used when building the image.
|
||||
* @return the environment
|
||||
*/
|
||||
@Input
|
||||
public Map<String, String> getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the environment that will be used when building the image.
|
||||
* @param environment the environment
|
||||
*/
|
||||
public void setEnvironment(Map<String, String> environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an entry to the environment that will be used when building the image.
|
||||
* @param name the name of the entry
|
||||
* @param value the value of the entry
|
||||
*/
|
||||
public void environment(String name, String value) {
|
||||
this.environment.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entries to the environment that will be used when building the image.
|
||||
* @param entries the entries to add to the environment
|
||||
*/
|
||||
public void environment(Map<String, String> entries) {
|
||||
this.environment.putAll(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether caches should be cleaned before packaging.
|
||||
* @return whether caches should be cleaned
|
||||
*/
|
||||
@Input
|
||||
public boolean isCleanCache() {
|
||||
return this.cleanCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether caches should be cleaned before packaging.
|
||||
* @param cleanCache {@code true} to clean the cache, otherwise {@code false}.
|
||||
*/
|
||||
public void setCleanCache(boolean cleanCache) {
|
||||
this.cleanCache = cleanCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether verbose logging should be enabled while building the image.
|
||||
* @return whether verbose logging should be enabled
|
||||
*/
|
||||
@Input
|
||||
public boolean isVerboseLogging() {
|
||||
return this.verboseLogging;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether verbose logging should be enabled while building the image.
|
||||
* @param verboseLogging {@code true} to enable verbose logging, otherwise
|
||||
* {@code false}.
|
||||
*/
|
||||
public void setVerboseLogging(boolean verboseLogging) {
|
||||
this.verboseLogging = verboseLogging;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void buildImage() throws DockerException, IOException {
|
||||
Builder builder = new Builder();
|
||||
BuildRequest request = createRequest();
|
||||
builder.build(request);
|
||||
}
|
||||
|
||||
BuildRequest createRequest() {
|
||||
BuildRequest request = customize(
|
||||
BuildRequest.of(determineImageReference(), (owner) -> new ZipFileTarArchive(this.jar.get(), owner)));
|
||||
return request;
|
||||
}
|
||||
|
||||
private ImageReference determineImageReference() {
|
||||
if (StringUtils.hasText(this.imageName)) {
|
||||
return ImageReference.of(this.imageName);
|
||||
}
|
||||
ImageName imageName = ImageName.of(getProject().getName());
|
||||
String version = getProject().getVersion().toString();
|
||||
if ("unspecified".equals(version)) {
|
||||
return ImageReference.of(imageName);
|
||||
}
|
||||
return ImageReference.of(imageName, version);
|
||||
}
|
||||
|
||||
private BuildRequest customize(BuildRequest request) {
|
||||
if (StringUtils.hasText(this.builder)) {
|
||||
request = request.withBuilder(ImageReference.of(this.builder));
|
||||
}
|
||||
if (this.environment != null && !this.environment.isEmpty()) {
|
||||
request = request.withEnv(this.environment);
|
||||
}
|
||||
request = request.withCleanCache(this.cleanCache);
|
||||
request = request.withVerboseLogging(this.verboseLogging);
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.springframework.boot.gradle.tasks.bundling;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.gradle.testkit.runner.TaskOutcome;
|
||||
import org.junit.jupiter.api.TestTemplate;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.gradle.junit.GradleCompatibilityExtension;
|
||||
import org.springframework.boot.gradle.testkit.GradleBuild;
|
||||
import org.springframework.boot.testsupport.testcontainers.DisabledIfDockerUnavailable;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link BootBuildImage}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@ExtendWith(GradleCompatibilityExtension.class)
|
||||
@DisabledIfDockerUnavailable
|
||||
class BootBuildImageIntegrationTests {
|
||||
|
||||
GradleBuild gradleBuild;
|
||||
|
||||
@TestTemplate
|
||||
void bootBuildImageBuildsImage() throws IOException {
|
||||
writeMainClass();
|
||||
BuildResult result = this.gradleBuild.build("bootBuildImage");
|
||||
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMainClass() {
|
||||
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
|
||||
examplePackage.mkdirs();
|
||||
File main = new File(examplePackage, "Main.java");
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(main))) {
|
||||
writer.println("package example;");
|
||||
writer.println();
|
||||
writer.println("import java.io.IOException;");
|
||||
writer.println();
|
||||
writer.println("public class Main {");
|
||||
writer.println();
|
||||
writer.println(" public static void main(String[] args) throws Exception {");
|
||||
writer.println(" System.out.println(\"Launched\");");
|
||||
writer.println(" synchronized(args) {");
|
||||
writer.println(" args.wait(); // Prevent exit");
|
||||
writer.println(" }");
|
||||
writer.println(" }");
|
||||
writer.println();
|
||||
writer.println("}");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.springframework.boot.gradle.tasks.bundling;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.testfixtures.ProjectBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BootBuildImage}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class BootBuildImageTests {
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
|
||||
Project project;
|
||||
|
||||
private final BootBuildImage buildImage;
|
||||
|
||||
BootBuildImageTests() {
|
||||
File projectDir = new File(this.temp, "project");
|
||||
projectDir.mkdirs();
|
||||
this.project = ProjectBuilder.builder().withProjectDir(projectDir).withName("build-image-test").build();
|
||||
this.project.setDescription("Test project for BootBuildImage");
|
||||
this.buildImage = this.project.getTasks().create("buildImage", BootBuildImage.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenProjectVersionIsUnspecifiedThenItIsIgnoredWhenDerivingImageName() {
|
||||
BuildRequest request = this.buildImage.createRequest();
|
||||
assertThat(request.getName().getDomain()).isEqualTo("docker.io");
|
||||
assertThat(request.getName().getName()).isEqualTo("library/build-image-test");
|
||||
assertThat(request.getName().getTag()).isEqualTo("latest");
|
||||
assertThat(request.getName().getDigest()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenProjectVersionIsSpecifiedThenItIsUsedInTagOfImageName() {
|
||||
this.project.setVersion("1.2.3");
|
||||
BuildRequest request = this.buildImage.createRequest();
|
||||
assertThat(request.getName().getDomain()).isEqualTo("docker.io");
|
||||
assertThat(request.getName().getName()).isEqualTo("library/build-image-test");
|
||||
assertThat(request.getName().getTag()).isEqualTo("1.2.3");
|
||||
assertThat(request.getName().getDigest()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenImageNameIsSpecifiedThenItIsUsedInRequest() {
|
||||
this.project.setVersion("1.2.3");
|
||||
this.buildImage.setImageName("example.com/test/build-image:1.0");
|
||||
BuildRequest request = this.buildImage.createRequest();
|
||||
assertThat(request.getName().getDomain()).isEqualTo("example.com");
|
||||
assertThat(request.getName().getName()).isEqualTo("test/build-image");
|
||||
assertThat(request.getName().getTag()).isEqualTo("1.0");
|
||||
assertThat(request.getName().getDigest()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenIndividualEntriesAreAddedToTheEnvironmentThenTheyAreIncludedInTheRequest() {
|
||||
this.buildImage.environment("ALPHA", "a");
|
||||
this.buildImage.environment("BRAVO", "b");
|
||||
assertThat(this.buildImage.createRequest().getEnv()).containsEntry("ALPHA", "a").containsEntry("BRAVO", "b")
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenEntriesAreAddedToTheEnvironmentThenTheyAreIncludedInTheRequest() {
|
||||
Map<String, String> environment = new HashMap<String, String>();
|
||||
environment.put("ALPHA", "a");
|
||||
environment.put("BRAVO", "b");
|
||||
this.buildImage.environment(environment);
|
||||
assertThat(this.buildImage.createRequest().getEnv()).containsEntry("ALPHA", "a").containsEntry("BRAVO", "b")
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheEnvironmentIsSetItIsIncludedInTheRequest() {
|
||||
Map<String, String> environment = new HashMap<String, String>();
|
||||
environment.put("ALPHA", "a");
|
||||
environment.put("BRAVO", "b");
|
||||
this.buildImage.setEnvironment(environment);
|
||||
assertThat(this.buildImage.createRequest().getEnv()).containsEntry("ALPHA", "a").containsEntry("BRAVO", "b")
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenTheEnvironmentIsSetItReplacesAnyExistingEntriesAndIsIncludedInTheRequest() {
|
||||
Map<String, String> environment = new HashMap<String, String>();
|
||||
environment.put("ALPHA", "a");
|
||||
environment.put("BRAVO", "b");
|
||||
this.buildImage.environment("C", "Charlie");
|
||||
this.buildImage.setEnvironment(environment);
|
||||
assertThat(this.buildImage.createRequest().getEnv()).containsEntry("ALPHA", "a").containsEntry("BRAVO", "b")
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingDefaultConfigurationThenRequestHasVerboseLoggingDisabled() {
|
||||
assertThat(this.buildImage.createRequest().isVerboseLogging()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenVerboseLoggingIsEnabledThenRequestHasVerboseLoggingEnabled() {
|
||||
this.buildImage.setVerboseLogging(true);
|
||||
assertThat(this.buildImage.createRequest().isVerboseLogging()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenUsingDefaultConfigurationThenRequestHasCleanCacheDisabled() {
|
||||
assertThat(this.buildImage.createRequest().isCleanCache()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenCleanCacheIsEnabledThenRequestHasCleanCacheEnabled() {
|
||||
this.buildImage.setCleanCache(true);
|
||||
assertThat(this.buildImage.createRequest().isCleanCache()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenNoBuilderIsConfiguredThenRequestHasDefaultBuilder() {
|
||||
assertThat(this.buildImage.createRequest().getBuilder().getName()).isEqualTo("cloudfoundry/cnb");
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenBuilderIsConfiguredThenRequestUsesSpecifiedBuilder() {
|
||||
this.buildImage.setBuilder("example.com/test/builder:1.2");
|
||||
assertThat(this.buildImage.createRequest().getBuilder().getName()).isEqualTo("test/builder");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,9 +27,16 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import com.fasterxml.jackson.core.Versioned;
|
||||
import com.fasterxml.jackson.databind.Module;
|
||||
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
|
||||
import com.sun.jna.Platform;
|
||||
import io.spring.gradle.dependencymanagement.DependencyManagementPlugin;
|
||||
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension;
|
||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.conn.HttpClientConnectionManager;
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.gradle.testkit.runner.GradleRunner;
|
||||
import org.gradle.util.GradleVersion;
|
||||
@@ -41,6 +48,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin;
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlugin;
|
||||
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.loader.tools.LaunchScript;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
@@ -94,7 +102,12 @@ public class GradleBuild {
|
||||
new File(pathOfJarContaining(KotlinPlugin.class)), new File(pathOfJarContaining(KotlinProject.class)),
|
||||
new File(pathOfJarContaining(KotlinCompilerClient.class)),
|
||||
new File(pathOfJarContaining(KotlinGradleSubplugin.class)),
|
||||
new File(pathOfJarContaining(ArchiveEntry.class)));
|
||||
new File(pathOfJarContaining(ArchiveEntry.class)), new File(pathOfJarContaining(BuildRequest.class)),
|
||||
new File(pathOfJarContaining(HttpClientConnectionManager.class)),
|
||||
new File(pathOfJarContaining(HttpRequest.class)), new File(pathOfJarContaining(Module.class)),
|
||||
new File(pathOfJarContaining(Versioned.class)),
|
||||
new File(pathOfJarContaining(ParameterNamesModule.class)),
|
||||
new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)));
|
||||
}
|
||||
|
||||
private String pathOfJarContaining(Class<?> type) {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
Reference in New Issue
Block a user