From e9d6f5656d868ccf2d287bbabbe3bd5f8e79b3db Mon Sep 17 00:00:00 2001 From: Gaurav Ghosh Date: Fri, 8 Jul 2022 00:08:36 +0530 Subject: [PATCH] fix: error when base image doesn't support target platforms --- .../jib/builder/steps/PlatformChecker.java | 36 +++++------ .../jib/builder/steps/PullBaseImageStep.java | 4 +- .../PlatformNotFoundInBaseImageException.java | 27 ++++++++ .../builder/steps/PlatformCheckerTest.java | 64 +++++++++++-------- .../builder/steps/PullBaseImageStepTest.java | 25 +++++--- 5 files changed, 99 insertions(+), 57 deletions(-) create mode 100644 jib-core/src/main/java/com/google/cloud/tools/jib/image/json/PlatformNotFoundInBaseImageException.java diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PlatformChecker.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PlatformChecker.java index 04819903b6..6f58544b73 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PlatformChecker.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PlatformChecker.java @@ -16,11 +16,10 @@ package com.google.cloud.tools.jib.builder.steps; -import com.google.cloud.tools.jib.api.LogEvent; import com.google.cloud.tools.jib.api.buildplan.Platform; import com.google.cloud.tools.jib.configuration.BuildContext; -import com.google.cloud.tools.jib.event.EventHandlers; import com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate; +import com.google.cloud.tools.jib.image.json.PlatformNotFoundInBaseImageException; import com.google.common.base.Verify; import java.nio.file.Path; import java.util.Optional; @@ -38,8 +37,8 @@ private PlatformChecker() {} * @param containerConfig container configuration JSON of the base image */ static void checkManifestPlatform( - BuildContext buildContext, ContainerConfigurationTemplate containerConfig) { - EventHandlers eventHandlers = buildContext.getEventHandlers(); + BuildContext buildContext, ContainerConfigurationTemplate containerConfig) + throws PlatformNotFoundInBaseImageException { Optional path = buildContext.getBaseImageConfiguration().getTarPath(); String baseImageName = path.map(Path::toString) @@ -49,9 +48,11 @@ static void checkManifestPlatform( Verify.verify(!platforms.isEmpty()); if (platforms.size() != 1) { - eventHandlers.dispatch( - LogEvent.warn( - "platforms configured, but '" + baseImageName + "' is not a manifest list")); + String msg = + String.format( + "cannot build for multiple platforms since the base image '%s' is not a manifest list.", + baseImageName); + throw new PlatformNotFoundInBaseImageException(msg); } else { Platform platform = platforms.iterator().next(); if (!platform.getArchitecture().equals(containerConfig.getArchitecture()) @@ -60,18 +61,15 @@ static void checkManifestPlatform( // Unfortunately, "platforms" has amd64/linux by default even if the user didn't explicitly // configure it. Skip reporting to suppress false alarm. if (!(platform.getArchitecture().equals("amd64") && platform.getOs().equals("linux"))) { - String warning = - "the configured platform (%s/%s) doesn't match the platform (%s/%s) of the base " - + "image (%s)"; - eventHandlers.dispatch( - LogEvent.warn( - String.format( - warning, - platform.getArchitecture(), - platform.getOs(), - containerConfig.getArchitecture(), - containerConfig.getOs(), - baseImageName))); + String msg = + String.format( + "the configured platform (%s/%s) doesn't match the platform (%s/%s) of the base image (%s)", + platform.getArchitecture(), + platform.getOs(), + containerConfig.getArchitecture(), + containerConfig.getOs(), + baseImageName); + throw new PlatformNotFoundInBaseImageException(msg); } } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStep.java index d47fcd2976..7c3481faf7 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStep.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStep.java @@ -42,6 +42,7 @@ import com.google.cloud.tools.jib.image.json.JsonToImageTranslator; import com.google.cloud.tools.jib.image.json.ManifestAndConfigTemplate; import com.google.cloud.tools.jib.image.json.ManifestTemplate; +import com.google.cloud.tools.jib.image.json.PlatformNotFoundInBaseImageException; import com.google.cloud.tools.jib.image.json.UnknownManifestFormatException; import com.google.cloud.tools.jib.image.json.UnlistedPlatformInManifestListException; import com.google.cloud.tools.jib.image.json.V21ManifestTemplate; @@ -442,7 +443,8 @@ private ContainerConfigurationTemplate pullContainerConfigJson( @VisibleForTesting List getCachedBaseImages() throws IOException, CacheCorruptedException, BadContainerConfigurationFormatException, - LayerCountMismatchException, UnlistedPlatformInManifestListException { + LayerCountMismatchException, UnlistedPlatformInManifestListException, + PlatformNotFoundInBaseImageException { ImageReference baseImage = buildContext.getBaseImageConfiguration().getImage(); Optional metadata = buildContext.getBaseImageLayersCache().retrieveMetadata(baseImage); diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/PlatformNotFoundInBaseImageException.java b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/PlatformNotFoundInBaseImageException.java new file mode 100644 index 0000000000..bafaac0a8b --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/image/json/PlatformNotFoundInBaseImageException.java @@ -0,0 +1,27 @@ +/* + * Copyright 2022 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.image.json; + +import com.google.cloud.tools.jib.api.RegistryException; + +/** Exception thrown when build target platforms are not found in the base image. */ +public class PlatformNotFoundInBaseImageException extends RegistryException { + + public PlatformNotFoundInBaseImageException(String message) { + super(message); + } +} diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PlatformCheckerTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PlatformCheckerTest.java index 05e026fe96..378a9c280b 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PlatformCheckerTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PlatformCheckerTest.java @@ -16,14 +16,16 @@ package com.google.cloud.tools.jib.builder.steps; +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + import com.google.cloud.tools.jib.api.ImageReference; -import com.google.cloud.tools.jib.api.LogEvent; import com.google.cloud.tools.jib.api.buildplan.Platform; import com.google.cloud.tools.jib.configuration.BuildContext; import com.google.cloud.tools.jib.configuration.ContainerConfiguration; import com.google.cloud.tools.jib.configuration.ImageConfiguration; -import com.google.cloud.tools.jib.event.EventHandlers; import com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate; +import com.google.cloud.tools.jib.image.json.PlatformNotFoundInBaseImageException; import com.google.common.collect.ImmutableSet; import java.nio.file.Path; import java.nio.file.Paths; @@ -40,13 +42,11 @@ public class PlatformCheckerTest { @Mock private BuildContext buildContext; @Mock private ContainerConfiguration containerConfig; - @Mock private EventHandlers eventHandlers; @Before public void setUp() { Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(ImageReference.scratch()).build()); - Mockito.when(buildContext.getEventHandlers()).thenReturn(eventHandlers); Mockito.when(buildContext.getContainerConfiguration()).thenReturn(containerConfig); } @@ -58,39 +58,43 @@ public void testCheckManifestPlatform_mismatch() { ContainerConfigurationTemplate containerConfigJson = new ContainerConfigurationTemplate(); containerConfigJson.setArchitecture("actual arch"); containerConfigJson.setOs("actual OS"); - - PlatformChecker.checkManifestPlatform(buildContext, containerConfigJson); - - Mockito.verify(eventHandlers) - .dispatch( - LogEvent.warn( - "the configured platform (configured arch/configured OS) doesn't match the " - + "platform (actual arch/actual OS) of the base image (scratch)")); + Exception ex = + assertThrows( + PlatformNotFoundInBaseImageException.class, + () -> PlatformChecker.checkManifestPlatform(buildContext, containerConfigJson)); + assertThat(ex) + .hasMessageThat() + .isEqualTo( + "the configured platform (configured arch/configured OS) doesn't match the " + + "platform (actual arch/actual OS) of the base image (scratch)"); } @Test - public void testCheckManifestPlatform_noWarningIfDefaultAmd64Linux() { + public void testCheckManifestPlatform_noWarningIfDefaultAmd64Linux() + throws PlatformNotFoundInBaseImageException { Mockito.when(containerConfig.getPlatforms()) .thenReturn(ImmutableSet.of(new Platform("amd64", "linux"))); ContainerConfigurationTemplate containerConfigJson = new ContainerConfigurationTemplate(); containerConfigJson.setArchitecture("actual arch"); containerConfigJson.setOs("actual OS"); - PlatformChecker.checkManifestPlatform(buildContext, containerConfigJson); - - Mockito.verifyNoInteractions(eventHandlers); } @Test public void testCheckManifestPlatform_multiplePlatformsConfigured() { Mockito.when(containerConfig.getPlatforms()) .thenReturn(ImmutableSet.of(new Platform("amd64", "linux"), new Platform("arch", "os"))); - - PlatformChecker.checkManifestPlatform(buildContext, new ContainerConfigurationTemplate()); - - Mockito.verify(eventHandlers) - .dispatch(LogEvent.warn("platforms configured, but 'scratch' is not a manifest list")); + Exception ex = + assertThrows( + PlatformNotFoundInBaseImageException.class, + () -> + PlatformChecker.checkManifestPlatform( + buildContext, new ContainerConfigurationTemplate())); + assertThat(ex) + .hasMessageThat() + .isEqualTo( + "cannot build for multiple platforms since the base image 'scratch' is not a manifest list."); } @Test @@ -101,11 +105,17 @@ public void testCheckManifestPlatform_tarBaseImage() { Mockito.when(containerConfig.getPlatforms()) .thenReturn(ImmutableSet.of(new Platform("amd64", "linux"), new Platform("arch", "os"))); - PlatformChecker.checkManifestPlatform(buildContext, new ContainerConfigurationTemplate()); - - Mockito.verify(eventHandlers) - .dispatch( - LogEvent.warn( - "platforms configured, but '" + tar.toString() + "' is not a manifest list")); + Exception ex = + assertThrows( + PlatformNotFoundInBaseImageException.class, + () -> + PlatformChecker.checkManifestPlatform( + buildContext, new ContainerConfigurationTemplate())); + assertThat(ex) + .hasMessageThat() + .isEqualTo( + "cannot build for multiple platforms since the base image '" + + tar + + "' is not a manifest list."); } } diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStepTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStepTest.java index cb353f10d4..d23caf0b54 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStepTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/steps/PullBaseImageStepTest.java @@ -38,6 +38,7 @@ import com.google.cloud.tools.jib.image.json.ContainerConfigurationTemplate; import com.google.cloud.tools.jib.image.json.ImageMetadataTemplate; import com.google.cloud.tools.jib.image.json.ManifestAndConfigTemplate; +import com.google.cloud.tools.jib.image.json.PlatformNotFoundInBaseImageException; import com.google.cloud.tools.jib.image.json.UnlistedPlatformInManifestListException; import com.google.cloud.tools.jib.image.json.V21ManifestTemplate; import com.google.cloud.tools.jib.image.json.V22ManifestListTemplate; @@ -243,8 +244,8 @@ public void testLookUpPlatformSpecificImageManifest() @Test public void testGetCachedBaseImages_emptyCache() throws InvalidImageReferenceException, IOException, CacheCorruptedException, - UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException { + UnlistedPlatformInManifestListException, PlatformNotFoundInBaseImageException, + BadContainerConfigurationFormatException, LayerCountMismatchException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -257,7 +258,7 @@ public void testGetCachedBaseImages_emptyCache() public void testGetCachedBaseImages_v21ManifestCached() throws InvalidImageReferenceException, IOException, CacheCorruptedException, UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException, DigestException { + LayerCountMismatchException, DigestException, PlatformNotFoundInBaseImageException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -286,7 +287,7 @@ public void testGetCachedBaseImages_v21ManifestCached() public void testGetCachedBaseImages_v22ManifestCached() throws InvalidImageReferenceException, IOException, CacheCorruptedException, UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException { + LayerCountMismatchException, PlatformNotFoundInBaseImageException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -312,7 +313,7 @@ public void testGetCachedBaseImages_v22ManifestCached() public void testGetCachedBaseImages_v22ManifestListCached() throws InvalidImageReferenceException, IOException, CacheCorruptedException, UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException { + LayerCountMismatchException, PlatformNotFoundInBaseImageException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -352,7 +353,7 @@ public void testGetCachedBaseImages_v22ManifestListCached() public void testGetCachedBaseImages_v22ManifestListCached_partialMatches() throws InvalidImageReferenceException, IOException, CacheCorruptedException, UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException { + LayerCountMismatchException, PlatformNotFoundInBaseImageException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -382,8 +383,8 @@ public void testGetCachedBaseImages_v22ManifestListCached_partialMatches() @Test public void testGetCachedBaseImages_v22ManifestListCached_onlyPlatforms() throws InvalidImageReferenceException, IOException, CacheCorruptedException, - UnlistedPlatformInManifestListException, BadContainerConfigurationFormatException, - LayerCountMismatchException { + UnlistedPlatformInManifestListException, PlatformNotFoundInBaseImageException, + BadContainerConfigurationFormatException, LayerCountMismatchException { ImageReference imageReference = ImageReference.parse("cat"); Mockito.when(buildContext.getBaseImageConfiguration()) .thenReturn(ImageConfiguration.builder(imageReference).build()); @@ -422,7 +423,8 @@ public void testGetCachedBaseImages_v22ManifestListCached_onlyPlatforms() @Test public void testTryMirrors_noMatchingMirrors() - throws LayerCountMismatchException, BadContainerConfigurationFormatException { + throws LayerCountMismatchException, BadContainerConfigurationFormatException, + PlatformNotFoundInBaseImageException { Mockito.when(imageConfiguration.getImageRegistry()).thenReturn("registry"); Mockito.when(buildContext.getRegistryMirrors()) .thenReturn(ImmutableListMultimap.of("unmatched1", "mirror1", "unmatched2", "mirror2")); @@ -475,6 +477,8 @@ public void testTryMirrors_multipleMirrors() .thenReturn(registryClientFactory); Mockito.when(registryClient.pullManifest(Mockito.any())) .thenThrow(new RegistryException("not found")); + Mockito.when(containerConfig.getPlatforms()) + .thenReturn(ImmutableSet.of(new Platform("amd64", "linux"))); RegistryClient.Factory gcrRegistryClientFactory = setUpWorkingRegistryClientFactory(); Mockito.when(buildContext.newBaseImageRegistryClientFactory("gcr.io")) @@ -513,7 +517,8 @@ public void testCall_allMirrorsFail() .thenReturn(registryClientFactory); Mockito.when(registryClient.pullManifest(Mockito.any())) .thenThrow(new RegistryException("not found")); - + Mockito.when(containerConfig.getPlatforms()) + .thenReturn(ImmutableSet.of(new Platform("amd64", "linux"))); RegistryClient.Factory dockerHubRegistryClientFactory = setUpWorkingRegistryClientFactory(); Mockito.when(buildContext.newBaseImageRegistryClientFactory()) .thenReturn(dockerHubRegistryClientFactory);