Skip to content

Commit

Permalink
Fail with Gradle bootBuildImage and war packaging
Browse files Browse the repository at this point in the history
Prior to this commit, running the bootBuildImage Gradle task on a
project configured for war packaging would result in a jar file being
built and used in the image instead of the war file. With this commit
an error will be thrown from the plugin in this case.

Fixes gh-24521
  • Loading branch information
scottfrederick committed Dec 16, 2020
1 parent 2930053 commit 0e5df22
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 1 deletion.
@@ -1,13 +1,15 @@
[[build-image]]
== Packaging OCI Images
The plugin can create an https://github.com/opencontainers/image-spec[OCI image] from executable jars using https://buildpacks.io[Cloud Native Buildpacks] (CNB).
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).
Images can be built using the `bootBuildImage` task.

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.

The task is automatically created when the `java` plugin is applied and is an instance of {boot-build-image-javadoc}[`BootBuildImage`].

NOTE: The `bootBuildImage` task is not supported with projects using <<packaging-executable-wars, war packaging>>.

NOTE: The `bootBuildImage` task can not be used with a <<packaging-executable-configuring-launch-script, fully executable Spring Boot archive>> that includes a launch script.
Disable launch script configuration in the `bootJar` task when building a jar file that is intended to be used with `bootBuildImage`.

Expand Down
Expand Up @@ -22,11 +22,13 @@
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.RegularFile;
import org.gradle.api.internal.artifacts.dsl.LazyPublishArtifact;
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.plugins.WarPlugin;
import org.gradle.api.tasks.TaskProvider;

import org.springframework.boot.gradle.tasks.bundling.BootBuildImage;
import org.springframework.boot.gradle.tasks.bundling.BootWar;

/**
Expand All @@ -51,6 +53,7 @@ public Class<? extends Plugin<? extends Project>> getPluginClass() {
@Override
public void execute(Project project) {
disableWarTask(project);
disableBootBuildImageTask(project);
TaskProvider<BootWar> bootWar = configureBootWarTask(project);
configureArtifactPublication(bootWar);
}
Expand All @@ -59,6 +62,11 @@ private void disableWarTask(Project project) {
project.getTasks().named(WarPlugin.WAR_TASK_NAME).configure((war) -> war.setEnabled(false));
}

private void disableBootBuildImageTask(Project project) {
project.getTasks().named(SpringBootPlugin.BOOT_BUILD_IMAGE_TASK_NAME, BootBuildImage.class)
.configure((buildImage) -> buildImage.getJar().set((RegularFile) null));
}

private TaskProvider<BootWar> configureBootWarTask(Project project) {
return project.getTasks().register(SpringBootPlugin.BOOT_WAR_TASK_NAME, BootWar.class, (bootWar) -> {
bootWar.setGroup(BasePlugin.BUILD_GROUP);
Expand Down
Expand Up @@ -21,6 +21,7 @@
import java.util.Map;

import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.JavaVersion;
import org.gradle.api.Project;
import org.gradle.api.Task;
Expand Down Expand Up @@ -79,6 +80,7 @@ public BootBuildImage() {
* @return the jar property
*/
@Input
@Optional
public RegularFileProperty getJar() {
return this.jar;
}
Expand Down Expand Up @@ -226,6 +228,9 @@ public void setVerboseLogging(boolean verboseLogging) {

@TaskAction
void buildImage() throws DockerEngineException, IOException {
if (!this.jar.isPresent()) {
throw new GradleException("Executable jar file required for building image");
}
Builder builder = new Builder();
BuildRequest request = createRequest();
builder.build(request);
Expand Down
Expand Up @@ -153,6 +153,36 @@ void failsWithInvalidImageName() {
.containsPattern("example/Invalid-Image-Name");
}

@TestTemplate
void failsWithWarPackaging() {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.buildAndFail("bootBuildImage", "-PapplyWarPlugin");
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.FAILED);
assertThat(result.getOutput()).contains("Executable jar file required for building image");
}

@TestTemplate
void buildsImageWithWarPackagingAndJarConfiguration() throws IOException {
writeMainClass();
writeLongNameResource();
BuildResult result = this.gradleBuild.build("bootBuildImage");
String projectName = this.gradleBuild.getProjectDir().getName();
assertThat(result.task(":bootBuildImage").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
assertThat(result.getOutput()).contains("docker.io/library/" + projectName);
assertThat(result.getOutput()).contains("paketo-buildpacks/builder");
File buildLibs = new File(this.gradleBuild.getProjectDir(), "build/libs");
assertThat(buildLibs.listFiles())
.containsExactly(new File(buildLibs, this.gradleBuild.getProjectDir().getName() + ".war"));
ImageReference imageReference = ImageReference.of(ImageName.of(projectName));
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();
Expand Down
@@ -0,0 +1,11 @@
plugins {
id 'war'
id 'org.springframework.boot' version '{version}'
}

bootBuildImage {
jar = bootWar.archiveFile
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
Expand Up @@ -3,5 +3,9 @@ plugins {
id 'org.springframework.boot' version '{version}'
}

if (project.hasProperty('applyWarPlugin')) {
apply plugin: 'war'
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

0 comments on commit 0e5df22

Please sign in to comment.