Skip to content

Commit

Permalink
Inherit ENTRYPOINT and CMD from base image for WAR projects (#1071)
Browse files Browse the repository at this point in the history
* Support WARs : inherit entrypoint from base images (Jetty / Tomcat / ? / ... )

* Improves UX

* Inherits entrypoint when entrypoint is null

* Some refactor

* Refactor

* Fixes rebasing + changes program arguments

* Fixes review comments

* Fixes rebase problems
Removes "CMD []" when entrypoint is present in Dockerfile
Changes a TODO description
  • Loading branch information
CyrilleH authored and chanseokoh committed Oct 15, 2018
1 parent 04cf15b commit 5b31da0
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ public JibContainerBuilder addLayer(LayerConfiguration layerConfiguration) {
* @param entrypoint a list of the entrypoint command
* @return this
*/
public JibContainerBuilder setEntrypoint(List<String> entrypoint) {
this.entrypoint = ImmutableList.copyOf(entrypoint);
public JibContainerBuilder setEntrypoint(@Nullable List<String> entrypoint) {
if (entrypoint != null) {
this.entrypoint = ImmutableList.copyOf(entrypoint);
}
return this;
}

Expand Down Expand Up @@ -194,8 +196,10 @@ public JibContainerBuilder setEntrypoint(String... entrypoint) {
* @param programArguments a list of program argument tokens
* @return this
*/
public JibContainerBuilder setProgramArguments(List<String> programArguments) {
this.programArguments = ImmutableList.copyOf(programArguments);
public JibContainerBuilder setProgramArguments(@Nullable List<String> programArguments) {
if (programArguments != null) {
this.programArguments = ImmutableList.copyOf(programArguments);
}
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,16 @@ public DescriptorDigest run() throws InterruptedException, ExecutionException {

if (buildConfiguration.getContainerConfiguration() != null) {
buildConfiguration.getEventDispatcher().dispatch(LogEvent.lifecycle(""));
buildConfiguration
.getEventDispatcher()
.dispatch(
LogEvent.lifecycle(
"Container entrypoint set to "
+ buildConfiguration.getContainerConfiguration().getEntrypoint()));
// TODO refactor code to also log ENTRYPOINT and CMD when inheriting them in this code,
// instead of logging them elsewhere.
if (buildConfiguration.getContainerConfiguration().getEntrypoint() != null) {
buildConfiguration
.getEventDispatcher()
.dispatch(
LogEvent.lifecycle(
"Container entrypoint set to "
+ buildConfiguration.getContainerConfiguration().getEntrypoint()));
}
}

return imageDigest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.google.cloud.tools.jib.cache.CacheEntry;
import com.google.cloud.tools.jib.configuration.BuildConfiguration;
import com.google.cloud.tools.jib.configuration.ContainerConfiguration;
import com.google.cloud.tools.jib.event.events.LogEvent;
import com.google.cloud.tools.jib.image.DescriptorDigest;
import com.google.cloud.tools.jib.image.Image;
import com.google.cloud.tools.jib.image.Layer;
Expand All @@ -40,6 +41,7 @@
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;

/** Builds a model {@link Image}. */
class BuildImageStep
Expand Down Expand Up @@ -176,8 +178,9 @@ private Image<Layer> afterCacheEntrySteps()
imageBuilder.addEnvironment(containerConfiguration.getEnvironmentMap());
imageBuilder.setCreated(containerConfiguration.getCreationTime());
imageBuilder.setUser(containerConfiguration.getUser());
imageBuilder.setEntrypoint(containerConfiguration.getEntrypoint());
imageBuilder.setProgramArguments(containerConfiguration.getProgramArguments());
imageBuilder.setEntrypoint(computeEntrypoint(baseImage, containerConfiguration));
imageBuilder.setProgramArguments(
computeProgramArguments(baseImage, containerConfiguration));
imageBuilder.setExposedPorts(containerConfiguration.getExposedPorts());
imageBuilder.addLabels(containerConfiguration.getLabels());
}
Expand All @@ -186,4 +189,59 @@ private Image<Layer> afterCacheEntrySteps()
return imageBuilder.build();
}
}

/**
* Computes the image entrypoint. If {@link ContainerConfiguration#getEntrypoint()} is null, the
* entrypoint is inherited from the base image. Otherwise {@link
* ContainerConfiguration#getEntrypoint()} is returned.
*
* @param baseImage the base image
* @param containerConfiguration the container configuration
* @return the container entrypoint
*/
@Nullable
private ImmutableList<String> computeEntrypoint(
Image<Layer> baseImage, ContainerConfiguration containerConfiguration) {
if (baseImage.getEntrypoint() == null || containerConfiguration.getEntrypoint() != null) {
return containerConfiguration.getEntrypoint();
}

buildConfiguration
.getEventDispatcher()
.dispatch(
LogEvent.lifecycle(
"Container entrypoint set to "
+ baseImage.getEntrypoint()
+ " (inherited from base image)"));
return baseImage.getEntrypoint();
}

/**
* Computes the image program arguments. If {@link ContainerConfiguration#getEntrypoint()} and
* {@link ContainerConfiguration#getProgramArguments()} are null, the program arguments are
* inherited from the base image. Otherwise {@link ContainerConfiguration#getProgramArguments()}
* is returned.
*
* @param baseImage the base image
* @param containerConfiguration the container configuration
* @return the container program arguments
*/
@Nullable
private ImmutableList<String> computeProgramArguments(
Image<Layer> baseImage, ContainerConfiguration containerConfiguration) {
if (baseImage.getProgramArguments() == null
|| containerConfiguration.getEntrypoint() != null
|| containerConfiguration.getProgramArguments() != null) {
return containerConfiguration.getProgramArguments();
}

buildConfiguration
.getEventDispatcher()
.dispatch(
LogEvent.lifecycle(
"Container program arguments set to "
+ baseImage.getProgramArguments()
+ " (inherited from base image)"));
return baseImage.getProgramArguments();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ private CopyDirective(
* @param listBuilder the {@link ImmutableList.Builder} to add to
* @param layerEntries the layer entries
* @param directoryInContext the directory in the context to put the source files for the layer
* @param extractionPath the extraction path to extract the directory to
*/
private static void addIfNotEmpty(
ImmutableList.Builder<CopyDirective> listBuilder,
Expand Down Expand Up @@ -132,9 +131,9 @@ private static String mapToDockerfileString(Map<String, String> map, String comm
private final ImmutableList<CopyDirective> copyDirectives;

@Nullable private String baseImage;
private List<String> entrypoint = Collections.emptyList();
@Nullable private List<String> entrypoint = Collections.emptyList();
@Nullable private List<String> programArguments = Collections.emptyList();
@Nullable private String user;
private List<String> programArguments = Collections.emptyList();
private Map<String, String> environment = Collections.emptyMap();
private List<String> exposedPorts = Collections.emptyList();
private Map<String, String> labels = Collections.emptyMap();
Expand Down Expand Up @@ -187,7 +186,7 @@ public JavaDockerContextGenerator setBaseImage(String baseImage) {
* @param entrypoint the entrypoint
* @return this
*/
public JavaDockerContextGenerator setEntrypoint(List<String> entrypoint) {
public JavaDockerContextGenerator setEntrypoint(@Nullable List<String> entrypoint) {
this.entrypoint = entrypoint;
return this;
}
Expand All @@ -209,7 +208,7 @@ public JavaDockerContextGenerator setUser(@Nullable String user) {
* @param programArguments the list of arguments to append to {@code ENTRYPOINT}
* @return this
*/
public JavaDockerContextGenerator setProgramArguments(List<String> programArguments) {
public JavaDockerContextGenerator setProgramArguments(@Nullable List<String> programArguments) {
this.programArguments = programArguments;
return this;
}
Expand Down Expand Up @@ -339,11 +338,12 @@ String makeDockerfile() throws JsonProcessingException {

dockerfile.append(mapToDockerfileString(environment, "ENV"));
dockerfile.append(mapToDockerfileString(labels, "LABEL"));
dockerfile
.append("\nENTRYPOINT ")
.append(objectMapper.writeValueAsString(entrypoint))
.append("\nCMD ")
.append(objectMapper.writeValueAsString(programArguments));
if (entrypoint != null) {
dockerfile.append("\nENTRYPOINT ").append(objectMapper.writeValueAsString(entrypoint));
}
if (programArguments != null) {
dockerfile.append("\nCMD ").append(objectMapper.writeValueAsString(programArguments));
}
if (user != null) {
dockerfile.append("\nUSER ").append(user);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,6 @@ public static List<String> makeDefaultEntrypoint(
mainClass);
}

/**
* Constructs the container entrypoint for the gcr.io/distroless/jetty base image.
*
* @return ["java", "-jar", "/jetty/start.jar"]
* @see <a href="https:/GoogleContainerTools/distroless/blob/master/java/jetty/BUILD">
* https:/GoogleContainerTools/distroless/blob/master/java/jetty/BUILD</a>
*/
// TODO: inherit CMD and ENTRYPOINT from the base image and remove this.
public static List<String> makeDistrolessJettyEntrypoint() {
return Arrays.asList("java", "-jar", "/jetty/start.jar");
}

/**
* Constructs the container entrypoint.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ public Blob getLayerBlob() {
.addEnvironment(ImmutableMap.of("BASE_ENV", "BASE_ENV_VALUE"))
.addLabel("base.label", "base.label.value")
.setWorkingDirectory("/base/working/directory")
.setEntrypoint(ImmutableList.of("baseImageEntrypoint"))
.setProgramArguments(ImmutableList.of("catalina.sh", "run"))
.addHistory(nonEmptyLayerHistory)
.addHistory(emptyLayerHistory)
.addHistory(emptyLayerHistory)
Expand Down Expand Up @@ -184,6 +186,74 @@ public void test_propagateBaseImageConfiguration()
Assert.assertEquals(image.getHistory().get(0), nonEmptyLayerHistory);
Assert.assertEquals(image.getHistory().get(1), emptyLayerHistory);
Assert.assertEquals(image.getHistory().get(2), emptyLayerHistory);
Assert.assertEquals(ImmutableList.of(), image.getEntrypoint());
Assert.assertEquals(ImmutableList.of(), image.getProgramArguments());
}

@Test
public void test_inheritedEntrypoint() throws ExecutionException, InterruptedException {
Mockito.when(mockContainerConfiguration.getEntrypoint()).thenReturn(null);
Mockito.when(mockContainerConfiguration.getProgramArguments())
.thenReturn(ImmutableList.of("test"));

BuildImageStep buildImageStep =
new BuildImageStep(
MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()),
mockBuildConfiguration,
mockPullBaseImageStep,
mockPullAndCacheBaseImageLayersStep,
ImmutableList.of(
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep));
Image<Layer> image = buildImageStep.getFuture().get().getFuture().get();

Assert.assertEquals(ImmutableList.of("baseImageEntrypoint"), image.getEntrypoint());
Assert.assertEquals(ImmutableList.of("test"), image.getProgramArguments());
}

@Test
public void test_inheritedEntrypointAndProgramArguments()
throws ExecutionException, InterruptedException {
Mockito.when(mockContainerConfiguration.getEntrypoint()).thenReturn(null);
Mockito.when(mockContainerConfiguration.getProgramArguments()).thenReturn(null);

BuildImageStep buildImageStep =
new BuildImageStep(
MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()),
mockBuildConfiguration,
mockPullBaseImageStep,
mockPullAndCacheBaseImageLayersStep,
ImmutableList.of(
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep));
Image<Layer> image = buildImageStep.getFuture().get().getFuture().get();

Assert.assertEquals(ImmutableList.of("baseImageEntrypoint"), image.getEntrypoint());
Assert.assertEquals(ImmutableList.of("catalina.sh", "run"), image.getProgramArguments());
}

@Test
public void test_notInheritedProgramArguments() throws ExecutionException, InterruptedException {
Mockito.when(mockContainerConfiguration.getEntrypoint())
.thenReturn(ImmutableList.of("myEntrypoint"));
Mockito.when(mockContainerConfiguration.getProgramArguments()).thenReturn(null);

BuildImageStep buildImageStep =
new BuildImageStep(
MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor()),
mockBuildConfiguration,
mockPullBaseImageStep,
mockPullAndCacheBaseImageLayersStep,
ImmutableList.of(
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep,
mockBuildAndCacheApplicationLayerStep));
Image<Layer> image = buildImageStep.getFuture().get().getFuture().get();

Assert.assertEquals(ImmutableList.of("myEntrypoint"), image.getEntrypoint());
Assert.assertNull(image.getProgramArguments());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,4 @@ public void testMakeDefaultEntrypoint_classpathStringWithNonDefaultAppRoot() {
AbsoluteUnixPath.get("/my/app"), Collections.emptyList(), "Main");
Assert.assertEquals("/my/app/resources:/my/app/classes:/my/app/libs/*", entrypoint.get(2));
}

@Test
public void testMakeDistrolessJettyEntrypoint() {
List<String> expected = Arrays.asList("java", "-jar", "/jetty/start.jar");
Assert.assertEquals(expected, JavaEntrypointConstructor.makeDistrolessJettyEntrypoint());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class ContainerParameters {
private Map<String, String> environment = Collections.emptyMap();
private List<String> entrypoint = Collections.emptyList();
@Nullable private String mainClass;
private List<String> args = Collections.emptyList();
@Nullable private List<String> args;
private ImageFormat format = ImageFormat.Docker;
private List<String> ports = Collections.emptyList();
private Map<String, String> labels = Collections.emptyMap();
Expand Down Expand Up @@ -115,6 +115,7 @@ public void setMainClass(String mainClass) {
}

@Input
@Nullable
@Optional
public List<String> getArgs() {
if (System.getProperty(PropertyNames.CONTAINER_ARGS) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import java.util.List;
import java.util.Optional;
import java.util.logging.Level;
import javax.annotation.Nullable;
import org.gradle.api.GradleException;
import org.gradle.api.logging.Logger;
import org.gradle.internal.logging.events.LogEvent;
Expand Down Expand Up @@ -139,6 +140,7 @@ static PluginConfigurationProcessor processCommonConfiguration(
.setLayers(projectProperties.getJavaLayerConfigurations().getLayerConfigurations())
.setEntrypoint(entrypoint)
.setEnvironment(jibExtension.getContainer().getEnvironment())
.setProgramArguments(jibExtension.getContainer().getArgs())
.setExposedPorts(ExposedPortsParser.parse(jibExtension.getContainer().getPorts()))
.setProgramArguments(jibExtension.getContainer().getArgs())
.setLabels(jibExtension.getContainer().getLabels())
Expand Down Expand Up @@ -174,7 +176,7 @@ static void configureContainerizer(
*
* <ol>
* <li>the user specified one, if set
* <li>for a WAR project, the Jetty default one
* <li>for a WAR project, null (it must be inherited from base image)
* <li>for a non-WAR project, by resolving the main class
* </ol>
*
Expand All @@ -183,6 +185,7 @@ static void configureContainerizer(
* @param projectProperties used for providing additional information
* @return the entrypoint
*/
@Nullable
static List<String> computeEntrypoint(
Logger logger, JibExtension jibExtension, GradleProjectProperties projectProperties) {
ContainerParameters parameters = jibExtension.getContainer();
Expand All @@ -194,7 +197,7 @@ static List<String> computeEntrypoint(
}

if (projectProperties.isWarProject()) {
return JavaEntrypointConstructor.makeDistrolessJettyEntrypoint();
return null;
}

String mainClass = projectProperties.getMainClass(jibExtension);
Expand Down
Loading

0 comments on commit 5b31da0

Please sign in to comment.