Commit 4be04b0e authored by Scott Frederick's avatar Scott Frederick

Support image building with Maven and war packaging

This commit updates the Maven image building goal to support building
images from executable and non-executable war files.

Fixes gh-23823
parent a80c4ad3
[[build-image]]
== Packaging OCI Images
The plugin can create an https://github.com/opencontainers/image-spec[OCI image] from an executable jar file using https://buildpacks.io/[Cloud Native Buildpacks] (CNB).
The plugin can create an https://github.com/opencontainers/image-spec[OCI image] from a jar or war file using https://buildpacks.io/[Cloud Native Buildpacks] (CNB).
Images can be built using the `build-image` goal.
NOTE: For security reasons, images build and run as non-root users.
See the {buildpacks-reference}/reference/spec/platform-api/#users[CNB specification] for more details.
NOTE: The `build-image` goal is not supported with projects using <<repackage, war packaging>>.
The easiest way to get started is to invoke `mvn spring-boot:build-image` on a project.
It is possible to automate the creation of an image whenever the `package` phase is invoked, as shown in the following example:
......
......@@ -68,6 +68,30 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests {
});
}
@TestTemplate
void whenBuildImageIsInvokedWithWarPackaging(MavenBuild mavenBuild) {
mavenBuild.project("build-image-war-packaging").goals("package")
.systemProperty("spring-boot.build-image.pullPolicy", "IF_NOT_PRESENT")
.prepare(this::writeLongNameResource).execute((project) -> {
File war = new File(project, "target/build-image-war-packaging-0.0.1.BUILD-SNAPSHOT.war");
assertThat(war).isFile();
File original = new File(project,
"target/build-image-war-packaging-0.0.1.BUILD-SNAPSHOT.war.original");
assertThat(original).doesNotExist();
assertThat(buildLog(project)).contains("Building image")
.contains("docker.io/library/build-image-war-packaging:0.0.1.BUILD-SNAPSHOT")
.contains("Successfully built image");
ImageReference imageReference = ImageReference.of(ImageName.of("build-image-war-packaging"),
"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 whenBuildImageIsInvokedWithCustomImageName(MavenBuild mavenBuild) {
mavenBuild.project("build-image-custom-name").goals("package")
......@@ -191,12 +215,6 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests {
.containsPattern("Builder lifecycle '.*' failed with status code"));
}
@TestTemplate
void failsWithWarPackaging(MavenBuild mavenBuild) {
mavenBuild.project("build-image-war-packaging").goals("package").executeAndFail(
(project) -> assertThat(buildLog(project)).contains("Executable jar file required for building image"));
}
@TestTemplate
void failsWithBuildpackNotInBuilder(MavenBuild mavenBuild) {
mavenBuild.project("build-image-bad-buildpack").goals("package")
......
......@@ -3,7 +3,7 @@
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-war</artifactId>
<artifactId>build-image-war-packaging</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
......
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
......@@ -19,6 +19,10 @@ package org.test;
public class SampleApplication {
public static void main(String[] args) throws Exception {
System.out.println("Launched");
synchronized(args) {
args.wait(); // Prevent exit"
}
}
}
......@@ -218,23 +218,26 @@ public class BuildImageMojo extends AbstractPackagerMojo {
}
private TarArchive getApplicationContent(Owner owner, Libraries libraries) {
ImagePackager packager = getConfiguredPackager(() -> new ImagePackager(getJarFile()));
ImagePackager packager = getConfiguredPackager(() -> new ImagePackager(getArchiveFile()));
return new PackagedTarArchive(owner, libraries, packager);
}
private File getJarFile() {
private File getArchiveFile() {
// We can use 'project.getArtifact().getFile()' because that was done in a
// forked lifecycle and is now null
StringBuilder name = new StringBuilder(this.finalName);
if (StringUtils.hasText(this.classifier)) {
name.append("-").append(this.classifier);
}
name.append(".jar");
File jarFile = new File(this.sourceDirectory, name.toString());
if (!jarFile.exists()) {
throw new IllegalStateException("Executable jar file required for building image");
File archiveFile = new File(this.sourceDirectory, name.toString() + ".jar");
if (archiveFile.exists()) {
return archiveFile;
}
return jarFile;
archiveFile = new File(this.sourceDirectory, name.toString() + ".war");
if (archiveFile.exists()) {
return archiveFile;
}
throw new IllegalStateException("A jar or war file is required for building image");
}
private BuildRequest customize(BuildRequest request) {
......
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