Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect bootJar task (Spring Boot fat JAR) and enable jar task to use the thin JAR #2178

Merged
merged 13 commits into from
Dec 6, 2019
2 changes: 2 additions & 0 deletions jib-gradle-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ All notable changes to this project will be documented in this file.

### Fixed

- Now `jib.containerizingMode='packaged'` works as intended with Spring Boot projects that generate a fat JAR. ([#2178](https://github.com/GoogleContainerTools/jib/pull/2178))

## 1.8.0

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@
import com.google.cloud.tools.jib.api.DescriptorDigest;
import com.google.cloud.tools.jib.api.ImageReference;
import com.google.cloud.tools.jib.api.InvalidImageReferenceException;
import com.google.cloud.tools.jib.blob.Blobs;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -29,6 +33,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.BuildTask;
import org.gradle.testkit.runner.TaskOutcome;
Expand All @@ -38,6 +43,23 @@
/** Helper class to run integration tests. */
public class JibRunHelper {

@Nullable
static String getContent(URL url) throws InterruptedException {
for (int i = 0; i < 40; i++) {
Thread.sleep(500);
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
try (InputStream in = connection.getInputStream()) {
return Blobs.writeToString(Blobs.from(in));
}
}
} catch (IOException ignored) {
}
}
return null;
}

static String buildAndRun(TestProject testProject, String imageReference)
throws IOException, InterruptedException, DigestException {
return buildAndRun(testProject, imageReference, "build.gradle");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2019 Google LLC.
*
* 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
*
* http://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 com.google.cloud.tools.jib.gradle;

import com.google.cloud.tools.jib.Command;
import com.google.cloud.tools.jib.IntegrationTestingConfiguration;
import java.io.IOException;
import java.net.URL;
import java.security.DigestException;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;

/** Integration tests for building Spring Boot images. */
public class SpringBootProjectIntegrationTest {

@ClassRule public static final TestProject springBootProject = new TestProject("spring-boot");

@Nullable private String containerName;

@After
public void tearDown() throws IOException, InterruptedException {
if (containerName != null) {
new Command("docker", "stop", containerName).run();
}
}

@Test
public void testBuild_packagedMode() throws IOException, InterruptedException, DigestException {
buildAndRunWebApp(springBootProject, "springboot:gradle", "build.gradle");

String output =
new Command(
"docker",
"exec",
containerName,
"/busybox/wc",
"-c",
"/app/classpath/spring-boot-original.jar")
.run();
Assert.assertEquals("1360 /app/classpath/spring-boot-original.jar\n", output);

Assert.assertEquals("Hello world", JibRunHelper.getContent(new URL("http://localhost:8080")));
}

private void buildAndRunWebApp(TestProject project, String label, String gradleBuildFile)
throws IOException, InterruptedException, DigestException {
String nameBase = IntegrationTestingConfiguration.getTestRepositoryLocation() + '/';
String targetImage = nameBase + label + System.nanoTime();
String output =
JibRunHelper.buildAndRun(
springBootProject, targetImage, gradleBuildFile, "--detach", "-p8080:8080");
containerName = output.trim();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@

import com.google.cloud.tools.jib.Command;
import com.google.cloud.tools.jib.IntegrationTestingConfiguration;
import com.google.cloud.tools.jib.blob.Blobs;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.DigestException;
import javax.annotation.Nullable;
Expand All @@ -35,23 +32,6 @@ public class WarProjectIntegrationTest {

@ClassRule public static final TestProject servlet25Project = new TestProject("war_servlet25");

@Nullable
private static String getContent(URL url) throws InterruptedException {
for (int i = 0; i < 40; i++) {
Thread.sleep(500);
try {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
try (InputStream in = connection.getInputStream()) {
return Blobs.writeToString(Blobs.from(in));
}
}
} catch (IOException ex) {
}
}
return null;
}

@Nullable private String containerName;

@After
Expand Down Expand Up @@ -80,6 +60,7 @@ private void verifyBuildAndRun(TestProject project, String label, String gradleB
JibRunHelper.buildAndRun(project, targetImage, gradleBuildFile, "--detach", "-p8080:8080");
containerName = output.trim();

Assert.assertEquals("Hello world", getContent(new URL("http://localhost:8080/hello")));
Assert.assertEquals(
"Hello world", JibRunHelper.getContent(new URL("http://localhost:8080/hello")));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
plugins {
id 'org.springframework.boot' version '2.1.6.RELEASE'
id 'io.spring.dependency-management' version '1.0.6.RELEASE'
id 'java'
id 'com.google.cloud.tools.jib'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
}

jib {
from.image = 'gcr.io/distroless/java:debug'
to.image = System.getProperty("_TARGET_IMAGE")
to.credHelper = 'gcr'
containerizingMode='packaged'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'spring-boot'
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hello;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

@RequestMapping("/")
public String index() {
return "Hello world";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,9 @@ public JibContainerBuilder createJibContainerBuilder(
case PACKAGED:
// Add a JAR
Jar jarTask = (Jar) project.getTasks().findByName("jar");
javaContainerBuilder.addToClasspath(
jarTask.getDestinationDir().toPath().resolve(jarTask.getArchiveName()));
Path jarPath = jarTask.getDestinationDir().toPath().resolve(jarTask.getArchiveName());
consoleLogger.log(Level.DEBUG, "Using JAR: " + jarPath);
javaContainerBuilder.addToClasspath(jarPath);
break;

default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.gradle.api.plugins.BasePlugin;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.jvm.tasks.Jar;
import org.gradle.util.GradleVersion;

public class JibPlugin implements Plugin<Project> {
Expand Down Expand Up @@ -168,8 +169,9 @@ public void apply(Project project) {
project.afterEvaluate(
projectAfterEvaluation -> {
try {
TaskProvider<Task> warTask = TaskCommon.getWarTaskProvider(project);
TaskProvider<Task> bootWarTask = TaskCommon.getBootWarTaskProvider(project);
TaskProvider<Task> warTask = TaskCommon.getWarTaskProvider(projectAfterEvaluation);
TaskProvider<Task> bootWarTask =
TaskCommon.getBootWarTaskProvider(projectAfterEvaluation);
List<TaskProvider<?>> dependsOnTask = new ArrayList<>();
if (warTask != null || bootWarTask != null) {
// Have all tasks depend on the 'war' and/or 'bootWar' task.
Expand All @@ -181,7 +183,14 @@ public void apply(Project project) {
}
} else if ("packaged".equals(jibExtension.getContainerizingMode())) {
// Have all tasks depend on the 'jar' task.
dependsOnTask.add(projectAfterEvaluation.getTasks().named("jar"));
TaskProvider<Task> jarTask = projectAfterEvaluation.getTasks().named("jar");
dependsOnTask.add(jarTask);

if (projectAfterEvaluation.getPlugins().hasPlugin("org.springframework.boot")) {
Jar jar = (Jar) jarTask.get();
jar.setEnabled(true);
jar.setClassifier("original");
}
} else {
// Have all tasks depend on the 'classes' task.
dependsOnTask.add(projectAfterEvaluation.getTasks().named("classes"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.jvm.tasks.Jar;
import org.gradle.testfixtures.ProjectBuilder;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.testkit.runner.UnexpectedBuildFailure;
Expand Down Expand Up @@ -264,6 +265,28 @@ public void testWebAppProject_bootWarDisabled() {
}
}

@Test
public void testSpringBootJarProject_nonPackagedMode() {
Project project =
createProject("java", "org.springframework.boot", "com.google.cloud.tools.jib");

Jar jarTask = (Jar) project.getTasks().getByPath(":jar");
Assert.assertFalse(jarTask.getEnabled());
Assert.assertEquals("", jarTask.getClassifier());
}

@Test
public void testSpringBootJarProject_packagedMode() {
Project project =
createProject("java", "org.springframework.boot", "com.google.cloud.tools.jib");
JibExtension jibExtension = (JibExtension) project.getExtensions().getByName("jib");
jibExtension.setContainerizingMode("packaged");

Jar jarTask = (Jar) project.getTasks().getByPath(":jar");
Assert.assertTrue(jarTask.getEnabled());
Assert.assertEquals("original", jarTask.getClassifier());
}

@Test
public void testNonWebAppProject() {
Project project = createProject("java", "com.google.cloud.tools.jib");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ public boolean isOffline() {
}

/**
* Gets the path of the JAR that the Maven JAR Plugin would generate.
* Gets the path of the JAR that the Maven JAR Plugin generates.
*
* <p>https://maven.apache.org/plugins/maven-jar-plugin/jar-mojo.html
* https://github.com/apache/maven-jar-plugin/blob/80f58a84aacff6e671f5a601d62a3a3800b507dc/src/main/java/org/apache/maven/plugins/jar/AbstractJarMojo.java#L177
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private static String getContent(URL url) throws InterruptedException {
return Blobs.writeToString(Blobs.from(in));
}
}
} catch (IOException ex) {
} catch (IOException ignored) {
}
}
return null;
Expand Down