Skip to content

Commit

Permalink
Check configured platforms for single image building (#2793)
Browse files Browse the repository at this point in the history
* Check configured platforms for single image building
* Fix copyright header
* Check cached manifest too
* Log when using cached local image too
* Fix style violation
* Fix path separator issue in tests on Windows
  • Loading branch information
chanseokoh authored Oct 5, 2020
1 parent 8862aa3 commit 7d29959
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ static Callable<LocalImage> retrieveDockerDaemonLayersStep(
Optional<LocalImage> cachedImage =
getCachedDockerImage(buildContext.getBaseImageLayersCache(), dockerImageDetails);
if (cachedImage.isPresent()) {
PlatformChecker.checkManifestPlatform(
buildContext, cachedImage.get().configurationTemplate);
return cachedImage.get();
}

Expand All @@ -125,11 +127,14 @@ static Callable<LocalImage> retrieveDockerDaemonLayersStep(
dockerClient.save(imageReference, tarPath, throttledProgressReporter);
}

return cacheDockerImageTar(
buildContext,
tarPath,
progressEventDispatcher.newChildProducer(),
tempDirectoryProvider);
LocalImage localImage =
cacheDockerImageTar(
buildContext,
tarPath,
progressEventDispatcher.newChildProducer(),
tempDirectoryProvider);
PlatformChecker.checkManifestPlatform(buildContext, localImage.configurationTemplate);
return localImage;
}
};
}
Expand All @@ -139,9 +144,13 @@ static Callable<LocalImage> retrieveTarLayersStep(
ProgressEventDispatcher.Factory progressEventDispatcherFactory,
Path tarPath,
TempDirectoryProvider tempDirectoryProvider) {
return () ->
cacheDockerImageTar(
buildContext, tarPath, progressEventDispatcherFactory, tempDirectoryProvider);
return () -> {
LocalImage localImage =
cacheDockerImageTar(
buildContext, tarPath, progressEventDispatcherFactory, tempDirectoryProvider);
PlatformChecker.checkManifestPlatform(buildContext, localImage.configurationTemplate);
return localImage;
};
}

static Callable<ImagesAndRegistryClient> returnImageAndRegistryClientStep(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2020 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.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.common.base.Verify;
import java.util.Set;

/** Provides helper methods to check platforms. */
public class PlatformChecker {

/**
* Assuming the base image is not a manifest list, checks and warns misconfigured platforms.
*
* @param buildContext the {@link BuildContext}
* @param containerConfig container configuration JSON of the base image
*/
static void checkManifestPlatform(
BuildContext buildContext, ContainerConfigurationTemplate containerConfig) {
EventHandlers eventHandlers = buildContext.getEventHandlers();
String baseImageName =
buildContext.getBaseImageConfiguration().getTarPath().isPresent()
? buildContext.getBaseImageConfiguration().getTarPath().get().toString()
: buildContext.getBaseImageConfiguration().getImage().toString();

Set<Platform> platforms = buildContext.getContainerConfiguration().getPlatforms();
Verify.verify(!platforms.isEmpty());

if (platforms.size() != 1) {
eventHandlers.dispatch(
LogEvent.warn(
"platforms configured, but '" + baseImageName + "' is not a manifest list"));
} else {
Platform platform = platforms.iterator().next();
if (!platform.getArchitecture().equals(containerConfig.getArchitecture())
|| !platform.getOs().equals(containerConfig.getOs())) {

// 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)));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ private List<Image> pullBaseImages(
BuildableManifestTemplate imageManifest = (BuildableManifestTemplate) manifestTemplate;
ContainerConfigurationTemplate containerConfig =
pullContainerConfigJson(manifestAndDigest, registryClient, progressEventDispatcher);
PlatformChecker.checkManifestPlatform(buildContext, containerConfig);
cache.writeMetadata(baseImageConfig.getImage(), imageManifest, containerConfig);
return Collections.singletonList(
JsonToImageTranslator.toImage(imageManifest, containerConfig));
Expand Down Expand Up @@ -379,10 +380,14 @@ List<Image> getCachedBaseImages()
return Collections.singletonList(
JsonToImageTranslator.toImage((V21ManifestTemplate) manifest));
}

ContainerConfigurationTemplate containerConfig =
Verify.verifyNotNull(manifestsAndConfigs.get(0).getConfig());
PlatformChecker.checkManifestPlatform(buildContext, containerConfig);

return Collections.singletonList(
JsonToImageTranslator.toImage(
(BuildableManifestTemplate) Verify.verifyNotNull(manifest),
Verify.verifyNotNull(manifestsAndConfigs.get(0).getConfig())));
(BuildableManifestTemplate) Verify.verifyNotNull(manifest), containerConfig));
}

// Manifest list cached. Identify matching platforms and check if all of them are cached.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright 2020 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.builder.steps;

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.builder.ProgressEventDispatcher;
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.registry.RegistryClient;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

/** Tests for {@link PlatformChecker}. */
@RunWith(MockitoJUnitRunner.class)
public class PlatformCheckerTest {

@Mock private ProgressEventDispatcher.Factory progressDispatcherFactory;
@Mock private BuildContext buildContext;
@Mock private RegistryClient registryClient;
@Mock private ImageConfiguration imageConfiguration;
@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);
}

@Test
public void testCheckManifestPlatform_mismatch() {
Mockito.when(containerConfig.getPlatforms())
.thenReturn(ImmutableSet.of(new Platform("configured arch", "configured OS")));

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)"));
}

@Test
public void testCheckManifestPlatform_noWarningIfDefaultAmd64Linux() {
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"));
}

@Test
public void testCheckManifestPlatform_tarBaseImage() {
Path tar = Paths.get("/foo/bar.tar");
Mockito.when(buildContext.getBaseImageConfiguration())
.thenReturn(ImageConfiguration.builder(ImageReference.scratch()).setTarPath(tar).build());
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"));
}
}

0 comments on commit 7d29959

Please sign in to comment.