Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attach build to docker daemon functionality to gradle task #265

Merged
merged 7 commits into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,15 @@
import com.google.cloud.tools.jib.Command;
import com.google.cloud.tools.jib.cache.Caches;
import com.google.cloud.tools.jib.image.ImageReference;
import com.google.cloud.tools.jib.registry.LocalRegistry;
import java.nio.file.Path;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

/** Integration tests for {@link BuildDockerSteps}. */
public class BuildDockerStepsIntegrationTest {

@ClassRule public static LocalRegistry localRegistry = new LocalRegistry(5000);

private static final TestBuildLogger logger = new TestBuildLogger();

@Rule public TemporaryFolder temporaryCacheDirectory = new TemporaryFolder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,19 @@ class BuildContainerConfigurationStep implements Callable<ListenableFuture<Blob>
@Override
public ListenableFuture<Blob> call() throws ExecutionException, InterruptedException {
// TODO: This might need to belong in BuildImageSteps.
List<ListenableFuture<?>> afterBaseImageLayerFuturesFutureDependencies = new ArrayList<>();
afterBaseImageLayerFuturesFutureDependencies.addAll(
List<ListenableFuture<?>> afterImageLayerFuturesFutureDependencies = new ArrayList<>();
afterImageLayerFuturesFutureDependencies.addAll(
NonBlockingFutures.get(pullBaseImageLayerFuturesFuture));
afterBaseImageLayerFuturesFutureDependencies.addAll(buildApplicationLayerFutures);
return Futures.whenAllSucceed(afterBaseImageLayerFuturesFutureDependencies)
.call(this::afterBaseImageLayerFuturesFuture, listeningExecutorService);
afterImageLayerFuturesFutureDependencies.addAll(buildApplicationLayerFutures);
return Futures.whenAllSucceed(afterImageLayerFuturesFutureDependencies)
.call(this::afterImageLayerFuturesFuture, listeningExecutorService);
}

/**
* Depends on {@code pushAuthorizationFuture}, {@code pullBaseImageLayerFuturesFuture.get()}, and
* {@code buildApplicationLayerFutures}.
*/
private Blob afterBaseImageLayerFuturesFuture()
private Blob afterImageLayerFuturesFuture()
throws ExecutionException, InterruptedException, LayerPropertyNotFoundException {
try (Timer ignored = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) {
// Constructs the image.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public class BuildDockerSteps {
private final SourceFilesConfiguration sourceFilesConfiguration;
private final Caches.Initializer cachesInitializer;

BuildDockerSteps(
public BuildDockerSteps(
BuildConfiguration buildConfiguration,
SourceFilesConfiguration sourceFilesConfiguration,
Caches.Initializer cachesInitializer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ private Void afterPushBaseImageLayerFuturesFuture()
}
for (Future<CachedLayer> cachedLayerFuture : buildApplicationLayerFutures) {
Path layerFile = NonBlockingFutures.get(cachedLayerFuture).getContentFile();
layerFiles.add(layerFile.getFileName().toString());
tarStreamBuilder.addEntry(
new TarArchiveEntry(layerFile.toFile(), layerFile.getFileName().toString()));
// TODO: Consolidate with build configuration step so we don't have to rebuild the image
if (!layerFiles.contains(layerFile.getFileName().toString())) {
layerFiles.add(layerFile.getFileName().toString());
tarStreamBuilder.addEntry(
new TarArchiveEntry(layerFile.toFile(), layerFile.getFileName().toString()));
}
}

// Add config to tarball
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private Void afterPushBaseImageLayerFuturesFuture()
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository());

// TODO: Consolidate with BuildAndPushContainerConfigurationStep.
// TODO: Consolidate with BuildContainerConfigurationStep.
// Constructs the image.
Image image = new Image();
for (Future<CachedLayer> cachedLayerFuture :
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* 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.frontend;

import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpStatusCodes;
import com.google.cloud.tools.jib.builder.BuildConfiguration;
import com.google.cloud.tools.jib.builder.BuildDockerSteps;
import com.google.cloud.tools.jib.builder.SourceFilesConfiguration;
import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException;
import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException;
import com.google.cloud.tools.jib.cache.Caches;
import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException;
import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException;
import java.io.IOException;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import org.apache.http.conn.HttpHostConnectException;

/**
* Runs {@link BuildDockerSteps} and builds helpful error messages.
*
* <p>TODO: Consolidate with {@link BuildImageStepsRunner}.
*/
public class BuildDockerStepsRunner {

/**
* Sets up a new {@link BuildDockerStepsRunner}. Creates the directory for the cache, if needed.
*
* @param useOnlyProjectCache if {@code true}, sets the base layers cache directory to be the same
* as the application layers cache directory
* @throws CacheDirectoryCreationException if the {@code cacheDirectory} could not be created
*/
public static BuildDockerStepsRunner newRunner(
BuildConfiguration buildConfiguration,
SourceFilesConfiguration sourceFilesConfiguration,
Path cacheDirectory,
boolean useOnlyProjectCache)
throws CacheDirectoryCreationException {
if (!Files.exists(cacheDirectory)) {
try {
Files.createDirectory(cacheDirectory);

} catch (IOException ex) {
throw new CacheDirectoryCreationException(cacheDirectory, ex);
}
}
Caches.Initializer cachesInitializer = Caches.newInitializer(cacheDirectory);
if (useOnlyProjectCache) {
cachesInitializer.setBaseCacheDirectory(cacheDirectory);
}

return new BuildDockerStepsRunner(
buildConfiguration, sourceFilesConfiguration, cachesInitializer);
}

private final Supplier<BuildDockerSteps> buildDockerStepsSupplier;

private BuildDockerStepsRunner(
BuildConfiguration buildConfiguration,
SourceFilesConfiguration sourceFilesConfiguration,
Caches.Initializer cachesInitializer) {
buildDockerStepsSupplier =
() -> new BuildDockerSteps(buildConfiguration, sourceFilesConfiguration, cachesInitializer);
}

/**
* Runs the {@link BuildDockerSteps}.
*
* @param helpfulSuggestions suggestions to use in help messages for exceptions
*/
public void buildDocker(HelpfulSuggestions helpfulSuggestions)
throws BuildImageStepsExecutionException {
BuildDockerSteps buildDockerSteps = buildDockerStepsSupplier.get();

try {
buildDockerSteps.run();

} catch (CacheMetadataCorruptedException cacheMetadataCorruptedException) {
// TODO: Have this be different for Maven and Gradle.
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forCacheMetadataCorrupted(), cacheMetadataCorruptedException);

} catch (ExecutionException executionException) {
BuildConfiguration buildConfiguration = buildDockerSteps.getBuildConfiguration();

if (executionException.getCause() instanceof HttpHostConnectException) {
// Failed to connect to registry.
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forHttpHostConnect(), executionException.getCause());

} else if (executionException.getCause() instanceof RegistryUnauthorizedException) {
handleRegistryUnauthorizedException(
(RegistryUnauthorizedException) executionException.getCause(),
buildConfiguration,
helpfulSuggestions);

} else if (executionException.getCause() instanceof RegistryAuthenticationFailedException
&& executionException.getCause().getCause() instanceof HttpResponseException) {
handleRegistryUnauthorizedException(
new RegistryUnauthorizedException(
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository(),
(HttpResponseException) executionException.getCause().getCause()),
buildConfiguration,
helpfulSuggestions);

} else if (executionException.getCause() instanceof UnknownHostException) {
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forUnknownHost(), executionException.getCause());

} else {
throw new BuildImageStepsExecutionException(
helpfulSuggestions.none(), executionException.getCause());
}

} catch (InterruptedException | IOException ex) {
// TODO: Add more suggestions for various build failures.
throw new BuildImageStepsExecutionException(helpfulSuggestions.none(), ex);

} catch (CacheDirectoryNotOwnedException ex) {
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forCacheDirectoryNotOwned(ex.getCacheDirectory()), ex);
}
}

private void handleRegistryUnauthorizedException(
RegistryUnauthorizedException registryUnauthorizedException,
BuildConfiguration buildConfiguration,
HelpfulSuggestions helpfulSuggestions)
throws BuildImageStepsExecutionException {
if (registryUnauthorizedException.getHttpResponseException().getStatusCode()
== HttpStatusCodes.STATUS_CODE_FORBIDDEN) {
// No permissions for registry/repository.
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forHttpStatusCodeForbidden(
registryUnauthorizedException.getImageReference()),
registryUnauthorizedException);

} else {
boolean isRegistryForBase =
registryUnauthorizedException
.getRegistry()
.equals(buildConfiguration.getBaseImageRegistry());
boolean areBaseImageCredentialsConfigured =
buildConfiguration.getBaseImageCredentialHelperName() != null
|| buildConfiguration.getKnownBaseRegistryCredentials() != null;
if (isRegistryForBase && !areBaseImageCredentialsConfigured) {
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forNoCredentialHelpersDefinedForBaseImage(
registryUnauthorizedException.getRegistry()),
registryUnauthorizedException);
}

// Credential helper probably was not configured correctly or did not have the necessary
// credentials.
throw new BuildImageStepsExecutionException(
helpfulSuggestions.forCredentialsNotCorrect(registryUnauthorizedException.getRegistry()),
registryUnauthorizedException);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ private static String buildAndRun(TestProject testProject, String imageReference
return new Command("docker", "run", imageReference).run();
}

private static String buildToDockerDaemonAndRun(TestProject testProject, String imageReference)
throws IOException, InterruptedException {
BuildResult buildResult = testProject.build("build", "jibBuildDocker");

BuildTask jibBuildDockerTask = buildResult.task(":jibBuildDocker");

Assert.assertNotNull(jibBuildDockerTask);
Assert.assertEquals(TaskOutcome.SUCCESS, jibBuildDockerTask.getOutcome());
Assert.assertThat(
buildResult.getOutput(),
CoreMatchers.containsString("Built image to Docker daemon as " + imageReference));

return new Command("docker", "run", imageReference).run();
}

@Test
public void testBuild_empty() throws IOException, InterruptedException {
Assert.assertEquals(
Expand All @@ -62,6 +77,22 @@ public void testBuild_simple() throws IOException, InterruptedException {
buildAndRun(simpleTestProject, "gcr.io/jib-integration-testing/simpleimage:gradle"));
}

@Test
public void testDockerDaemon_empty() throws IOException, InterruptedException {
Assert.assertEquals(
"",
buildToDockerDaemonAndRun(
emptyTestProject, "gcr.io/jib-integration-testing/emptyimage:gradle"));
}

@Test
public void testDockerDaemon_simple() throws IOException, InterruptedException {
Assert.assertEquals(
"Hello, world\n",
buildToDockerDaemonAndRun(
simpleTestProject, "gcr.io/jib-integration-testing/simpleimage:gradle"));
}

@Test
public void testDockerContext() throws IOException, InterruptedException {
BuildResult buildResult = simpleTestProject.build("build", "jibDockerContext", "--info");
Expand Down
Loading