From cfdfc7630d05c1d6c99236d4b46cb34e3d04dfee Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Wed, 15 May 2024 03:33:01 +0000 Subject: [PATCH 01/18] feat: determine platform from local docker env when building to docker daemon --- .../cloud/tools/jib/api/DockerClient.java | 2 + .../tools/jib/api/DockerInfoDetails.java | 40 +++++++++++++++++++ .../tools/jib/builder/steps/StepsRunner.java | 38 +++++++++++++++--- .../tools/jib/docker/CliDockerClient.java | 12 ++++++ .../tools/jib/docker/AnotherDockerClient.java | 6 +++ 5 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 50ce2f463f..4c79581a8c 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -69,4 +69,6 @@ void save(ImageReference imageReference, Path outputPath, Consumer written * @throws InterruptedException if the {@code docker inspect} process was interrupted */ ImageDetails inspect(ImageReference imageReference) throws IOException, InterruptedException; + + DockerInfoDetails info() throws IOException, InterruptedException; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java new file mode 100644 index 0000000000..d904b550fd --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java @@ -0,0 +1,40 @@ +/* + * Copyright 2024 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.api; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.cloud.tools.jib.json.JsonTemplate; + +/** Contains the os and architecture from {@code docker info}. */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DockerInfoDetails implements JsonTemplate { + + @JsonProperty("OSType") + private String osType = ""; + + @JsonProperty("Architecture") + private String architecture = ""; + + public String getOsType() { + return osType; + } + + public String getArchitecture() { + return architecture; + } +} 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 46c63bfc9d..e21c0ed697 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 @@ -18,6 +18,7 @@ import com.google.cloud.tools.jib.api.DescriptorDigest; import com.google.cloud.tools.jib.api.DockerClient; +import com.google.cloud.tools.jib.api.DockerInfoDetails; import com.google.cloud.tools.jib.blob.BlobDescriptor; import com.google.cloud.tools.jib.builder.ProgressEventDispatcher; import com.google.cloud.tools.jib.builder.steps.LocalBaseImageSteps.LocalImage; @@ -38,11 +39,13 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; +import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -52,6 +55,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.function.Consumer; +import java.util.logging.Logger; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -64,6 +68,8 @@ */ public class StepsRunner { + Logger logger = Logger.getLogger(StepResults.class.getName()); + /** Holds the individual step results. */ private static class StepResults { @@ -427,6 +433,7 @@ 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); @@ -616,11 +623,7 @@ private void loadDocker( results.buildResult = executorService.submit( () -> { - Verify.verify( - results.baseImagesAndBuiltImages.get().size() == 1, - "multi-platform image building not supported when pushing to Docker engine"); - Image builtImage = - results.baseImagesAndBuiltImages.get().values().iterator().next().get(); + Image builtImage = fetchBaseImageForLocalBuild(dockerClient); return new LoadDockerStep( buildContext, progressDispatcherFactory, dockerClient, builtImage) .call(); @@ -647,4 +650,29 @@ private void writeTarFile( private List> scheduleCallables(ImmutableList> callables) { return callables.stream().map(executorService::submit).collect(Collectors.toList()); } + + private String computeArchitecture(String architecture) { + if (architecture.equals("x86_64")) { + return "amd64"; + } else if (architecture.equals("aarch64")) { + return "arm64"; + } + return architecture; + } + + private Image fetchBaseImageForLocalBuild(DockerClient dockerClient) + throws IOException, InterruptedException, ExecutionException { + DockerInfoDetails dockerInfoDetails = dockerClient.info(); + String osType = dockerInfoDetails.getOsType(); + String dockerArchitecture = computeArchitecture(dockerInfoDetails.getArchitecture()); + Iterator> imageIterator = + results.baseImagesAndBuiltImages.get().values().iterator(); + while (imageIterator.hasNext()) { + Image image = imageIterator.next().get(); + if (image.getArchitecture().equals(dockerArchitecture) && image.getOs().equals(osType)) { + return image; + } + } + return results.baseImagesAndBuiltImages.get().values().iterator().next().get(); + } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/docker/CliDockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/docker/CliDockerClient.java index 7c9c23cd9f..1842a79de3 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/docker/CliDockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/docker/CliDockerClient.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.cloud.tools.jib.api.DescriptorDigest; import com.google.cloud.tools.jib.api.DockerClient; +import com.google.cloud.tools.jib.api.DockerInfoDetails; import com.google.cloud.tools.jib.api.ImageDetails; import com.google.cloud.tools.jib.api.ImageReference; import com.google.cloud.tools.jib.http.NotifyingOutputStream; @@ -184,6 +185,17 @@ public boolean supported(Map parameters) { return true; } + @Override + public DockerInfoDetails info() throws IOException, InterruptedException { + // Runs 'docker info'. + Process infoProcess = docker("info", "-f", "{{json .}}"); + if (infoProcess.waitFor() != 0) { + throw new IOException( + "'docker info' command failed with error: " + getStderrOutput(infoProcess)); + } + return JsonTemplateMapper.readJson(infoProcess.getInputStream(), DockerInfoDetails.class); + } + @Override public String load(ImageTarball imageTarball, Consumer writtenByteCountListener) throws InterruptedException, IOException { diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java index 64926306ec..418c4ebc17 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java @@ -20,6 +20,7 @@ import com.google.cloud.tools.jib.api.ImageDetails; import com.google.cloud.tools.jib.api.ImageReference; import com.google.cloud.tools.jib.image.ImageTarball; +import com.google.cloud.tools.jib.json.JsonTemplate; import java.io.IOException; import java.nio.file.Path; import java.util.Map; @@ -47,4 +48,9 @@ public ImageDetails inspect(ImageReference imageReference) throws IOException, InterruptedException { return null; } + + @Override + public JsonTemplate info() throws IOException, InterruptedException { + return null; + } } From 888d78075d29f40fdaf0746db3af31c30ec60515 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Wed, 15 May 2024 15:49:20 +0000 Subject: [PATCH 02/18] add javadoc for info(); fix formatting --- .../java/com/google/cloud/tools/jib/api/DockerClient.java | 7 +++++++ .../google/cloud/tools/jib/builder/steps/StepsRunner.java | 8 +++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 4c79581a8c..304f62512f 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -70,5 +70,12 @@ void save(ImageReference imageReference, Path outputPath, Consumer written */ ImageDetails inspect(ImageReference imageReference) throws IOException, InterruptedException; + /** + * Gets the os and architecture of the local docker installation. + * + * @return the os type and architecture of the image + * @throws IOException if an I/O exception occurs or {@code docker info} failed + * @throws InterruptedException if the {@code docker info} process was interrupted + */ DockerInfoDetails info() throws IOException, InterruptedException; } 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 e21c0ed697..a1dbea1762 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 @@ -45,7 +45,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -665,10 +664,9 @@ private Image fetchBaseImageForLocalBuild(DockerClient dockerClient) DockerInfoDetails dockerInfoDetails = dockerClient.info(); String osType = dockerInfoDetails.getOsType(); String dockerArchitecture = computeArchitecture(dockerInfoDetails.getArchitecture()); - Iterator> imageIterator = - results.baseImagesAndBuiltImages.get().values().iterator(); - while (imageIterator.hasNext()) { - Image image = imageIterator.next().get(); + for (Map.Entry> imageEntry : + results.baseImagesAndBuiltImages.get().entrySet()) { + Image image = imageEntry.getValue().get(); if (image.getArchitecture().equals(dockerArchitecture) && image.getOs().equals(osType)) { return image; } From c209b14a6b98466cb8cc5a01ef0f700b6fed5fb9 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Sun, 19 May 2024 00:51:06 +0000 Subject: [PATCH 03/18] make info() default --- .../java/com/google/cloud/tools/jib/api/DockerClient.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 304f62512f..9d6644963d 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -74,8 +74,10 @@ void save(ImageReference imageReference, Path outputPath, Consumer written * Gets the os and architecture of the local docker installation. * * @return the os type and architecture of the image - * @throws IOException if an I/O exception occurs or {@code docker info} failed + * @throws IOException if an I/O exception occurs or {@code docker info} failed * @throws InterruptedException if the {@code docker info} process was interrupted */ - DockerInfoDetails info() throws IOException, InterruptedException; + default DockerInfoDetails info() throws IOException, InterruptedException { + return null; + } } From f6fb53ab84962a0af6a93bf520f2333be4ea7f86 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Sun, 19 May 2024 01:04:53 +0000 Subject: [PATCH 04/18] avoid returning null --- .../main/java/com/google/cloud/tools/jib/api/DockerClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 9d6644963d..7864778348 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -78,6 +78,6 @@ void save(ImageReference imageReference, Path outputPath, Consumer written * @throws InterruptedException if the {@code docker info} process was interrupted */ default DockerInfoDetails info() throws IOException, InterruptedException { - return null; + return new DockerInfoDetails(); } } From 1dee2d4cfd9f55c2f92188a49607d2239d9684a0 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Sun, 19 May 2024 01:05:10 +0000 Subject: [PATCH 05/18] avoid returning null --- .../google/cloud/tools/jib/docker/AnotherDockerClient.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java index 418c4ebc17..9878014367 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java @@ -48,9 +48,4 @@ public ImageDetails inspect(ImageReference imageReference) throws IOException, InterruptedException { return null; } - - @Override - public JsonTemplate info() throws IOException, InterruptedException { - return null; - } } From 83805fc322179958c37ece23c086f8e942ab3fe7 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Mon, 20 May 2024 20:38:46 +0000 Subject: [PATCH 06/18] fix copyright and formatting --- config/checkstyle/copyright-java.header | 2 +- .../main/java/com/google/cloud/tools/jib/api/DockerClient.java | 2 +- .../com/google/cloud/tools/jib/docker/AnotherDockerClient.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/config/checkstyle/copyright-java.header b/config/checkstyle/copyright-java.header index d15ad5912e..e60211cae3 100644 --- a/config/checkstyle/copyright-java.header +++ b/config/checkstyle/copyright-java.header @@ -1,5 +1,5 @@ ^/\*$ -^ \* Copyright 20(17|18|19|20|21|22|23) Google LLC\.$ +^ \* Copyright 20(17|18|19|20|21|22|23|24) 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$ diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 7864778348..2091265b27 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -74,7 +74,7 @@ void save(ImageReference imageReference, Path outputPath, Consumer written * Gets the os and architecture of the local docker installation. * * @return the os type and architecture of the image - * @throws IOException if an I/O exception occurs or {@code docker info} failed + * @throws IOException if an I/O exception occurs or {@code docker info} failed * @throws InterruptedException if the {@code docker info} process was interrupted */ default DockerInfoDetails info() throws IOException, InterruptedException { diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java index 9878014367..64926306ec 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/AnotherDockerClient.java @@ -20,7 +20,6 @@ import com.google.cloud.tools.jib.api.ImageDetails; import com.google.cloud.tools.jib.api.ImageReference; import com.google.cloud.tools.jib.image.ImageTarball; -import com.google.cloud.tools.jib.json.JsonTemplate; import java.io.IOException; import java.nio.file.Path; import java.util.Map; From 877ae0ec23f021b175b0009744dbe7d3b5c14cbb Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Wed, 22 May 2024 02:35:55 +0000 Subject: [PATCH 07/18] add testing --- .../tools/jib/api/JibIntegrationTest.java | 62 +++++++++++++++++ .../tools/jib/builder/steps/StepsRunner.java | 32 +++++---- .../tools/jib/docker/CliDockerClientTest.java | 40 +++++++++++ .../gradle/SingleProjectIntegrationTest.java | 25 +++++++ .../build-multi-platform-invalid.gradle | 34 ++++++++++ .../simple/build-multi-platform.gradle | 34 ++++++++++ .../maven/BuildDockerMojoIntegrationTest.java | 26 +++++++ .../simple/pom-multiplatform-build.xml | 2 +- .../pom-multiplatform-invalid-platforms.xml | 67 +++++++++++++++++++ 9 files changed, 309 insertions(+), 13 deletions(-) create mode 100644 jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle create mode 100644 jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle create mode 100644 jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java index a9916708c3..1d4dd2cc35 100644 --- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java +++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java @@ -16,6 +16,9 @@ package com.google.cloud.tools.jib.api; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + import com.google.cloud.tools.jib.Command; import com.google.cloud.tools.jib.api.buildplan.Platform; import com.google.cloud.tools.jib.blob.Blobs; @@ -304,6 +307,65 @@ public void testScratch_multiPlatform() Assert.assertEquals("windows", platform2.getOs()); } + @Test + public void testBasic_jibImageToDockerDaemon() + throws IOException, InterruptedException, InvalidImageReferenceException, ExecutionException, + RegistryException, CacheDirectoryCreationException { + Jib.from(DockerDaemonImage.named(dockerHost + ":5000/busybox")) + .setEntrypoint("echo", "Hello World") + .containerize( + Containerizer.to(DockerDaemonImage.named(dockerHost + ":5000/docker-to-docker"))); + + String output = + new Command("docker", "run", "--rm", dockerHost + ":5000/docker-to-docker").run(); + Assert.assertEquals("Hello World\n", output); + } + + @Test + public void testBasicMultiPlatform_toDockerDaemon() + throws IOException, InterruptedException, ExecutionException, RegistryException, + CacheDirectoryCreationException, InvalidImageReferenceException { + Jib.from( + RegistryImage.named( + "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977")) + .setPlatforms( + ImmutableSet.of(new Platform("arm64", "linux"), new Platform("amd64", "linux"))) + .setEntrypoint("echo", "Hello World") + .containerize( + Containerizer.to( + DockerDaemonImage.named(dockerHost + ":5000/docker-daemon-multi-platform")) + .setAllowInsecureRegistries(true)); + + String output = + new Command("docker", "run", "--rm", dockerHost + ":5000/docker-daemon-multi-platform") + .run(); + Assert.assertEquals("Hello World\n", output); + } + + @Test + public void testBasicMultiPlatform_toDockerDaemon_noMatchingImage() { + ExecutionException exception = + assertThrows( + ExecutionException.class, + () -> + Jib.from( + RegistryImage.named( + "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977")) + .setPlatforms( + ImmutableSet.of( + new Platform("arm64", "linux"), new Platform("arm", "linux"))) + .setEntrypoint("echo", "Hello World") + .containerize( + Containerizer.to( + DockerDaemonImage.named( + dockerHost + ":5000/docker-daemon-multi-platform")) + .setAllowInsecureRegistries(true))); + assertThat(exception) + .hasCauseThat() + .hasMessageThat() + .startsWith("The configured platforms don't match the Docker Engine's OS and architecture"); + } + @Test public void testDistroless_ociManifest() throws IOException, InterruptedException, ExecutionException, RegistryException, 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 a1dbea1762..3679e59316 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 @@ -39,7 +39,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; -import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -67,7 +66,7 @@ */ public class StepsRunner { - Logger logger = Logger.getLogger(StepResults.class.getName()); + private static final Logger LOGGER = Logger.getLogger(StepsRunner.class.getName()); /** Holds the individual step results. */ private static class StepResults { @@ -622,9 +621,17 @@ private void loadDocker( results.buildResult = executorService.submit( () -> { - Image builtImage = fetchBaseImageForLocalBuild(dockerClient); + DockerInfoDetails dockerInfoDetails = dockerClient.info(); + String osType = dockerInfoDetails.getOsType(); + String architecture = computeArchitecture(dockerInfoDetails.getArchitecture()); + Optional builtImage = fetchBuiltImageForLocalBuild(osType, architecture); + Preconditions.checkState( + builtImage.isPresent(), + String.format( + "The configured platforms don't match the Docker Engine's OS and architecture (%s/%s)", + osType, architecture)); return new LoadDockerStep( - buildContext, progressDispatcherFactory, dockerClient, builtImage) + buildContext, progressDispatcherFactory, dockerClient, builtImage.get()) .call(); }); } @@ -659,18 +666,19 @@ private String computeArchitecture(String architecture) { return architecture; } - private Image fetchBaseImageForLocalBuild(DockerClient dockerClient) - throws IOException, InterruptedException, ExecutionException { - DockerInfoDetails dockerInfoDetails = dockerClient.info(); - String osType = dockerInfoDetails.getOsType(); - String dockerArchitecture = computeArchitecture(dockerInfoDetails.getArchitecture()); + private 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"); + } for (Map.Entry> imageEntry : results.baseImagesAndBuiltImages.get().entrySet()) { Image image = imageEntry.getValue().get(); - if (image.getArchitecture().equals(dockerArchitecture) && image.getOs().equals(osType)) { - return image; + if (image.getArchitecture().equals(architecture) && image.getOs().equals(osType)) { + return Optional.of(image); } } - return results.baseImagesAndBuiltImages.get().values().iterator().next().get(); + return Optional.empty(); } } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java index 3e7cca5b4a..ac9b17ffa9 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java @@ -16,8 +16,12 @@ package com.google.cloud.tools.jib.docker; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + import com.google.cloud.tools.jib.api.DescriptorDigest; import com.google.cloud.tools.jib.api.DockerClient; +import com.google.cloud.tools.jib.api.DockerInfoDetails; import com.google.cloud.tools.jib.api.ImageReference; import com.google.cloud.tools.jib.docker.CliDockerClient.DockerImageDetails; import com.google.cloud.tools.jib.image.ImageTarball; @@ -83,6 +87,42 @@ public void testIsDockerInstalled_pass() throws URISyntaxException { Paths.get(Resources.getResource("core/docker/emptyFile").toURI()))); } + @Test + public void testInfo() throws InterruptedException, IOException { + String dockerInfoJson = "{ \"OSType\": \"windows\"," + "\"Architecture\": \"arm64\"}"; + DockerClient testDockerClient = + new CliDockerClient( + subcommand -> { + assertThat(subcommand).containsExactly("info", "-f", "{{json .}}"); + return mockProcessBuilder; + }); + // Simulates stdout. + Mockito.when(mockProcess.getInputStream()) + .thenReturn(new ByteArrayInputStream(dockerInfoJson.getBytes())); + + DockerInfoDetails infoDetails = testDockerClient.info(); + assertThat(infoDetails.getArchitecture()).isEqualTo("arm64"); + assertThat(infoDetails.getOsType()).isEqualTo("windows"); + } + + @Test + public void testInfo_fail() throws InterruptedException { + DockerClient testDockerClient = + new CliDockerClient( + subcommand -> { + assertThat(subcommand).containsExactly("info", "-f", "{{json .}}"); + return mockProcessBuilder; + }); + Mockito.when(mockProcess.waitFor()).thenReturn(1); + Mockito.when(mockProcess.getErrorStream()) + .thenReturn(new ByteArrayInputStream("error".getBytes(StandardCharsets.UTF_8))); + + IOException exception = assertThrows(IOException.class, testDockerClient::info); + assertThat(exception) + .hasMessageThat() + .contains("'docker info' command failed with error: error"); + } + @Test public void testLoad() throws IOException, InterruptedException { DockerClient testDockerClient = 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 7c53878dc5..6a5159da61 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 @@ -39,6 +39,7 @@ import org.gradle.testkit.runner.UnexpectedBuildFailure; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; +import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -592,4 +593,28 @@ 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 new file mode 100644 index 0000000000..fc85ed7b4a --- /dev/null +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle @@ -0,0 +1,34 @@ +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-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle new file mode 100644 index 0000000000..b3ae633ca9 --- /dev/null +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle @@ -0,0 +1,34 @@ +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 = 'eclipse-temurin:8-jdk-focal' + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + platform { + architecture = 'arm64' + 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 7d3be1d8c5..d7c84abb77 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,6 +17,7 @@ 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; @@ -275,4 +276,29 @@ 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 76a5966111..3269081f05 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} - busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977 + eclipse-temurin:11 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 new file mode 100644 index 0000000000..32a2a81c17 --- /dev/null +++ b/jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml @@ -0,0 +1,67 @@ + + + 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 + + + + + + + From ca74068ed4e30942dd6b834278aa1a2c0e4d8e9c Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Wed, 22 May 2024 02:43:08 +0000 Subject: [PATCH 08/18] fix formatting --- .../cloud/tools/jib/gradle/SingleProjectIntegrationTest.java | 1 - 1 file changed, 1 deletion(-) 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 6a5159da61..c5ef53a431 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 @@ -39,7 +39,6 @@ import org.gradle.testkit.runner.UnexpectedBuildFailure; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; -import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; From f16f6a7b2c9672923974821565d4eb5ee94ed9e0 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Wed, 22 May 2024 15:16:14 +0000 Subject: [PATCH 09/18] add arch not supported by macos --- .../java/com/google/cloud/tools/jib/api/JibIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java index 1d4dd2cc35..4e49d1e12d 100644 --- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java +++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/api/JibIntegrationTest.java @@ -353,7 +353,7 @@ public void testBasicMultiPlatform_toDockerDaemon_noMatchingImage() { "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977")) .setPlatforms( ImmutableSet.of( - new Platform("arm64", "linux"), new Platform("arm", "linux"))) + new Platform("s390x", "linux"), new Platform("arm", "linux"))) .setEntrypoint("echo", "Hello World") .containerize( Containerizer.to( From 8f81743ca610ac055a9f8e87ddedee4b15e1848e Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Thu, 23 May 2024 19:22:13 +0000 Subject: [PATCH 10/18] add unit testing --- .../cloud/tools/jib/api/DockerInfoDetails.java | 2 +- .../tools/jib/docker/CliDockerClientTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java index d904b550fd..fa486c40f9 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerInfoDetails.java @@ -20,7 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.cloud.tools.jib.json.JsonTemplate; -/** Contains the os and architecture from {@code docker info}. */ +/** Contains docker info details outputted by {@code docker info}. */ @JsonIgnoreProperties(ignoreUnknown = true) public class DockerInfoDetails implements JsonTemplate { diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java index ac9b17ffa9..a88539f8c0 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/docker/CliDockerClientTest.java @@ -123,6 +123,24 @@ public void testInfo_fail() throws InterruptedException { .contains("'docker info' command failed with error: error"); } + @Test + public void testInfo_returnsUnknownKeys() throws InterruptedException, IOException { + String dockerInfoJson = "{ \"unknownOS\": \"windows\"," + "\"unknownArchitecture\": \"arm64\"}"; + DockerClient testDockerClient = + new CliDockerClient( + subcommand -> { + assertThat(subcommand).containsExactly("info", "-f", "{{json .}}"); + return mockProcessBuilder; + }); + Mockito.when(mockProcess.waitFor()).thenReturn(0); + Mockito.when(mockProcess.getInputStream()) + .thenReturn(new ByteArrayInputStream(dockerInfoJson.getBytes())); + + DockerInfoDetails infoDetails = testDockerClient.info(); + assertThat(infoDetails.getArchitecture()).isEmpty(); + assertThat(infoDetails.getOsType()).isEmpty(); + } + @Test public void testLoad() throws IOException, InterruptedException { DockerClient testDockerClient = From b399f5e660103dab6116a0f3571dbcec2475cca2 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 15:31:34 +0000 Subject: [PATCH 11/18] replace plugin integration tests with unit tests --- .../tools/jib/builder/steps/StepsRunner.java | 14 +- .../jib/builder/steps/StepsRunnerTest.java | 131 +++++++++++++++++- .../gradle/SingleProjectIntegrationTest.java | 24 ---- .../build-multi-platform-invalid.gradle | 34 ----- .../maven/BuildDockerMojoIntegrationTest.java | 26 ---- .../simple/pom-multiplatform-build.xml | 2 +- .../pom-multiplatform-invalid-platforms.xml | 67 --------- 7 files changed, 134 insertions(+), 164 deletions(-) delete mode 100644 jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform-invalid.gradle delete mode 100644 jib-maven-plugin/src/test/resources/maven/projects/simple/pom-multiplatform-invalid-platforms.xml 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 - - - - - - - From 5e5e0332034a879f40fd2eb968f46a63185b380b Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 16:19:39 +0000 Subject: [PATCH 12/18] add comment clarifying supported archs --- .../cloud/tools/jib/builder/steps/StepsRunner.java | 1 + .../cloud/tools/jib/builder/steps/StepsRunnerTest.java | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) 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 43188388f7..c3dba511e9 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 @@ -659,6 +659,7 @@ private List> scheduleCallables(ImmutableList Date: Tue, 28 May 2024 16:25:02 +0000 Subject: [PATCH 13/18] fix javadoc; remove unused test gradle file --- .../cloud/tools/jib/api/DockerClient.java | 4 +-- .../simple/build-multi-platform.gradle | 34 ------------------- 2 files changed, 2 insertions(+), 36 deletions(-) delete mode 100644 jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java index 2091265b27..7e08a79495 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/api/DockerClient.java @@ -71,9 +71,9 @@ void save(ImageReference imageReference, Path outputPath, Consumer written ImageDetails inspect(ImageReference imageReference) throws IOException, InterruptedException; /** - * Gets the os and architecture of the local docker installation. + * Gets docker info details of local docker installation. * - * @return the os type and architecture of the image + * @return docker info details. * @throws IOException if an I/O exception occurs or {@code docker info} failed * @throws InterruptedException if the {@code docker info} process was interrupted */ diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle deleted file mode 100644 index b3ae633ca9..0000000000 --- a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.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 = 'eclipse-temurin:8-jdk-focal' - platforms { - platform { - architecture = 'amd64' - os = 'linux' - } - platform { - architecture = 'arm64' - os = 'linux' - } - } - } - to { - image = System.getProperty('_TARGET_IMAGE') - } -} From bb7c98dd92dc0e603b779ac9acfd81831dae93fb Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 16:41:30 +0000 Subject: [PATCH 14/18] rename mocks --- .../jib/builder/steps/StepsRunnerTest.java | 86 +++++++++---------- 1 file changed, 39 insertions(+), 47 deletions(-) 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 984b61e361..e4e0b4da0f 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 @@ -88,6 +88,12 @@ protected ExecutorService delegate() { @Mock private ProgressEventDispatcher progressDispatcher; @Mock private ExecutorService executorService; + @Mock private Image builtArm64AndLinuxImage; + @Mock private Image builtAmd64AndWindowsImage; + + @Mock private Image baseImage1; + @Mock private Image baseImage2; + private StepsRunner stepsRunner; @Before @@ -186,51 +192,44 @@ public void testIsImagePushed_skipExistingImageEnabledAndManifestNotPresent() { @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(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); + when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); + when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); + when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( ImmutableMap.of( baseImage1, - Futures.immediateFuture(mockImage1), + Futures.immediateFuture(builtArm64AndLinuxImage), baseImage2, - Futures.immediateFuture(mockImage2)))); + Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os2", "arch2"); - assertThat(expectedImage.get().getOs()).isEqualTo("os2"); - assertThat(expectedImage.get().getArchitecture()).isEqualTo("arch2"); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("linux", "arm64"); + + assertThat(expectedImage.get().getOs()).isEqualTo("linux"); + assertThat(expectedImage.get().getArchitecture()).isEqualTo("arm64"); } @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(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); + when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); + when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); + when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( ImmutableMap.of( baseImage1, - Futures.immediateFuture(builtImage1), + Futures.immediateFuture(builtArm64AndLinuxImage), baseImage2, - Futures.immediateFuture(builtImage2)))); + Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("linux", "arm64"); + + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os", "arch"); assertThat(expectedImage.isPresent()).isFalse(); } @@ -238,24 +237,21 @@ public void testFetchBuildImageForLocalBuild_differentOsAndArch() @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(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); + when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); + when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); + when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( ImmutableMap.of( baseImage1, - Futures.immediateFuture(builtImage1), + Futures.immediateFuture(builtArm64AndLinuxImage), baseImage2, - Futures.immediateFuture(builtImage2)))); + Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os1", "arch3"); + + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("linux", "arch"); assertThat(expectedImage.isPresent()).isFalse(); } @@ -263,25 +259,21 @@ public void testFetchBuildImageForLocalBuild_matchingOsDifferentArch() @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(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); + when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); + when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); + when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( ImmutableMap.of( baseImage1, - Futures.immediateFuture(builtImage1), + Futures.immediateFuture(builtArm64AndLinuxImage), baseImage2, - Futures.immediateFuture(builtImage2)))); + Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os3", "arch1"); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os", "arm64"); assertThat(expectedImage.isPresent()).isFalse(); } From 6edce95d1fbc99c5f755919c3ace4e1357278662 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 16:53:47 +0000 Subject: [PATCH 15/18] reformat spacing of mocks --- .../google/cloud/tools/jib/builder/steps/StepsRunnerTest.java | 2 -- 1 file changed, 2 deletions(-) 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 e4e0b4da0f..9bc183c667 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 @@ -87,10 +87,8 @@ protected ExecutorService delegate() { @Mock private ProgressEventDispatcher.Factory progressDispatcherFactory; @Mock private ProgressEventDispatcher progressDispatcher; @Mock private ExecutorService executorService; - @Mock private Image builtArm64AndLinuxImage; @Mock private Image builtAmd64AndWindowsImage; - @Mock private Image baseImage1; @Mock private Image baseImage2; From c96ac7f4872c3a60c67a7064706bfc9b701e78f3 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 17:08:04 +0000 Subject: [PATCH 16/18] remove redundant mocking --- .../jib/builder/steps/StepsRunnerTest.java | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) 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 9bc183c667..d19f8106ca 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 @@ -191,7 +191,6 @@ public void testIsImagePushed_skipExistingImageEnabledAndManifestNotPresent() { public void testFetchBuildImageForLocalBuild_matchingOsAndArch() throws ExecutionException, InterruptedException { when(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); - when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) @@ -204,19 +203,18 @@ public void testFetchBuildImageForLocalBuild_matchingOsAndArch() Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("linux", "arm64"); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("windows", "amd64"); - assertThat(expectedImage.get().getOs()).isEqualTo("linux"); - assertThat(expectedImage.get().getArchitecture()).isEqualTo("arm64"); + assertThat(expectedImage.get().getOs()).isEqualTo("windows"); + assertThat(expectedImage.get().getArchitecture()).isEqualTo("amd64"); } @Test - public void testFetchBuildImageForLocalBuild_differentOsAndArch() + public void testFetchBuildImageForLocalBuild_differentOs() throws ExecutionException, InterruptedException { when(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); - when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( @@ -227,18 +225,16 @@ public void testFetchBuildImageForLocalBuild_differentOsAndArch() Futures.immediateFuture(builtAmd64AndWindowsImage)))); stepsRunner.buildImages(progressDispatcherFactory); - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os", "arch"); + Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os", "arm64"); assertThat(expectedImage.isPresent()).isFalse(); } @Test - public void testFetchBuildImageForLocalBuild_matchingOsDifferentArch() + public void testFetchBuildImageForLocalBuild_differentArch() throws ExecutionException, InterruptedException { when(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); - when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); - when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); when(executorService.submit(Mockito.any(Callable.class))) .thenReturn( Futures.immediateFuture( @@ -254,28 +250,6 @@ public void testFetchBuildImageForLocalBuild_matchingOsDifferentArch() assertThat(expectedImage.isPresent()).isFalse(); } - @Test - public void testFetchBuildImageForLocalBuild_differentOsMatchingArch() - throws ExecutionException, InterruptedException { - when(builtArm64AndLinuxImage.getArchitecture()).thenReturn("arm64"); - when(builtArm64AndLinuxImage.getOs()).thenReturn("linux"); - when(builtAmd64AndWindowsImage.getArchitecture()).thenReturn("amd64"); - when(builtAmd64AndWindowsImage.getOs()).thenReturn("windows"); - when(executorService.submit(Mockito.any(Callable.class))) - .thenReturn( - Futures.immediateFuture( - ImmutableMap.of( - baseImage1, - Futures.immediateFuture(builtArm64AndLinuxImage), - baseImage2, - Futures.immediateFuture(builtAmd64AndWindowsImage)))); - stepsRunner.buildImages(progressDispatcherFactory); - - Optional expectedImage = stepsRunner.fetchBuiltImageForLocalBuild("os", "arm64"); - - assertThat(expectedImage.isPresent()).isFalse(); - } - @Test public void testComputeArchitecture_aarch64() { assertThat(stepsRunner.computeArchitecture("aarch64")).isEqualTo("arm64"); From f7121d10085be45569d1de707a4959c2fdda09f6 Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 22:08:51 +0000 Subject: [PATCH 17/18] happy path multiplatform build --- .../gradle/SingleProjectIntegrationTest.java | 10 ++++++ .../simple/build-multi-platform.gradle | 34 +++++++++++++++++++ .../maven/BuildDockerMojoIntegrationTest.java | 10 ++++++ .../simple/pom-multiplatform-build.xml | 2 +- 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle 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 7c53878dc5..faf32e883e 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,4 +592,14 @@ 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"); + } } diff --git a/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle new file mode 100644 index 0000000000..5235164884 --- /dev/null +++ b/jib-gradle-plugin/src/integration-test/resources/gradle/projects/simple/build-multi-platform.gradle @@ -0,0 +1,34 @@ +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 = 'eclipse-temurin:11' + platforms { + platform { + architecture = 'amd64' + os = 'linux' + } + platform { + architecture = 'arm64' + 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 7d3be1d8c5..d41781d223 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 @@ -275,4 +275,14 @@ 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()); + } } 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 76a5966111..3269081f05 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} - busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977 + eclipse-temurin:11 arm64 From 8eadc59b484b567cad1f32fc2a785a372f71fe9a Mon Sep 17 00:00:00 2001 From: mpeddada1 Date: Tue, 28 May 2024 22:29:51 +0000 Subject: [PATCH 18/18] rename arch mapping method --- .../cloud/tools/jib/builder/steps/StepsRunner.java | 4 ++-- .../tools/jib/builder/steps/StepsRunnerTest.java | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) 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 c3dba511e9..be760a13eb 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 @@ -623,7 +623,7 @@ private void loadDocker( () -> { DockerInfoDetails dockerInfoDetails = dockerClient.info(); String osType = dockerInfoDetails.getOsType(); - String architecture = computeArchitecture(dockerInfoDetails.getArchitecture()); + String architecture = normalizeArchitecture(dockerInfoDetails.getArchitecture()); Optional builtImage = fetchBuiltImageForLocalBuild(osType, architecture); Preconditions.checkState( builtImage.isPresent(), @@ -658,7 +658,7 @@ private List> scheduleCallables(ImmutableList