diff --git a/jib-gradle-plugin/CHANGELOG.md b/jib-gradle-plugin/CHANGELOG.md index 68ccd2831e..4af7c7ad99 100644 --- a/jib-gradle-plugin/CHANGELOG.md +++ b/jib-gradle-plugin/CHANGELOG.md @@ -6,10 +6,12 @@ All notable changes to this project will be documented in this file. ### Added - Can now set timestamps (last modified time) of the files in the built image with `jib.container.filesModificationTime`. The value should either be `EPOCH_PLUS_SECOND` to set the timestamps to Epoch + 1 second (default behavior), or an ISO 8601 date time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z` ([#1818](https://github.com/GoogleContainerTools/jib/pull/1818)) +- Can now set container creation timestamp with `jib.container.creationTime`. The value should be `EPOCH`, `USE_CURRENT_TIMESTAMP`, or an ISO 8601 date time ([#1609](https://github.com/GoogleContainerTools/jib/issues/1609)) ### Changed - When building to a registry, Jib now skips downloading and caching base image layers that already exist in the target registry. This feature will be particularly useful in CI/CD environments. However, if you want to force caching base image layers locally, set the system property `-Djib.alwaysCacheBaseImage=true` ([#1840](https://github.com/GoogleContainerTools/jib/pull/1840)) +- `jib.container.useCurrentTimestamp` has been deprecated in favor of `jib.container.creationTime` with `USE_CURRENT_TIMESTAMP` ([#1609](https://github.com/GoogleContainerTools/jib/issues/1609)) ### Fixed diff --git a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/EmptyProjectIntegrationTest.java b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/EmptyProjectIntegrationTest.java index db839e9e0e..e35c3e0aca 100644 --- a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/EmptyProjectIntegrationTest.java +++ b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/EmptyProjectIntegrationTest.java @@ -21,6 +21,7 @@ import com.google.cloud.tools.jib.api.InvalidImageReferenceException; import java.io.IOException; import java.security.DigestException; +import java.time.Instant; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.ClassRule; @@ -67,7 +68,7 @@ public void testBuild_empty() throws IOException, InterruptedException, DigestEx + System.nanoTime(); Assert.assertEquals("", JibRunHelper.buildAndRun(emptyTestProject, targetImage)); assertDockerInspect(targetImage); - JibRunHelper.assertCreationTimeEpoch(targetImage); + JibRunHelper.assertSimpleCreationTimeIsEqual(Instant.EPOCH, targetImage); } @Test @@ -87,9 +88,7 @@ public void testDockerDaemon_empty() throws IOException, InterruptedException, D String targetImage = "emptyimage:gradle" + System.nanoTime(); Assert.assertEquals( "", JibRunHelper.buildToDockerDaemonAndRun(emptyTestProject, targetImage, "build.gradle")); - Assert.assertEquals( - "1970-01-01T00:00:00Z", - new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); + JibRunHelper.assertSimpleCreationTimeIsEqual(Instant.EPOCH, targetImage); assertDockerInspect(targetImage); } diff --git a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationIntegrationTest.java b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationIntegrationTest.java index 26eea41970..250c6d72d6 100644 --- a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationIntegrationTest.java +++ b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/GradleLayerConfigurationIntegrationTest.java @@ -53,7 +53,6 @@ public void testGradleLayerConfiguration_multiModule() throws IOException { // snapshot dependencies (4) // dependencies (5) - Path complexServiceRoot = multiTestProject.getProjectRoot().resolve("complex-service"); // verify dependencies List dependencies = layers.get(5); List expectedDependencies = diff --git a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibRunHelper.java b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibRunHelper.java index 17e909a05f..8db0811e48 100644 --- a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibRunHelper.java +++ b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/JibRunHelper.java @@ -25,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.security.DigestException; +import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -87,8 +88,8 @@ static void buildAndRunAdditionalTag( Assert.assertEquals(expectedOutput, pullAndRunBuiltImage(imageReference)); Assert.assertEquals(expectedOutput, pullAndRunBuiltImage(additionalImageReference)); - assertCreationTimeEpoch(imageReference); - assertCreationTimeEpoch(additionalImageReference); + assertSimpleCreationTimeIsEqual(Instant.EPOCH, imageReference); + assertSimpleCreationTimeIsEqual(Instant.EPOCH, additionalImageReference); } static BuildResult buildToDockerDaemon( @@ -138,11 +139,20 @@ static void assertBuildSuccess(BuildResult buildResult, String taskName, String Assert.assertThat(buildResult.getOutput(), CoreMatchers.containsString(successMessage)); } - static void assertCreationTimeEpoch(String imageReference) + static void assertSimpleCreationTimeIsEqual(Instant match, String imageReference) throws IOException, InterruptedException { - Assert.assertEquals( - "1970-01-01T00:00:00Z", - new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim()); + String inspect = + new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim(); + Instant parsed = Instant.parse(inspect); + Assert.assertEquals(match, parsed); + } + + static void assertSimpleCreationTimeIsAfter(Instant before, String imageReference) + throws IOException, InterruptedException { + String inspect = + new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim(); + Instant parsed = Instant.parse(inspect); + Assert.assertTrue(parsed.isAfter(before)); } static void assertImageDigestAndId(Path projectRoot) throws IOException, DigestException { diff --git a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/SingleProjectIntegrationTest.java b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/SingleProjectIntegrationTest.java index 6ed601e0ab..a01bc3c9b1 100644 --- a/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/SingleProjectIntegrationTest.java +++ b/jib-gradle-plugin/src/integration-test/java/com/google/cloud/tools/jib/gradle/SingleProjectIntegrationTest.java @@ -52,24 +52,6 @@ private static boolean isJava11RuntimeOrHigher() { return Integer.valueOf(split.iterator().next()) >= 11; } - /** - * Asserts that the creation time of the simple test project is set. If the time parsed from the - * {@code docker inspect} command occurs before the specified time (i.e. if it is 1970), then the - * assertion will fail. - * - * @param before the specified time to compare the resulting image's creation time to - * @param imageReference the image to test - * @throws IOException if the {@code docker inspect} command fails to run - * @throws InterruptedException if the {@code docker inspect} command is interrupted - */ - private static void assertSimpleCreationTimeIsAfter(Instant before, String imageReference) - throws IOException, InterruptedException { - String inspect = - new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim(); - Instant parsed = Instant.parse(inspect); - Assert.assertTrue(parsed.isAfter(before) || parsed.equals(before)); - } - private static void assertWorkingDirectory(String expected, String imageReference) throws IOException, InterruptedException { Assert.assertEquals( @@ -131,11 +113,11 @@ private static void assertDockerInspect(String imageReference) + " }")); } - private static void assertExtraDirectoryDeprecationWarning(String pomXml) + private static void assertExtraDirectoryDeprecationWarning(String buildFile) throws DigestException, IOException, InterruptedException { String targetImage = "localhost:6000/simpleimage:gradle" + System.nanoTime(); BuildResult buildResult = - JibRunHelper.buildToDockerDaemon(simpleTestProject, targetImage, pomXml); + JibRunHelper.buildToDockerDaemon(simpleTestProject, targetImage, buildFile); Assert.assertEquals( "Hello, world. \n1970-01-01T00:00:01Z\nrw-r--r--\nrw-r--r--\nfoo\ncat\n" + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", @@ -209,13 +191,12 @@ public void testBuild_simple() throws IOException, InterruptedException, DigestE "No classes files were found - did you compile your project?")); } - Instant beforeBuild = Instant.now(); Assert.assertEquals( "Hello, world. An argument.\n1970-01-01T00:00:01Z\nrw-r--r--\nrw-r--r--\nfoo\ncat\n" + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", JibRunHelper.buildAndRun(simpleTestProject, targetImage)); assertDockerInspect(targetImage); - assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + JibRunHelper.assertSimpleCreationTimeIsEqual(Instant.EPOCH, targetImage); assertWorkingDirectory("/home", targetImage); assertEntrypoint( "[java -cp /d1:/d2:/app/resources:/app/classes:/app/libs/* com.test.HelloWorld]", @@ -326,7 +307,7 @@ public void testBuild_complex() throws IOException, InterruptedException { String targetImage = "localhost:6000/compleximage:gradle" + System.nanoTime(); Instant beforeBuild = Instant.now(); buildAndRunComplex(targetImage, "testuser2", "testpassword2", localRegistry2); - assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + JibRunHelper.assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); assertWorkingDirectory("", targetImage); } @@ -335,19 +316,18 @@ public void testBuild_complex_sameFromAndToRegistry() throws IOException, Interr String targetImage = "localhost:5000/compleximage:gradle" + System.nanoTime(); Instant beforeBuild = Instant.now(); buildAndRunComplex(targetImage, "testuser", "testpassword", localRegistry1); - assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + JibRunHelper.assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); assertWorkingDirectory("", targetImage); } @Test public void testDockerDaemon_simple() throws IOException, InterruptedException, DigestException { String targetImage = "simpleimage:gradle" + System.nanoTime(); - Instant beforeBuild = Instant.now(); Assert.assertEquals( "Hello, world. An argument.\n1970-01-01T00:00:01Z\nrw-r--r--\nrw-r--r--\nfoo\ncat\n" + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", JibRunHelper.buildToDockerDaemonAndRun(simpleTestProject, targetImage, "build.gradle")); - assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + JibRunHelper.assertSimpleCreationTimeIsEqual(Instant.EPOCH, targetImage); assertDockerInspect(targetImage); assertWorkingDirectory("/home", targetImage); } @@ -379,13 +359,46 @@ public void testBuild_skipDownloadingBaseImageLayers() throws IOException, Inter } @Test - public void testDockerDaemon_filesModificationTimeCustom() + public void testDockerDaemon_timestampCustom() throws DigestException, IOException, InterruptedException { String targetImage = "simpleimage:gradle" + System.nanoTime(); Assert.assertEquals( "Hello, world. \n2011-12-03T01:15:30Z\n", JibRunHelper.buildToDockerDaemonAndRun( - simpleTestProject, targetImage, "build-files-modification-time-custom.gradle")); + simpleTestProject, targetImage, "build-timestamps-custom.gradle")); + JibRunHelper.assertSimpleCreationTimeIsEqual( + Instant.parse("2013-11-04T21:29:30Z"), targetImage); + } + + @Test + public void testDockerDaemon_timestampDeprecated() + throws DigestException, IOException, InterruptedException { + Instant beforeBuild = Instant.now(); + String targetImage = "simpleimage:gradle" + System.nanoTime(); + BuildResult buildResult = + JibRunHelper.buildToDockerDaemon( + simpleTestProject, targetImage, "build-usecurrent-deprecated.gradle"); + JibRunHelper.assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + Assert.assertThat( + buildResult.getOutput(), + CoreMatchers.containsString( + "'jib.container.useCurrentTimestamp' is deprecated; use 'jib.container.creationTime' with the value 'USE_CURRENT_TIMESTAMP' instead")); + } + + @Test + public void testDockerDaemon_timestampFail() + throws InterruptedException, IOException, DigestException { + try { + String targetImage = "simpleimage:gradle" + System.nanoTime(); + JibRunHelper.buildToDockerDaemonAndRun( + simpleTestProject, targetImage, "build-usecurrent-deprecated2.gradle"); + Assert.fail(); + } catch (UnexpectedBuildFailure ex) { + Assert.assertThat( + ex.getMessage(), + CoreMatchers.containsString( + "You cannot configure both 'jib.container.useCurrentTimestamp' and 'jib.container.creationTime'")); + } } @Test @@ -420,7 +433,6 @@ public void testBuildTar_simple() throws IOException, InterruptedException { String outputPath = simpleTestProject.getProjectRoot().resolve("build").resolve("jib-image.tar").toString(); - Instant beforeBuild = Instant.now(); BuildResult buildResult = simpleTestProject.build( "clean", @@ -438,7 +450,7 @@ public void testBuildTar_simple() throws IOException, InterruptedException { + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", new Command("docker", "run", "--rm", targetImage).run()); assertDockerInspect(targetImage); - assertSimpleCreationTimeIsAfter(beforeBuild, targetImage); + JibRunHelper.assertSimpleCreationTimeIsEqual(Instant.EPOCH, targetImage); assertWorkingDirectory("/home", targetImage); } } diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-files-modification-time-custom.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-timestamps-custom.gradle similarity index 85% rename from jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-files-modification-time-custom.gradle rename to jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-timestamps-custom.gradle index 2a9fd1340a..6fc9bb42a4 100644 --- a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-files-modification-time-custom.gradle +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-timestamps-custom.gradle @@ -17,4 +17,5 @@ dependencies { jib { to.image = System.getProperty("_TARGET_IMAGE") container.filesModificationTime = '2011-12-03T10:15:30+09:00' + container.creationTime = '2013-11-05T06:29:30+09:00' } diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated.gradle new file mode 100644 index 0000000000..919c215900 --- /dev/null +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated.gradle @@ -0,0 +1,20 @@ +plugins { + id 'java' + id 'com.google.cloud.tools.jib' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile files('libs/dependency-1.0.0.jar') +} + +jib { + to.image = System.getProperty("_TARGET_IMAGE") + container.useCurrentTimestamp = true +} diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated2.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated2.gradle new file mode 100644 index 0000000000..092247c19b --- /dev/null +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-usecurrent-deprecated2.gradle @@ -0,0 +1,21 @@ +plugins { + id 'java' + id 'com.google.cloud.tools.jib' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile files('libs/dependency-1.0.0.jar') +} + +jib { + to.image = System.getProperty("_TARGET_IMAGE") + container.useCurrentTimestamp = true + container.creationTime = 'USE_CURRENT_TIMESTAMP' +} diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build.gradle index 84b461ad2a..4c9914c010 100644 --- a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build.gradle +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build.gradle @@ -20,7 +20,7 @@ jib { credHelper = 'gcr' } container { - useCurrentTimestamp = true + creationTime = 'EPOCH' args = ['An argument.'] ports = ['1000/tcp', '2000-2003/udp'] labels = [key1:'value1', key2:'value2'] diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/complex-build.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/complex-build.gradle index de303ff081..4d7ad8c00c 100644 --- a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/complex-build.gradle +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/complex-build.gradle @@ -30,7 +30,7 @@ jib { } } container { - useCurrentTimestamp = true + creationTime = 'USE_CURRENT_TIMESTAMP' args = ['An argument.'] mainClass = 'com.test.HelloWorld' jvmFlags = ['-Xms512m', '-Xdebug'] 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 b131b9df19..bee61893c7 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 @@ -26,6 +26,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -172,6 +173,14 @@ public void buildDocker() + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new GradleException( + "container.creationTime should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new GradleException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForGradle( 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 ef55b10451..cf16d06939 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 @@ -25,6 +25,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -152,6 +153,14 @@ public void buildImage() + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new GradleException( + "container.creationTime should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new GradleException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForGradle( 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 179b93d21f..f7612d523b 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 @@ -24,6 +24,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -170,6 +171,14 @@ public void buildTar() + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new GradleException( + "container.creationTime should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new GradleException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForGradle( diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java index d9db45e31e..21d1831e28 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/ContainerParameters.java @@ -48,6 +48,7 @@ public class ContainerParameters { @Nullable private String user; @Nullable private String workingDirectory; private String filesModificationTime = "EPOCH_PLUS_SECOND"; + private String creationTime = "EPOCH"; @Input @Optional @@ -261,4 +262,17 @@ public String getFilesModificationTime() { public void setFilesModificationTime(String filesModificationTime) { this.filesModificationTime = filesModificationTime; } + + @Input + @Optional + public String getCreationTime() { + if (System.getProperty(PropertyNames.CONTAINER_CREATION_TIME) != null) { + return System.getProperty(PropertyNames.CONTAINER_CREATION_TIME); + } + return creationTime; + } + + public void setCreationTime(String creationTime) { + this.creationTime = creationTime; + } } diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java index bcbf1786bf..d5517d7744 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/GradleRawConfiguration.java @@ -156,6 +156,11 @@ public String getFilesModificationTime() { return jibExtension.getContainer().getFilesModificationTime(); } + @Override + public String getCreationTime() { + return jibExtension.getContainer().getCreationTime(); + } + @Override public List getExtraDirectories() { return jibExtension.getExtraDirectories().getPaths(); diff --git a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/TaskCommon.java b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/TaskCommon.java index b4f95db67e..27b084dc98 100644 --- a/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/TaskCommon.java +++ b/jib-gradle-plugin/src/main/java/com/google/cloud/tools/jib/gradle/TaskCommon.java @@ -89,6 +89,17 @@ static void checkDeprecatedUsage(JibExtension jibExtension, Logger logger) { "You cannot configure both 'jib.extraDirectory.path' and 'jib.extraDirectories.paths'"); } } + + if (jibExtension.getContainer().getUseCurrentTimestamp()) { + if (!jibExtension.getContainer().getCreationTime().equals("EPOCH")) { + throw new IllegalArgumentException( + "You cannot configure both 'jib.container.useCurrentTimestamp' and " + + "'jib.container.creationTime'"); + } + logger.warn( + "'jib.container.useCurrentTimestamp' is deprecated; use 'jib.container.creationTime' " + + "with the value 'USE_CURRENT_TIMESTAMP' instead"); + } } /** diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java index 62411e64de..24cb345a82 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/JibExtensionTest.java @@ -101,6 +101,7 @@ public void testContainer() { Assert.assertEquals("", testJibExtension.getContainer().getAppRoot()); Assert.assertEquals( "EPOCH_PLUS_SECOND", testJibExtension.getContainer().getFilesModificationTime()); + Assert.assertEquals("EPOCH", testJibExtension.getContainer().getCreationTime()); testJibExtension.container( container -> { diff --git a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/TaskCommonTest.java b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/TaskCommonTest.java index 203da7d672..a8ab91f92e 100644 --- a/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/TaskCommonTest.java +++ b/jib-gradle-plugin/src/test/java/com/google/cloud/tools/jib/gradle/TaskCommonTest.java @@ -41,6 +41,7 @@ public class TaskCommonTest { @Rule public final RestoreSystemProperties systemPropertyRestorer = new RestoreSystemProperties(); @Mock private JibExtension jibExtension; + @Mock private ContainerParameters containerParameters; @Mock private Logger logger; @Before @@ -49,6 +50,7 @@ public void setUp() { System.clearProperty("jib.extraDirectory.permissions"); System.clearProperty("jib.extraDirectories.paths"); System.clearProperty("jib.extraDirectories.permissions"); + Mockito.when(jibExtension.getContainer()).thenReturn(containerParameters); } @Test @@ -126,6 +128,32 @@ public void testCheckDeprecatedUsage_extraDirectoryAndExtraDirectoriesConfigured } } + @Test + public void testCheckDeprecatedUsage_useCurrentTimestampConfigured() { + Mockito.when(containerParameters.getUseCurrentTimestamp()).thenReturn(true); + Mockito.when(containerParameters.getCreationTime()).thenReturn("EPOCH"); + TaskCommon.checkDeprecatedUsage(jibExtension, logger); + Mockito.verify(logger) + .warn( + "'jib.container.useCurrentTimestamp' is deprecated; use 'jib.container.creationTime' " + + "with the value 'USE_CURRENT_TIMESTAMP' instead"); + } + + @Test + public void testCheckDeprecatedUsage_useCurrentTimestampAndCreationTimeConfigured() { + Mockito.when(containerParameters.getUseCurrentTimestamp()).thenReturn(true); + Mockito.when(containerParameters.getCreationTime()).thenReturn("USE_CURRENT_TIMESTAMP"); + try { + TaskCommon.checkDeprecatedUsage(jibExtension, logger); + Assert.fail(); + } catch (IllegalArgumentException ex) { + Assert.assertEquals( + "You cannot configure both 'jib.container.useCurrentTimestamp' and " + + "'jib.container.creationTime'", + ex.getMessage()); + } + } + @Test public void testGetWarTask_normalJavaProject() { Project project = ProjectBuilder.builder().build(); diff --git a/jib-maven-plugin/CHANGELOG.md b/jib-maven-plugin/CHANGELOG.md index 92042d7524..70ec67d215 100644 --- a/jib-maven-plugin/CHANGELOG.md +++ b/jib-maven-plugin/CHANGELOG.md @@ -6,10 +6,12 @@ All notable changes to this project will be documented in this file. ### Added - Can now set file timestamps (last modified time) in the image with ``. The value should either be `EPOCH_PLUS_SECOND` to set the timestamps to Epoch + 1 second (default behavior), or an ISO 8601 date time parsable with [`DateTimeFormatter.ISO_DATE_TIME`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html) such as `2019-07-15T10:15:30+09:00` or `2011-12-03T22:42:05Z` ([#1818](https://github.com/GoogleContainerTools/jib/pull/1818)) +- Can now set container creation timestamp with ``. The value should be `EPOCH`, `USE_CURRENT_TIMESTAMP`, or an ISO 8601 date time ([#1609](https://github.com/GoogleContainerTools/jib/issues/1609)) ### Changed - When building to a registry, Jib now skips downloading and caching base image layers that already exist in the target registry. This feature will be particularly useful in CI/CD environments. However, if you want to force caching base image layers locally, set the system property `-Djib.alwaysCacheBaseImage=true` ([#1840](https://github.com/GoogleContainerTools/jib/pull/1840)) +- `` has been deprecated in favor of `` with `USE_CURRENT_TIMESTAMP` ([#1609](https://github.com/GoogleContainerTools/jib/issues/1609)) ### Fixed 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 ee8ee77276..52a79a1dd1 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 @@ -27,6 +27,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -97,6 +98,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { HelpfulSuggestions.forDockerNotInstalled(HELPFUL_SUGGESTIONS_PREFIX)); } + MojoCommon.checkUseCurrentTimestampDeprecation(this); + try { RawConfiguration mavenRawConfiguration = new MavenRawConfiguration(this); MavenProjectProperties projectProperties = @@ -170,6 +173,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new MojoExecutionException( + " should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new MojoExecutionException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForMaven( 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 2f0271a6c9..1f5c82e248 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 @@ -26,6 +26,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -73,6 +74,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { return; } + MojoCommon.checkUseCurrentTimestampDeprecation(this); + // Validates 'format'. if (Arrays.stream(ImageFormat.values()).noneMatch(value -> value.name().equals(getFormat()))) { throw new MojoFailureException( @@ -164,6 +167,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new MojoExecutionException( + " should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new MojoExecutionException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForMaven( 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 a1998e11f3..a30d47de21 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 @@ -24,6 +24,7 @@ import com.google.cloud.tools.jib.plugins.common.InvalidAppRootException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerVolumeException; import com.google.cloud.tools.jib.plugins.common.InvalidContainerizingModeException; +import com.google.cloud.tools.jib.plugins.common.InvalidCreationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidFilesModificationTimeException; import com.google.cloud.tools.jib.plugins.common.InvalidWorkingDirectoryException; import com.google.cloud.tools.jib.plugins.common.JibBuildRunner; @@ -71,6 +72,8 @@ public void execute() throws MojoExecutionException, MojoFailureException { return; } + MojoCommon.checkUseCurrentTimestampDeprecation(this); + try { RawConfiguration mavenRawConfiguration = new MavenRawConfiguration(this); MavenProjectProperties projectProperties = @@ -142,6 +145,14 @@ public void execute() throws MojoExecutionException, MojoFailureException { + ex.getInvalidFilesModificationTime(), ex); + } catch (InvalidCreationTimeException ex) { + throw new MojoExecutionException( + " should be an ISO 8601 date-time (see " + + "DateTimeFormatter.ISO_DATE_TIME) or a special keyword (\"EPOCH\", " + + "\"USE_CURRENT_TIMESTAMP\"): " + + ex.getInvalidCreationTime(), + ex); + } catch (IncompatibleBaseImageJavaVersionException ex) { throw new MojoExecutionException( HelpfulSuggestions.forIncompatibleBaseImageJavaVesionForMaven( 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 5c5ea0ddb1..9f1304e4d6 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 @@ -186,6 +186,8 @@ public static class ContainerParameters { @Nullable @Parameter private String workingDirectory; @Parameter private String filesModificationTime = "EPOCH_PLUS_SECOND"; + + @Parameter private String creationTime = "EPOCH"; } /** Configuration for the {@code extraDirectories} parameter. */ @@ -363,6 +365,7 @@ AuthConfiguration getTargetImageAuth() { * * @return {@code true} if the build should use the current timestamp, {@code false} if not */ + @Deprecated boolean getUseCurrentTimestamp() { String property = getProperty(PropertyNames.CONTAINER_USE_CURRENT_TIMESTAMP); if (property != null) { @@ -558,6 +561,19 @@ String getFilesModificationTime() { return container.filesModificationTime; } + /** + * Gets the configured container creation time value. + * + * @return the configured container creation time value + */ + String getCreationTime() { + String property = getProperty(PropertyNames.CONTAINER_CREATION_TIME); + if (property != null) { + return property; + } + return container.creationTime; + } + /** * Gets the list of configured extra directory paths. * diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenRawConfiguration.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenRawConfiguration.java index 913c9c89fa..d08649986e 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenRawConfiguration.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MavenRawConfiguration.java @@ -161,6 +161,11 @@ public String getFilesModificationTime() { return jibPluginConfiguration.getFilesModificationTime(); } + @Override + public String getCreationTime() { + return jibPluginConfiguration.getCreationTime(); + } + @Override public List getExtraDirectories() { return MojoCommon.getExtraDirectories(jibPluginConfiguration); diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MojoCommon.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MojoCommon.java index 88d741dd42..62a820e557 100644 --- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MojoCommon.java +++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/MojoCommon.java @@ -38,6 +38,22 @@ public class MojoCommon { @VisibleForTesting public static final String REQUIRED_VERSION_PROPERTY_NAME = "jib.requiredVersion"; + @Deprecated + static void checkUseCurrentTimestampDeprecation(JibPluginConfiguration jibPluginConfiguration) { + if (jibPluginConfiguration.getUseCurrentTimestamp()) { + if (!jibPluginConfiguration.getCreationTime().equals("EPOCH")) { + throw new IllegalArgumentException( + "You cannot configure both and " + + ""); + } + jibPluginConfiguration + .getLog() + .warn( + " is deprecated; use with " + + "the value USE_CURRENT_TIMESTAMP instead"); + } + } + /** * Gets the list of extra directory paths from a {@link JibPluginConfiguration}. Returns {@code * (project dir)/src/main/jib} by default if not configured. diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java index 06a3515bd0..61e1b8e88f 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.nio.file.Path; import java.security.DigestException; -import java.time.Instant; import java.util.Arrays; import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; @@ -101,15 +100,13 @@ public void testExecute_simple() throws VerificationException, IOException, InterruptedException, DigestException { String targetImage = "simpleimage:maven" + System.nanoTime(); - Instant before = Instant.now(); Assert.assertEquals( "Hello, world. An argument.\n1970-01-01T00:00:01Z\nrw-r--r--\nrw-r--r--\nfoo\ncat\n" + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", buildToDockerDaemonAndRun(simpleTestProject.getProjectRoot(), targetImage)); - Instant buildTime = - Instant.parse( - new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); - Assert.assertTrue(buildTime.isAfter(before) || buildTime.equals(before)); + Assert.assertEquals( + "1970-01-01T00:00:00Z", + new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); } @Test @@ -231,7 +228,6 @@ public void testExecute_jarContainerizationOnMissingJar() throws IOException { public void testExecute_jibRequireVersion_ok() throws VerificationException, IOException { String targetImage = "simpleimage:maven" + System.nanoTime(); - Instant before = Instant.now(); Verifier verifier = new Verifier(simpleTestProject.getProjectRoot().toString()); // this plugin should match 1.0 verifier.setSystemProperty("jib.requiredVersion", "1.0"); diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoIntegrationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoIntegrationTest.java index 84851ac8a9..3fab07385e 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoIntegrationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoIntegrationTest.java @@ -202,7 +202,6 @@ private static String buildAndRunComplex( LocalRegistry targetRegistry, String pomFile) throws VerificationException, IOException, InterruptedException, DigestException { - Instant before = Instant.now(); Verifier verifier = new Verifier(simpleTestProject.getProjectRoot().toString()); verifier.setSystemProperty("jib.useOnlyProjectCache", "true"); verifier.setSystemProperty("_TARGET_IMAGE", imageReference); @@ -220,10 +219,6 @@ private static String buildAndRunComplex( // Verify output targetRegistry.pull(imageReference); assertDockerInspectParameters(imageReference); - Instant buildTime = - Instant.parse( - new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim()); - Assert.assertTrue(buildTime.isAfter(before) || buildTime.equals(before)); return new Command("docker", "run", "--rm", imageReference).run(); } @@ -289,6 +284,14 @@ private static void assertCreationTimeEpoch(String imageReference) new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim()); } + private static void assertCreationTimeIsAfter(Instant before, String imageReference) + throws IOException, InterruptedException { + String inspect = + new Command("docker", "inspect", "-f", "{{.Created}}", imageReference).run().trim(); + Instant after = Instant.parse(inspect); + Assert.assertTrue(after.isAfter(before)); + } + private static void assertWorkingDirectory(String expected, String imageReference) throws IOException, InterruptedException { Assert.assertEquals( @@ -374,10 +377,7 @@ public void testExecute_simple() + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", buildAndRun(simpleTestProject.getProjectRoot(), targetImage, "pom.xml", true)); - Instant buildTime = - Instant.parse( - new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); - Assert.assertTrue(buildTime.isAfter(before) || buildTime.equals(before)); + assertCreationTimeEpoch(targetImage); assertWorkingDirectory("/home", targetImage); assertLayerSize(8, targetImage); } @@ -511,12 +511,14 @@ public void testExecute_defaultTarget() throws IOException { public void testExecute_complex() throws IOException, InterruptedException, VerificationException, DigestException { String targetImage = "localhost:6000/compleximage:maven" + System.nanoTime(); + Instant before = Instant.now(); Assert.assertEquals( "Hello, world. An argument.\n1970-01-01T00:00:01Z\nrwxr-xr-x\nrwxrwxrwx\nfoo\ncat\n" + "1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n" + "-Xms512m\n-Xdebug\nenvvalue1\nenvvalue2\n", buildAndRunComplex( targetImage, "testuser2", "testpassword2", localRegistry2, "pom-complex.xml")); + assertCreationTimeIsAfter(before, targetImage); assertWorkingDirectory("", targetImage); assertEntrypoint( "[java -Xms512m -Xdebug -cp /other:/app/resources:/app/classes:/app/libs/* " @@ -525,14 +527,46 @@ public void testExecute_complex() } @Test - public void testExecute_filesModificationTimeCustom() + public void testExecute_timestampCustom() throws IOException, InterruptedException, VerificationException, DigestException { String targetImage = "localhost:6000/simpleimage:maven" + System.nanoTime(); - String pom = "pom-complex-files-modification-time-custom.xml"; + String pom = "pom-timestamps-custom.xml"; Assert.assertEquals( "Hello, world. \n2019-06-17T16:30:00Z\nrw-r--r--\nrw-r--r--\n" + "foo\ncat\n2019-06-17T16:30:00Z\n2019-06-17T16:30:00Z\n", buildAndRunComplex(targetImage, "testuser2", "testpassword2", localRegistry2, pom)); + + String inspect = + new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim(); + Instant parsed = Instant.parse(inspect); + Assert.assertEquals(Instant.parse("2013-11-05T06:29:30Z"), parsed); + } + + @Test + public void testDockerDaemon_timestampDeprecated() + throws IOException, VerificationException, InterruptedException { + Instant before = Instant.now(); + String targetImage = getTestImageReference("simpleimage:gradle" + System.nanoTime()); + build(simpleTestProject.getProjectRoot(), targetImage, "pom-usecurrent-deprecated.xml", false) + .verifyTextInLog( + " is deprecated; use with the value USE_CURRENT_TIMESTAMP instead"); + new Command("docker", "pull", targetImage).run(); + assertCreationTimeIsAfter(before, targetImage); + } + + @Test + public void testDockerDaemon_timestampFail() throws IOException { + try { + String targetImage = getTestImageReference("simpleimage:gradle" + System.nanoTime()); + build( + simpleTestProject.getProjectRoot(), targetImage, "pom-usecurrent-deprecated2.xml", false); + Assert.fail(); + } catch (VerificationException ex) { + Assert.assertThat( + ex.getMessage(), + CoreMatchers.containsString( + "You cannot configure both and ")); + } } @Test diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildTarMojoIntegrationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildTarMojoIntegrationTest.java index 19f231e98f..03643d68d4 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildTarMojoIntegrationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildTarMojoIntegrationTest.java @@ -19,7 +19,6 @@ import com.google.cloud.tools.jib.Command; import java.io.IOException; import java.security.DigestException; -import java.time.Instant; import java.util.Arrays; import org.apache.maven.it.VerificationException; import org.apache.maven.it.Verifier; @@ -38,18 +37,11 @@ public class BuildTarMojoIntegrationTest { @ClassRule public static final TestProject skippedTestProject = new TestProject(testPlugin, "empty"); - /** - * Builds and runs jib:buildTar on a project at {@code projectRoot} pushing to {@code - * imageReference}. - * - * @throws DigestException - */ @Test public void testExecute_simple() throws VerificationException, IOException, InterruptedException, DigestException { String targetImage = "simpleimage:maven" + System.nanoTime(); - Instant before = Instant.now(); Verifier verifier = new Verifier(simpleTestProject.getProjectRoot().toString()); verifier.setSystemProperty("jib.useOnlyProjectCache", "true"); verifier.setSystemProperty("_TARGET_IMAGE", targetImage); @@ -74,11 +66,9 @@ public void testExecute_simple() Assert.assertEquals( "Hello, world. An argument.\n1970-01-01T00:00:01Z\nrw-r--r--\nrw-r--r--\nfoo\ncat\n1970-01-01T00:00:01Z\n1970-01-01T00:00:01Z\n", new Command("docker", "run", "--rm", targetImage).run()); - - Instant buildTime = - Instant.parse( - new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); - Assert.assertTrue(buildTime.isAfter(before) || buildTime.equals(before)); + Assert.assertEquals( + "1970-01-01T00:00:00Z", + new Command("docker", "inspect", "-f", "{{.Created}}", targetImage).run().trim()); } @Test @@ -95,7 +85,6 @@ public void testExecute_jibContainerizeSkips() throws VerificationException, IOE public void testExecute_jibRequireVersion_ok() throws VerificationException, IOException { String targetImage = "simpleimage:maven" + System.nanoTime(); - Instant before = Instant.now(); Verifier verifier = new Verifier(simpleTestProject.getProjectRoot().toString()); // this plugin should match 1.0 verifier.setSystemProperty("jib.requiredVersion", "1.0"); diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/JibPluginConfigurationTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/JibPluginConfigurationTest.java index 1457b52e85..14267a1c20 100644 --- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/JibPluginConfigurationTest.java +++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/JibPluginConfigurationTest.java @@ -70,6 +70,7 @@ public void testDefaults() { Assert.assertTrue(testPluginConfiguration.getExtraClasspath().isEmpty()); Assert.assertEquals("exploded", testPluginConfiguration.getContainerizingMode()); Assert.assertEquals("EPOCH_PLUS_SECOND", testPluginConfiguration.getFilesModificationTime()); + Assert.assertEquals("EPOCH", testPluginConfiguration.getCreationTime()); } @Test @@ -123,6 +124,8 @@ public void testSystemProperties() { Assert.assertEquals("/working/directory", testPluginConfiguration.getWorkingDirectory()); sessionProperties.put("jib.container.filesModificationTime", "2011-12-03T22:42:05Z"); Assert.assertEquals("2011-12-03T22:42:05Z", testPluginConfiguration.getFilesModificationTime()); + sessionProperties.put("jib.container.creationTime", "2011-12-03T22:42:05Z"); + Assert.assertEquals("2011-12-03T22:42:05Z", testPluginConfiguration.getCreationTime()); sessionProperties.put("jib.container.extraClasspath", "/foo,/bar"); Assert.assertEquals( ImmutableList.of("/foo", "/bar"), testPluginConfiguration.getExtraClasspath()); @@ -194,6 +197,8 @@ public void testPomProperties() { .getProperties() .setProperty("jib.container.filesModificationTime", "2011-12-03T22:42:05Z"); Assert.assertEquals("2011-12-03T22:42:05Z", testPluginConfiguration.getFilesModificationTime()); + project.getProperties().setProperty("jib.container.creationTime", "2011-12-03T22:42:05Z"); + Assert.assertEquals("2011-12-03T22:42:05Z", testPluginConfiguration.getCreationTime()); project.getProperties().setProperty("jib.container.extraClasspath", "/foo,/bar"); Assert.assertEquals( ImmutableList.of("/foo", "/bar"), testPluginConfiguration.getExtraClasspath()); @@ -228,7 +233,7 @@ public void testEmptyOrNullTags() { } @Test - public void testDeprecatedSystemProperties() { + public void testDeprecatedSystemProperties_extraDirectory() { sessionProperties.put("jib.extraDirectory.path", "custom-jib"); Assert.assertEquals( Arrays.asList(Paths.get("custom-jib")), testPluginConfiguration.getExtraDirectories()); @@ -247,7 +252,7 @@ public void testDeprecatedSystemProperties() { } @Test - public void testDeprecatedProperties() { + public void testDeprecatedProperties_extraDirectory() { Properties projectProperties = project.getProperties(); projectProperties.setProperty("jib.extraDirectory.path", "this-is-extra"); diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex.xml index 6c3a379ef3..82610e9c8e 100644 --- a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex.xml +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex.xml @@ -54,7 +54,7 @@ - true + USE_CURRENT_TIMESTAMP An argument. diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex-files-modification-time-custom.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-timestamps-custom.xml similarity index 97% rename from jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex-files-modification-time-custom.xml rename to jib-maven-plugin/src/test/resources/maven/projects/simple/pom-timestamps-custom.xml index 02acd783bd..e9928e11d8 100644 --- a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-complex-files-modification-time-custom.xml +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-timestamps-custom.xml @@ -54,7 +54,6 @@ - true 1000/tcp 2000-2003/udp @@ -64,6 +63,7 @@ value2 2019-06-17T16:30:00Z + 2013-11-05T06:29:30Z src/main/jib-custom diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated.xml new file mode 100644 index 0000000000..a666d0bebd --- /dev/null +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.test + hello-world + 1 + + + UTF-8 + UTF-8 + @@PluginVersion@@ + + + + + com.test + dependency + 1.0.0 + system + ${project.basedir}/libs/dependency-1.0.0.jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + com.google.cloud.tools + jib-maven-plugin + ${jib-maven-plugin.version} + + + ${_TARGET_IMAGE} + + + true + + + + + + diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated2.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated2.xml new file mode 100644 index 0000000000..1220d24bb8 --- /dev/null +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-usecurrent-deprecated2.xml @@ -0,0 +1,53 @@ + + + 4.0.0 + + com.test + hello-world + 1 + + + UTF-8 + UTF-8 + @@PluginVersion@@ + + + + + com.test + dependency + 1.0.0 + system + ${project.basedir}/libs/dependency-1.0.0.jar + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + com.google.cloud.tools + jib-maven-plugin + ${jib-maven-plugin.version} + + + ${_TARGET_IMAGE} + + + true + USE_CURRENT_TIMESTAMP + + + + + + diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom.xml index 6d6776aa38..9847100fa3 100644 --- a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom.xml +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom.xml @@ -43,7 +43,7 @@ ${_TARGET_IMAGE} - true + EPOCH An argument. 1000/tcp diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidCreationTimeException.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidCreationTimeException.java new file mode 100644 index 0000000000..f24ca19583 --- /dev/null +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidCreationTimeException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2019 Google LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.google.cloud.tools.jib.plugins.common; + +import java.time.format.DateTimeParseException; + +/** Exception when an invalid container creation timestamp configuration is encountered. */ +public class InvalidCreationTimeException extends Exception { + + private final String invalidValue; + + public InvalidCreationTimeException( + String message, String invalidValue, DateTimeParseException ex) { + super(message, ex); + this.invalidValue = invalidValue; + } + + public String getInvalidCreationTime() { + return invalidValue; + } +} diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidFilesModificationTimeException.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidFilesModificationTimeException.java index 421f328eb0..392fd1098b 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidFilesModificationTimeException.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/InvalidFilesModificationTimeException.java @@ -18,6 +18,7 @@ import java.time.format.DateTimeParseException; +/** Exception when an invalid file timestamp configuration is encountered. */ public class InvalidFilesModificationTimeException extends Exception { private final String invalidValue; diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java index 17e7889a93..64d6f97d8a 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessor.java @@ -67,7 +67,8 @@ public static PluginConfigurationProcessor processCommonConfigurationForDockerDa throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { ImageReference targetImageReference = getGeneratedTargetDockerTag(rawConfiguration, projectProperties, helpfulSuggestions); DockerDaemonImage targetImage = DockerDaemonImage.named(targetImageReference); @@ -97,7 +98,8 @@ public static PluginConfigurationProcessor processCommonConfigurationForTarImage throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { ImageReference targetImageReference = getGeneratedTargetDockerTag(rawConfiguration, projectProperties, helpfulSuggestions); TarImage targetImage = TarImage.named(targetImageReference).saveTo(tarImagePath); @@ -119,7 +121,8 @@ public static PluginConfigurationProcessor processCommonConfigurationForRegistry throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { Preconditions.checkArgument(rawConfiguration.getToImage().isPresent()); ImageReference targetImageReference = ImageReference.parse(rawConfiguration.getToImage().get()); @@ -160,7 +163,8 @@ static PluginConfigurationProcessor processCommonConfiguration( throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { JibSystemProperties.checkHttpTimeoutProperty(); JibSystemProperties.checkProxyPortProperty(); @@ -212,6 +216,9 @@ static PluginConfigurationProcessor processCommonConfiguration( LogEvent.warn( "Setting image creation time to current time; your image may not be reproducible.")); jibContainerBuilder.setCreationTime(Instant.now()); + } else { + jibContainerBuilder.setCreationTime( + getCreationTime(rawConfiguration.getCreationTime(), projectProperties)); } // Adds all the extra files. @@ -474,6 +481,43 @@ static BiFunction createModificationTimeProvide } } + /** + * Creates an {@link Instant} based on the config value. The value can be: + * + *
    + *
  1. {@code EPOCH} to return epoch + *
  2. {@code USE_CURRENT_TIMESTAMP} to return the current time + *
  3. date in ISO 8601 format + *
