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 3679e59316..43188388f7 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 @@ -417,7 +417,8 @@ private void buildAndCacheApplicationLayers( BuildAndCacheApplicationLayerStep.makeList(buildContext, progressDispatcherFactory)); } - private void buildImages(ProgressEventDispatcher.Factory progressDispatcherFactory) { + @VisibleForTesting + void buildImages(ProgressEventDispatcher.Factory progressDispatcherFactory) { results.baseImagesAndBuiltImages = executorService.submit( () -> { @@ -431,7 +432,6 @@ private void buildImages(ProgressEventDispatcher.Factory progressDispatcherFacto Image baseImage = entry.getKey(); List> baseLayers = entry.getValue(); - // Image is immutable once built. Future builtImage = buildImage(baseImage, baseLayers, progressDispatcher.newChildProducer()); baseImagesAndBuiltImages.put(baseImage, builtImage); @@ -657,7 +657,8 @@ private List> scheduleCallables(ImmutableList fetchBuiltImageForLocalBuild(String osType, String architecture) + @VisibleForTesting + Optional fetchBuiltImageForLocalBuild(String osType, String architecture) throws InterruptedException, ExecutionException { if (results.baseImagesAndBuiltImages.get().size() > 1) { LOGGER.warning( - "Detected multi-platform configuration, only building the one that matches the local Docker Engine's os and architecture"); + String.format( + "Detected multi-platform configuration, only building the one that matches the local Docker Engine's os and architecture (%s/%s)", + osType, architecture)); } for (Map.Entry> imageEntry : results.baseImagesAndBuiltImages.get().entrySet()) { diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/StepsRunnerTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/StepsRunnerTest.java index e8a60de2d5..afd0355bd5 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/StepsRunnerTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/StepsRunnerTest.java @@ -16,6 +16,9 @@ package com.google.cloud.tools.jib.builder.steps; +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.when; + import com.google.cloud.tools.jib.api.DescriptorDigest; import com.google.cloud.tools.jib.builder.ProgressEventDispatcher; import com.google.cloud.tools.jib.builder.steps.PullBaseImageStep.ImagesAndRegistryClient; @@ -26,6 +29,7 @@ import com.google.cloud.tools.jib.image.json.ManifestTemplate; import com.google.cloud.tools.jib.registry.ManifestAndDigest; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ForwardingExecutorService; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -90,14 +94,14 @@ protected ExecutorService delegate() { public void setup() { stepsRunner = new StepsRunner(new MockListeningExecutorService(), buildContext); - Mockito.when(progressDispatcherFactory.create(Mockito.anyString(), Mockito.anyLong())) + when(progressDispatcherFactory.create(Mockito.anyString(), Mockito.anyLong())) .thenReturn(progressDispatcher); } @Test public void testObtainBaseImageLayers_skipObtainingDuplicateLayers() throws DigestException, InterruptedException, ExecutionException { - Mockito.when(executorService.submit(Mockito.any(PullBaseImageStep.class))) + when(executorService.submit(Mockito.any(PullBaseImageStep.class))) .thenReturn(Futures.immediateFuture(new ImagesAndRegistryClient(null, null))); // Pretend that a thread pulling base images returned some (meaningless) result. stepsRunner.pullBaseImages(progressDispatcherFactory); @@ -118,7 +122,7 @@ public void testObtainBaseImageLayers_skipObtainingDuplicateLayers() PreparedLayer preparedLayer1 = Mockito.mock(PreparedLayer.class); PreparedLayer preparedLayer2 = Mockito.mock(PreparedLayer.class); PreparedLayer preparedLayer3 = Mockito.mock(PreparedLayer.class); - Mockito.when(executorService.submit(Mockito.any(ObtainBaseImageLayerStep.class))) + when(executorService.submit(Mockito.any(ObtainBaseImageLayerStep.class))) .thenReturn(Futures.immediateFuture(preparedLayer1)) .thenReturn(Futures.immediateFuture(preparedLayer2)) .thenReturn(Futures.immediateFuture(preparedLayer3)); @@ -127,7 +131,7 @@ public void testObtainBaseImageLayers_skipObtainingDuplicateLayers() // 1. Should schedule two threads to obtain new layers. Image image = Mockito.mock(Image.class); - Mockito.when(image.getLayers()).thenReturn(ImmutableList.of(layer1, layer2)); + when(image.getLayers()).thenReturn(ImmutableList.of(layer1, layer2)); stepsRunner.obtainBaseImageLayers(image, true, preparedLayersCache, progressDispatcherFactory); Assert.assertEquals(2, preparedLayersCache.size()); // two new layers cached @@ -141,7 +145,7 @@ public void testObtainBaseImageLayers_skipObtainingDuplicateLayers() Assert.assertEquals(preparedLayer2, preparedLayersCache.get(digest2).get()); // 3. Another image with one duplicate layer. - Mockito.when(image.getLayers()).thenReturn(ImmutableList.of(layer3, layer2)); + when(image.getLayers()).thenReturn(ImmutableList.of(layer3, layer2)); stepsRunner.obtainBaseImageLayers(image, true, preparedLayersCache, progressDispatcherFactory); Assert.assertEquals(3, preparedLayersCache.size()); // one new layer cached Assert.assertEquals(preparedLayer1, preparedLayersCache.get(digest1).get()); @@ -156,7 +160,7 @@ public void testObtainBaseImageLayers_skipObtainingDuplicateLayers() @Test public void testIsImagePushed_skipExistingEnabledAndManifestPresent() { Optional> manifestResult = Mockito.mock(Optional.class); - Mockito.when(manifestResult.isPresent()).thenReturn(true); + when(manifestResult.isPresent()).thenReturn(true); System.setProperty(JibSystemProperties.SKIP_EXISTING_IMAGES, "true"); Assert.assertFalse(stepsRunner.isImagePushed(manifestResult)); @@ -174,8 +178,121 @@ public void testIsImagePushed_skipExistingImageDisabledAndManifestPresent() { public void testIsImagePushed_skipExistingImageEnabledAndManifestNotPresent() { Optional> manifestResult = Mockito.mock(Optional.class); System.setProperty(JibSystemProperties.SKIP_EXISTING_IMAGES, "true"); - Mockito.when(manifestResult.isPresent()).thenReturn(false); + when(manifestResult.isPresent()).thenReturn(false); Assert.assertTrue(stepsRunner.isImagePushed(manifestResult)); } + + @Test + public void testFetchBuildImageForLocalBuild_matchingOsAndArch() + throws ExecutionException, InterruptedException { + Image mockImage1 = Mockito.mock(Image.class); + Image mockImage2 = Mockito.mock(Image.class); + Image baseImage1 = Mockito.mock(Image.class); + Image baseImage2 = Mockito.mock(Image.class); + when(mockImage1.getArchitecture()).thenReturn("arch1"); + when(mockImage1.getOs()).thenReturn("os1"); + when(mockImage1.getArchitecture()).thenReturn("arch2"); + when(mockImage1.getOs()).thenReturn("os2"); + + when(executorService.submit(Mockito.any(Callable.class))) + .thenReturn( + Futures.immediateFuture( + ImmutableMap.of( + baseImage1, + Futures.immediateFuture(mockImage1), + baseImage2, + Futures.immediateFuture(mockImage2)))); + stepsRunner.buildImages(progressDispatcherFactory); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os2", "arch2"); + + assertThat(expectedImage.get().getOs()).isEqualTo("os2"); + assertThat(expectedImage.get().getArchitecture()).isEqualTo("arch2"); + } + + @Test + public void testFetchBuildImageForLocalBuild_differentOsAndArch() + throws ExecutionException, InterruptedException { + Image builtImage1 = Mockito.mock(Image.class); + Image builtImage2 = Mockito.mock(Image.class); + Image baseImage1 = Mockito.mock(Image.class); + Image baseImage2 = Mockito.mock(Image.class); + when(builtImage1.getArchitecture()).thenReturn("os1"); + when(builtImage1.getOs()).thenReturn("arch1"); + when(builtImage2.getArchitecture()).thenReturn("os2"); + when(builtImage2.getOs()).thenReturn("arch2"); + when(executorService.submit(Mockito.any(Callable.class))) + .thenReturn( + Futures.immediateFuture( + ImmutableMap.of( + baseImage1, + Futures.immediateFuture(builtImage1), + baseImage2, + Futures.immediateFuture(builtImage2)))); + stepsRunner.buildImages(progressDispatcherFactory); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("linux", "arm64"); + + assertThat(expectedImage.isPresent()).isFalse(); + } + + @Test + public void testFetchBuildImageForLocalBuild_matchingOSDifferentArch() + throws ExecutionException, InterruptedException { + Image builtImage1 = Mockito.mock(Image.class); + Image builtImage2 = Mockito.mock(Image.class); + Image baseImage1 = Mockito.mock(Image.class); + Image baseImage2 = Mockito.mock(Image.class); + when(builtImage1.getArchitecture()).thenReturn("arch1"); + when(builtImage1.getOs()).thenReturn("os1"); + when(builtImage2.getArchitecture()).thenReturn("arch2"); + when(builtImage2.getOs()).thenReturn("os1"); + when(executorService.submit(Mockito.any(Callable.class))) + .thenReturn( + Futures.immediateFuture( + ImmutableMap.of( + baseImage1, + Futures.immediateFuture(builtImage1), + baseImage2, + Futures.immediateFuture(builtImage2)))); + stepsRunner.buildImages(progressDispatcherFactory); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os1", "arch3"); + + assertThat(expectedImage.isPresent()).isFalse(); + } + + @Test + public void testFetchBuildImageForLocalBuild_differentOSMatchingArch() + throws ExecutionException, InterruptedException { + Image builtImage1 = Mockito.mock(Image.class); + Image builtImage2 = Mockito.mock(Image.class); + Image baseImage1 = Mockito.mock(Image.class); + Image baseImage2 = Mockito.mock(Image.class); + when(builtImage1.getArchitecture()).thenReturn("arch1"); + when(builtImage1.getOs()).thenReturn("os1"); + when(builtImage2.getArchitecture()).thenReturn("arch2"); + when(builtImage2.getOs()).thenReturn("os2"); + when(executorService.submit(Mockito.any(Callable.class))) + .thenReturn( + Futures.immediateFuture( + ImmutableMap.of( + baseImage1, + Futures.immediateFuture(builtImage1), + baseImage2, + Futures.immediateFuture(builtImage2)))); + stepsRunner.buildImages(progressDispatcherFactory); + + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os3", "arch1"); + + assertThat(expectedImage.isPresent()).isFalse(); + } + + @Test + public void testComputeArchitecture_aarch64() { + assertThat(stepsRunner.computeArchitecture("aarch64")).isEqualTo("arm64"); + } + + @Test + public void testComputeArchitecture_x86_64() { + assertThat(stepsRunner.computeArchitecture("x86_64")).isEqualTo("amd64"); + } } 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 c5ef53a431..7c53878dc5 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 @@ -592,28 +592,4 @@ public void testCredHelperConfiguration() simpleTestProject, targetImage, "build-cred-helper.gradle")) .isEqualTo("Hello, world. \n1970-01-01T00:00:01Z\n"); } - - @Test - public void testToDockerDaemon_multiPlatform() - throws DigestException, IOException, InterruptedException { - String targetImage = "multiplatform:gradle" + System.nanoTime(); - assertThat( - JibRunHelper.buildToDockerDaemonAndRun( - simpleTestProject, targetImage, "build-multi-platform.gradle")) - .isEqualTo("Hello, world. \n1970-01-01T00:00:01Z\n"); - } - - @Test - public void testToDockerDaemon_multiPlatform_invalid() { - String targetImage = "multiplatform:gradle" + System.nanoTime(); - UnexpectedBuildFailure exception = - assertThrows( - UnexpectedBuildFailure.class, - () -> - JibRunHelper.buildToDockerDaemonAndRun( - simpleTestProject, targetImage, "build-multi-platform-invalid.gradle")); - assertThat(exception) - .hasMessageThat() - .contains("The configured platforms don't match the Docker Engine's OS and architecture"); - } } diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle deleted file mode 100644 index fc85ed7b4a..0000000000 --- a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle +++ /dev/null @@ -1,34 +0,0 @@ -plugins { - id 'java' - id 'com.google.cloud.tools.jib' -} - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -repositories { - mavenCentral() -} - -dependencies { - implementation files('libs/dependency-1.0.0.jar') -} - -jib { - from { - image = 'busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977' - platforms { - platform { - architecture = 'arm' - os = 'linux' - } - platform { - architecture = 's390x' - os = 'linux' - } - } - } - to { - image = System.getProperty('_TARGET_IMAGE') - } -} diff --git a/jib-maven-plugin/src/integration-test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java b/jib-maven-plugin/src/integration-test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java index d7c84abb77..7d3be1d8c5 100644 --- a/jib-maven-plugin/src/integration-test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java +++ b/jib-maven-plugin/src/integration-test/java/com/google/cloud/tools/jib/maven/BuildDockerMojoIntegrationTest.java @@ -17,7 +17,6 @@ package com.google.cloud.tools.jib.maven; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; import com.google.cloud.tools.jib.Command; import java.io.IOException; @@ -276,29 +275,4 @@ public void testCredHelperConfigurationComplex() "Hello, world. \n1970-01-01T00:00:01Z\n", new Command("docker", "run", "--rm", targetImage).run()); } - - @Test - public void testMultiPlatform() - throws DigestException, VerificationException, IOException, InterruptedException { - String targetImage = "multiplatformproject:maven" + System.nanoTime(); - buildToDockerDaemon(simpleTestProject, targetImage, "pom-multiplatform-build.xml"); - Assert.assertEquals( - "Hello, world. \n1970-01-01T00:00:01Z\n", - new Command("docker", "run", "--rm", targetImage).run()); - } - - @Test - public void testMultiPlatform_invalidPlatforms() - throws DigestException, VerificationException, IOException, InterruptedException { - String targetImage = "multiplatformproject:maven" + System.nanoTime(); - VerificationException exception = - assertThrows( - VerificationException.class, - () -> - buildToDockerDaemon( - simpleTestProject, targetImage, "pom-multiplatform-invalid-platforms.xml")); - assertThat(exception) - .hasMessageThat() - .contains("The configured platforms don't match the Docker Engine's OS and architecture"); - } } diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-build.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-build.xml index 3269081f05..76a5966111 100644 --- a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-build.xml +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-build.xml @@ -41,7 +41,7 @@ ${jib-maven-plugin.version} - eclipse-temurin:11 + busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977 arm64 diff --git a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml deleted file mode 100644 index 32a2a81c17..0000000000 --- a/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 4.0.0 - - com.test - my-artifact-id - 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 - 3.8.0 - - 1.8 - 1.8 - - - - - com.google.cloud.tools - jib-maven-plugin - ${jib-maven-plugin.version} - - - busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977 - - - arm - linux - - - s390x - linux - - - - - ${_TARGET_IMAGE} - - latest - another - - - - - - -