Browse Source

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
pull/25416/head
Scott Frederick 5 years ago
parent
commit
4be04b0ea2
  1. 4
      spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc
  2. 30
      spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java
  3. 2
      spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml
  4. 6
      spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/src/main/java/org/test/SampleApplication.java
  5. 17
      spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java

4
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/docs/asciidoc/packaging-oci-image.adoc

@ -1,13 +1,11 @@ @@ -1,13 +1,11 @@
[[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:

30
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/java/org/springframework/boot/maven/BuildImageTests.java

@ -68,6 +68,30 @@ public class BuildImageTests extends AbstractArchiveIntegrationTests { @@ -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 { @@ -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")

2
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/pom.xml

@ -3,7 +3,7 @@ @@ -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>

6
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/intTest/projects/build-image-war-packaging/src/main/java/org/test/SampleApplication.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* 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; @@ -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"
}
}
}

17
spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/BuildImageMojo.java

@ -218,23 +218,26 @@ public class BuildImageMojo extends AbstractPackagerMojo { @@ -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) {

Loading…
Cancel
Save