+ * + * @param configuredCreationTime the config value + * @param projectProperties used for logging warnings + * @return corresponding {@link Instant} + * @throws InvalidCreationTimeException if the config value is invalid + */ + @VisibleForTesting + static Instant getCreationTime(String configuredCreationTime, ProjectProperties projectProperties) + throws DateTimeParseException, InvalidCreationTimeException { + try { + switch (configuredCreationTime) { + case "EPOCH": + return Instant.EPOCH; + + case "USE_CURRENT_TIMESTAMP": + projectProperties.log( + LogEvent.warn( + "Setting image creation time to current time; your image may not be reproducible.")); + return Instant.now(); + + default: + return DateTimeFormatter.ISO_DATE_TIME.parse(configuredCreationTime, Instant::from); + } + + } catch (DateTimeParseException ex) { + throw new InvalidCreationTimeException(configuredCreationTime, configuredCreationTime, ex); + } + } + // TODO: find a way to reduce the number of arguments. private static boolean configureCredentialRetrievers( RawConfiguration rawConfiguration, diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PropertyNames.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PropertyNames.java index 9fe63a276d..132ed6a0ec 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PropertyNames.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/PropertyNames.java @@ -42,19 +42,14 @@ public class PropertyNames { public static final String CONTAINER_WORKING_DIRECTORY = "jib.container.workingDirectory"; public static final String CONTAINER_VOLUMES = "jib.container.volumes"; public static final String CONTAINER_PORTS = "jib.container.ports"; - public static final String CONTAINER_USE_CURRENT_TIMESTAMP = "jib.container.useCurrentTimestamp"; public static final String CONTAINER_FILES_MODIFICATION_TIME = "jib.container.filesModificationTime"; + public static final String CONTAINER_CREATION_TIME = "jib.container.creationTime"; public static final String USE_ONLY_PROJECT_CACHE = "jib.useOnlyProjectCache"; public static final String BASE_IMAGE_CACHE = "jib.baseImageCache"; public static final String APPLICATION_CACHE = "jib.applicationCache"; public static final String ALLOW_INSECURE_REGISTRIES = "jib.allowInsecureRegistries"; - @Deprecated public static final String EXTRA_DIRECTORY_PATH = "jib.extraDirectory.path"; public static final String EXTRA_DIRECTORIES_PATHS = "jib.extraDirectories.paths"; - - @Deprecated - public static final String EXTRA_DIRECTORY_PERMISSIONS = "jib.extraDirectory.permissions"; - public static final String EXTRA_DIRECTORIES_PERMISSIONS = "jib.extraDirectories.permissions"; public static final String DOCKER_CLIENT_EXECUTABLE = "jib.dockerClient.executable"; public static final String DOCKER_CLIENT_ENVIRONMENT = "jib.dockerClient.environment"; @@ -63,5 +58,13 @@ public class PropertyNames { public static final String CONSOLE = "jib.console"; public static final String CONTAINERIZE = "jib.containerize"; + @Deprecated + public static final String CONTAINER_USE_CURRENT_TIMESTAMP = "jib.container.useCurrentTimestamp"; + + @Deprecated public static final String EXTRA_DIRECTORY_PATH = "jib.extraDirectory.path"; + + @Deprecated + public static final String EXTRA_DIRECTORY_PERMISSIONS = "jib.extraDirectory.permissions"; + private PropertyNames() {} } diff --git a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/RawConfiguration.java b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/RawConfiguration.java index 92c57636dc..72f308c8e8 100644 --- a/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/RawConfiguration.java +++ b/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/RawConfiguration.java @@ -69,6 +69,7 @@ public interface RawConfiguration { Optional getWorkingDirectory(); + @Deprecated boolean getUseCurrentTimestamp(); boolean getAllowInsecureRegistries(); @@ -79,6 +80,8 @@ public interface RawConfiguration { String getFilesModificationTime(); + String getCreationTime(); + List getExtraDirectories(); Map getExtraDirectoryPermissions(); diff --git a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java index d32a804441..1c5e42b41d 100644 --- a/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java +++ b/jib-plugins-common/src/test/java/com/google/cloud/tools/jib/plugins/common/PluginConfigurationProcessorTest.java @@ -101,6 +101,7 @@ public void setUp() throws IOException, InvalidImageReferenceException { Mockito.when(rawConfiguration.getEntrypoint()).thenReturn(Optional.empty()); Mockito.when(rawConfiguration.getAppRoot()).thenReturn("/app"); Mockito.when(rawConfiguration.getFilesModificationTime()).thenReturn("EPOCH_PLUS_SECOND"); + Mockito.when(rawConfiguration.getCreationTime()).thenReturn("EPOCH"); Mockito.when(rawConfiguration.getExtraDirectories()) .thenReturn(Arrays.asList(Paths.get("nonexistent/path"))); Mockito.when(rawConfiguration.getContainerizingMode()).thenReturn("exploded"); @@ -130,7 +131,7 @@ public void testPluginConfigurationProcessor_defaults() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); BuildConfiguration buildConfiguration = getBuildConfiguration(processor.getJibContainerBuilder()); @@ -154,7 +155,8 @@ public void testPluginConfigurationProcessor_extraDirectory() InvalidAppRootException, IOException, IncompatibleBaseImageJavaVersionException, InvalidWorkingDirectoryException, InvalidImageReferenceException, CacheDirectoryCreationException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { Path extraDirectory = Paths.get(Resources.getResource("core/layer").toURI()); Mockito.when(rawConfiguration.getExtraDirectories()).thenReturn(Arrays.asList(extraDirectory)); Mockito.when(rawConfiguration.getExtraDirectoryPermissions()) @@ -200,7 +202,8 @@ public void testPluginConfigurationProcessor_cacheDirectorySystemProperties() throws InvalidContainerVolumeException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidImageReferenceException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { System.setProperty(PropertyNames.BASE_IMAGE_CACHE, "new/base/cache"); System.setProperty(PropertyNames.APPLICATION_CACHE, "/new/application/cache"); @@ -215,7 +218,8 @@ public void testPluginConfigurationProcessor_warProjectBaseImage() throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { Mockito.when(projectProperties.isWarProject()).thenReturn(true); PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); @@ -232,7 +236,7 @@ public void testEntrypoint() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()) .thenReturn(Optional.of(Arrays.asList("custom", "entrypoint"))); @@ -294,7 +298,7 @@ public void testEntrypoint_defaultWarPackaging() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()).thenReturn(Optional.empty()); Mockito.when(projectProperties.isWarProject()).thenReturn(true); @@ -313,7 +317,7 @@ public void testEntrypoint_defaultNonWarPackaging() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()).thenReturn(Optional.empty()); Mockito.when(projectProperties.isWarProject()).thenReturn(false); @@ -336,7 +340,7 @@ public void testEntrypoint_extraClasspathNonWarPackaging() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()).thenReturn(Optional.empty()); Mockito.when(rawConfiguration.getExtraClasspath()) .thenReturn(Collections.singletonList("/foo")); @@ -362,7 +366,7 @@ public void testUser() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getUser()).thenReturn(Optional.of("customUser")); PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); @@ -379,7 +383,7 @@ public void testUser_null() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); BuildConfiguration buildConfiguration = getBuildConfiguration(processor.getJibContainerBuilder()); @@ -394,7 +398,7 @@ public void testEntrypoint_warningOnJvmFlags() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()) .thenReturn(Optional.of(Arrays.asList("custom", "entrypoint"))); Mockito.when(rawConfiguration.getJvmFlags()).thenReturn(Collections.singletonList("jvmFlag")); @@ -419,7 +423,7 @@ public void testEntrypoint_warningOnMainclass() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getEntrypoint()) .thenReturn(Optional.of(Arrays.asList("custom", "entrypoint"))); Mockito.when(rawConfiguration.getMainClass()).thenReturn(Optional.of("java.util.Object")); @@ -444,7 +448,7 @@ public void testEntrypointClasspath_nonDefaultAppRoot() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(rawConfiguration.getAppRoot()).thenReturn("/my/app"); PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); @@ -468,7 +472,7 @@ public void testWebAppEntrypoint_inheritedFromBaseImage() MainClassInferenceException, InvalidAppRootException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, InvalidContainerizingModeException, - InvalidFilesModificationTimeException { + InvalidFilesModificationTimeException, InvalidCreationTimeException { Mockito.when(projectProperties.isWarProject()).thenReturn(true); PluginConfigurationProcessor processor = createPluginConfigurationProcessor(); @@ -853,7 +857,7 @@ public void testGetInvalidVolumesList() { } @Test - public void createModificationTimeProvider_epochPlusSecond() + public void testCreateModificationTimeProvider_epochPlusSecond() throws InvalidFilesModificationTimeException { BiFunction timeProvider = PluginConfigurationProcessor.createModificationTimeProvider("EPOCH_PLUS_SECOND"); @@ -863,7 +867,7 @@ public void createModificationTimeProvider_epochPlusSecond() } @Test - public void createModificationTimeProvider_isoDateTimeValue() + public void testCreateModificationTimeProvider_isoDateTimeValue() throws InvalidFilesModificationTimeException { BiFunction timeProvider = PluginConfigurationProcessor.createModificationTimeProvider("2011-12-03T10:15:30+09:00"); @@ -873,7 +877,7 @@ public void createModificationTimeProvider_isoDateTimeValue() } @Test - public void createModificationTimeProvider_invalidValue() { + public void testCreateModificationTimeProvider_invalidValue() { try { BiFunction timeProvider = PluginConfigurationProcessor.createModificationTimeProvider("invalid format"); @@ -885,11 +889,46 @@ public void createModificationTimeProvider_invalidValue() { } } + @Test + public void testGetCreationTime_epoch() throws InvalidCreationTimeException { + Instant time = PluginConfigurationProcessor.getCreationTime("EPOCH", projectProperties); + Assert.assertEquals(Instant.EPOCH, time); + } + + @Test + public void testGetCreationTime_useCurrentTimestamp() throws InvalidCreationTimeException { + Instant now = Instant.now().minusSeconds(2); + Instant time = + PluginConfigurationProcessor.getCreationTime("USE_CURRENT_TIMESTAMP", projectProperties); + Assert.assertTrue(time.isAfter(now)); + } + + @Test + public void testGetCreationTime_isoDateTimeValue() throws InvalidCreationTimeException { + Instant time = + PluginConfigurationProcessor.getCreationTime( + "2011-12-03T10:15:30+09:00", projectProperties); + Instant expected = DateTimeFormatter.ISO_DATE_TIME.parse("2011-12-03T01:15:30Z", Instant::from); + Assert.assertEquals(expected, time); + } + + @Test + public void testGetCreationTime_invalidValue() { + try { + PluginConfigurationProcessor.getCreationTime("invalid format", projectProperties); + Assert.fail(); + } catch (InvalidCreationTimeException ex) { + Assert.assertEquals("invalid format", ex.getMessage()); + Assert.assertEquals("invalid format", ex.getInvalidCreationTime()); + } + } + private PluginConfigurationProcessor createPluginConfigurationProcessor() throws InvalidImageReferenceException, MainClassInferenceException, InvalidAppRootException, IOException, InvalidWorkingDirectoryException, InvalidContainerVolumeException, IncompatibleBaseImageJavaVersionException, NumberFormatException, - InvalidContainerizingModeException, InvalidFilesModificationTimeException { + InvalidContainerizingModeException, InvalidFilesModificationTimeException, + InvalidCreationTimeException { return PluginConfigurationProcessor.processCommonConfiguration( rawConfiguration, ignored -> Optional.empty(),