diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildStepsIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildStepsIntegrationTest.java index f85160a109..d73112c53f 100644 --- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildStepsIntegrationTest.java +++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildStepsIntegrationTest.java @@ -21,16 +21,23 @@ import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.cache.Caches; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.image.ImageReference; import com.google.cloud.tools.jib.image.InvalidImageReferenceException; import com.google.cloud.tools.jib.registry.LocalRegistry; +import com.google.common.collect.ImmutableList; +import com.google.common.io.Resources; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Before; @@ -42,25 +49,36 @@ /** Integration tests for {@link BuildSteps}. */ public class BuildStepsIntegrationTest { + /** Lists the files in the {@code resourcePath} resources directory. */ + private static ImmutableList getResourceFilesList(String resourcePath) + throws URISyntaxException, IOException { + try (Stream fileStream = + Files.list(Paths.get(Resources.getResource(resourcePath).toURI()))) { + return fileStream.collect(ImmutableList.toImmutableList()); + } + } + @ClassRule public static LocalRegistry localRegistry = new LocalRegistry(5000); private static final TestBuildLogger logger = new TestBuildLogger(); - @Rule public TemporaryFolder temporaryCacheDirectory = new TemporaryFolder(); - - @Rule public TemporaryFolder temporaryTarOutput = new TemporaryFolder(); + @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - private SourceFilesConfiguration sourceFilesConfiguration; + private ImmutableList fakeLayerConfigurations; @Before public void setUp() throws IOException, URISyntaxException { - sourceFilesConfiguration = - TestSourceFilesConfiguration.builder() - .withClasses() - .withDependencies() - .withSnapshotDependencies() - .withResources() - .build(); + fakeLayerConfigurations = + ImmutableList.of( + LayerConfiguration.builder() + .addEntry(getResourceFilesList("application/dependencies"), "/app/libs/") + .build(), + LayerConfiguration.builder() + .addEntry(getResourceFilesList("application/resources"), "/app/resources/") + .build(), + LayerConfiguration.builder() + .addEntry(getResourceFilesList("application/classes"), "/app/classes/") + .build()); } @Test @@ -72,11 +90,14 @@ public void testSteps_forBuildToDockerRegistry() BuildConfiguration.builder(logger) .setBaseImage(ImageReference.of("gcr.io", "distroless/java", "latest")) .setTargetImage(ImageReference.of("localhost:5000", "testimage", "testtag")) - .setMainClass("HelloWorld") .setJavaArguments(Collections.singletonList("An argument.")) .setExposedPorts( ExposedPortsParser.parse(Arrays.asList("1000", "2000-2002/tcp", "3000/udp"))) .setAllowInsecureRegistries(true) + .setLayerConfigurations(fakeLayerConfigurations) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + Collections.emptyList(), "HelloWorld")) .build()); long lastTime = System.nanoTime(); @@ -110,9 +131,12 @@ public void testSteps_forBuildToDockerRegistry_dockerHubBaseImage() BuildConfiguration.builder(logger) .setBaseImage(ImageReference.parse("openjdk:8-jre-alpine")) .setTargetImage(ImageReference.of("localhost:5000", "testimage", "testtag")) - .setMainClass("HelloWorld") .setJavaArguments(Collections.singletonList("An argument.")) .setAllowInsecureRegistries(true) + .setLayerConfigurations(fakeLayerConfigurations) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + Collections.emptyList(), "HelloWorld")) .build()) .run(); @@ -130,16 +154,18 @@ public void testSteps_forBuildToDockerDaemon() BuildConfiguration.builder(logger) .setBaseImage(ImageReference.of("gcr.io", "distroless/java", "latest")) .setTargetImage(ImageReference.of(null, "testdocker", null)) - .setMainClass("HelloWorld") .setJavaArguments(Collections.singletonList("An argument.")) .setExposedPorts( ExposedPortsParser.parse(Arrays.asList("1000", "2000-2002/tcp", "3000/udp"))) + .setLayerConfigurations(fakeLayerConfigurations) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + Collections.emptyList(), "HelloWorld")) .build(); - Path cacheDirectory = temporaryCacheDirectory.newFolder().toPath(); + Path cacheDirectory = temporaryFolder.newFolder().toPath(); BuildSteps.forBuildToDockerDaemon( buildConfiguration, - sourceFilesConfiguration, new Caches.Initializer(cacheDirectory, false, cacheDirectory, false)) .run(); @@ -164,16 +190,18 @@ public void testSteps_forBuildToTarball() BuildConfiguration.builder(logger) .setBaseImage(ImageReference.of("gcr.io", "distroless/java", "latest")) .setTargetImage(ImageReference.of(null, "testtar", null)) - .setMainClass("HelloWorld") .setJavaArguments(Collections.singletonList("An argument.")) + .setLayerConfigurations(fakeLayerConfigurations) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + Collections.emptyList(), "HelloWorld")) .build(); - Path outputPath = temporaryTarOutput.newFolder().toPath().resolve("test.tar"); - Path cacheDirectory = temporaryCacheDirectory.newFolder().toPath(); + Path outputPath = temporaryFolder.newFolder().toPath().resolve("test.tar"); + Path cacheDirectory = temporaryFolder.newFolder().toPath(); BuildSteps.forBuildToTar( outputPath, buildConfiguration, - sourceFilesConfiguration, new Caches.Initializer(cacheDirectory, false, cacheDirectory, false)) .run(); @@ -183,10 +211,8 @@ public void testSteps_forBuildToTarball() } private BuildSteps getBuildSteps(BuildConfiguration buildConfiguration) throws IOException { - Path cacheDirectory = temporaryCacheDirectory.newFolder().toPath(); + Path cacheDirectory = temporaryFolder.newFolder().toPath(); return BuildSteps.forBuildToDockerRegistry( - buildConfiguration, - sourceFilesConfiguration, - new Caches.Initializer(cacheDirectory, false, cacheDirectory, false)); + buildConfiguration, new Caches.Initializer(cacheDirectory, false, cacheDirectory, false)); } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java index 890b50db2f..4d0412c971 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java @@ -49,16 +49,16 @@ public static class Builder { @Nullable private ImageReference targetImageReference; @Nullable private String targetImageCredentialHelperName; @Nullable private RegistryCredentials knownTargetRegistryCredentials; - @Nullable private String mainClass; - private ImmutableList javaArguments = ImmutableList.of(); - private ImmutableList jvmFlags = ImmutableList.of(); - private ImmutableMap environmentMap = ImmutableMap.of(); - private ImmutableList exposedPorts = ImmutableList.of(); + // TODO: Should rename to not be java-specific. + @Nullable private ImmutableList javaArguments; + @Nullable private ImmutableMap environmentMap; + @Nullable private ImmutableList exposedPorts; private Class targetFormat = V22ManifestTemplate.class; @Nullable private CacheConfiguration applicationLayersCacheConfiguration; @Nullable private CacheConfiguration baseImageLayersCacheConfiguration; private boolean allowInsecureRegistries = false; - @Nullable private LayerConfiguration extraFilesLayerConfiguration; + private ImmutableList layerConfigurations = ImmutableList.of(); + @Nullable private ImmutableList entrypoint; private BuildLogger buildLogger; @@ -98,11 +98,6 @@ public Builder setKnownTargetRegistryCredentials( return this; } - public Builder setMainClass(@Nullable String mainClass) { - this.mainClass = mainClass; - return this; - } - public Builder setJavaArguments(@Nullable List javaArguments) { if (javaArguments != null) { Preconditions.checkArgument(!javaArguments.contains(null)); @@ -111,16 +106,10 @@ public Builder setJavaArguments(@Nullable List javaArguments) { return this; } - public Builder setJvmFlags(@Nullable List jvmFlags) { - if (jvmFlags != null) { - Preconditions.checkArgument(!jvmFlags.contains(null)); - this.jvmFlags = ImmutableList.copyOf(jvmFlags); - } - return this; - } - public Builder setEnvironment(@Nullable Map environmentMap) { - if (environmentMap != null) { + if (environmentMap == null) { + this.environmentMap = null; + } else { Preconditions.checkArgument( !Iterables.any(environmentMap.keySet(), Objects::isNull) && !Iterables.any(environmentMap.values(), Objects::isNull)); @@ -130,7 +119,9 @@ public Builder setEnvironment(@Nullable Map environmentMap) { } public Builder setExposedPorts(@Nullable List exposedPorts) { - if (exposedPorts != null) { + if (exposedPorts == null) { + this.exposedPorts = null; + } else { Preconditions.checkArgument(!exposedPorts.contains(null)); this.exposedPorts = ImmutableList.copyOf(exposedPorts); } @@ -178,14 +169,13 @@ public Builder setAllowInsecureRegistries(boolean allowInsecureRegistries) { } /** - * Sets the {@link LayerConfiguration} for an extra layer. + * Sets the layers to build. * - * @param extraFilesLayerConfiguration the layer configuration for the extra layer + * @param layerConfigurations the configurations for the layers * @return this */ - public Builder setExtraFilesLayerConfiguration( - @Nullable LayerConfiguration extraFilesLayerConfiguration) { - this.extraFilesLayerConfiguration = extraFilesLayerConfiguration; + public Builder setLayerConfigurations(List layerConfigurations) { + this.layerConfigurations = ImmutableList.copyOf(layerConfigurations); return this; } @@ -200,6 +190,22 @@ public Builder setCreationTime(Instant creationTime) { return this; } + /** + * Sets the container entrypoint. + * + * @param entrypoint the tokenized command to run when the container starts + * @return this + */ + public Builder setEntrypoint(@Nullable List entrypoint) { + if (entrypoint == null) { + this.entrypoint = null; + } else { + Preconditions.checkArgument(!entrypoint.contains(null)); + this.entrypoint = ImmutableList.copyOf(entrypoint); + } + return this; + } + /** @return the corresponding build configuration */ public BuildConfiguration build() { // Validates the parameters. @@ -210,13 +216,10 @@ public BuildConfiguration build() { if (targetImageReference == null) { errorMessages.add("target image is required but not set"); } - if (mainClass == null) { - errorMessages.add("main class is required but not set"); - } switch (errorMessages.size()) { case 0: // No errors - if (baseImageReference == null || targetImageReference == null || mainClass == null) { + if (baseImageReference == null || targetImageReference == null) { throw new IllegalStateException("Required fields should not be null"); } if (baseImageReference.usesDefaultTag()) { @@ -234,16 +237,15 @@ public BuildConfiguration build() { targetImageReference, targetImageCredentialHelperName, knownTargetRegistryCredentials, - mainClass, javaArguments, - jvmFlags, environmentMap, exposedPorts, targetFormat, applicationLayersCacheConfiguration, baseImageLayersCacheConfiguration, allowInsecureRegistries, - extraFilesLayerConfiguration); + layerConfigurations, + entrypoint); case 1: throw new IllegalStateException(errorMessages.get(0)); @@ -252,19 +254,8 @@ public BuildConfiguration build() { throw new IllegalStateException(errorMessages.get(0) + " and " + errorMessages.get(1)); default: - // Appends the descriptions in correct grammar. - StringBuilder errorMessage = new StringBuilder(errorMessages.get(0)); - for (int errorMessageIndex = 1; - errorMessageIndex < errorMessages.size(); - errorMessageIndex++) { - if (errorMessageIndex == errorMessages.size() - 1) { - errorMessage.append(", and "); - } else { - errorMessage.append(", "); - } - errorMessage.append(errorMessages.get(errorMessageIndex)); - } - throw new IllegalStateException(errorMessage.toString()); + // Should never reach here. + throw new IllegalStateException(); } } } @@ -294,16 +285,15 @@ public static Builder builder(BuildLogger buildLogger) { private final ImageReference targetImageReference; @Nullable private final String targetImageCredentialHelperName; @Nullable private final RegistryCredentials knownTargetRegistryCredentials; - private final String mainClass; - private final ImmutableList javaArguments; - private final ImmutableList jvmFlags; - private final ImmutableMap environmentMap; - private final ImmutableList exposedPorts; + @Nullable private final ImmutableList javaArguments; + @Nullable private final ImmutableMap environmentMap; + @Nullable private final ImmutableList exposedPorts; private final Class targetFormat; @Nullable private final CacheConfiguration applicationLayersCacheConfiguration; @Nullable private final CacheConfiguration baseImageLayersCacheConfiguration; private final boolean allowInsecureRegistries; - @Nullable private final LayerConfiguration extraFilesLayerConfiguration; + private final ImmutableList layerConfigurations; + @Nullable private final ImmutableList entrypoint; /** Instantiate with {@link Builder#build}. */ private BuildConfiguration( @@ -315,16 +305,15 @@ private BuildConfiguration( ImageReference targetImageReference, @Nullable String targetImageCredentialHelperName, @Nullable RegistryCredentials knownTargetRegistryCredentials, - String mainClass, - ImmutableList javaArguments, - ImmutableList jvmFlags, - ImmutableMap environmentMap, - ImmutableList exposedPorts, + @Nullable ImmutableList javaArguments, + @Nullable ImmutableMap environmentMap, + @Nullable ImmutableList exposedPorts, Class targetFormat, @Nullable CacheConfiguration applicationLayersCacheConfiguration, @Nullable CacheConfiguration baseImageLayersCacheConfiguration, boolean allowInsecureRegistries, - @Nullable LayerConfiguration extraFilesLayerConfiguration) { + ImmutableList layerConfigurations, + @Nullable ImmutableList entrypoint) { this.buildLogger = buildLogger; this.creationTime = creationTime; this.baseImageReference = baseImageReference; @@ -333,16 +322,15 @@ private BuildConfiguration( this.targetImageReference = targetImageReference; this.targetImageCredentialHelperName = targetImageCredentialHelperName; this.knownTargetRegistryCredentials = knownTargetRegistryCredentials; - this.mainClass = mainClass; this.javaArguments = javaArguments; - this.jvmFlags = jvmFlags; this.environmentMap = environmentMap; this.exposedPorts = exposedPorts; this.targetFormat = targetFormat; this.applicationLayersCacheConfiguration = applicationLayersCacheConfiguration; this.baseImageLayersCacheConfiguration = baseImageLayersCacheConfiguration; this.allowInsecureRegistries = allowInsecureRegistries; - this.extraFilesLayerConfiguration = extraFilesLayerConfiguration; + this.layerConfigurations = layerConfigurations; + this.entrypoint = entrypoint; } public BuildLogger getBuildLogger() { @@ -405,22 +393,32 @@ public RegistryCredentials getKnownTargetRegistryCredentials() { return knownTargetRegistryCredentials; } - public String getMainClass() { - return mainClass; - } - + /** + * Gets the arguments to pass to the entrypoint. + * + * @return the list of arguments, or {@code null} if not set + */ + @Nullable public ImmutableList getJavaArguments() { return javaArguments; } - public ImmutableList getJvmFlags() { - return jvmFlags; - } - + /** + * Gets the map from environment variable names to values for the container. + * + * @return the map of environment variables, or {@code null} if not set + */ + @Nullable public ImmutableMap getEnvironment() { return environmentMap; } + /** + * Gets the ports to expose on the container. + * + * @return the list of exposed ports, or {@code null} if not set + */ + @Nullable public ImmutableList getExposedPorts() { return exposedPorts; } @@ -460,12 +458,21 @@ public boolean getAllowInsecureRegistries() { } /** - * Gets the {@link LayerConfiguration} for an extra layer. + * Gets the configurations for building the layers. + * + * @return the list of layer configurations + */ + public ImmutableList getLayerConfigurations() { + return layerConfigurations; + } + + /** + * Gets the container entrypoint. * - * @return the layer configuration + * @return the list of entrypoint tokens, or {@code null} if not set */ @Nullable - public LayerConfiguration getExtraFilesLayerConfiguration() { - return extraFilesLayerConfiguration; + public ImmutableList getEntrypoint() { + return entrypoint; } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildSteps.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildSteps.java index c100eca3da..f74032c76b 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildSteps.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildSteps.java @@ -23,7 +23,6 @@ import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.cache.Caches; -import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.ExecutionException; @@ -63,18 +62,14 @@ private interface StepsRunnerConsumer { * All the steps to build an image to a Docker registry. * * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @param cachesInitializer the {@link Caches.Initializer} used to setup the cache * @return a new {@link BuildSteps} for building to a registry */ public static BuildSteps forBuildToDockerRegistry( - BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, - Caches.Initializer cachesInitializer) { + BuildConfiguration buildConfiguration, Caches.Initializer cachesInitializer) { return new BuildSteps( DESCRIPTION_FOR_DOCKER_REGISTRY, buildConfiguration, - sourceFilesConfiguration, cachesInitializer, String.format( STARTUP_MESSAGE_FORMAT_FOR_DOCKER_REGISTRY, @@ -90,7 +85,7 @@ public static BuildSteps forBuildToDockerRegistry( .runPullAndCacheBaseImageLayersStep() .runPushBaseImageLayersStep() .runBuildAndCacheApplicationLayerSteps() - .runBuildImageStep(getEntrypoint(buildConfiguration, sourceFilesConfiguration)) + .runBuildImageStep() .runPushContainerConfigurationStep() .runPushApplicationLayersStep() .runFinalizingPushStep() @@ -102,18 +97,14 @@ public static BuildSteps forBuildToDockerRegistry( * All the steps to build to Docker daemon * * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @param cachesInitializer the {@link Caches.Initializer} used to setup the cache * @return a new {@link BuildSteps} for building to a Docker daemon */ public static BuildSteps forBuildToDockerDaemon( - BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, - Caches.Initializer cachesInitializer) { + BuildConfiguration buildConfiguration, Caches.Initializer cachesInitializer) { return new BuildSteps( DESCRIPTION_FOR_DOCKER_DAEMON, buildConfiguration, - sourceFilesConfiguration, cachesInitializer, String.format( STARTUP_MESSAGE_FORMAT_FOR_DOCKER_DAEMON, buildConfiguration.getTargetImageReference()), @@ -124,7 +115,7 @@ public static BuildSteps forBuildToDockerDaemon( .runPullBaseImageStep() .runPullAndCacheBaseImageLayersStep() .runBuildAndCacheApplicationLayerSteps() - .runBuildImageStep(getEntrypoint(buildConfiguration, sourceFilesConfiguration)) + .runBuildImageStep() .runFinalizingBuildStep() .runLoadDockerStep() .waitOnLoadDockerStep()); @@ -135,19 +126,16 @@ public static BuildSteps forBuildToDockerDaemon( * * @param outputPath the path to output the tarball to * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @param cachesInitializer the {@link Caches.Initializer} used to setup the cache * @return a new {@link BuildSteps} for building a tarball */ public static BuildSteps forBuildToTar( Path outputPath, BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, Caches.Initializer cachesInitializer) { return new BuildSteps( DESCRIPTION_FOR_TARBALL, buildConfiguration, - sourceFilesConfiguration, cachesInitializer, String.format(STARTUP_MESSAGE_FORMAT_FOR_TARBALL, outputPath.toString()), String.format(SUCCESS_MESSAGE_FORMAT_FOR_TARBALL, outputPath.toString()), @@ -156,24 +144,14 @@ public static BuildSteps forBuildToTar( .runPullBaseImageStep() .runPullAndCacheBaseImageLayersStep() .runBuildAndCacheApplicationLayerSteps() - .runBuildImageStep(getEntrypoint(buildConfiguration, sourceFilesConfiguration)) + .runBuildImageStep() .runFinalizingBuildStep() .runWriteTarFileStep(outputPath) .waitOnWriteTarFileStep()); } - /** Creates the container entrypoint for a given configuration. */ - private static ImmutableList getEntrypoint( - BuildConfiguration buildConfiguration, SourceFilesConfiguration sourceFilesConfiguration) { - return EntrypointBuilder.makeEntrypoint( - sourceFilesConfiguration, - buildConfiguration.getJvmFlags(), - buildConfiguration.getMainClass()); - } - private final String description; private final BuildConfiguration buildConfiguration; - private final SourceFilesConfiguration sourceFilesConfiguration; private final Caches.Initializer cachesInitializer; private final String startupMessage; private final String successMessage; @@ -188,14 +166,12 @@ private static ImmutableList getEntrypoint( private BuildSteps( String description, BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, Caches.Initializer cachesInitializer, String startupMessage, String successMessage, StepsRunnerConsumer stepsRunnerConsumer) { this.description = description; this.buildConfiguration = buildConfiguration; - this.sourceFilesConfiguration = sourceFilesConfiguration; this.cachesInitializer = cachesInitializer; this.startupMessage = startupMessage; this.successMessage = successMessage; @@ -206,10 +182,6 @@ public BuildConfiguration getBuildConfiguration() { return buildConfiguration; } - public SourceFilesConfiguration getSourceFilesConfiguration() { - return sourceFilesConfiguration; - } - public String getStartupMessage() { return startupMessage; } @@ -229,11 +201,7 @@ public void run() Cache applicationLayersCache = caches.getApplicationCache(); StepsRunner stepsRunner = - new StepsRunner( - buildConfiguration, - sourceFilesConfiguration, - baseImageLayersCache, - applicationLayersCache); + new StepsRunner(buildConfiguration, baseImageLayersCache, applicationLayersCache); stepsRunnerConsumer.accept(stepsRunner); // Writes the cached layers to the cache metadata. @@ -246,8 +214,6 @@ public void run() buildConfiguration.getBuildLogger().lifecycle(""); buildConfiguration .getBuildLogger() - .lifecycle( - "Container entrypoint set to " - + getEntrypoint(buildConfiguration, sourceFilesConfiguration)); + .lifecycle("Container entrypoint set to " + buildConfiguration.getEntrypoint()); } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/EntrypointBuilder.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/EntrypointBuilder.java deleted file mode 100644 index 2c1c06ffea..0000000000 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/EntrypointBuilder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2018 Google LLC. All rights reserved. - * - * 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.builder; - -import com.google.common.collect.ImmutableList; -import java.util.List; - -/** Builds an image entrypoint for the Java application. */ -public class EntrypointBuilder { - - /** - * Builds the container entrypoint. - * - *

The entrypoint is {@code java [jvm flags] -cp [classpaths] [main class]}. - * - * @param sourceFilesConfiguration configuration defining where files are copied onto the image - * @param jvmFlags the JVM flags to start the container with - * @param mainClass the name of the main class to run on startup - * @return a list of the entrypoint tokens - */ - public static ImmutableList makeEntrypoint( - SourceFilesConfiguration sourceFilesConfiguration, List jvmFlags, String mainClass) { - ImmutableList classPaths = - ImmutableList.of( - sourceFilesConfiguration.getDependenciesPathOnImage() + "*", - sourceFilesConfiguration.getResourcesPathOnImage(), - sourceFilesConfiguration.getClassesPathOnImage()); - - String classPathsString = String.join(":", classPaths); - - ImmutableList.Builder entrypointBuilder = - ImmutableList.builderWithExpectedSize(4 + jvmFlags.size()); - entrypointBuilder.add("java"); - entrypointBuilder.addAll(jvmFlags); - entrypointBuilder.add("-cp"); - entrypointBuilder.add(classPathsString); - entrypointBuilder.add(mainClass); - return entrypointBuilder.build(); - } - - private EntrypointBuilder() {} -} diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/SourceFilesConfiguration.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/SourceFilesConfiguration.java deleted file mode 100644 index 695227c751..0000000000 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/SourceFilesConfiguration.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 Google LLC. All rights reserved. - * - * 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.builder; - -import com.google.common.collect.ImmutableList; -import java.nio.file.Path; - -/** - * Immutable configuration that defines where the source files for each of the application layers - * are. - */ -public interface SourceFilesConfiguration { - - String DEFAULT_DEPENDENCIES_PATH_ON_IMAGE = "/app/libs/"; - String DEFAULT_RESOURCES_PATH_ON_IMAGE = "/app/resources/"; - String DEFAULT_CLASSES_PATH_ON_IMAGE = "/app/classes/"; - - /** - * @return the source files for the dependencies layer. These files should be in a deterministic - * order. - */ - ImmutableList getDependenciesFiles(); - - /** - * @return the source files for snapshot dependencies. These files should be in a deterministic - * order - */ - ImmutableList getSnapshotDependenciesFiles(); - - /** - * @return the source files for the resources layer. These files should be in a deterministic - * order. - */ - ImmutableList getResourcesFiles(); - - /** - * @return the source files for the classes layer. These files should be in a deterministic order. - */ - ImmutableList getClassesFiles(); - - /** - * @return the Unix-style path where the dependencies source files are placed in the container - * filesystem. Must end with slash. This includes both regular and snapshot dependencies. - */ - String getDependenciesPathOnImage(); - - /** - * @return the Unix-style path where the resources source files are placed in the container - * filesystem. Must end with slash. - */ - String getResourcesPathOnImage(); - - /** - * @return the Unix-style path where the classes source files are placed in the container - * filesystem. Must end with slash. - */ - String getClassesPathOnImage(); -} diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStep.java index 0536ea01f6..d6ad5a23de 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStep.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStep.java @@ -19,7 +19,6 @@ import com.google.cloud.tools.jib.Timer; import com.google.cloud.tools.jib.async.AsyncStep; import com.google.cloud.tools.jib.builder.BuildConfiguration; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.cache.Cache; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.cache.CacheReader; @@ -47,71 +46,28 @@ class BuildAndCacheApplicationLayerStep static ImmutableList makeList( ListeningExecutorService listeningExecutorService, BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, Cache cache) { try (Timer ignored = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) { - ImmutableList.Builder buildLayerStepsBuilder = - ImmutableList.builder() - .add( - new BuildAndCacheApplicationLayerStep( - "dependencies", - listeningExecutorService, - buildConfiguration, - LayerConfiguration.builder() - .addEntry( - sourceFilesConfiguration.getDependenciesFiles(), - sourceFilesConfiguration.getDependenciesPathOnImage()) - .build(), - cache)) - .add( - new BuildAndCacheApplicationLayerStep( - "resources", - listeningExecutorService, - buildConfiguration, - LayerConfiguration.builder() - .addEntry( - sourceFilesConfiguration.getResourcesFiles(), - sourceFilesConfiguration.getResourcesPathOnImage()) - .build(), - cache)) - .add( - new BuildAndCacheApplicationLayerStep( - "classes", - listeningExecutorService, - buildConfiguration, - LayerConfiguration.builder() - .addEntry( - sourceFilesConfiguration.getClassesFiles(), - sourceFilesConfiguration.getClassesPathOnImage()) - .build(), - cache)); - - // Adds a snapshot dependencies layer, if snapshot files present. - if (!sourceFilesConfiguration.getSnapshotDependenciesFiles().isEmpty()) { - buildLayerStepsBuilder.add( + ImmutableList.Builder buildAndCacheApplicationLayerSteps = + ImmutableList.builderWithExpectedSize(buildConfiguration.getLayerConfigurations().size()); + for (LayerConfiguration layerConfiguration : buildConfiguration.getLayerConfigurations()) { + // Skips the layer if empty. + if (layerConfiguration + .getLayerEntries() + .stream() + .allMatch(layerEntry -> layerEntry.getSourceFiles().isEmpty())) { + continue; + } + + buildAndCacheApplicationLayerSteps.add( new BuildAndCacheApplicationLayerStep( - "snapshot-dependencies", + layerConfiguration.getLabel(), listeningExecutorService, buildConfiguration, - LayerConfiguration.builder() - .addEntry( - sourceFilesConfiguration.getSnapshotDependenciesFiles(), - sourceFilesConfiguration.getDependenciesPathOnImage()) - .build(), + layerConfiguration, cache)); } - // Adds the extra layer to be built, if configured. - if (buildConfiguration.getExtraFilesLayerConfiguration() != null) { - buildLayerStepsBuilder.add( - new BuildAndCacheApplicationLayerStep( - "extra files", - listeningExecutorService, - buildConfiguration, - buildConfiguration.getExtraFilesLayerConfiguration(), - cache)); - } - - return buildLayerStepsBuilder.build(); + return buildAndCacheApplicationLayerSteps.build(); } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildImageStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildImageStep.java index 94a48e2108..4e7273bd79 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildImageStep.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/BuildImageStep.java @@ -41,7 +41,6 @@ class BuildImageStep private final BuildConfiguration buildConfiguration; private final PullAndCacheBaseImageLayersStep pullAndCacheBaseImageLayersStep; private final ImmutableList buildAndCacheApplicationLayerSteps; - private final ImmutableList entrypoint; private final ListeningExecutorService listeningExecutorService; private final ListenableFuture>> listenableFuture; @@ -50,13 +49,11 @@ class BuildImageStep ListeningExecutorService listeningExecutorService, BuildConfiguration buildConfiguration, PullAndCacheBaseImageLayersStep pullAndCacheBaseImageLayersStep, - ImmutableList buildAndCacheApplicationLayerSteps, - ImmutableList entrypoint) { + ImmutableList buildAndCacheApplicationLayerSteps) { this.listeningExecutorService = listeningExecutorService; this.buildConfiguration = buildConfiguration; this.pullAndCacheBaseImageLayersStep = pullAndCacheBaseImageLayersStep; this.buildAndCacheApplicationLayerSteps = buildAndCacheApplicationLayerSteps; - this.entrypoint = entrypoint; listenableFuture = Futures.whenAllSucceed(pullAndCacheBaseImageLayersStep.getFuture()) @@ -101,7 +98,7 @@ private Image afterCachedLayersSteps() } imageBuilder.setCreated(buildConfiguration.getCreationTime()); imageBuilder.setEnvironment(buildConfiguration.getEnvironment()); - imageBuilder.setEntrypoint(entrypoint); + imageBuilder.setEntrypoint(buildConfiguration.getEntrypoint()); imageBuilder.setJavaArguments(buildConfiguration.getJavaArguments()); imageBuilder.setExposedPorts(buildConfiguration.getExposedPorts()); diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/StepsRunner.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/StepsRunner.java index c400b43019..99b8091a45 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/StepsRunner.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/StepsRunner.java @@ -19,7 +19,6 @@ import com.google.cloud.tools.jib.async.AsyncSteps; import com.google.cloud.tools.jib.async.NonBlockingSteps; import com.google.cloud.tools.jib.builder.BuildConfiguration; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.cache.Cache; import com.google.cloud.tools.jib.cache.CachedLayer; import com.google.cloud.tools.jib.cache.CachedLayerWithMetadata; @@ -47,7 +46,6 @@ public class StepsRunner { private final ListeningExecutorService listeningExecutorService; private final BuildConfiguration buildConfiguration; - private final SourceFilesConfiguration sourceFilesConfiguration; private final Cache baseLayersCache; private final Cache applicationLayersCache; @@ -68,12 +66,8 @@ public class StepsRunner { @Nullable private WriteTarFileStep writeTarFileStep; public StepsRunner( - BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration, - Cache baseLayersCache, - Cache applicationLayersCache) { + BuildConfiguration buildConfiguration, Cache baseLayersCache, Cache applicationLayersCache) { this.buildConfiguration = buildConfiguration; - this.sourceFilesConfiguration = sourceFilesConfiguration; this.baseLayersCache = baseLayersCache; this.applicationLayersCache = applicationLayersCache; @@ -128,21 +122,17 @@ public StepsRunner runPushBaseImageLayersStep() { public StepsRunner runBuildAndCacheApplicationLayerSteps() { buildAndCacheApplicationLayerSteps = BuildAndCacheApplicationLayerStep.makeList( - listeningExecutorService, - buildConfiguration, - sourceFilesConfiguration, - applicationLayersCache); + listeningExecutorService, buildConfiguration, applicationLayersCache); return this; } - public StepsRunner runBuildImageStep(ImmutableList entrypoint) { + public StepsRunner runBuildImageStep() { buildImageStep = new BuildImageStep( listeningExecutorService, buildConfiguration, Preconditions.checkNotNull(pullAndCacheBaseImageLayersStep), - Preconditions.checkNotNull(buildAndCacheApplicationLayerSteps), - entrypoint); + Preconditions.checkNotNull(buildAndCacheApplicationLayerSteps)); return this; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/configuration/LayerConfiguration.java b/jib-core/src/main/java/com/google/cloud/tools/jib/configuration/LayerConfiguration.java index e420381c39..b87774e6a8 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/configuration/LayerConfiguration.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/configuration/LayerConfiguration.java @@ -29,9 +29,21 @@ public class LayerConfiguration { public static class Builder { private final ImmutableList.Builder layerEntries = ImmutableList.builder(); + private String label = ""; private Builder() {} + /** + * Sets a label for this layer. + * + * @param label the label + * @return this + */ + public Builder setLabel(String label) { + this.label = label; + return this; + } + /** * Adds an entry to the layer. * @@ -56,7 +68,7 @@ public Builder addEntry(List sourceFiles, String destinationOnImage) { * @return the built {@link LayerConfiguration} */ public LayerConfiguration build() { - return new LayerConfiguration(layerEntries.build()); + return new LayerConfiguration(label, layerEntries.build()); } } @@ -65,16 +77,23 @@ public static Builder builder() { } private final ImmutableList layerEntries; + private final String label; /** * Constructs a new layer configuration. * + * @param label an optional label for the layer * @param layerEntries the list of {@link LayerEntry}s */ - private LayerConfiguration(ImmutableList layerEntries) { + private LayerConfiguration(String label, ImmutableList layerEntries) { + this.label = label; this.layerEntries = layerEntries; } + public String getLabel() { + return label; + } + public ImmutableList getLayerEntries() { return layerEntries; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/docker/DockerContextGenerator.java b/jib-core/src/main/java/com/google/cloud/tools/jib/docker/DockerContextGenerator.java index 83275229f2..2840a007c7 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/docker/DockerContextGenerator.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/docker/DockerContextGenerator.java @@ -18,11 +18,12 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.cloud.tools.jib.builder.EntrypointBuilder; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.filesystem.FileOperations; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.io.MoreFiles; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -36,17 +37,67 @@ /** * Generates a Docker context that mimics how Jib builds the image. * - *

The image consists of a base image layer and three application layers under the directories: + *

The image consists of a base image layer and 5 application layers under the directories: * *

    - *
  • libs/ (dependency jars) - *
  • resources/ - *
  • classes + *
  • {@code libs/} (dependency jars) + *
  • {@code snapshot-libs/} (snapshot dependency jars) + *
  • {@code resources/} (resource files) + *
  • {@code classes/} ({@code .class} files) + *
  • {@code root/} (extra files) *
+ * + * Empty application layers are omitted. */ public class DockerContextGenerator { - private final SourceFilesConfiguration sourceFilesConfiguration; + private static final String DEPENDENCIES_LAYER_DIRECTORY = "libs"; + private static final String SNAPSHOT_DEPENDENCIES_LAYER_DIRECTORY = "snapshot-libs"; + private static final String RESOURCES_LAYER_DIRECTORY = "resources"; + private static final String CLASSES_LAYER_DIRECTORY = "classes"; + private static final String EXTRA_FILES_LAYER_DIRECTORY = "root"; + + /** Represents a Dockerfile {@code COPY} directive. */ + private static class CopyDirective { + + /** The source files to put into the context. */ + private final ImmutableList sourceFiles; + + /** The directory in the context to put the source files for the layer */ + private final String directoryInContext; + + /** The extraction path in the image. */ + private final String extractionPath; + + private CopyDirective( + ImmutableList sourceFiles, String directoryInContext, String extractionPath) { + this.sourceFiles = sourceFiles; + this.directoryInContext = directoryInContext; + this.extractionPath = extractionPath; + } + } + + /** + * Adds a copy directive for the {@code layerEntry} if it's not empty. + * + * @param listBuilder the {@link ImmutableList.Builder} to add to + * @param layerEntry the layer entry + * @param directoryInContext the directory in the context to put the source files for the layer + */ + private static void addIfNotEmpty( + ImmutableList.Builder listBuilder, + LayerEntry layerEntry, + String directoryInContext) { + if (layerEntry.getSourceFiles().isEmpty()) { + return; + } + + listBuilder.add( + new CopyDirective( + layerEntry.getSourceFiles(), directoryInContext, layerEntry.getExtractionPath())); + } + + private final ImmutableList copyDirectives; @Nullable private String baseImage; private List jvmFlags = Collections.emptyList(); @@ -54,8 +105,23 @@ public class DockerContextGenerator { private List javaArguments = Collections.emptyList(); private List exposedPorts = Collections.emptyList(); - public DockerContextGenerator(SourceFilesConfiguration sourceFilesConfiguration) { - this.sourceFilesConfiguration = sourceFilesConfiguration; + // TODO: Just take the LayerConfigurations. + public DockerContextGenerator( + LayerEntry dependenciesLayerEntry, + LayerEntry snapshotDependenciesLayerEntry, + LayerEntry resourcesLayerEntry, + LayerEntry classesLayerEntry, + LayerEntry extraFilesLayerEntry) { + ImmutableList.Builder copyDirectivesBuilder = ImmutableList.builder(); + addIfNotEmpty(copyDirectivesBuilder, dependenciesLayerEntry, DEPENDENCIES_LAYER_DIRECTORY); + addIfNotEmpty( + copyDirectivesBuilder, + snapshotDependenciesLayerEntry, + SNAPSHOT_DEPENDENCIES_LAYER_DIRECTORY); + addIfNotEmpty(copyDirectivesBuilder, resourcesLayerEntry, RESOURCES_LAYER_DIRECTORY); + addIfNotEmpty(copyDirectivesBuilder, classesLayerEntry, CLASSES_LAYER_DIRECTORY); + addIfNotEmpty(copyDirectivesBuilder, extraFilesLayerEntry, EXTRA_FILES_LAYER_DIRECTORY); + copyDirectives = copyDirectivesBuilder.build(); } /** @@ -134,22 +200,14 @@ public void generate(Path targetDirectory) throws IOException { Files.createDirectory(targetDirectory); - // Creates the directories. - Path dependenciesDir = targetDirectory.resolve("libs"); - Path snapshotDependenciesDir = targetDirectory.resolve("snapshot-libs"); - Path resourcesDIr = targetDirectory.resolve("resources"); - Path classesDir = targetDirectory.resolve("classes"); - Files.createDirectory(dependenciesDir); - Files.createDirectories(snapshotDependenciesDir); - Files.createDirectory(resourcesDIr); - Files.createDirectory(classesDir); - - // Copies dependencies. - FileOperations.copy(sourceFilesConfiguration.getDependenciesFiles(), dependenciesDir); - FileOperations.copy( - sourceFilesConfiguration.getSnapshotDependenciesFiles(), snapshotDependenciesDir); - FileOperations.copy(sourceFilesConfiguration.getResourcesFiles(), resourcesDIr); - FileOperations.copy(sourceFilesConfiguration.getClassesFiles(), classesDir); + for (CopyDirective copyDirective : copyDirectives) { + // Creates the directories. + Path directoryInContext = targetDirectory.resolve(copyDirective.directoryInContext); + Files.createDirectory(directoryInContext); + + // Copies dependencies. + FileOperations.copy(copyDirective.sourceFiles, directoryInContext); + } // Creates the Dockerfile. Files.write( @@ -163,8 +221,10 @@ public void generate(Path targetDirectory) throws IOException { * FROM [base image] * * COPY libs [path/to/dependencies] + * COPY snapshot-libs [path/to/dependencies] * COPY resources [path/to/resources] * COPY classes [path/to/classes] + * COPY root [path/to/classes] * * EXPOSE [port] * [More EXPOSE instructions, if necessary] @@ -178,18 +238,15 @@ public void generate(Path targetDirectory) throws IOException { String makeDockerfile() throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); StringBuilder dockerfile = new StringBuilder(); - dockerfile - .append("FROM ") - .append(Preconditions.checkNotNull(baseImage)) - .append("\n\nCOPY libs ") - .append(sourceFilesConfiguration.getDependenciesPathOnImage()) - .append("\nCOPY snapshot-libs ") - .append(sourceFilesConfiguration.getDependenciesPathOnImage()) - .append("\nCOPY resources ") - .append(sourceFilesConfiguration.getResourcesPathOnImage()) - .append("\nCOPY classes ") - .append(sourceFilesConfiguration.getClassesPathOnImage()) - .append("\n"); + dockerfile.append("FROM ").append(Preconditions.checkNotNull(baseImage)).append("\n"); + for (CopyDirective copyDirective : copyDirectives) { + dockerfile + .append("\nCOPY ") + .append(copyDirective.directoryInContext) + .append(" ") + .append(copyDirective.extractionPath); + } + dockerfile.append("\n"); for (String port : exposedPorts) { dockerfile.append("\nEXPOSE ").append(port); } @@ -197,7 +254,7 @@ String makeDockerfile() throws JsonProcessingException { .append("\nENTRYPOINT ") .append( objectMapper.writeValueAsString( - EntrypointBuilder.makeEntrypoint(sourceFilesConfiguration, jvmFlags, mainClass))) + JavaEntrypointConstructor.makeDefaultEntrypoint(jvmFlags, mainClass))) .append("\nCMD ") .append(objectMapper.writeValueAsString(javaArguments)); return dockerfile.toString(); diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/BuildStepsRunner.java b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/BuildStepsRunner.java index 79aa0a1d49..b45f888f91 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/BuildStepsRunner.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/BuildStepsRunner.java @@ -21,13 +21,14 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.builder.BuildLogger; import com.google.cloud.tools.jib.builder.BuildSteps; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.cache.Caches; import com.google.cloud.tools.jib.cache.Caches.Initializer; import com.google.cloud.tools.jib.configuration.CacheConfiguration; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.cloud.tools.jib.registry.InsecureRegistryException; import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException; import com.google.cloud.tools.jib.registry.RegistryCredentialsNotSentException; @@ -48,32 +49,28 @@ public class BuildStepsRunner { * Creates a runner to build an image. Creates a directory for the cache, if needed. * * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @return a {@link BuildStepsRunner} for building to a registry * @throws CacheDirectoryCreationException if the {@code cacheDirectory} could not be created */ - public static BuildStepsRunner forBuildImage( - BuildConfiguration buildConfiguration, SourceFilesConfiguration sourceFilesConfiguration) + public static BuildStepsRunner forBuildImage(BuildConfiguration buildConfiguration) throws CacheDirectoryCreationException { return new BuildStepsRunner( BuildSteps.forBuildToDockerRegistry( - buildConfiguration, sourceFilesConfiguration, getCacheInitializer(buildConfiguration))); + buildConfiguration, getCacheInitializer(buildConfiguration))); } /** * Creates a runner to build to the Docker daemon. Creates a directory for the cache, if needed. * * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @return a {@link BuildStepsRunner} for building to a Docker daemon * @throws CacheDirectoryCreationException if the {@code cacheDirectory} could not be created */ - public static BuildStepsRunner forBuildToDockerDaemon( - BuildConfiguration buildConfiguration, SourceFilesConfiguration sourceFilesConfiguration) + public static BuildStepsRunner forBuildToDockerDaemon(BuildConfiguration buildConfiguration) throws CacheDirectoryCreationException { return new BuildStepsRunner( BuildSteps.forBuildToDockerDaemon( - buildConfiguration, sourceFilesConfiguration, getCacheInitializer(buildConfiguration))); + buildConfiguration, getCacheInitializer(buildConfiguration))); } /** @@ -81,21 +78,14 @@ public static BuildStepsRunner forBuildToDockerDaemon( * * @param outputPath the path to output the tarball to * @param buildConfiguration the configuration parameters for the build - * @param sourceFilesConfiguration the source/destination file configuration for the image * @return a {@link BuildStepsRunner} for building a tarball * @throws CacheDirectoryCreationException if the {@code cacheDirectory} could not be created */ - public static BuildStepsRunner forBuildTar( - Path outputPath, - BuildConfiguration buildConfiguration, - SourceFilesConfiguration sourceFilesConfiguration) + public static BuildStepsRunner forBuildTar(Path outputPath, BuildConfiguration buildConfiguration) throws CacheDirectoryCreationException { return new BuildStepsRunner( BuildSteps.forBuildToTar( - outputPath, - buildConfiguration, - sourceFilesConfiguration, - getCacheInitializer(buildConfiguration))); + outputPath, buildConfiguration, getCacheInitializer(buildConfiguration))); } // TODO: Move this up to somewhere where defaults for cache location are provided and ownership is @@ -168,6 +158,13 @@ private static void handleRegistryUnauthorizedException( } } + private static String capitalizeFirstLetter(String string) { + if (string.length() == 0) { + return string; + } + return Character.toUpperCase(string.charAt(0)) + string.substring(1); + } + private final BuildSteps buildSteps; @VisibleForTesting @@ -192,23 +189,16 @@ public void build(HelpfulSuggestions helpfulSuggestions) throws BuildStepsExecut // Logs the different source files used. buildLogger.info("Containerizing application with the following files:"); - buildLogger.info("\tClasses:"); - buildSteps - .getSourceFilesConfiguration() - .getClassesFiles() - .forEach(classesFile -> buildLogger.info("\t\t" + classesFile)); - - buildLogger.info("\tResources:"); - buildSteps - .getSourceFilesConfiguration() - .getResourcesFiles() - .forEach(resourceFile -> buildLogger.info("\t\t" + resourceFile)); - - buildLogger.info("\tDependencies:"); - buildSteps - .getSourceFilesConfiguration() - .getDependenciesFiles() - .forEach(dependencyFile -> buildLogger.info("\t\t" + dependencyFile)); + for (LayerConfiguration layerConfiguration : + buildSteps.getBuildConfiguration().getLayerConfigurations()) { + buildLogger.info("\t" + capitalizeFirstLetter(layerConfiguration.getLabel()) + ":"); + + for (LayerEntry layerEntry : layerConfiguration.getLayerEntries()) { + for (Path sourceFile : layerEntry.getSourceFiles()) { + buildLogger.info("\t\t" + sourceFile); + } + } + } buildSteps.run(); diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructor.java b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructor.java new file mode 100644 index 0000000000..d18fcf09eb --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructor.java @@ -0,0 +1,64 @@ +/* + * Copyright 2018 Google LLC. All rights reserved. + * + * 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.frontend; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** Constructs an image entrypoint for the Java application. */ +public class JavaEntrypointConstructor { + + public static final String DEFAULT_DEPENDENCIES_PATH_ON_IMAGE = "/app/libs/"; + public static final String DEFAULT_RESOURCES_PATH_ON_IMAGE = "/app/resources/"; + public static final String DEFAULT_CLASSES_PATH_ON_IMAGE = "/app/classes/"; + + public static List makeDefaultEntrypoint(List jvmFlags, String mainClass) { + return makeEntrypoint( + Arrays.asList( + DEFAULT_DEPENDENCIES_PATH_ON_IMAGE + "*", + DEFAULT_RESOURCES_PATH_ON_IMAGE, + DEFAULT_CLASSES_PATH_ON_IMAGE), + jvmFlags, + mainClass); + } + + /** + * Constructs the container entrypoint. + * + *

The entrypoint is {@code java [jvm flags] -cp [classpaths] [main class]}. + * + * @param classpathElements paths to add to the classpath (will be separated by {@code :} + * @param jvmFlags the JVM flags to start the container with + * @param mainClass the name of the main class to run on startup + * @return a list of the entrypoint tokens + */ + public static List makeEntrypoint( + List classpathElements, List jvmFlags, String mainClass) { + String classpathString = String.join(":", classpathElements); + + List entrypointBuilder = new ArrayList<>(4 + jvmFlags.size()); + entrypointBuilder.add("java"); + entrypointBuilder.addAll(jvmFlags); + entrypointBuilder.add("-cp"); + entrypointBuilder.add(classpathString); + entrypointBuilder.add(mainClass); + return entrypointBuilder; + } + + private JavaEntrypointConstructor() {} +} diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/MainClassFinder.java b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/MainClassFinder.java index 42babaa9da..009ae52e68 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/MainClassFinder.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/MainClassFinder.java @@ -21,6 +21,7 @@ import com.google.cloud.tools.jib.filesystem.DirectoryWalker; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Modifier; @@ -74,9 +75,11 @@ public static String resolveMainClass( try { // Adds each file in the classes output directory to the classes files list. - Set visitedRoots = new HashSet<>(); + ImmutableList classesFiles = + projectProperties.getClassesLayerEntry().getSourceFiles(); List mainClasses = new ArrayList<>(); - for (Path classPath : projectProperties.getSourceFilesConfiguration().getClassesFiles()) { + Set visitedRoots = new HashSet<>(); + for (Path classPath : classesFiles) { Path root = classPath.getParent(); if (visitedRoots.contains(root)) { continue; diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/ProjectProperties.java b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/ProjectProperties.java index 1ddfa8460c..6afd732de6 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/ProjectProperties.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/frontend/ProjectProperties.java @@ -17,7 +17,9 @@ package com.google.cloud.tools.jib.frontend; import com.google.cloud.tools.jib.builder.BuildLogger; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; +import com.google.cloud.tools.jib.image.LayerEntry; +import com.google.common.collect.ImmutableList; import java.nio.file.Path; import javax.annotation.Nullable; @@ -31,7 +33,7 @@ public interface ProjectProperties { String getPluginName(); - SourceFilesConfiguration getSourceFilesConfiguration(); + ImmutableList getLayerConfigurations(); Path getCacheDirectory(); @@ -41,6 +43,16 @@ public interface ProjectProperties { @Nullable String getMainClassFromJar(); + LayerEntry getDependenciesLayerEntry(); + + LayerEntry getSnapshotDependenciesLayerEntry(); + + LayerEntry getResourcesLayerEntry(); + + LayerEntry getClassesLayerEntry(); + + LayerEntry getExtraFilesLayerEntry(); + /** * @param prefix the prefix message for the {@link HelpfulSuggestions}. * @return a {@link HelpfulSuggestions} instance for main class inference failure. diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/image/Image.java b/jib-core/src/main/java/com/google/cloud/tools/jib/image/Image.java index b9d4c56384..58704e2b7a 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/image/Image.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/image/Image.java @@ -30,12 +30,12 @@ public class Image { public static class Builder { private final ImageLayers.Builder imageLayersBuilder = ImageLayers.builder(); - private final ImmutableList.Builder environmentBuilder = ImmutableList.builder(); + private ImmutableList.Builder environmentBuilder = ImmutableList.builder(); @Nullable private Instant created; - private ImmutableList entrypoint = ImmutableList.of(); - private ImmutableList javaArguments = ImmutableList.of(); - private ImmutableList exposedPorts = ImmutableList.of(); + @Nullable private ImmutableList entrypoint; + @Nullable private ImmutableList javaArguments; + @Nullable private ImmutableList exposedPorts; /** * Sets the image creation time. @@ -54,9 +54,13 @@ public Builder setCreated(Instant created) { * @param environment the map of environment variables * @return this */ - public Builder setEnvironment(Map environment) { - for (Map.Entry environmentVariable : environment.entrySet()) { - setEnvironmentVariable(environmentVariable.getKey(), environmentVariable.getValue()); + public Builder setEnvironment(@Nullable Map environment) { + if (environment == null) { + this.environmentBuilder = ImmutableList.builder(); + } else { + for (Map.Entry environmentVariable : environment.entrySet()) { + setEnvironmentVariable(environmentVariable.getKey(), environmentVariable.getValue()); + } } return this; } @@ -90,8 +94,12 @@ public Builder addEnvironmentVariableDefinition(String environmentVariableDef * @param entrypoint the list of entrypoint tokens * @return this */ - public Builder setEntrypoint(List entrypoint) { - this.entrypoint = ImmutableList.copyOf(entrypoint); + public Builder setEntrypoint(@Nullable List entrypoint) { + if (entrypoint == null) { + this.entrypoint = null; + } else { + this.entrypoint = ImmutableList.copyOf(entrypoint); + } return this; } @@ -101,8 +109,12 @@ public Builder setEntrypoint(List entrypoint) { * @param javaArguments the list of main args to add * @return this */ - public Builder setJavaArguments(List javaArguments) { - this.javaArguments = ImmutableList.copyOf(javaArguments); + public Builder setJavaArguments(@Nullable List javaArguments) { + if (javaArguments == null) { + this.javaArguments = null; + } else { + this.javaArguments = ImmutableList.copyOf(javaArguments); + } return this; } @@ -112,8 +124,12 @@ public Builder setJavaArguments(List javaArguments) { * @param exposedPorts the list of exposed ports to add * @return this */ - public Builder setExposedPorts(ImmutableList exposedPorts) { - this.exposedPorts = exposedPorts; + public Builder setExposedPorts(@Nullable ImmutableList exposedPorts) { + if (exposedPorts == null) { + this.exposedPorts = null; + } else { + this.exposedPorts = exposedPorts; + } return this; } @@ -134,8 +150,8 @@ public Image build() { created, imageLayersBuilder.build(), environmentBuilder.build(), - ImmutableList.copyOf(entrypoint), - ImmutableList.copyOf(javaArguments), + entrypoint, + javaArguments, exposedPorts); } } @@ -151,27 +167,27 @@ public static Builder builder() { private final ImageLayers layers; /** Environment variable definitions for running the image, in the format {@code NAME=VALUE}. */ - private final ImmutableList environmentBuilder; + @Nullable private final ImmutableList environment; /** Initial command to run when running the image. */ - private final ImmutableList entrypoint; + @Nullable private final ImmutableList entrypoint; /** Arguments to pass into main when running the image. */ - private final ImmutableList javaArguments; + @Nullable private final ImmutableList javaArguments; /** Ports that the container listens on. */ - private final ImmutableList exposedPorts; + @Nullable private final ImmutableList exposedPorts; private Image( @Nullable Instant created, ImageLayers layers, - ImmutableList environment, - ImmutableList entrypoint, - ImmutableList javaArguments, - ImmutableList exposedPorts) { + @Nullable ImmutableList environment, + @Nullable ImmutableList entrypoint, + @Nullable ImmutableList javaArguments, + @Nullable ImmutableList exposedPorts) { this.created = created; this.layers = layers; - this.environmentBuilder = environment; + this.environment = environment; this.entrypoint = entrypoint; this.javaArguments = javaArguments; this.exposedPorts = exposedPorts; @@ -182,18 +198,22 @@ public Instant getCreated() { return created; } + @Nullable public ImmutableList getEnvironment() { - return environmentBuilder; + return environment; } + @Nullable public ImmutableList getEntrypoint() { return entrypoint; } + @Nullable public ImmutableList getJavaArguments() { return javaArguments; } + @Nullable public ImmutableList getExposedPorts() { return exposedPorts; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ContainerConfigurationTemplate.java b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ContainerConfigurationTemplate.java index 2ff134c05c..9d8f0f2b4f 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ContainerConfigurationTemplate.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ContainerConfigurationTemplate.java @@ -109,19 +109,19 @@ public void setCreated(@Nullable String created) { this.created = created; } - public void setContainerEnvironment(List environment) { + public void setContainerEnvironment(@Nullable List environment) { config.Env = environment; } - public void setContainerEntrypoint(List command) { + public void setContainerEntrypoint(@Nullable List command) { config.Entrypoint = command; } - public void setContainerCmd(List cmd) { + public void setContainerCmd(@Nullable List cmd) { config.Cmd = cmd; } - public void setContainerExposedPorts(Map> exposedPorts) { + public void setContainerExposedPorts(@Nullable Map> exposedPorts) { config.ExposedPorts = exposedPorts; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ImageToJsonTranslator.java b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ImageToJsonTranslator.java index ad0145d11e..5731005163 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ImageToJsonTranslator.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/ImageToJsonTranslator.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; /** * Translates an {@link Image} into a manifest or container configuration JSON BLOB. @@ -48,12 +49,16 @@ public class ImageToJsonTranslator { * Converts a list of {@link Port}s to the corresponding container config format for exposed ports * (e.g. {@code Port(1000, Protocol.TCP)} -> {@code {"1000/tcp":{}}}). * - * @param exposedPorts the list of {@link Port}s to translate + * @param exposedPorts the list of {@link Port}s to translate, or {@code null} * @return a sorted map with the string representation of the ports as keys and empty maps as - * values + * values, or {@code null} if {@code exposedPorts} is {@code null} */ @VisibleForTesting - static Map> portListToMap(List exposedPorts) { + @Nullable + static Map> portListToMap(@Nullable List exposedPorts) { + if (exposedPorts == null) { + return null; + } ImmutableSortedMap.Builder> result = new ImmutableSortedMap.Builder<>(String::compareTo); for (Port port : exposedPorts) { diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialHelper.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialHelper.java index 72117e72e8..e292867814 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialHelper.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialHelper.java @@ -23,6 +23,7 @@ import com.google.cloud.tools.jib.json.JsonTemplate; import com.google.cloud.tools.jib.json.JsonTemplateMapper; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.io.CharStreams; import java.io.IOException; import java.io.InputStreamReader; @@ -104,7 +105,8 @@ public Authorization retrieve() try { DockerCredentialsTemplate dockerCredentials = JsonTemplateMapper.readJson(output, DockerCredentialsTemplate.class); - if (dockerCredentials.Username == null || dockerCredentials.Secret == null) { + if (Strings.isNullOrEmpty(dockerCredentials.Username) + || Strings.isNullOrEmpty(dockerCredentials.Secret)) { throw new NonexistentServerUrlDockerCredentialHelperException( credentialHelper, serverUrl, output); } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java index c7a4af6e45..686bc265ef 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java @@ -57,9 +57,7 @@ public void testBuilder() { String expectedTargetImageCredentialHelperName = "anotherCredentialHelper"; RegistryCredentials expectedKnownTargetRegistryCredentials = Mockito.mock(RegistryCredentials.class); - String expectedMainClass = "mainclass"; List expectedJavaArguments = Arrays.asList("arg1", "arg2"); - List expectedJvmFlags = Arrays.asList("some", "jvm", "flags"); Map expectedEnvironment = ImmutableMap.of("key", "value"); ImmutableList expectedExposedPorts = ImmutableList.of(new Port(1000, Protocol.TCP), new Port(2000, Protocol.TCP)); @@ -68,8 +66,10 @@ public void testBuilder() { CacheConfiguration.forPath(Paths.get("application/layers")); CacheConfiguration expectedBaseImageLayersCacheConfiguration = CacheConfiguration.forPath(Paths.get("base/image/layers")); - LayerConfiguration expectedExtraFilesLayerConfiguration = - LayerConfiguration.builder().addEntry(Collections.emptyList(), "destination").build(); + List expectedLayerConfigurations = + Collections.singletonList( + LayerConfiguration.builder().addEntry(Collections.emptyList(), "destination").build()); + List expectedEntrypoint = Arrays.asList("some", "entrypoint"); BuildConfiguration.Builder buildConfigurationBuilder = BuildConfiguration.builder(Mockito.mock(BuildLogger.class)) @@ -84,16 +84,15 @@ public void testBuilder() { expectedTargetServerUrl, expectedTargetImageName, expectedTargetTag)) .setTargetImageCredentialHelperName(expectedTargetImageCredentialHelperName) .setKnownTargetRegistryCredentials(expectedKnownTargetRegistryCredentials) - .setMainClass(expectedMainClass) .setJavaArguments(expectedJavaArguments) - .setJvmFlags(expectedJvmFlags) .setEnvironment(expectedEnvironment) .setExposedPorts(expectedExposedPorts) .setTargetFormat(OCIManifestTemplate.class) .setApplicationLayersCacheConfiguration(expectedApplicationLayersCacheConfiguration) .setBaseImageLayersCacheConfiguration(expectedBaseImageLayersCacheConfiguration) .setAllowInsecureRegistries(true) - .setExtraFilesLayerConfiguration(expectedExtraFilesLayerConfiguration); + .setLayerConfigurations(expectedLayerConfigurations) + .setEntrypoint(expectedEntrypoint); BuildConfiguration buildConfiguration = buildConfigurationBuilder.build(); Assert.assertEquals(expectedCreationTime, buildConfiguration.getCreationTime()); @@ -109,9 +108,7 @@ public void testBuilder() { Assert.assertEquals( expectedTargetImageCredentialHelperName, buildConfiguration.getTargetImageCredentialHelperName()); - Assert.assertEquals(expectedMainClass, buildConfiguration.getMainClass()); Assert.assertEquals(expectedJavaArguments, buildConfiguration.getJavaArguments()); - Assert.assertEquals(expectedJvmFlags, buildConfiguration.getJvmFlags()); Assert.assertEquals(expectedEnvironment, buildConfiguration.getEnvironment()); Assert.assertEquals(expectedExposedPorts, buildConfiguration.getExposedPorts()); Assert.assertEquals(expectedTargetFormat, buildConfiguration.getTargetFormat()); @@ -122,8 +119,8 @@ public void testBuilder() { expectedBaseImageLayersCacheConfiguration, buildConfiguration.getBaseImageLayersCacheConfiguration()); Assert.assertTrue(buildConfiguration.getAllowInsecureRegistries()); - Assert.assertEquals( - expectedExtraFilesLayerConfiguration, buildConfiguration.getExtraFilesLayerConfiguration()); + Assert.assertEquals(expectedLayerConfigurations, buildConfiguration.getLayerConfigurations()); + Assert.assertEquals(expectedEntrypoint, buildConfiguration.getEntrypoint()); } @Test @@ -135,7 +132,6 @@ public void testBuilder_default() { String expectedTargetServerUrl = "someotherserver"; String expectedTargetImageName = "targetimage"; String expectedTargetTag = "targettag"; - String expectedMainClass = "mainclass"; BuildConfiguration buildConfiguration = BuildConfiguration.builder(Mockito.mock(BuildLogger.class)) @@ -145,7 +141,6 @@ public void testBuilder_default() { .setTargetImage( ImageReference.of( expectedTargetServerUrl, expectedTargetImageName, expectedTargetTag)) - .setMainClass(expectedMainClass) .build(); Assert.assertEquals(buildConfiguration.getCreationTime(), Instant.EPOCH); @@ -153,32 +148,20 @@ public void testBuilder_default() { Assert.assertNull(buildConfiguration.getKnownBaseRegistryCredentials()); Assert.assertNull(buildConfiguration.getTargetImageCredentialHelperName()); Assert.assertNull(buildConfiguration.getKnownTargetRegistryCredentials()); - Assert.assertEquals(Collections.emptyList(), buildConfiguration.getJavaArguments()); - Assert.assertEquals(Collections.emptyList(), buildConfiguration.getJvmFlags()); - Assert.assertEquals(Collections.emptyMap(), buildConfiguration.getEnvironment()); - Assert.assertEquals(Collections.emptyList(), buildConfiguration.getExposedPorts()); + Assert.assertNull(buildConfiguration.getJavaArguments()); + Assert.assertNull(buildConfiguration.getEnvironment()); + Assert.assertNull(buildConfiguration.getExposedPorts()); Assert.assertEquals(V22ManifestTemplate.class, buildConfiguration.getTargetFormat()); Assert.assertNull(buildConfiguration.getApplicationLayersCacheConfiguration()); Assert.assertNull(buildConfiguration.getBaseImageLayersCacheConfiguration()); Assert.assertFalse(buildConfiguration.getAllowInsecureRegistries()); - Assert.assertNull(buildConfiguration.getExtraFilesLayerConfiguration()); + Assert.assertEquals(Collections.emptyList(), buildConfiguration.getLayerConfigurations()); + Assert.assertNull(buildConfiguration.getEntrypoint()); } @Test public void testBuilder_missingValues() { - // Main class is missing - try { - BuildConfiguration.builder(Mockito.mock(BuildLogger.class)) - .setBaseImage(Mockito.mock(ImageReference.class)) - .setTargetImage(Mockito.mock(ImageReference.class)) - .build(); - Assert.fail("Build configuration should not be built with missing values"); - - } catch (IllegalStateException ex) { - Assert.assertEquals("main class is required but not set", ex.getMessage()); - } - - // Main class and target image are missing + // Target image is missing try { BuildConfiguration.builder(Mockito.mock(BuildLogger.class)) .setBaseImage(Mockito.mock(ImageReference.class)) @@ -186,9 +169,7 @@ public void testBuilder_missingValues() { Assert.fail("Build configuration should not be built with missing values"); } catch (IllegalStateException ex) { - Assert.assertEquals( - "target image is required but not set and main class is required but not set", - ex.getMessage()); + Assert.assertEquals("target image is required but not set", ex.getMessage()); } // All required fields missing @@ -198,7 +179,7 @@ public void testBuilder_missingValues() { } catch (IllegalStateException ex) { Assert.assertEquals( - "base image is required but not set, target image is required but not set, and main class is required but not set", + "base image is required but not set and target image is required but not set", ex.getMessage()); } } @@ -215,10 +196,10 @@ public void testBuilder_nullValues() { Assert.assertNull(ex.getMessage()); } - // JVM flags element should not be null. + // Entrypoint element should not be null. try { BuildConfiguration.builder(Mockito.mock(BuildLogger.class)) - .setJvmFlags(Arrays.asList("first", null)); + .setEntrypoint(Arrays.asList("first", null)); Assert.fail("The IllegalArgumentException should be thrown."); } catch (IllegalArgumentException ex) { Assert.assertNull(ex.getMessage()); diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/TestSourceFilesConfiguration.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/TestSourceFilesConfiguration.java deleted file mode 100644 index 33396e3295..0000000000 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/TestSourceFilesConfiguration.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2018 Google LLC. All rights reserved. - * - * 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.builder; - -import com.google.common.collect.ImmutableList; -import com.google.common.io.Resources; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.stream.Stream; - -/** Implementation of {@link SourceFilesConfiguration} that uses test resources. */ -public class TestSourceFilesConfiguration implements SourceFilesConfiguration { - - public static class Builder { - private ImmutableList dependenciesSourceFiles = ImmutableList.of(); - private ImmutableList snapshotDependenciesSourceFiles = ImmutableList.of(); - private ImmutableList resourcesSourceFiles = ImmutableList.of(); - private ImmutableList classesSourceFiles = ImmutableList.of(); - - public Builder withDependencies() throws IOException, URISyntaxException { - dependenciesSourceFiles = getFilesList("application/dependencies"); - return this; - } - - public Builder withSnapshotDependencies() throws IOException, URISyntaxException { - snapshotDependenciesSourceFiles = getFilesList("application/snapshot-dependencies"); - return this; - } - - public Builder withResources() throws IOException, URISyntaxException { - resourcesSourceFiles = getFilesList("application/resources"); - return this; - } - - public Builder withClasses() throws IOException, URISyntaxException { - classesSourceFiles = getFilesList("application/classes"); - return this; - } - - public TestSourceFilesConfiguration build() { - return new TestSourceFilesConfiguration( - dependenciesSourceFiles, - snapshotDependenciesSourceFiles, - resourcesSourceFiles, - classesSourceFiles); - } - - /** Lists the files in the {@code resourcePath} resources directory. */ - private ImmutableList getFilesList(String resourcePath) - throws URISyntaxException, IOException { - try (Stream fileStream = - Files.list(Paths.get(Resources.getResource(resourcePath).toURI()))) { - return fileStream.collect(ImmutableList.toImmutableList()); - } - } - } - - public static Builder builder() { - return new Builder(); - } - - private static final String EXTRACTION_PATH = "/some/extraction/path/"; - - private final ImmutableList dependenciesSourceFiles; - private final ImmutableList snapshotDependenciesSourceFiles; - private final ImmutableList resourcesSourceFiles; - private final ImmutableList classesSourceFiles; - - private TestSourceFilesConfiguration( - ImmutableList dependenciesSourceFiles, - ImmutableList snapshotDependenciesSourceFiles, - ImmutableList resourcesSourceFiles, - ImmutableList classesSourceFiles) { - this.dependenciesSourceFiles = dependenciesSourceFiles; - this.snapshotDependenciesSourceFiles = snapshotDependenciesSourceFiles; - this.resourcesSourceFiles = resourcesSourceFiles; - this.classesSourceFiles = classesSourceFiles; - } - - @Override - public ImmutableList getDependenciesFiles() { - return dependenciesSourceFiles; - } - - @Override - public ImmutableList getSnapshotDependenciesFiles() { - return snapshotDependenciesSourceFiles; - } - - @Override - public ImmutableList getResourcesFiles() { - return resourcesSourceFiles; - } - - @Override - public ImmutableList getClassesFiles() { - return classesSourceFiles; - } - - @Override - public String getDependenciesPathOnImage() { - return EXTRACTION_PATH + "libs/"; - } - - @Override - public String getResourcesPathOnImage() { - return EXTRACTION_PATH + "resources/"; - } - - @Override - public String getClassesPathOnImage() { - return EXTRACTION_PATH + "classes/"; - } -} diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStepTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStepTest.java index 0c92492ad2..b2eadaf04c 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStepTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildAndCacheApplicationLayerStepTest.java @@ -18,9 +18,7 @@ import com.google.cloud.tools.jib.async.NonBlockingSteps; import com.google.cloud.tools.jib.builder.BuildConfiguration; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.builder.TestBuildLogger; -import com.google.cloud.tools.jib.builder.TestSourceFilesConfiguration; import com.google.cloud.tools.jib.cache.Cache; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.cache.CacheReader; @@ -34,9 +32,11 @@ import com.google.common.util.concurrent.MoreExecutors; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.ExecutionException; +import java.util.stream.Stream; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -51,34 +51,64 @@ @RunWith(MockitoJUnitRunner.class) public class BuildAndCacheApplicationLayerStepTest { + // TODO: Consolidate with BuildStepsIntegrationTest. + private static final String EXTRACTION_PATH = "/some/extraction/path/"; + private static final String EXTRA_FILES_LAYER_EXTRACTION_PATH = "/extra"; + /** Lists the files in the {@code resourcePath} resources directory. */ + private static ImmutableList getFilesList(String resourcePath) + throws URISyntaxException, IOException { + try (Stream fileStream = + Files.list(Paths.get(Resources.getResource(resourcePath).toURI()))) { + return fileStream.collect(ImmutableList.toImmutableList()); + } + } + @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Mock private BuildConfiguration mockBuildConfiguration; private Path temporaryCacheDirectory; + private LayerConfiguration fakeDependenciesLayerConfiguration; + private LayerConfiguration fakeSnapshotDependenciesLayerConfiguration; + private LayerConfiguration fakeResourcesLayerConfiguration; + private LayerConfiguration fakeClassesLayerConfiguration; + private LayerConfiguration fakeExtraFilesLayerConfiguration; + private LayerConfiguration emptyLayerConfiguration; + @Before - public void setUp() throws IOException { + public void setUp() throws IOException, URISyntaxException { + fakeDependenciesLayerConfiguration = + LayerConfiguration.builder() + .addEntry(getFilesList("application/dependencies"), EXTRACTION_PATH + "libs/") + .build(); + fakeSnapshotDependenciesLayerConfiguration = + LayerConfiguration.builder() + .addEntry(getFilesList("application/snapshot-dependencies"), EXTRACTION_PATH + "libs/") + .build(); + fakeResourcesLayerConfiguration = + LayerConfiguration.builder() + .addEntry(getFilesList("application/resources"), EXTRACTION_PATH + "resources/") + .build(); + fakeClassesLayerConfiguration = + LayerConfiguration.builder() + .addEntry(getFilesList("application/classes"), EXTRACTION_PATH + "classes/") + .build(); + fakeExtraFilesLayerConfiguration = + LayerConfiguration.builder() + .addEntry( + ImmutableList.of( + Paths.get(Resources.getResource("fileA").toURI()), + Paths.get(Resources.getResource("fileB").toURI())), + EXTRA_FILES_LAYER_EXTRACTION_PATH) + .build(); + emptyLayerConfiguration = LayerConfiguration.builder().build(); Mockito.when(mockBuildConfiguration.getBuildLogger()).thenReturn(new TestBuildLogger()); temporaryCacheDirectory = temporaryFolder.newFolder().toPath(); } - private ImmutableList configureExtraFilesLayer() throws URISyntaxException { - ImmutableList extraFilesLayerSourceFiles = - ImmutableList.of( - Paths.get(Resources.getResource("fileA").toURI()), - Paths.get(Resources.getResource("fileB").toURI())); - Mockito.when(mockBuildConfiguration.getExtraFilesLayerConfiguration()) - .thenReturn( - LayerConfiguration.builder() - .addEntry(extraFilesLayerSourceFiles, EXTRA_FILES_LAYER_EXTRACTION_PATH) - .build()); - return extraFilesLayerSourceFiles; - } - - private ImageLayers configureAvailableLayers( - SourceFilesConfiguration testSourceFilesConfiguration) + private ImageLayers buildFakeLayersToCache() throws CacheMetadataCorruptedException, IOException, ExecutionException { ImageLayers.Builder applicationLayersBuilder = ImageLayers.builder(); ImageLayers applicationLayers; @@ -86,10 +116,7 @@ private ImageLayers configureAvailableLayers( try (Cache cache = Cache.init(temporaryCacheDirectory)) { ImmutableList buildAndCacheApplicationLayerSteps = BuildAndCacheApplicationLayerStep.makeList( - MoreExecutors.newDirectExecutorService(), - mockBuildConfiguration, - testSourceFilesConfiguration, - cache); + MoreExecutors.newDirectExecutorService(), mockBuildConfiguration, cache); for (BuildAndCacheApplicationLayerStep buildAndCacheApplicationLayerStep : buildAndCacheApplicationLayerSteps) { @@ -105,117 +132,94 @@ private ImageLayers configureAvailableLayers( @Test public void testRun() throws LayerPropertyNotFoundException, IOException, CacheMetadataCorruptedException, - URISyntaxException, ExecutionException { - - TestSourceFilesConfiguration testSourceFilesConfiguration = - TestSourceFilesConfiguration.builder() - .withDependencies() - .withSnapshotDependencies() - .withClasses() - .withResources() - .build(); - - // Adds an extra file layer. - ImmutableList extraFilesLayerSourceFiles = configureExtraFilesLayer(); - - // Populate the cache - ImageLayers applicationLayers = - configureAvailableLayers(testSourceFilesConfiguration); + ExecutionException { + ImmutableList fakeLayerConfigurations = + ImmutableList.of( + fakeDependenciesLayerConfiguration, + fakeSnapshotDependenciesLayerConfiguration, + fakeResourcesLayerConfiguration, + fakeClassesLayerConfiguration, + fakeExtraFilesLayerConfiguration); + Mockito.when(mockBuildConfiguration.getLayerConfigurations()) + .thenReturn(fakeLayerConfigurations); + + // Populates the cache. + ImageLayers applicationLayers = buildFakeLayersToCache(); Assert.assertEquals(5, applicationLayers.size()); // Re-initialize cache with the updated metadata. Cache cache = Cache.init(temporaryCacheDirectory); - ImmutableList dependenciesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getDependenciesFiles(), - testSourceFilesConfiguration.getDependenciesPathOnImage())); - ImmutableList snapshotDependenciesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getSnapshotDependenciesFiles(), - testSourceFilesConfiguration.getDependenciesPathOnImage())); - ImmutableList resourcesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getResourcesFiles(), - testSourceFilesConfiguration.getResourcesPathOnImage())); - ImmutableList classesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getClassesFiles(), - testSourceFilesConfiguration.getClassesPathOnImage())); - ImmutableList extraFilesLayerEntry = - ImmutableList.of( - new LayerEntry(extraFilesLayerSourceFiles, EXTRA_FILES_LAYER_EXTRACTION_PATH)); + ImmutableList dependenciesLayerEntries = + fakeLayerConfigurations.get(0).getLayerEntries(); + ImmutableList snapshotDependenciesLayerEntries = + fakeLayerConfigurations.get(1).getLayerEntries(); + ImmutableList resourcesLayerEntries = + fakeLayerConfigurations.get(2).getLayerEntries(); + ImmutableList classesLayerEntries = + fakeLayerConfigurations.get(3).getLayerEntries(); + ImmutableList extraFilesLayerEntries = + fakeLayerConfigurations.get(4).getLayerEntries(); // Verifies that the cached layers are up-to-date. CacheReader cacheReader = new CacheReader(cache); Assert.assertEquals( applicationLayers.get(0).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(dependenciesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(dependenciesLayerEntries).getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(1).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(resourcesLayerEntry).getBlobDescriptor()); + cacheReader + .getUpToDateLayerByLayerEntries(snapshotDependenciesLayerEntries) + .getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(2).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(classesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(resourcesLayerEntries).getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(3).getBlobDescriptor(), - cacheReader - .getUpToDateLayerByLayerEntries(snapshotDependenciesLayerEntry) - .getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(classesLayerEntries).getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(4).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(extraFilesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(extraFilesLayerEntries).getBlobDescriptor()); // Verifies that the cache reader gets the same layers as the newest application layers. Assert.assertEquals( applicationLayers.get(0).getContentFile(), - cacheReader.getLayerFile(dependenciesLayerEntry)); + cacheReader.getLayerFile(dependenciesLayerEntries)); Assert.assertEquals( - applicationLayers.get(1).getContentFile(), cacheReader.getLayerFile(resourcesLayerEntry)); + applicationLayers.get(1).getContentFile(), + cacheReader.getLayerFile(snapshotDependenciesLayerEntries)); Assert.assertEquals( - applicationLayers.get(2).getContentFile(), cacheReader.getLayerFile(classesLayerEntry)); + applicationLayers.get(2).getContentFile(), cacheReader.getLayerFile(resourcesLayerEntries)); Assert.assertEquals( - applicationLayers.get(3).getContentFile(), - cacheReader.getLayerFile(snapshotDependenciesLayerEntry)); + applicationLayers.get(3).getContentFile(), cacheReader.getLayerFile(classesLayerEntries)); Assert.assertEquals( - applicationLayers.get(4).getContentFile(), cacheReader.getLayerFile(extraFilesLayerEntry)); + applicationLayers.get(4).getContentFile(), + cacheReader.getLayerFile(extraFilesLayerEntries)); } @Test public void testRun_emptyLayersIgnored() - throws IOException, URISyntaxException, CacheMetadataCorruptedException, ExecutionException { - - TestSourceFilesConfiguration testSourceFilesConfiguration = - TestSourceFilesConfiguration.builder() - .withDependencies() - .withClasses() - .withResources() - .build(); - - // Populate the cache - ImageLayers applicationLayers = - configureAvailableLayers(testSourceFilesConfiguration); + throws IOException, CacheMetadataCorruptedException, ExecutionException { + ImmutableList fakeLayerConfigurations = + ImmutableList.of( + fakeDependenciesLayerConfiguration, + emptyLayerConfiguration, + fakeResourcesLayerConfiguration, + fakeClassesLayerConfiguration, + emptyLayerConfiguration); + Mockito.when(mockBuildConfiguration.getLayerConfigurations()) + .thenReturn(fakeLayerConfigurations); + + // Populates the cache. + ImageLayers applicationLayers = buildFakeLayersToCache(); Assert.assertEquals(3, applicationLayers.size()); - ImmutableList dependenciesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getDependenciesFiles(), - testSourceFilesConfiguration.getDependenciesPathOnImage())); - ImmutableList resourcesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getResourcesFiles(), - testSourceFilesConfiguration.getResourcesPathOnImage())); - ImmutableList classesLayerEntry = - ImmutableList.of( - new LayerEntry( - testSourceFilesConfiguration.getClassesFiles(), - testSourceFilesConfiguration.getClassesPathOnImage())); + ImmutableList dependenciesLayerEntries = + fakeLayerConfigurations.get(0).getLayerEntries(); + ImmutableList resourcesLayerEntries = + fakeLayerConfigurations.get(2).getLayerEntries(); + ImmutableList classesLayerEntries = + fakeLayerConfigurations.get(3).getLayerEntries(); // Re-initialize cache with the updated metadata. Cache cache = Cache.init(temporaryCacheDirectory); @@ -224,21 +228,23 @@ public void testRun_emptyLayersIgnored() CacheReader cacheReader = new CacheReader(cache); Assert.assertEquals( applicationLayers.get(0).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(dependenciesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(dependenciesLayerEntries).getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(1).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(resourcesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(resourcesLayerEntries).getBlobDescriptor()); Assert.assertEquals( applicationLayers.get(2).getBlobDescriptor(), - cacheReader.getUpToDateLayerByLayerEntries(classesLayerEntry).getBlobDescriptor()); + cacheReader.getUpToDateLayerByLayerEntries(classesLayerEntries).getBlobDescriptor()); // Verifies that the cache reader gets the same layers as the newest application layers. Assert.assertEquals( applicationLayers.get(0).getContentFile(), - cacheReader.getLayerFile(dependenciesLayerEntry)); + cacheReader.getLayerFile(fakeLayerConfigurations.get(0).getLayerEntries())); Assert.assertEquals( - applicationLayers.get(1).getContentFile(), cacheReader.getLayerFile(resourcesLayerEntry)); + applicationLayers.get(1).getContentFile(), + cacheReader.getLayerFile(fakeLayerConfigurations.get(2).getLayerEntries())); Assert.assertEquals( - applicationLayers.get(2).getContentFile(), cacheReader.getLayerFile(classesLayerEntry)); + applicationLayers.get(2).getContentFile(), + cacheReader.getLayerFile(fakeLayerConfigurations.get(3).getLayerEntries())); } } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildImageStepTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildImageStepTest.java index ea865b518c..e9f1000a38 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildImageStepTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/BuildImageStepTest.java @@ -68,6 +68,7 @@ public void setUp() throws DigestException { Mockito.when(mockBuildConfiguration.getEnvironment()).thenReturn(ImmutableMap.of()); Mockito.when(mockBuildConfiguration.getJavaArguments()).thenReturn(ImmutableList.of()); Mockito.when(mockBuildConfiguration.getExposedPorts()).thenReturn(ImmutableList.of()); + Mockito.when(mockBuildConfiguration.getEntrypoint()).thenReturn(ImmutableList.of()); Mockito.when(mockPullAndCacheBaseImageLayersStep.getFuture()) .thenReturn( @@ -92,8 +93,7 @@ public void test_validateAsyncDependencies() throws ExecutionException, Interrup ImmutableList.of( mockBuildAndCacheApplicationLayerStep, mockBuildAndCacheApplicationLayerStep, - mockBuildAndCacheApplicationLayerStep), - ImmutableList.of()); + mockBuildAndCacheApplicationLayerStep)); Image image = buildImageStep.getFuture().get().getFuture().get(); Assert.assertEquals( testDescriptorDigest, image.getLayers().asList().get(0).getBlobDescriptor().getDigest()); diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/DockerContextGeneratorTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/DockerContextGeneratorTest.java index 8dc223fba9..15ba372345 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/DockerContextGeneratorTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/DockerContextGeneratorTest.java @@ -16,8 +16,8 @@ package com.google.cloud.tools.jib.docker; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.filesystem.DirectoryWalker; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.common.collect.ImmutableList; import com.google.common.io.Resources; import java.io.IOException; @@ -30,20 +30,22 @@ import java.util.Arrays; import java.util.Deque; import java.util.List; +import java.util.stream.Stream; import org.junit.Assert; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; /** Tests for {@link DockerContextGenerator}. */ @RunWith(MockitoJUnitRunner.class) public class DockerContextGeneratorTest { + private static final String EXPECTED_DEPENDENCIES_PATH = "/app/libs/"; + private static final String EXPECTED_RESOURCES_PATH = "/app/resources/"; + private static final String EXPECTED_CLASSES_PATH = "/app/classes/"; + private static void assertSameFiles(Path directory1, Path directory2) throws IOException { Deque directory1Paths = new ArrayDeque<>(new DirectoryWalker(directory1).walk()); @@ -58,24 +60,14 @@ private static void assertSameFiles(Path directory1, Path directory2) throws IOE Assert.assertEquals(0, directory1Paths.size()); } - @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); - - @Mock private SourceFilesConfiguration mockSourceFilesConfiguration; - - @Before - public void setUpMocks() { - String expectedDependenciesPath = "/app/libs/"; - String expectedResourcesPath = "/app/resources/"; - String expectedClassesPath = "/app/classes/"; - - Mockito.when(mockSourceFilesConfiguration.getDependenciesPathOnImage()) - .thenReturn(expectedDependenciesPath); - Mockito.when(mockSourceFilesConfiguration.getResourcesPathOnImage()) - .thenReturn(expectedResourcesPath); - Mockito.when(mockSourceFilesConfiguration.getClassesPathOnImage()) - .thenReturn(expectedClassesPath); + private static ImmutableList listFilesInDirectory(Path directory) throws IOException { + try (Stream files = Files.list(directory)) { + return files.collect(ImmutableList.toImmutableList()); + } } + @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + @Test public void testGenerate() throws IOException, URISyntaxException { Path testDependencies = Paths.get(Resources.getResource("application/dependencies").toURI()); @@ -83,21 +75,14 @@ public void testGenerate() throws IOException, URISyntaxException { Paths.get(Resources.getResource("application/snapshot-dependencies").toURI()); Path testResources = Paths.get(Resources.getResource("application/resources").toURI()); Path testClasses = Paths.get(Resources.getResource("application/classes").toURI()); + Path testExtraFiles = Paths.get(Resources.getResource("layer").toURI()); - ImmutableList expectedDependenciesFiles = - new DirectoryWalker(testDependencies).filterRoot().walk(); + ImmutableList expectedDependenciesFiles = listFilesInDirectory(testDependencies); ImmutableList expectedSnapshotDependenciesFiles = - new DirectoryWalker(testSnapshotDependencies).filterRoot().walk(); - ImmutableList expectedResourcesFiles = - new DirectoryWalker(testResources).filterRoot().walk(); - ImmutableList expectedClassesFiles = new DirectoryWalker(testClasses).filterRoot().walk(); - Mockito.when(mockSourceFilesConfiguration.getDependenciesFiles()) - .thenReturn(expectedDependenciesFiles); - Mockito.when(mockSourceFilesConfiguration.getSnapshotDependenciesFiles()) - .thenReturn(expectedSnapshotDependenciesFiles); - Mockito.when(mockSourceFilesConfiguration.getResourcesFiles()) - .thenReturn(expectedResourcesFiles); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()).thenReturn(expectedClassesFiles); + listFilesInDirectory(testSnapshotDependencies); + ImmutableList expectedResourcesFiles = listFilesInDirectory(testResources); + ImmutableList expectedClassesFiles = listFilesInDirectory(testClasses); + ImmutableList expectedExtraFiles = listFilesInDirectory(testExtraFiles); Path targetDirectory = temporaryFolder.newFolder().toPath(); @@ -107,7 +92,12 @@ public void testGenerate() throws IOException, URISyntaxException { */ Files.delete(targetDirectory); - new DockerContextGenerator(mockSourceFilesConfiguration) + new DockerContextGenerator( + new LayerEntry(expectedDependenciesFiles, EXPECTED_DEPENDENCIES_PATH), + new LayerEntry(expectedSnapshotDependenciesFiles, EXPECTED_DEPENDENCIES_PATH), + new LayerEntry(expectedResourcesFiles, EXPECTED_RESOURCES_PATH), + new LayerEntry(expectedClassesFiles, EXPECTED_CLASSES_PATH), + new LayerEntry(expectedExtraFiles, "/")) .setBaseImage("somebaseimage") .generate(targetDirectory); @@ -116,6 +106,7 @@ public void testGenerate() throws IOException, URISyntaxException { assertSameFiles(targetDirectory.resolve("snapshot-libs"), testSnapshotDependencies); assertSameFiles(targetDirectory.resolve("resources"), testResources); assertSameFiles(targetDirectory.resolve("classes"), testClasses); + assertSameFiles(targetDirectory.resolve("root"), testExtraFiles); } @Test @@ -127,7 +118,12 @@ public void testMakeDockerfile() throws IOException { List exposedPorts = Arrays.asList("1000/tcp", "2000-2010/udp"); String dockerfile = - new DockerContextGenerator(mockSourceFilesConfiguration) + new DockerContextGenerator( + new LayerEntry(ImmutableList.of(Paths.get("ignored")), EXPECTED_DEPENDENCIES_PATH), + new LayerEntry(ImmutableList.of(Paths.get("ignored")), EXPECTED_DEPENDENCIES_PATH), + new LayerEntry(ImmutableList.of(Paths.get("ignored")), EXPECTED_RESOURCES_PATH), + new LayerEntry(ImmutableList.of(Paths.get("ignored")), EXPECTED_CLASSES_PATH), + new LayerEntry(ImmutableList.of(Paths.get("ignored")), "/")) .setBaseImage(expectedBaseImage) .setJvmFlags(expectedJvmFlags) .setMainClass(expectedMainClass) @@ -138,8 +134,6 @@ public void testMakeDockerfile() throws IOException { // Need to split/rejoin the string here to avoid cross-platform troubles List sampleDockerfile = Resources.readLines(Resources.getResource("sampleDockerfile"), StandardCharsets.UTF_8); - Assert.assertArrayEquals( - String.join("\n", sampleDockerfile).getBytes(StandardCharsets.UTF_8), - dockerfile.getBytes(StandardCharsets.UTF_8)); + Assert.assertEquals(String.join("\n", sampleDockerfile), dockerfile); } } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/BuildStepsRunnerTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/BuildStepsRunnerTest.java index 946c14074a..f80fbc7ac9 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/BuildStepsRunnerTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/BuildStepsRunnerTest.java @@ -21,11 +21,11 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.builder.BuildLogger; import com.google.cloud.tools.jib.builder.BuildSteps; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException; import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.registry.InsecureRegistryException; import com.google.cloud.tools.jib.registry.RegistryCredentialsNotSentException; import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException; @@ -62,7 +62,6 @@ public class BuildStepsRunnerTest { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Mock private BuildSteps mockBuildSteps; - @Mock private SourceFilesConfiguration mockSourceFilesConfiguration; @Mock private BuildLogger mockBuildLogger; @Mock private RegistryUnauthorizedException mockRegistryUnauthorizedException; @Mock private RegistryCredentialsNotSentException mockRegistryCredentialsNotSentException; @@ -78,12 +77,12 @@ public void setUpMocks() { Mockito.when(mockBuildSteps.getBuildConfiguration()).thenReturn(mockBuildConfiguration); Mockito.when(mockBuildConfiguration.getBuildLogger()).thenReturn(mockBuildLogger); - Mockito.when(mockBuildSteps.getSourceFilesConfiguration()) - .thenReturn(mockSourceFilesConfiguration); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()).thenReturn(ImmutableList.of()); - Mockito.when(mockSourceFilesConfiguration.getResourcesFiles()).thenReturn(ImmutableList.of()); - Mockito.when(mockSourceFilesConfiguration.getDependenciesFiles()) - .thenReturn(ImmutableList.of()); + Mockito.when(mockBuildConfiguration.getLayerConfigurations()) + .thenReturn( + ImmutableList.of( + LayerConfiguration.builder().addEntry(ImmutableList.of(), "ignored").build(), + LayerConfiguration.builder().addEntry(ImmutableList.of(), "ignored").build(), + LayerConfiguration.builder().addEntry(ImmutableList.of(), "ignored").build())); } @Test diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/EntrypointBuilderTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructorTest.java similarity index 60% rename from jib-core/src/test/java/com/google/cloud/tools/jib/builder/EntrypointBuilderTest.java rename to jib-core/src/test/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructorTest.java index f6da756da0..3159645b9b 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/EntrypointBuilderTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/JavaEntrypointConstructorTest.java @@ -14,35 +14,29 @@ * the License. */ -package com.google.cloud.tools.jib.builder; +package com.google.cloud.tools.jib.frontend; import java.util.Arrays; import java.util.List; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; -/** Tests for {@link EntrypointBuilder}. */ -public class EntrypointBuilderTest { +/** Tests for {@link JavaEntrypointConstructor}. */ +public class JavaEntrypointConstructorTest { @Test public void testMakeEntrypoint() { - String expectedDependenciesPath = "/app/libs/"; + String expectedDependenciesPath = "/app/libs/*"; String expectedResourcesPath = "/app/resources/"; String expectedClassesPath = "/app/classes/"; List expectedJvmFlags = Arrays.asList("-flag", "anotherFlag"); String expectedMainClass = "SomeMainClass"; - SourceFilesConfiguration mockSourceFilesConfiguration = - Mockito.mock(SourceFilesConfiguration.class); - - Mockito.when(mockSourceFilesConfiguration.getDependenciesPathOnImage()) - .thenReturn(expectedDependenciesPath); - Mockito.when(mockSourceFilesConfiguration.getResourcesPathOnImage()) - .thenReturn(expectedResourcesPath); - Mockito.when(mockSourceFilesConfiguration.getClassesPathOnImage()) - .thenReturn(expectedClassesPath); - + List entrypoint = + JavaEntrypointConstructor.makeEntrypoint( + Arrays.asList(expectedDependenciesPath, expectedResourcesPath, expectedClassesPath), + expectedJvmFlags, + expectedMainClass); Assert.assertEquals( Arrays.asList( "java", @@ -51,7 +45,11 @@ public void testMakeEntrypoint() { "-cp", "/app/libs/*:/app/resources/:/app/classes/", "SomeMainClass"), - EntrypointBuilder.makeEntrypoint( - mockSourceFilesConfiguration, expectedJvmFlags, expectedMainClass)); + entrypoint); + + // Checks that this is also the default entrypoint. + Assert.assertEquals( + JavaEntrypointConstructor.makeDefaultEntrypoint(expectedJvmFlags, expectedMainClass), + entrypoint); } } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/MainClassFinderTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/MainClassFinderTest.java index 3b2d5cc7b6..29cde64bbf 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/MainClassFinderTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/frontend/MainClassFinderTest.java @@ -17,7 +17,7 @@ package com.google.cloud.tools.jib.frontend; import com.google.cloud.tools.jib.builder.BuildLogger; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.common.collect.ImmutableList; import com.google.common.io.Resources; import java.io.IOException; @@ -39,7 +39,6 @@ public class MainClassFinderTest { @Mock private BuildLogger mockBuildLogger; - @Mock private SourceFilesConfiguration mockSourceFilesConfiguration; @Mock private ProjectProperties mockProjectProperties; @Mock private HelpfulSuggestions mockHelpfulSuggestions; @@ -49,8 +48,6 @@ public class MainClassFinderTest { public void setup() { Mockito.when(mockProjectProperties.getLogger()).thenReturn(mockBuildLogger); Mockito.when(mockProjectProperties.getPluginName()).thenReturn("plugin"); - Mockito.when(mockProjectProperties.getSourceFilesConfiguration()) - .thenReturn(mockSourceFilesConfiguration); Mockito.when(mockProjectProperties.getMainClassHelpfulSuggestions(ArgumentMatchers.any())) .thenReturn(mockHelpfulSuggestions); Mockito.when(mockProjectProperties.getJarPluginName()).thenReturn("jar-plugin"); @@ -136,7 +133,8 @@ public void testResolveMainClass() throws MainClassInferenceException { @Test public void testResolveMainClass_notValid() throws MainClassInferenceException { Mockito.when(mockProjectProperties.getMainClassFromJar()).thenReturn("${start-class}"); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()).thenReturn(fakeClassesPath); + Mockito.when(mockProjectProperties.getClassesLayerEntry()) + .thenReturn(new LayerEntry(fakeClassesPath, "ignored")); Assert.assertEquals( "${start-class}", MainClassFinder.resolveMainClass(null, mockProjectProperties)); Mockito.verify(mockBuildLogger).warn("'mainClass' is not a valid Java class : ${start-class}"); @@ -146,14 +144,18 @@ public void testResolveMainClass_notValid() throws MainClassInferenceException { public void testResolveMainClass_multipleInferredWithBackup() throws MainClassInferenceException, URISyntaxException { Mockito.when(mockProjectProperties.getMainClassFromJar()).thenReturn("${start-class}"); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()) + Mockito.when(mockProjectProperties.getClassesLayerEntry()) .thenReturn( - ImmutableList.of( - Paths.get(Resources.getResource("class-finder-tests/multiple/multi").toURI()), - Paths.get( - Resources.getResource("class-finder-tests/multiple/HelloWorld.class").toURI()), - Paths.get( - Resources.getResource("class-finder-tests/multiple/NotMain.class").toURI()))); + new LayerEntry( + ImmutableList.of( + Paths.get(Resources.getResource("class-finder-tests/multiple/multi").toURI()), + Paths.get( + Resources.getResource("class-finder-tests/multiple/HelloWorld.class") + .toURI()), + Paths.get( + Resources.getResource("class-finder-tests/multiple/NotMain.class") + .toURI())), + "ignored")); Assert.assertEquals( "${start-class}", MainClassFinder.resolveMainClass(null, mockProjectProperties)); Mockito.verify(mockBuildLogger).warn("'mainClass' is not a valid Java class : ${start-class}"); @@ -162,14 +164,18 @@ public void testResolveMainClass_multipleInferredWithBackup() @Test public void testResolveMainClass_multipleInferredWithoutBackup() throws URISyntaxException { Mockito.when(mockProjectProperties.getMainClassFromJar()).thenReturn(null); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()) + Mockito.when(mockProjectProperties.getClassesLayerEntry()) .thenReturn( - ImmutableList.of( - Paths.get(Resources.getResource("class-finder-tests/multiple/multi").toURI()), - Paths.get( - Resources.getResource("class-finder-tests/multiple/HelloWorld.class").toURI()), - Paths.get( - Resources.getResource("class-finder-tests/multiple/NotMain.class").toURI()))); + new LayerEntry( + ImmutableList.of( + Paths.get(Resources.getResource("class-finder-tests/multiple/multi").toURI()), + Paths.get( + Resources.getResource("class-finder-tests/multiple/HelloWorld.class") + .toURI()), + Paths.get( + Resources.getResource("class-finder-tests/multiple/NotMain.class") + .toURI())), + "ignored")); try { MainClassFinder.resolveMainClass(null, mockProjectProperties); Assert.fail(); @@ -183,7 +189,8 @@ public void testResolveMainClass_multipleInferredWithoutBackup() throws URISynta @Test public void testResolveMainClass_noneInferredWithBackup() throws MainClassInferenceException { Mockito.when(mockProjectProperties.getMainClassFromJar()).thenReturn("${start-class}"); - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()).thenReturn(ImmutableList.of()); + Mockito.when(mockProjectProperties.getClassesLayerEntry()) + .thenReturn(new LayerEntry(ImmutableList.of(), "ignored")); Assert.assertEquals( "${start-class}", MainClassFinder.resolveMainClass(null, mockProjectProperties)); Mockito.verify(mockBuildLogger).warn("'mainClass' is not a valid Java class : ${start-class}"); @@ -191,7 +198,8 @@ public void testResolveMainClass_noneInferredWithBackup() throws MainClassInfere @Test public void testResolveMainClass_noneInferredWithoutBackup() { - Mockito.when(mockSourceFilesConfiguration.getClassesFiles()).thenReturn(ImmutableList.of()); + Mockito.when(mockProjectProperties.getClassesLayerEntry()) + .thenReturn(new LayerEntry(ImmutableList.of(), "ignored")); try { MainClassFinder.resolveMainClass(null, mockProjectProperties); Assert.fail(); diff --git a/jib-core/src/test/resources/sampleDockerfile b/jib-core/src/test/resources/sampleDockerfile index 474ea0c4e2..5dde515f99 100644 --- a/jib-core/src/test/resources/sampleDockerfile +++ b/jib-core/src/test/resources/sampleDockerfile @@ -4,6 +4,7 @@ COPY libs /app/libs/ COPY snapshot-libs /app/libs/ COPY resources /app/resources/ COPY classes /app/classes/ +COPY root / EXPOSE 1000/tcp EXPOSE 2000-2010/udp diff --git a/jib-gradle-plugin/CHANGELOG.md b/jib-gradle-plugin/CHANGELOG.md index 096f846de8..009e3327ab 100644 --- a/jib-gradle-plugin/CHANGELOG.md +++ b/jib-gradle-plugin/CHANGELOG.md @@ -5,10 +5,11 @@ All notable changes to this project will be documented in this file. ### Added -### Changed +- Docker context generation now includes snapshot dependencies and extra files ([#516](https://github.com/GoogleContainerTools/jib/pull/516/files)) -### Fixed +### Changed +- Only builds non-empty layers ([#516](https://github.com/GoogleContainerTools/jib/pull/516/files)) - Fixed slow image reference parsing ([#680](https://github.com/GoogleContainerTools/jib/pull/680)) ## 0.9.7 @@ -20,6 +21,7 @@ All notable changes to this project will be documented in this file. - `container.useCurrentTimestamp` parameter to set the image creation time to the build time ([#413](https://github.com/GoogleContainerTools/jib/issues/413)) - Authentication over HTTP using the `sendCredentialsOverHttp` system property ([#599](https://github.com/GoogleContainerTools/jib/issues/599)) - HTTP connection and read timeouts for registry interactions configurable with the `jib.httpTimeout` system property ([#656](https://github.com/GoogleContainerTools/jib/pull/656)) +- Docker context export command-line option `--targetDir` to `--jibTargetDir` ([#662](https://github.com/GoogleContainerTools/jib/issues/662)) ### Changed diff --git a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibPluginIntegrationTest.java b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibPluginIntegrationTest.java index f9df14cbd8..75380d0085 100644 --- a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibPluginIntegrationTest.java +++ b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibPluginIntegrationTest.java @@ -246,7 +246,7 @@ public void testDockerContext() throws IOException, InterruptedException { + " \"2002/udp\": {},\n" + " \"2003/udp\": {}")); Assert.assertEquals( - "Hello, world. An argument.\n", new Command("docker", "run", imageName).run()); + "Hello, world. An argument.\nfoo\ncat\n", new Command("docker", "run", imageName).run()); // Checks that generating the Docker context again is skipped. BuildTask upToDateJibDockerContextTask = diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildDockerTask.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildDockerTask.java index c067a20134..24831b5535 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildDockerTask.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildDockerTask.java @@ -19,12 +19,12 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.docker.DockerClient; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.http.Authorization; import com.google.cloud.tools.jib.image.ImageReference; @@ -32,12 +32,7 @@ import com.google.cloud.tools.jib.registry.RegistryClient; import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; import com.google.common.base.Preconditions; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.time.Instant; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; @@ -79,7 +74,7 @@ public void setTargetImage(String targetImage) { } @TaskAction - public void buildDocker() throws InvalidImageReferenceException, IOException { + public void buildDocker() throws InvalidImageReferenceException { if (!new DockerClient().isDockerInstalled()) { throw new GradleException(HELPFUL_SUGGESTIONS.forDockerNotInstalled()); } @@ -102,7 +97,8 @@ public void buildDocker() throws InvalidImageReferenceException, IOException { } GradleProjectProperties gradleProjectProperties = - GradleProjectProperties.getForProject(getProject(), gradleBuildLogger); + GradleProjectProperties.getForProject( + getProject(), gradleBuildLogger, jibExtension.getExtraDirectory().toPath()); String mainClass = gradleProjectProperties.getMainClass(jibExtension); ImageReference targetImage = gradleProjectProperties.getGeneratedTargetDockerTag(jibExtension, gradleBuildLogger); @@ -115,20 +111,13 @@ public void buildDocker() throws InvalidImageReferenceException, IOException { .setTargetImage(targetImage) .setBaseImageCredentialHelperName(jibExtension.getFrom().getCredHelper()) .setKnownBaseRegistryCredentials(knownBaseRegistryCredentials) - .setMainClass(mainClass) .setJavaArguments(jibExtension.getArgs()) - .setJvmFlags(jibExtension.getJvmFlags()) .setExposedPorts(ExposedPortsParser.parse(jibExtension.getExposedPorts())) - .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()); - if (Files.exists(jibExtension.getExtraDirectory().toPath())) { - try (Stream extraFilesLayerDirectoryFiles = - Files.list(jibExtension.getExtraDirectory().toPath())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - } - } + .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()) + .setLayerConfigurations(gradleProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + jibExtension.getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(gradleProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -152,9 +141,7 @@ public void buildDocker() throws InvalidImageReferenceException, IOException { // Uses a directory in the Gradle build cache as the Jib cache. try { - BuildStepsRunner.forBuildToDockerDaemon( - buildConfiguration, gradleProjectProperties.getSourceFilesConfiguration()) - .build(HELPFUL_SUGGESTIONS); + BuildStepsRunner.forBuildToDockerDaemon(buildConfiguration).build(HELPFUL_SUGGESTIONS); } catch (CacheDirectoryCreationException | BuildStepsExecutionException ex) { throw new GradleException(ex.getMessage(), ex.getCause()); diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildImageTask.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildImageTask.java index 4c958ce22e..1a4f114b83 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildImageTask.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildImageTask.java @@ -19,11 +19,11 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.http.Authorization; import com.google.cloud.tools.jib.image.ImageReference; @@ -32,12 +32,7 @@ import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.time.Instant; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; @@ -79,7 +74,7 @@ public void setTargetImage(String targetImage) { } @TaskAction - public void buildImage() throws InvalidImageReferenceException, IOException { + public void buildImage() throws InvalidImageReferenceException { // Asserts required @Input parameters are not null. Preconditions.checkNotNull(jibExtension); GradleBuildLogger gradleBuildLogger = new GradleBuildLogger(getLogger()); @@ -110,7 +105,8 @@ public void buildImage() throws InvalidImageReferenceException, IOException { } GradleProjectProperties gradleProjectProperties = - GradleProjectProperties.getForProject(getProject(), gradleBuildLogger); + GradleProjectProperties.getForProject( + getProject(), gradleBuildLogger, jibExtension.getExtraDirectory().toPath()); String mainClass = gradleProjectProperties.getMainClass(jibExtension); // Builds the BuildConfiguration. @@ -122,21 +118,14 @@ public void buildImage() throws InvalidImageReferenceException, IOException { .setKnownBaseRegistryCredentials(knownBaseRegistryCredentials) .setTargetImageCredentialHelperName(jibExtension.getTo().getCredHelper()) .setKnownTargetRegistryCredentials(knownTargetRegistryCredentials) - .setMainClass(mainClass) .setJavaArguments(jibExtension.getArgs()) - .setJvmFlags(jibExtension.getJvmFlags()) .setExposedPorts(ExposedPortsParser.parse(jibExtension.getExposedPorts())) .setTargetFormat(jibExtension.getFormat()) - .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()); - if (Files.exists(jibExtension.getExtraDirectory().toPath())) { - try (Stream extraFilesLayerDirectoryFiles = - Files.list(jibExtension.getExtraDirectory().toPath())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - } - } + .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()) + .setLayerConfigurations(gradleProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + jibExtension.getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(gradleProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -159,9 +148,7 @@ public void buildImage() throws InvalidImageReferenceException, IOException { RegistryClient.setUserAgentSuffix(USER_AGENT_SUFFIX); try { - BuildStepsRunner.forBuildImage( - buildConfiguration, gradleProjectProperties.getSourceFilesConfiguration()) - .build(HELPFUL_SUGGESTIONS); + BuildStepsRunner.forBuildImage(buildConfiguration).build(HELPFUL_SUGGESTIONS); } catch (CacheDirectoryCreationException | BuildStepsExecutionException ex) { throw new GradleException(ex.getMessage(), ex.getCause()); diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildTarTask.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildTarTask.java index f9b1c6f8ae..7cac24b230 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildTarTask.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/BuildTarTask.java @@ -19,11 +19,11 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.http.Authorization; import com.google.cloud.tools.jib.image.ImageReference; @@ -31,13 +31,8 @@ import com.google.cloud.tools.jib.registry.RegistryClient; import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; import com.google.common.base.Preconditions; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.annotation.Nullable; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; @@ -111,7 +106,7 @@ private String getTargetPath() { } @TaskAction - public void buildTar() throws InvalidImageReferenceException, IOException { + public void buildTar() throws InvalidImageReferenceException { // Asserts required @Input parameters are not null. Preconditions.checkNotNull(jibExtension); GradleBuildLogger gradleBuildLogger = new GradleBuildLogger(getLogger()); @@ -130,7 +125,8 @@ public void buildTar() throws InvalidImageReferenceException, IOException { } GradleProjectProperties gradleProjectProperties = - GradleProjectProperties.getForProject(getProject(), gradleBuildLogger); + GradleProjectProperties.getForProject( + getProject(), gradleBuildLogger, jibExtension.getExtraDirectory().toPath()); String mainClass = gradleProjectProperties.getMainClass(jibExtension); ImageReference targetImage = gradleProjectProperties.getGeneratedTargetDockerTag(jibExtension, gradleBuildLogger); @@ -143,20 +139,13 @@ public void buildTar() throws InvalidImageReferenceException, IOException { .setTargetImage(targetImage) .setBaseImageCredentialHelperName(jibExtension.getFrom().getCredHelper()) .setKnownBaseRegistryCredentials(knownBaseRegistryCredentials) - .setMainClass(mainClass) .setJavaArguments(jibExtension.getArgs()) - .setJvmFlags(jibExtension.getJvmFlags()) .setExposedPorts(ExposedPortsParser.parse(jibExtension.getExposedPorts())) - .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()); - if (Files.exists(jibExtension.getExtraDirectory().toPath())) { - try (Stream extraFilesLayerDirectoryFiles = - Files.list(jibExtension.getExtraDirectory().toPath())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - } - } + .setAllowInsecureRegistries(jibExtension.getAllowInsecureRegistries()) + .setLayerConfigurations(gradleProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint( + jibExtension.getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(gradleProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -180,10 +169,7 @@ public void buildTar() throws InvalidImageReferenceException, IOException { // Uses a directory in the Gradle build cache as the Jib cache. try { - BuildStepsRunner.forBuildTar( - Paths.get(getTargetPath()), - buildConfiguration, - gradleProjectProperties.getSourceFilesConfiguration()) + BuildStepsRunner.forBuildTar(Paths.get(getTargetPath()), buildConfiguration) .build(HELPFUL_SUGGESTIONS); } catch (CacheDirectoryCreationException | BuildStepsExecutionException ex) { diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/DockerContextTask.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/DockerContextTask.java index 2e10cb4521..a5effd5953 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/DockerContextTask.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/DockerContextTask.java @@ -104,7 +104,8 @@ public void generateDockerContext() { SystemPropertyValidator.checkHttpTimeoutProperty(GradleException::new); GradleProjectProperties gradleProjectProperties = - GradleProjectProperties.getForProject(getProject(), gradleBuildLogger); + GradleProjectProperties.getForProject( + getProject(), gradleBuildLogger, jibExtension.getExtraDirectory().toPath()); String mainClass = gradleProjectProperties.getMainClass(jibExtension); String targetDir = getTargetDir(); @@ -113,8 +114,12 @@ public void generateDockerContext() { // here. ExposedPortsParser.parse(jibExtension.getExposedPorts()); - // TODO: Add support for extra files layer. - new DockerContextGenerator(gradleProjectProperties.getSourceFilesConfiguration()) + new DockerContextGenerator( + gradleProjectProperties.getDependenciesLayerEntry(), + gradleProjectProperties.getSnapshotDependenciesLayerEntry(), + gradleProjectProperties.getResourcesLayerEntry(), + gradleProjectProperties.getClassesLayerEntry(), + gradleProjectProperties.getExtraFilesLayerEntry()) .setBaseImage(jibExtension.getBaseImage()) .setJvmFlags(jibExtension.getJvmFlags()) .setMainClass(mainClass) diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurations.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurations.java new file mode 100644 index 0000000000..641f285426 --- /dev/null +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurations.java @@ -0,0 +1,203 @@ +/* + * Copyright 2018 Google Inc. + * + * 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.configuration.LayerConfiguration; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; +import com.google.cloud.tools.jib.image.LayerEntry; +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; +import org.gradle.api.plugins.JavaPluginConvention; +import org.gradle.api.tasks.SourceSet; + +/** Builds {@link LayerConfiguration}s based on inputs from a {@link Project}. */ +class GradleLayerConfigurations { + + /** Name of the `main` {@link SourceSet} to use as source files. */ + private static final String MAIN_SOURCE_SET_NAME = "main"; + + private static final String DEPENDENCIES_LAYER_LABEL = "dependencies"; + private static final String SNAPSHOT_DEPENDENCIES_LAYER_LABEL = "snapshot dependencies"; + private static final String RESOURCES_LAYER_LABEL = "resources"; + private static final String CLASSES_LAYER_LABEL = "classes"; + private static final String EXTRA_FILES_LAYER_LABEL = "extra files"; + + /** + * Resolves the source files configuration for a Gradle {@link Project}. + * + * @param project the Gradle {@link Project} + * @param gradleBuildLogger the build logger for providing feedback about the resolution + * @param extraDirectory path to the directory for the extra files layer + * @return a {@link GradleLayerConfigurations} for building the layers for the Gradle {@link + * Project} + * @throws IOException if an I/O exception occurred during resolution + */ + static GradleLayerConfigurations getForProject( + Project project, GradleBuildLogger gradleBuildLogger, Path extraDirectory) + throws IOException { + JavaPluginConvention javaPluginConvention = + project.getConvention().getPlugin(JavaPluginConvention.class); + + SourceSet mainSourceSet = javaPluginConvention.getSourceSets().getByName(MAIN_SOURCE_SET_NAME); + + List dependenciesFiles = new ArrayList<>(); + List snapshotDependenciesFiles = new ArrayList<>(); + List resourcesFiles = new ArrayList<>(); + List classesFiles = new ArrayList<>(); + List extraFiles = new ArrayList<>(); + + // Adds each file in each classes output directory to the classes files list. + FileCollection classesOutputDirectories = mainSourceSet.getOutput().getClassesDirs(); + for (File classesOutputDirectory : classesOutputDirectories) { + if (Files.notExists(classesOutputDirectory.toPath())) { + // Warns that output directory was not found. + gradleBuildLogger.warn( + "Could not find build output directory '" + classesOutputDirectory + "'"); + continue; + } + try (Stream classFileStream = Files.list(classesOutputDirectory.toPath())) { + classFileStream.forEach(classesFiles::add); + } + } + if (classesFiles.isEmpty()) { + gradleBuildLogger.warn("No classes files were found - did you compile your project?"); + } + + // Adds each file in the resources output directory to the resources files list. + Path resourcesOutputDirectory = mainSourceSet.getOutput().getResourcesDir().toPath(); + if (Files.exists(resourcesOutputDirectory)) { + try (Stream resourceFileStream = Files.list(resourcesOutputDirectory)) { + resourceFileStream.forEach(resourcesFiles::add); + } + } + + // Adds all other files to the dependencies files list. + FileCollection allFiles = mainSourceSet.getRuntimeClasspath(); + // Removes the classes output directories. + allFiles = allFiles.minus(classesOutputDirectories); + for (File dependencyFile : allFiles) { + // Removes the resources output directory. + if (resourcesOutputDirectory.equals(dependencyFile.toPath())) { + continue; + } + if (dependencyFile.getName().contains("SNAPSHOT")) { + snapshotDependenciesFiles.add(dependencyFile.toPath()); + } else { + dependenciesFiles.add(dependencyFile.toPath()); + } + } + + // Adds all the extra files. + if (Files.exists(extraDirectory)) { + try (Stream extraFilesLayerDirectoryFiles = Files.list(extraDirectory)) { + extraFiles = extraFilesLayerDirectoryFiles.collect(Collectors.toList()); + } + } + + // Sorts all files by path for consistent ordering. + Collections.sort(dependenciesFiles); + Collections.sort(snapshotDependenciesFiles); + Collections.sort(resourcesFiles); + Collections.sort(classesFiles); + Collections.sort(extraFiles); + + return new GradleLayerConfigurations( + LayerConfiguration.builder() + .addEntry( + dependenciesFiles, JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE) + .setLabel(DEPENDENCIES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry( + snapshotDependenciesFiles, + JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE) + .setLabel(SNAPSHOT_DEPENDENCIES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(resourcesFiles, JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE) + .setLabel(RESOURCES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(classesFiles, JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE) + .setLabel(CLASSES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(extraFiles, "/") + .setLabel(EXTRA_FILES_LAYER_LABEL) + .build()); + } + + private final LayerConfiguration dependenciesLayerConfiguration; + private final LayerConfiguration snapshotDependenciesLayerConfiguration; + private final LayerConfiguration resourcesLayerConfiguration; + private final LayerConfiguration classesLayerConfiguration; + private final LayerConfiguration extraFilesLayerConfiguration; + + // TODO: Consolidate with MavenLayerConfigurations. + /** Instantiate with {@link #getForProject}. */ + private GradleLayerConfigurations( + LayerConfiguration dependenciesLayerConfiguration, + LayerConfiguration snapshotDependenciesLayerConfiguration, + LayerConfiguration resourcesLayerConfiguration, + LayerConfiguration classesLayerConfiguration, + LayerConfiguration extraFilesLayerConfiguration) { + this.dependenciesLayerConfiguration = dependenciesLayerConfiguration; + this.snapshotDependenciesLayerConfiguration = snapshotDependenciesLayerConfiguration; + this.resourcesLayerConfiguration = resourcesLayerConfiguration; + this.classesLayerConfiguration = classesLayerConfiguration; + this.extraFilesLayerConfiguration = extraFilesLayerConfiguration; + } + + ImmutableList getLayerConfigurations() { + return ImmutableList.of( + dependenciesLayerConfiguration, + snapshotDependenciesLayerConfiguration, + resourcesLayerConfiguration, + classesLayerConfiguration, + extraFilesLayerConfiguration); + } + + LayerEntry getDependenciesLayerEntry() { + return dependenciesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getSnapshotDependenciesLayerEntry() { + return snapshotDependenciesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getResourcesLayerEntry() { + return resourcesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getClassesLayerEntry() { + return classesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getExtraFilesLayerEntry() { + return extraFilesLayerConfiguration.getLayerEntries().get(0); + } +} diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleProjectProperties.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleProjectProperties.java index 259ac8f3f0..f93b2ea204 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleProjectProperties.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleProjectProperties.java @@ -17,16 +17,18 @@ package com.google.cloud.tools.jib.gradle; import com.google.cloud.tools.jib.builder.BuildLogger; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; import com.google.cloud.tools.jib.frontend.MainClassFinder; import com.google.cloud.tools.jib.frontend.MainClassInferenceException; import com.google.cloud.tools.jib.frontend.ProjectProperties; import com.google.cloud.tools.jib.image.ImageReference; import com.google.cloud.tools.jib.image.InvalidImageReferenceException; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -49,12 +51,12 @@ class GradleProjectProperties implements ProjectProperties { /** @return a GradleProjectProperties from the given project and logger. */ static GradleProjectProperties getForProject( - Project project, GradleBuildLogger gradleBuildLogger) { + Project project, GradleBuildLogger gradleBuildLogger, Path extraDirectory) { try { return new GradleProjectProperties( project, gradleBuildLogger, - GradleSourceFilesConfiguration.getForProject(project, gradleBuildLogger)); + GradleLayerConfigurations.getForProject(project, gradleBuildLogger, extraDirectory)); } catch (IOException ex) { throw new GradleException("Obtaining project build output files failed", ex); @@ -63,21 +65,46 @@ static GradleProjectProperties getForProject( private final Project project; private final GradleBuildLogger gradleBuildLogger; - private final SourceFilesConfiguration sourceFilesConfiguration; + private final GradleLayerConfigurations gradleLayerConfigurations; @VisibleForTesting GradleProjectProperties( Project project, GradleBuildLogger gradleBuildLogger, - SourceFilesConfiguration sourceFilesConfiguration) { + GradleLayerConfigurations gradleLayerConfigurations) { this.project = project; this.gradleBuildLogger = gradleBuildLogger; - this.sourceFilesConfiguration = sourceFilesConfiguration; + this.gradleLayerConfigurations = gradleLayerConfigurations; } @Override - public SourceFilesConfiguration getSourceFilesConfiguration() { - return sourceFilesConfiguration; + public ImmutableList getLayerConfigurations() { + return gradleLayerConfigurations.getLayerConfigurations(); + } + + @Override + public LayerEntry getDependenciesLayerEntry() { + return gradleLayerConfigurations.getDependenciesLayerEntry(); + } + + @Override + public LayerEntry getSnapshotDependenciesLayerEntry() { + return gradleLayerConfigurations.getSnapshotDependenciesLayerEntry(); + } + + @Override + public LayerEntry getResourcesLayerEntry() { + return gradleLayerConfigurations.getResourcesLayerEntry(); + } + + @Override + public LayerEntry getClassesLayerEntry() { + return gradleLayerConfigurations.getClassesLayerEntry(); + } + + @Override + public LayerEntry getExtraFilesLayerEntry() { + return gradleLayerConfigurations.getExtraFilesLayerEntry(); } @Override diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfiguration.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfiguration.java deleted file mode 100644 index 56397b79b9..0000000000 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfiguration.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * 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.builder.SourceFilesConfiguration; -import com.google.common.collect.ImmutableList; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; -import org.gradle.api.Project; -import org.gradle.api.file.FileCollection; -import org.gradle.api.plugins.JavaPluginConvention; -import org.gradle.api.tasks.SourceSet; - -/** {@link SourceFilesConfiguration} implementation based on inputs from a {@link Project}. */ -class GradleSourceFilesConfiguration implements SourceFilesConfiguration { - - /** Name of the `main` {@link SourceSet} to use as source files. */ - private static final String MAIN_SOURCE_SET_NAME = "main"; - - /** Resolves the source files configuration for a Gradle {@link Project}. */ - static GradleSourceFilesConfiguration getForProject( - Project project, GradleBuildLogger gradleBuildLogger) throws IOException { - return new GradleSourceFilesConfiguration(project, gradleBuildLogger); - } - - private final ImmutableList dependenciesFiles; - private final ImmutableList snapshotDependenciesFiles; - private final ImmutableList resourcesFiles; - private final ImmutableList classesFiles; - - /** Instantiate with {@link #getForProject}. */ - private GradleSourceFilesConfiguration(Project project, GradleBuildLogger gradleBuildLogger) - throws IOException { - JavaPluginConvention javaPluginConvention = - project.getConvention().getPlugin(JavaPluginConvention.class); - - SourceSet mainSourceSet = javaPluginConvention.getSourceSets().getByName(MAIN_SOURCE_SET_NAME); - - List dependenciesFiles = new ArrayList<>(); - List snapshotDependenciesFiles = new ArrayList<>(); - List resourcesFiles = new ArrayList<>(); - List classesFiles = new ArrayList<>(); - - // Adds each file in each classes output directory to the classes files list. - FileCollection classesOutputDirectories = mainSourceSet.getOutput().getClassesDirs(); - for (File classesOutputDirectory : classesOutputDirectories) { - if (Files.notExists(classesOutputDirectory.toPath())) { - // Warns that output directory was not found. - gradleBuildLogger.warn( - "Could not find build output directory '" + classesOutputDirectory + "'"); - continue; - } - try (Stream classFileStream = Files.list(classesOutputDirectory.toPath())) { - classFileStream.forEach(classesFiles::add); - } - } - if (classesFiles.isEmpty()) { - gradleBuildLogger.warn("No classes files were found - did you compile your project?"); - } - - // Adds each file in the resources output directory to the resources files list. - Path resourcesOutputDirectory = mainSourceSet.getOutput().getResourcesDir().toPath(); - if (Files.exists(resourcesOutputDirectory)) { - try (Stream resourceFileStream = Files.list(resourcesOutputDirectory)) { - resourceFileStream.forEach(resourcesFiles::add); - } - } - - // Adds all other files to the dependencies files list. - FileCollection allFiles = mainSourceSet.getRuntimeClasspath(); - // Removes the classes output directories. - allFiles = allFiles.minus(classesOutputDirectories); - for (File dependencyFile : allFiles) { - // Removes the resources output directory. - if (resourcesOutputDirectory.equals(dependencyFile.toPath())) { - continue; - } - if (dependencyFile.getName().contains("SNAPSHOT")) { - snapshotDependenciesFiles.add(dependencyFile.toPath()); - } else { - dependenciesFiles.add(dependencyFile.toPath()); - } - } - - // Sorts all files by path for consistent ordering. - this.dependenciesFiles = ImmutableList.sortedCopyOf(dependenciesFiles); - this.snapshotDependenciesFiles = ImmutableList.sortedCopyOf(snapshotDependenciesFiles); - this.resourcesFiles = ImmutableList.sortedCopyOf(resourcesFiles); - this.classesFiles = ImmutableList.sortedCopyOf(classesFiles); - } - - @Override - public ImmutableList getDependenciesFiles() { - return dependenciesFiles; - } - - @Override - public ImmutableList getSnapshotDependenciesFiles() { - return snapshotDependenciesFiles; - } - - @Override - public ImmutableList getResourcesFiles() { - return resourcesFiles; - } - - @Override - public ImmutableList getClassesFiles() { - return classesFiles; - } - - @Override - public String getDependenciesPathOnImage() { - return DEFAULT_DEPENDENCIES_PATH_ON_IMAGE; - } - - @Override - public String getResourcesPathOnImage() { - return DEFAULT_RESOURCES_PATH_ON_IMAGE; - } - - @Override - public String getClassesPathOnImage() { - return DEFAULT_CLASSES_PATH_ON_IMAGE; - } -} diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfigurationTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationsTest.java similarity index 72% rename from jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfigurationTest.java rename to jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationsTest.java index cd70617596..56d372eff3 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleSourceFilesConfigurationTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationsTest.java @@ -42,9 +42,9 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -/** Test for {@link GradleSourceFilesConfiguration}. */ +/** Test for {@link GradleLayerConfigurations}. */ @RunWith(MockitoJUnitRunner.class) -public class GradleSourceFilesConfigurationTest { +public class GradleLayerConfigurationsTest { /** Implementation of {@link FileCollection} that just holds a set of {@link File}s. */ private static class TestFileCollection extends AbstractFileCollection { @@ -74,7 +74,7 @@ public Set getFiles() { @Mock private SourceSetOutput mockMainSourceSetOutput; @Mock private GradleBuildLogger mockGradleBuildLogger; - private GradleSourceFilesConfiguration testGradleSourceFilesConfiguration; + private GradleLayerConfigurations testGradleLayerConfigurations; @Before public void setUp() throws URISyntaxException, IOException { @@ -110,8 +110,9 @@ public void setUp() throws URISyntaxException, IOException { Mockito.when(mockMainSourceSetOutput.getResourcesDir()).thenReturn(resourcesOutputDir); Mockito.when(mockMainSourceSet.getRuntimeClasspath()).thenReturn(runtimeFileCollection); - testGradleSourceFilesConfiguration = - GradleSourceFilesConfiguration.getForProject(mockProject, mockGradleBuildLogger); + testGradleLayerConfigurations = + GradleLayerConfigurations.getForProject( + mockProject, mockGradleBuildLogger, Paths.get("nonexistent/path")); } @Test @@ -137,15 +138,23 @@ public void test_correctFiles() throws URISyntaxException { Paths.get(Resources.getResource("application/classes").toURI()) .resolve("HelloWorld.class"), Paths.get(Resources.getResource("application/classes").toURI()).resolve("some.class")); + ImmutableList expectedExtraFiles = ImmutableList.of(); Assert.assertEquals( - expectedDependenciesFiles, testGradleSourceFilesConfiguration.getDependenciesFiles()); + expectedDependenciesFiles, + testGradleLayerConfigurations.getDependenciesLayerEntry().getSourceFiles()); Assert.assertEquals( expectedSnapshotDependenciesFiles, - testGradleSourceFilesConfiguration.getSnapshotDependenciesFiles()); + testGradleLayerConfigurations.getSnapshotDependenciesLayerEntry().getSourceFiles()); Assert.assertEquals( - expectedResourcesFiles, testGradleSourceFilesConfiguration.getResourcesFiles()); - Assert.assertEquals(expectedClassesFiles, testGradleSourceFilesConfiguration.getClassesFiles()); + expectedResourcesFiles, + testGradleLayerConfigurations.getResourcesLayerEntry().getSourceFiles()); + Assert.assertEquals( + expectedClassesFiles, + testGradleLayerConfigurations.getClassesLayerEntry().getSourceFiles()); + Assert.assertEquals( + expectedExtraFiles, + testGradleLayerConfigurations.getExtraFilesLayerEntry().getSourceFiles()); } @Test @@ -154,8 +163,9 @@ public void test_noClassesFiles() throws IOException { Mockito.when(mockMainSourceSetOutput.getClassesDirs()) .thenReturn(new TestFileCollection(ImmutableSet.of(nonexistentFile))); - testGradleSourceFilesConfiguration = - GradleSourceFilesConfiguration.getForProject(mockProject, mockGradleBuildLogger); + testGradleLayerConfigurations = + GradleLayerConfigurations.getForProject( + mockProject, mockGradleBuildLogger, Paths.get("nonexistent/path")); Mockito.verify(mockGradleBuildLogger) .warn("Could not find build output directory '" + nonexistentFile + "'"); @@ -163,13 +173,39 @@ public void test_noClassesFiles() throws IOException { .warn("No classes files were found - did you compile your project?"); } + @Test + public void test_extraFiles() throws URISyntaxException, IOException { + Path extraFilesDirectory = Paths.get(Resources.getResource("layer").toURI()); + + testGradleLayerConfigurations = + GradleLayerConfigurations.getForProject( + mockProject, mockGradleBuildLogger, extraFilesDirectory); + + ImmutableList expectedExtraFiles = + ImmutableList.of( + Paths.get(Resources.getResource("layer/a").toURI()), + Paths.get(Resources.getResource("layer/c").toURI()), + Paths.get(Resources.getResource("layer/foo").toURI())); + + Assert.assertEquals( + expectedExtraFiles, + testGradleLayerConfigurations.getExtraFilesLayerEntry().getSourceFiles()); + } + @Test public void test_correctPathsOnImage() { Assert.assertEquals( - "/app/libs/", testGradleSourceFilesConfiguration.getDependenciesPathOnImage()); + "/app/libs/", + testGradleLayerConfigurations.getDependenciesLayerEntry().getExtractionPath()); + Assert.assertEquals( + "/app/libs/", + testGradleLayerConfigurations.getSnapshotDependenciesLayerEntry().getExtractionPath()); + Assert.assertEquals( + "/app/resources/", + testGradleLayerConfigurations.getResourcesLayerEntry().getExtractionPath()); Assert.assertEquals( - "/app/resources/", testGradleSourceFilesConfiguration.getResourcesPathOnImage()); + "/app/classes/", testGradleLayerConfigurations.getClassesLayerEntry().getExtractionPath()); Assert.assertEquals( - "/app/classes/", testGradleSourceFilesConfiguration.getClassesPathOnImage()); + "/", testGradleLayerConfigurations.getExtraFilesLayerEntry().getExtractionPath()); } } diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleProjectPropertiesTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleProjectPropertiesTest.java index 4e054165ad..696a46f5d7 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleProjectPropertiesTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/GradleProjectPropertiesTest.java @@ -16,7 +16,6 @@ package com.google.cloud.tools.jib.gradle; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.image.ImageReference; import com.google.cloud.tools.jib.image.InvalidImageReferenceException; import com.google.common.collect.ImmutableMap; @@ -44,9 +43,9 @@ public class GradleProjectPropertiesTest { @Mock private Jar mockJar2; @Mock private Project mockProject; @Mock private GradleBuildLogger mockGradleBuildLogger; - @Mock private SourceFilesConfiguration mockSourceFilesConfiguration; @Mock private JibExtension mockJibExtension; @Mock private GradleBuildLogger mockBuildLogger; + @Mock private GradleLayerConfigurations mockGradleLayerConfigurations; private Manifest manifest; private GradleProjectProperties gradleProjectProperties; @@ -58,7 +57,7 @@ public void setup() { gradleProjectProperties = new GradleProjectProperties( - mockProject, mockGradleBuildLogger, mockSourceFilesConfiguration); + mockProject, mockGradleBuildLogger, mockGradleLayerConfigurations); } @Test @@ -71,7 +70,7 @@ public void testGetMainClassFromJar_success() { @Test public void testGetMainClassFromJar_missing() { Mockito.when(mockProject.getTasksByName("jar", false)).thenReturn(Collections.emptySet()); - Assert.assertEquals(null, gradleProjectProperties.getMainClassFromJar()); + Assert.assertNull(gradleProjectProperties.getMainClassFromJar()); } @Test @@ -79,7 +78,7 @@ public void testGetMainClassFromJar_multiple() { manifest.attributes(ImmutableMap.of("Main-Class", "some.main.class")); Mockito.when(mockProject.getTasksByName("jar", false)) .thenReturn(ImmutableSet.of(mockJar, mockJar2)); - Assert.assertEquals(null, gradleProjectProperties.getMainClassFromJar()); + Assert.assertNull(gradleProjectProperties.getMainClassFromJar()); } @Test diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildDockerMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildDockerMojo.java index 956c8fa243..af7ae1f47e 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildDockerMojo.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildDockerMojo.java @@ -19,24 +19,19 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.docker.DockerClient; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.image.ImageReference; import com.google.cloud.tools.jib.registry.RegistryClient; import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.time.Instant; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.ResolutionScope; @@ -67,7 +62,7 @@ public void execute() throws MojoExecutionException { // Parses 'from' and 'to' into image reference. MavenProjectProperties mavenProjectProperties = - MavenProjectProperties.getForProject(getProject(), mavenBuildLogger); + MavenProjectProperties.getForProject(getProject(), mavenBuildLogger, getExtraDirectory()); ImageReference baseImage = parseImageReference(getBaseImage(), "from"); ImageReference targetImage = mavenProjectProperties.getGeneratedTargetDockerTag(getTargetImage(), mavenBuildLogger); @@ -94,24 +89,13 @@ public void execute() throws MojoExecutionException { .setBaseImageCredentialHelperName(getBaseImageCredentialHelperName()) .setKnownBaseRegistryCredentials(knownBaseRegistryCredentials) .setTargetImage(targetImage) - .setMainClass(mainClass) .setJavaArguments(getArgs()) - .setJvmFlags(getJvmFlags()) .setEnvironment(getEnvironment()) .setExposedPorts(ExposedPortsParser.parse(getExposedPorts())) - .setAllowInsecureRegistries(getAllowInsecureRegistries()); - if (getExtraDirectory() != null && Files.exists(getExtraDirectory())) { - try (Stream extraFilesLayerDirectoryFiles = Files.list(getExtraDirectory())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - - } catch (IOException ex) { - throw new MojoExecutionException( - "Failed to list directory for extra files: " + getExtraDirectory(), ex); - } - } + .setAllowInsecureRegistries(getAllowInsecureRegistries()) + .setLayerConfigurations(mavenProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint(getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(mavenProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -134,9 +118,7 @@ public void execute() throws MojoExecutionException { RegistryClient.setUserAgentSuffix(USER_AGENT_SUFFIX); try { - BuildStepsRunner.forBuildToDockerDaemon( - buildConfiguration, mavenProjectProperties.getSourceFilesConfiguration()) - .build(HELPFUL_SUGGESTIONS); + BuildStepsRunner.forBuildToDockerDaemon(buildConfiguration).build(HELPFUL_SUGGESTIONS); getLog().info(""); } catch (CacheDirectoryCreationException | BuildStepsExecutionException ex) { diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java index 68b46468d2..4d88722635 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java @@ -19,11 +19,11 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.image.ImageFormat; import com.google.cloud.tools.jib.image.ImageReference; @@ -32,13 +32,8 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.time.Instant; import java.util.Arrays; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; @@ -103,7 +98,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { mavenSettingsServerCredentials.retrieve(targetImage.getRegistry()); MavenProjectProperties mavenProjectProperties = - MavenProjectProperties.getForProject(getProject(), mavenBuildLogger); + MavenProjectProperties.getForProject(getProject(), mavenBuildLogger, getExtraDirectory()); String mainClass = mavenProjectProperties.getMainClass(this); // Builds the BuildConfiguration. @@ -115,25 +110,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { .setTargetImage(targetImage) .setTargetImageCredentialHelperName(getTargetImageCredentialHelperName()) .setKnownTargetRegistryCredentials(knownTargetRegistryCredentials) - .setMainClass(mainClass) .setJavaArguments(getArgs()) - .setJvmFlags(getJvmFlags()) .setEnvironment(getEnvironment()) .setExposedPorts(ExposedPortsParser.parse(getExposedPorts())) .setTargetFormat(ImageFormat.valueOf(getFormat()).getManifestTemplateClass()) - .setAllowInsecureRegistries(getAllowInsecureRegistries()); - if (getExtraDirectory() != null && Files.exists(getExtraDirectory())) { - try (Stream extraFilesLayerDirectoryFiles = Files.list(getExtraDirectory())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - - } catch (IOException ex) { - throw new MojoExecutionException( - "Failed to list directory for extra files: " + getExtraDirectory(), ex); - } - } + .setAllowInsecureRegistries(getAllowInsecureRegistries()) + .setLayerConfigurations(mavenProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint(getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(mavenProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -156,9 +140,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { RegistryClient.setUserAgentSuffix(USER_AGENT_SUFFIX); try { - BuildStepsRunner.forBuildImage( - buildConfiguration, mavenProjectProperties.getSourceFilesConfiguration()) - .build(HELPFUL_SUGGESTIONS); + BuildStepsRunner.forBuildImage(buildConfiguration).build(HELPFUL_SUGGESTIONS); getLog().info(""); } catch (CacheDirectoryCreationException | BuildStepsExecutionException ex) { diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildTarMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildTarMojo.java index 9ea8915265..b5c59fe153 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildTarMojo.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildTarMojo.java @@ -19,24 +19,19 @@ import com.google.cloud.tools.jib.builder.BuildConfiguration; import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException; import com.google.cloud.tools.jib.configuration.CacheConfiguration; -import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.BuildStepsExecutionException; import com.google.cloud.tools.jib.frontend.BuildStepsRunner; import com.google.cloud.tools.jib.frontend.ExposedPortsParser; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; import com.google.cloud.tools.jib.frontend.SystemPropertyValidator; import com.google.cloud.tools.jib.image.ImageReference; import com.google.cloud.tools.jib.registry.RegistryClient; import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.ResolutionScope; @@ -65,7 +60,7 @@ public void execute() throws MojoExecutionException { // Parses 'from' and 'to' into image reference. MavenProjectProperties mavenProjectProperties = - MavenProjectProperties.getForProject(getProject(), mavenBuildLogger); + MavenProjectProperties.getForProject(getProject(), mavenBuildLogger, getExtraDirectory()); ImageReference baseImage = parseImageReference(getBaseImage(), "from"); ImageReference targetImage = mavenProjectProperties.getGeneratedTargetDockerTag(getTargetImage(), mavenBuildLogger); @@ -92,24 +87,13 @@ public void execute() throws MojoExecutionException { .setBaseImageCredentialHelperName(getBaseImageCredentialHelperName()) .setKnownBaseRegistryCredentials(knownBaseRegistryCredentials) .setTargetImage(targetImage) - .setMainClass(mainClass) .setJavaArguments(getArgs()) - .setJvmFlags(getJvmFlags()) .setEnvironment(getEnvironment()) .setExposedPorts(ExposedPortsParser.parse(getExposedPorts())) - .setAllowInsecureRegistries(getAllowInsecureRegistries()); - if (getExtraDirectory() != null && Files.exists(getExtraDirectory())) { - try (Stream extraFilesLayerDirectoryFiles = Files.list(getExtraDirectory())) { - buildConfigurationBuilder.setExtraFilesLayerConfiguration( - LayerConfiguration.builder() - .addEntry(extraFilesLayerDirectoryFiles.collect(Collectors.toList()), "/") - .build()); - - } catch (IOException ex) { - throw new MojoExecutionException( - "Failed to list directory for extra files: " + getExtraDirectory(), ex); - } - } + .setAllowInsecureRegistries(getAllowInsecureRegistries()) + .setLayerConfigurations(mavenProjectProperties.getLayerConfigurations()) + .setEntrypoint( + JavaEntrypointConstructor.makeDefaultEntrypoint(getJvmFlags(), mainClass)); CacheConfiguration applicationLayersCacheConfiguration = CacheConfiguration.forPath(mavenProjectProperties.getCacheDirectory()); buildConfigurationBuilder.setApplicationLayersCacheConfiguration( @@ -134,8 +118,7 @@ public void execute() throws MojoExecutionException { try { BuildStepsRunner.forBuildTar( Paths.get(getProject().getBuild().getDirectory()).resolve("jib-image.tar"), - buildConfiguration, - mavenProjectProperties.getSourceFilesConfiguration()) + buildConfiguration) .build(HELPFUL_SUGGESTIONS); getLog().info(""); diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/DockerContextMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/DockerContextMojo.java index 9911661fee..bff24b268a 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/DockerContextMojo.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/DockerContextMojo.java @@ -26,7 +26,6 @@ import java.nio.file.Paths; import javax.annotation.Nullable; import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; @@ -47,7 +46,7 @@ public class DockerContextMojo extends JibPluginConfiguration { private String targetDir; @Override - public void execute() throws MojoExecutionException, MojoFailureException { + public void execute() throws MojoExecutionException { MavenBuildLogger mavenBuildLogger = new MavenBuildLogger(getLog()); handleDeprecatedParameters(mavenBuildLogger); SystemPropertyValidator.checkHttpTimeoutProperty(MojoExecutionException::new); @@ -55,7 +54,7 @@ public void execute() throws MojoExecutionException, MojoFailureException { Preconditions.checkNotNull(targetDir); MavenProjectProperties mavenProjectProperties = - MavenProjectProperties.getForProject(getProject(), mavenBuildLogger); + MavenProjectProperties.getForProject(getProject(), mavenBuildLogger, getExtraDirectory()); String mainClass = mavenProjectProperties.getMainClass(this); try { @@ -63,8 +62,12 @@ public void execute() throws MojoExecutionException, MojoFailureException { // here. ExposedPortsParser.parse(getExposedPorts()); - // TODO: Add support for extra files layer. - new DockerContextGenerator(mavenProjectProperties.getSourceFilesConfiguration()) + new DockerContextGenerator( + mavenProjectProperties.getDependenciesLayerEntry(), + mavenProjectProperties.getSnapshotDependenciesLayerEntry(), + mavenProjectProperties.getResourcesLayerEntry(), + mavenProjectProperties.getClassesLayerEntry(), + mavenProjectProperties.getExtraFilesLayerEntry()) .setBaseImage(getBaseImage()) .setJvmFlags(getJvmFlags()) .setMainClass(mainClass) diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/JibPluginConfiguration.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/JibPluginConfiguration.java index 28b16c082c..59dfa41ed1 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/JibPluginConfiguration.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/JibPluginConfiguration.java @@ -231,10 +231,9 @@ boolean getAllowInsecureRegistries() { return allowInsecureRegistries; } - @Nullable Path getExtraDirectory() { // TODO: Should inform user about nonexistent directory if using custom directory. - return Paths.get(extraDirectory); + return Paths.get(Preconditions.checkNotNull(extraDirectory)); } @VisibleForTesting diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurations.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurations.java new file mode 100644 index 0000000000..78f3e25411 --- /dev/null +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurations.java @@ -0,0 +1,194 @@ +/* + * Copyright 2018 Google LLC. All rights reserved. + * + * 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.maven; + +import com.google.cloud.tools.jib.configuration.LayerConfiguration; +import com.google.cloud.tools.jib.frontend.JavaEntrypointConstructor; +import com.google.cloud.tools.jib.image.LayerEntry; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.maven.artifact.Artifact; +import org.apache.maven.project.MavenProject; + +/** Builds {@link LayerConfiguration}s based on inputs from a {@link MavenProject}. */ +class MavenLayerConfigurations { + + private static final String DEPENDENCIES_LAYER_LABEL = "dependencies"; + private static final String SNAPSHOT_DEPENDENCIES_LAYER_LABEL = "snapshot dependencies"; + private static final String RESOURCES_LAYER_LABEL = "resources"; + private static final String CLASSES_LAYER_LABEL = "classes"; + private static final String EXTRA_FILES_LAYER_LABEL = "extra files"; + + /** + * Resolves the source files configuration for a {@link MavenProject}. + * + * @param project the {@link MavenProject} + * @param extraDirectory path to the directory for the extra files layer + * @return a new {@link MavenLayerConfigurations} for the project + * @throws IOException if collecting the project files fails + */ + static MavenLayerConfigurations getForProject(MavenProject project, Path extraDirectory) + throws IOException { + Path classesSourceDirectory = Paths.get(project.getBuild().getSourceDirectory()); + Path classesOutputDirectory = Paths.get(project.getBuild().getOutputDirectory()); + + List dependenciesFiles = new ArrayList<>(); + List snapshotDependenciesFiles = new ArrayList<>(); + List resourcesFiles = new ArrayList<>(); + List classesFiles = new ArrayList<>(); + List extraFiles = new ArrayList<>(); + + // Gets all the dependencies. + for (Artifact artifact : project.getArtifacts()) { + if (artifact.isSnapshot()) { + snapshotDependenciesFiles.add(artifact.getFile().toPath()); + } else { + dependenciesFiles.add(artifact.getFile().toPath()); + } + } + + // Gets the classes files in the 'classes' output directory. It finds the files that are classes + // files by matching them against the .java source files. All other files are deemed resources. + try (Stream classFileStream = Files.list(classesOutputDirectory)) { + classFileStream.forEach( + classFile -> { + /* + * Adds classFile to classesFiles if it is a .class file or is a directory that also + * exists in the classes source directory; otherwise, adds file to resourcesFiles. + */ + if (Files.isDirectory(classFile) + && Files.exists( + classesSourceDirectory.resolve(classesOutputDirectory.relativize(classFile)))) { + classesFiles.add(classFile); + return; + } + + if (FileSystems.getDefault().getPathMatcher("glob:**.class").matches(classFile)) { + classesFiles.add(classFile); + return; + } + + resourcesFiles.add(classFile); + }); + } + + // Adds all the extra files. + if (Files.exists(extraDirectory)) { + try (Stream extraFilesLayerDirectoryFiles = Files.list(extraDirectory)) { + extraFiles = extraFilesLayerDirectoryFiles.collect(Collectors.toList()); + + } catch (IOException ex) { + throw new IOException("Failed to list directory for extra files: " + extraDirectory, ex); + } + } + + // Sort all files by path for consistent ordering. + Collections.sort(dependenciesFiles); + Collections.sort(snapshotDependenciesFiles); + Collections.sort(resourcesFiles); + Collections.sort(classesFiles); + Collections.sort(extraFiles); + + return new MavenLayerConfigurations( + LayerConfiguration.builder() + .addEntry( + dependenciesFiles, JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE) + .setLabel(DEPENDENCIES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry( + snapshotDependenciesFiles, + JavaEntrypointConstructor.DEFAULT_DEPENDENCIES_PATH_ON_IMAGE) + .setLabel(SNAPSHOT_DEPENDENCIES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(resourcesFiles, JavaEntrypointConstructor.DEFAULT_RESOURCES_PATH_ON_IMAGE) + .setLabel(RESOURCES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(classesFiles, JavaEntrypointConstructor.DEFAULT_CLASSES_PATH_ON_IMAGE) + .setLabel(CLASSES_LAYER_LABEL) + .build(), + LayerConfiguration.builder() + .addEntry(extraFiles, "/") + .setLabel(EXTRA_FILES_LAYER_LABEL) + .build()); + } + + private final LayerConfiguration dependenciesLayerConfiguration; + private final LayerConfiguration snapshotDependenciesLayerConfiguration; + private final LayerConfiguration resourcesLayerConfiguration; + private final LayerConfiguration classesLayerConfiguration; + private final LayerConfiguration extraFilesLayerConfiguration; + + /** Instantiate with {@link #getForProject}. */ + private MavenLayerConfigurations( + LayerConfiguration dependenciesLayerConfiguration, + LayerConfiguration snapshotDependenciesLayerConfiguration, + LayerConfiguration resourcesLayerConfiguration, + LayerConfiguration classesLayerConfiguration, + LayerConfiguration extraFilesLayerConfiguration) { + this.dependenciesLayerConfiguration = dependenciesLayerConfiguration; + this.snapshotDependenciesLayerConfiguration = snapshotDependenciesLayerConfiguration; + this.resourcesLayerConfiguration = resourcesLayerConfiguration; + this.classesLayerConfiguration = classesLayerConfiguration; + this.extraFilesLayerConfiguration = extraFilesLayerConfiguration; + } + + /** + * Gets the list of {@link LayerConfiguration}s to use to build the container image. + * + * @return the list of {@link LayerConfiguration}s + */ + ImmutableList getLayerConfigurations() { + return ImmutableList.of( + dependenciesLayerConfiguration, + snapshotDependenciesLayerConfiguration, + resourcesLayerConfiguration, + classesLayerConfiguration, + extraFilesLayerConfiguration); + } + + LayerEntry getDependenciesLayerEntry() { + return dependenciesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getSnapshotDependenciesLayerEntry() { + return snapshotDependenciesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getResourcesLayerEntry() { + return resourcesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getClassesLayerEntry() { + return classesLayerConfiguration.getLayerEntries().get(0); + } + + LayerEntry getExtraFilesLayerEntry() { + return extraFilesLayerConfiguration.getLayerEntries().get(0); + } +} diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenProjectProperties.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenProjectProperties.java index 45239fc581..7b2c175cec 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenProjectProperties.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenProjectProperties.java @@ -17,14 +17,16 @@ package com.google.cloud.tools.jib.maven; import com.google.cloud.tools.jib.builder.BuildLogger; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; +import com.google.cloud.tools.jib.configuration.LayerConfiguration; import com.google.cloud.tools.jib.frontend.HelpfulSuggestions; import com.google.cloud.tools.jib.frontend.MainClassFinder; import com.google.cloud.tools.jib.frontend.MainClassInferenceException; import com.google.cloud.tools.jib.frontend.ProjectProperties; import com.google.cloud.tools.jib.image.ImageReference; +import com.google.cloud.tools.jib.image.LayerEntry; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -43,14 +45,18 @@ class MavenProjectProperties implements ProjectProperties { /** * @param project the {@link MavenProject} for the plugin. * @param mavenBuildLogger the logger used for printing status messages. + * @param extraDirectory path to the directory for the extra files layer * @return a MavenProjectProperties from the given project and logger. * @throws MojoExecutionException if no class files are found in the output directory. */ static MavenProjectProperties getForProject( - MavenProject project, MavenBuildLogger mavenBuildLogger) throws MojoExecutionException { + MavenProject project, MavenBuildLogger mavenBuildLogger, Path extraDirectory) + throws MojoExecutionException { try { return new MavenProjectProperties( - project, mavenBuildLogger, MavenSourceFilesConfiguration.getForProject(project)); + project, + mavenBuildLogger, + MavenLayerConfigurations.getForProject(project, extraDirectory)); } catch (IOException ex) { throw new MojoExecutionException( "Obtaining project build output files failed; make sure you have compiled your project " @@ -62,21 +68,46 @@ static MavenProjectProperties getForProject( private final MavenProject project; private final MavenBuildLogger mavenBuildLogger; - private final SourceFilesConfiguration sourceFilesConfiguration; + private final MavenLayerConfigurations mavenLayerConfigurations; @VisibleForTesting MavenProjectProperties( MavenProject project, MavenBuildLogger mavenBuildLogger, - SourceFilesConfiguration sourceFilesConfiguration) { + MavenLayerConfigurations mavenLayerConfigurations) { this.project = project; this.mavenBuildLogger = mavenBuildLogger; - this.sourceFilesConfiguration = sourceFilesConfiguration; + this.mavenLayerConfigurations = mavenLayerConfigurations; } @Override - public SourceFilesConfiguration getSourceFilesConfiguration() { - return sourceFilesConfiguration; + public ImmutableList getLayerConfigurations() { + return mavenLayerConfigurations.getLayerConfigurations(); + } + + @Override + public LayerEntry getDependenciesLayerEntry() { + return mavenLayerConfigurations.getDependenciesLayerEntry(); + } + + @Override + public LayerEntry getSnapshotDependenciesLayerEntry() { + return mavenLayerConfigurations.getSnapshotDependenciesLayerEntry(); + } + + @Override + public LayerEntry getResourcesLayerEntry() { + return mavenLayerConfigurations.getResourcesLayerEntry(); + } + + @Override + public LayerEntry getClassesLayerEntry() { + return mavenLayerConfigurations.getClassesLayerEntry(); + } + + @Override + public LayerEntry getExtraFilesLayerEntry() { + return mavenLayerConfigurations.getExtraFilesLayerEntry(); } @Override diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfiguration.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfiguration.java deleted file mode 100644 index a3d919eaf5..0000000000 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfiguration.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2018 Google LLC. All rights reserved. - * - * 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.maven; - -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; -import com.google.common.collect.ImmutableList; -import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Stream; -import org.apache.maven.artifact.Artifact; -import org.apache.maven.project.MavenProject; - -/** {@link SourceFilesConfiguration} implementation based on inputs from a {@link MavenProject}. */ -class MavenSourceFilesConfiguration implements SourceFilesConfiguration { - - /** - * Resolves the source files configuration for a Maven {@link MavenProject}. - * - * @param project the {@link MavenProject} - * @return a new {@link MavenSourceFilesConfiguration} for the project - * @throws IOException if collecting the project files fails - */ - static MavenSourceFilesConfiguration getForProject(MavenProject project) throws IOException { - return new MavenSourceFilesConfiguration(project); - } - - private final ImmutableList dependenciesFiles; - private final ImmutableList snapshotDependenciesFiles; - private final ImmutableList resourcesFiles; - private final ImmutableList classesFiles; - - /** Instantiate with {@link #getForProject}. */ - private MavenSourceFilesConfiguration(MavenProject project) throws IOException { - Path classesSourceDirectory = Paths.get(project.getBuild().getSourceDirectory()); - Path classesOutputDirectory = Paths.get(project.getBuild().getOutputDirectory()); - - List dependenciesFiles = new ArrayList<>(); - List snapshotDependenciesFiles = new ArrayList<>(); - List resourcesFiles = new ArrayList<>(); - List classesFiles = new ArrayList<>(); - - // Gets all the dependencies. - for (Artifact artifact : project.getArtifacts()) { - if (artifact.isSnapshot()) { - snapshotDependenciesFiles.add(artifact.getFile().toPath()); - } else { - dependenciesFiles.add(artifact.getFile().toPath()); - } - } - - // Gets the classes files in the 'classes' output directory. It finds the files that are classes - // files by matching them against the .java source files. All other files are deemed resources. - try (Stream classFileStream = Files.list(classesOutputDirectory)) { - classFileStream.forEach( - classFile -> { - /* - * Adds classFile to classesFiles if it is a .class file or is a directory that also - * exists in the classes source directory; otherwise, adds file to resourcesFiles. - */ - if (Files.isDirectory(classFile) - && Files.exists( - classesSourceDirectory.resolve(classesOutputDirectory.relativize(classFile)))) { - classesFiles.add(classFile); - return; - } - - if (FileSystems.getDefault().getPathMatcher("glob:**.class").matches(classFile)) { - classesFiles.add(classFile); - return; - } - - resourcesFiles.add(classFile); - }); - } - - // Sort all files by path for consistent ordering. - this.dependenciesFiles = ImmutableList.sortedCopyOf(dependenciesFiles); - this.snapshotDependenciesFiles = ImmutableList.sortedCopyOf(snapshotDependenciesFiles); - this.resourcesFiles = ImmutableList.sortedCopyOf(resourcesFiles); - this.classesFiles = ImmutableList.sortedCopyOf(classesFiles); - } - - @Override - public ImmutableList getDependenciesFiles() { - return dependenciesFiles; - } - - @Override - public ImmutableList getSnapshotDependenciesFiles() { - return snapshotDependenciesFiles; - } - - @Override - public ImmutableList getResourcesFiles() { - return resourcesFiles; - } - - @Override - public ImmutableList getClassesFiles() { - return classesFiles; - } - - @Override - public String getDependenciesPathOnImage() { - return DEFAULT_DEPENDENCIES_PATH_ON_IMAGE; - } - - @Override - public String getResourcesPathOnImage() { - return DEFAULT_RESOURCES_PATH_ON_IMAGE; - } - - @Override - public String getClassesPathOnImage() { - return DEFAULT_CLASSES_PATH_ON_IMAGE; - } -} diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/DockerContextMojoIntegrationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/DockerContextMojoIntegrationTest.java index 8929ea48b9..66b6a7b190 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/DockerContextMojoIntegrationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/DockerContextMojoIntegrationTest.java @@ -62,6 +62,6 @@ public void testExecute() throws VerificationException, IOException, Interrupted + " \"2003/udp\": {}")); Assert.assertEquals( - "Hello, world. An argument.\n", new Command("docker", "run", imageName).run()); + "Hello, world. An argument.\nfoo\ncat\n", new Command("docker", "run", imageName).run()); } } diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfigurationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurationsTest.java similarity index 66% rename from jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfigurationTest.java rename to jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurationsTest.java index cecf83e1f2..e9fb8177e7 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenSourceFilesConfigurationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenLayerConfigurationsTest.java @@ -26,6 +26,7 @@ import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.model.Build; +import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.junit.Assert; import org.junit.Before; @@ -36,19 +37,19 @@ import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -/** Tests for {@link MavenSourceFilesConfiguration}. */ +/** Tests for {@link MavenLayerConfigurations}. */ @RunWith(MockitoJUnitRunner.class) -public class MavenSourceFilesConfigurationTest { +public class MavenLayerConfigurationsTest { @Rule public TestRepository testRepository = new TestRepository(); @Mock private MavenProject mockMavenProject; @Mock private Build mockBuild; - private MavenSourceFilesConfiguration testMavenSourceFilesConfiguration; + private MavenLayerConfigurations testMavenLayerConfigurations; @Before - public void setUp() throws IOException, URISyntaxException { + public void setUp() throws IOException, URISyntaxException, MojoExecutionException { Path sourcePath = Paths.get(Resources.getResource("application/source").toURI()); Path outputPath = Paths.get(Resources.getResource("application/output").toURI()); @@ -66,8 +67,8 @@ public void setUp() throws IOException, URISyntaxException { testRepository.findArtifact("com.test", "dependencyX", "1.0.0-SNAPSHOT")); Mockito.when(mockMavenProject.getArtifacts()).thenReturn(artifacts); - testMavenSourceFilesConfiguration = - MavenSourceFilesConfiguration.getForProject(mockMavenProject); + testMavenLayerConfigurations = + MavenLayerConfigurations.getForProject(mockMavenProject, Paths.get("nonexistent/path")); } @Test @@ -95,22 +96,50 @@ public void test_correctFiles() throws URISyntaxException { Paths.get(Resources.getResource("application/output/some.class").toURI())); Assert.assertEquals( - expectedDependenciesFiles, testMavenSourceFilesConfiguration.getDependenciesFiles()); + expectedDependenciesFiles, + testMavenLayerConfigurations.getDependenciesLayerEntry().getSourceFiles()); Assert.assertEquals( expectedSnapshotDependenciesFiles, - testMavenSourceFilesConfiguration.getSnapshotDependenciesFiles()); + testMavenLayerConfigurations.getSnapshotDependenciesLayerEntry().getSourceFiles()); Assert.assertEquals( - expectedResourcesFiles, testMavenSourceFilesConfiguration.getResourcesFiles()); - Assert.assertEquals(expectedClassesFiles, testMavenSourceFilesConfiguration.getClassesFiles()); + expectedResourcesFiles, + testMavenLayerConfigurations.getResourcesLayerEntry().getSourceFiles()); + Assert.assertEquals( + expectedClassesFiles, testMavenLayerConfigurations.getClassesLayerEntry().getSourceFiles()); + } + + @Test + public void test_extraFiles() throws URISyntaxException, IOException, MojoExecutionException { + Path extraFilesDirectory = Paths.get(Resources.getResource("layer").toURI()); + + testMavenLayerConfigurations = + MavenLayerConfigurations.getForProject(mockMavenProject, extraFilesDirectory); + + ImmutableList expectedExtraFiles = + ImmutableList.of( + Paths.get(Resources.getResource("layer/a").toURI()), + Paths.get(Resources.getResource("layer/c").toURI()), + Paths.get(Resources.getResource("layer/foo").toURI())); + + Assert.assertEquals( + expectedExtraFiles, + testMavenLayerConfigurations.getExtraFilesLayerEntry().getSourceFiles()); } @Test public void test_correctPathsOnImage() { Assert.assertEquals( - "/app/libs/", testMavenSourceFilesConfiguration.getDependenciesPathOnImage()); + "/app/libs/", testMavenLayerConfigurations.getDependenciesLayerEntry().getExtractionPath()); + Assert.assertEquals( + "/app/libs/", + testMavenLayerConfigurations.getSnapshotDependenciesLayerEntry().getExtractionPath()); + Assert.assertEquals( + "/app/resources/", + testMavenLayerConfigurations.getResourcesLayerEntry().getExtractionPath()); + Assert.assertEquals( + "/app/classes/", testMavenLayerConfigurations.getClassesLayerEntry().getExtractionPath()); Assert.assertEquals( - "/app/resources/", testMavenSourceFilesConfiguration.getResourcesPathOnImage()); - Assert.assertEquals("/app/classes/", testMavenSourceFilesConfiguration.getClassesPathOnImage()); + "/", testMavenLayerConfigurations.getExtraFilesLayerEntry().getExtractionPath()); } private Artifact makeArtifact(Path path) { diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenProjectPropertiesTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenProjectPropertiesTest.java index 8bc97e4fd6..d77341aea9 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenProjectPropertiesTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/MavenProjectPropertiesTest.java @@ -16,7 +16,6 @@ package com.google.cloud.tools.jib.maven; -import com.google.cloud.tools.jib.builder.SourceFilesConfiguration; import com.google.cloud.tools.jib.image.ImageReference; import org.apache.maven.model.Plugin; import org.apache.maven.project.MavenProject; @@ -35,7 +34,7 @@ public class MavenProjectPropertiesTest { @Mock private MavenProject mockMavenProject; @Mock private MavenBuildLogger mockMavenBuildLogger; - @Mock private SourceFilesConfiguration mockSourcesFilesConfiguration; + @Mock private MavenLayerConfigurations mockMavenLayerConfigurations; @Mock private Plugin mockJarPlugin; @Mock private MavenBuildLogger mockBuildLogger; @@ -52,7 +51,7 @@ public void setup() { Mockito.when(mockMavenProject.getVersion()).thenReturn("project-version"); mavenProjectProperties = new MavenProjectProperties( - mockMavenProject, mockMavenBuildLogger, mockSourcesFilesConfiguration); + mockMavenProject, mockMavenBuildLogger, mockMavenLayerConfigurations); jarPluginConfiguration = new Xpp3Dom(""); archive = new Xpp3Dom("archive"); manifest = new Xpp3Dom("manifest"); @@ -80,7 +79,7 @@ public void testGetMainClassFromJar_missingMainClass() { jarPluginConfiguration.addChild(archive); archive.addChild(manifest); - Assert.assertEquals(null, mavenProjectProperties.getMainClassFromJar()); + Assert.assertNull(mavenProjectProperties.getMainClassFromJar()); } @Test @@ -90,7 +89,7 @@ public void testGetMainClassFromJar_missingManifest() { Mockito.when(mockJarPlugin.getConfiguration()).thenReturn(jarPluginConfiguration); jarPluginConfiguration.addChild(archive); - Assert.assertEquals(null, mavenProjectProperties.getMainClassFromJar()); + Assert.assertNull(mavenProjectProperties.getMainClassFromJar()); } @Test @@ -99,7 +98,7 @@ public void testGetMainClassFromJar_missingArchive() { .thenReturn(mockJarPlugin); Mockito.when(mockJarPlugin.getConfiguration()).thenReturn(jarPluginConfiguration); - Assert.assertEquals(null, mavenProjectProperties.getMainClassFromJar()); + Assert.assertNull(mavenProjectProperties.getMainClassFromJar()); } @Test @@ -107,12 +106,12 @@ public void testGetMainClassFromJar_missingConfiguration() { Mockito.when(mockMavenProject.getPlugin("org.apache.maven.plugins:maven-jar-plugin")) .thenReturn(mockJarPlugin); - Assert.assertEquals(null, mavenProjectProperties.getMainClassFromJar()); + Assert.assertNull(mavenProjectProperties.getMainClassFromJar()); } @Test public void testGetMainClassFromJar_missingPlugin() { - Assert.assertEquals(null, mavenProjectProperties.getMainClassFromJar()); + Assert.assertNull(mavenProjectProperties.getMainClassFromJar()); } @Test