diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildAndPushContainerConfigurationStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildContainerConfigurationStep.java similarity index 63% rename from jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildAndPushContainerConfigurationStep.java rename to jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildContainerConfigurationStep.java index fae50e7cf9..bbcde14340 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildAndPushContainerConfigurationStep.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildContainerConfigurationStep.java @@ -18,48 +18,39 @@ import com.google.cloud.tools.jib.Timer; import com.google.cloud.tools.jib.blob.Blob; -import com.google.cloud.tools.jib.blob.BlobDescriptor; import com.google.cloud.tools.jib.cache.CachedLayer; -import com.google.cloud.tools.jib.hash.CountingDigestOutputStream; -import com.google.cloud.tools.jib.http.Authorization; import com.google.cloud.tools.jib.image.Image; import com.google.cloud.tools.jib.image.LayerPropertyNotFoundException; import com.google.cloud.tools.jib.image.json.ImageToJsonTranslator; -import com.google.cloud.tools.jib.registry.RegistryClient; -import com.google.cloud.tools.jib.registry.RegistryException; -import com.google.common.io.ByteStreams; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -class BuildAndPushContainerConfigurationStep implements Callable> { +/** Builds the container configuration. */ +class BuildContainerConfigurationStep implements Callable> { private static final String DESCRIPTION = "Building container configuration"; private final BuildConfiguration buildConfiguration; private final ListeningExecutorService listeningExecutorService; - private final ListenableFuture pushAuthorizationFuture; private final ListenableFuture>> pullBaseImageLayerFuturesFuture; private final List> buildApplicationLayerFutures; private final List entrypoint; - BuildAndPushContainerConfigurationStep( + BuildContainerConfigurationStep( BuildConfiguration buildConfiguration, ListeningExecutorService listeningExecutorService, - ListenableFuture pushAuthorizationFuture, ListenableFuture>> pullBaseImageLayerFuturesFuture, List> buildApplicationLayerFutures, List entrypoint) { this.buildConfiguration = buildConfiguration; this.listeningExecutorService = listeningExecutorService; - this.pushAuthorizationFuture = pushAuthorizationFuture; this.pullBaseImageLayerFuturesFuture = pullBaseImageLayerFuturesFuture; this.buildApplicationLayerFutures = buildApplicationLayerFutures; this.entrypoint = entrypoint; @@ -67,10 +58,9 @@ class BuildAndPushContainerConfigurationStep implements Callable call() throws ExecutionException, InterruptedException { + public ListenableFuture call() throws ExecutionException, InterruptedException { // TODO: This might need to belong in BuildImageSteps. List> afterBaseImageLayerFuturesFutureDependencies = new ArrayList<>(); - afterBaseImageLayerFuturesFutureDependencies.add(pushAuthorizationFuture); afterBaseImageLayerFuturesFutureDependencies.addAll( NonBlockingFutures.get(pullBaseImageLayerFuturesFuture)); afterBaseImageLayerFuturesFutureDependencies.addAll(buildApplicationLayerFutures); @@ -82,17 +72,9 @@ public ListenableFuture call() throws ExecutionException, Interr * Depends on {@code pushAuthorizationFuture}, {@code pullBaseImageLayerFuturesFuture.get()}, and * {@code buildApplicationLayerFutures}. */ - private BlobDescriptor afterBaseImageLayerFuturesFuture() - throws ExecutionException, InterruptedException, LayerPropertyNotFoundException, IOException, - RegistryException { - try (Timer timer = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) { - RegistryClient registryClient = - new RegistryClient( - NonBlockingFutures.get(pushAuthorizationFuture), - buildConfiguration.getTargetRegistry(), - buildConfiguration.getTargetRepository()) - .setTimer(timer); - + private Blob afterBaseImageLayerFuturesFuture() + throws ExecutionException, InterruptedException, LayerPropertyNotFoundException { + try (Timer ignored = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) { // Constructs the image. Image image = new Image(); for (Future cachedLayerFuture : @@ -105,24 +87,8 @@ private BlobDescriptor afterBaseImageLayerFuturesFuture() image.setEnvironment(buildConfiguration.getEnvironment()); image.setEntrypoint(entrypoint); - ImageToJsonTranslator imageToJsonTranslator = new ImageToJsonTranslator(image); - // Gets the container configuration content descriptor. - Blob containerConfigurationBlob = imageToJsonTranslator.getContainerConfigurationBlob(); - CountingDigestOutputStream digestOutputStream = - new CountingDigestOutputStream(ByteStreams.nullOutputStream()); - containerConfigurationBlob.writeTo(digestOutputStream); - BlobDescriptor containerConfigurationBlobDescriptor = digestOutputStream.toBlobDescriptor(); - - timer.lap( - "Pushing container configuration " + containerConfigurationBlobDescriptor.getDigest()); - - // TODO: Use PushBlobStep. - // Pushes the container configuration. - registryClient.pushBlob( - containerConfigurationBlobDescriptor.getDigest(), containerConfigurationBlob); - - return containerConfigurationBlobDescriptor; + return new ImageToJsonTranslator(image).getContainerConfigurationBlob(); } } } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildImageSteps.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildImageSteps.java index 1889c2cf15..f98502bc6a 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildImageSteps.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildImageSteps.java @@ -17,6 +17,7 @@ package com.google.cloud.tools.jib.builder; import com.google.cloud.tools.jib.Timer; +import com.google.cloud.tools.jib.blob.Blob; import com.google.cloud.tools.jib.blob.BlobDescriptor; import com.google.cloud.tools.jib.cache.Cache; import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException; @@ -144,19 +145,30 @@ public void run() listeningExecutorService) .call(); + timer2.lap("Setting up build container configuration"); + // Builds the container configuration. + ListenableFuture> buildContainerConfigurationFutureFuture = + Futures.whenAllSucceed(pullBaseImageLayerFuturesFuture) + .call( + new BuildContainerConfigurationStep( + buildConfiguration, + listeningExecutorService, + pullBaseImageLayerFuturesFuture, + buildAndCacheApplicationLayerFutures, + entrypoint), + listeningExecutorService); + timer2.lap("Setting up container configuration push"); - // Builds and pushes the container configuration. + // Pushes the container configuration. ListenableFuture> - buildAndPushContainerConfigurationFutureFuture = - Futures.whenAllSucceed(pullBaseImageLayerFuturesFuture) + pushContainerConfigurationFutureFuture = + Futures.whenAllSucceed(buildContainerConfigurationFutureFuture) .call( - new BuildAndPushContainerConfigurationStep( + new PushContainerConfigurationStep( buildConfiguration, - listeningExecutorService, authenticatePushFuture, - pullBaseImageLayerFuturesFuture, - buildAndCacheApplicationLayerFutures, - entrypoint), + buildContainerConfigurationFutureFuture, + listeningExecutorService), listeningExecutorService); timer2.lap("Setting up application layer push"); @@ -173,8 +185,7 @@ public void run() // Pushes the new image manifest. ListenableFuture pushImageFuture = Futures.whenAllSucceed( - pushBaseImageLayerFuturesFuture, - buildAndPushContainerConfigurationFutureFuture) + pushBaseImageLayerFuturesFuture, pushContainerConfigurationFutureFuture) .call( new PushImageStep( buildConfiguration, @@ -184,7 +195,7 @@ public void run() buildAndCacheApplicationLayerFutures, pushBaseImageLayerFuturesFuture, pushApplicationLayersFuture, - buildAndPushContainerConfigurationFutureFuture), + pushContainerConfigurationFutureFuture), listeningExecutorService); timer2.lap("Running push new image"); diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushContainerConfigurationStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushContainerConfigurationStep.java new file mode 100644 index 0000000000..d143b4aa9d --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushContainerConfigurationStep.java @@ -0,0 +1,94 @@ +/* + * Copyright 2018 Google LLC. All Rights Reserved. + * + * 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; + +import com.google.cloud.tools.jib.Timer; +import com.google.cloud.tools.jib.blob.Blob; +import com.google.cloud.tools.jib.blob.BlobDescriptor; +import com.google.cloud.tools.jib.hash.CountingDigestOutputStream; +import com.google.cloud.tools.jib.http.Authorization; +import com.google.cloud.tools.jib.registry.RegistryClient; +import com.google.cloud.tools.jib.registry.RegistryException; +import com.google.common.io.ByteStreams; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +/** Pushes the container configuration. */ +class PushContainerConfigurationStep implements Callable> { + + private static final String DESCRIPTION = "Pushing container configuration"; + + private final BuildConfiguration buildConfiguration; + private final ListenableFuture pushAuthorizationFuture; + private final ListenableFuture> buildConfigurationFutureFuture; + private final ListeningExecutorService listeningExecutorService; + + PushContainerConfigurationStep( + BuildConfiguration buildConfiguration, + ListenableFuture pushAuthorizationFuture, + ListenableFuture> buildConfigurationFutureFuture, + ListeningExecutorService listeningExecutorService) { + this.buildConfiguration = buildConfiguration; + this.pushAuthorizationFuture = pushAuthorizationFuture; + this.buildConfigurationFutureFuture = buildConfigurationFutureFuture; + this.listeningExecutorService = listeningExecutorService; + } + + /** Depends on {@code buildConfigurationFutureFuture} and {@code pushAuthorizationFuture}. */ + @Override + public ListenableFuture call() throws ExecutionException, InterruptedException { + List> afterBuildConfigurationFutureFutureDependencies = new ArrayList<>(); + afterBuildConfigurationFutureFutureDependencies.add(pushAuthorizationFuture); + afterBuildConfigurationFutureFutureDependencies.add( + NonBlockingFutures.get(buildConfigurationFutureFuture)); + return Futures.whenAllSucceed(afterBuildConfigurationFutureFutureDependencies) + .call(this::afterBuildConfigurationFutureFuture, listeningExecutorService); + } + + /** + * Depends on {@code buildConfigurationFutureFuture.get()} and {@code pushAuthorizationFuture}. + */ + private BlobDescriptor afterBuildConfigurationFutureFuture() + throws ExecutionException, InterruptedException, IOException, RegistryException { + try (Timer timer = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) { + // TODO: Use PushBlobStep. + // Pushes the container configuration. + RegistryClient registryClient = + new RegistryClient( + NonBlockingFutures.get(pushAuthorizationFuture), + buildConfiguration.getTargetRegistry(), + buildConfiguration.getTargetRepository()) + .setTimer(timer); + + CountingDigestOutputStream digestOutputStream = + new CountingDigestOutputStream(ByteStreams.nullOutputStream()); + Blob containerConfigurationBlob = + NonBlockingFutures.get(NonBlockingFutures.get(buildConfigurationFutureFuture)); + containerConfigurationBlob.writeTo(digestOutputStream); + + BlobDescriptor descriptor = digestOutputStream.toBlobDescriptor(); + registryClient.pushBlob(descriptor.getDigest(), containerConfigurationBlob); + return descriptor; + } + } +} diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/hash/CountingDigestOutputStream.java b/jib-core/src/main/java/com/google/cloud/tools/jib/hash/CountingDigestOutputStream.java index fd82a758a7..fcb78218fb 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/hash/CountingDigestOutputStream.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/hash/CountingDigestOutputStream.java @@ -44,7 +44,10 @@ public CountingDigestOutputStream(OutputStream outputStream) { } } - /** Builds a {@link BlobDescriptor} with the hash and size of the bytes written. */ + /** + * Builds a {@link BlobDescriptor} with the hash and size of the bytes written. The buffer resets + * after this method is called, so this method should only be called once per BlobDescriptor. + */ public BlobDescriptor toBlobDescriptor() { try { byte[] hashedBytes = digest.digest();