diff --git a/CHANGELOG.md b/CHANGELOG.md
index a6f19f559a..b6a41ef1fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,9 +13,10 @@ All notable changes to this project will be documented in this file.
- Simple example `helloworld` project under `examples/` ([#62](https://github.com/google/jib/pull/62))
- Better error messages when pushing an image manifest ([#63](https://github.com/google/jib/pull/63))
- Validates target image configuration ([#63](https://github.com/google/jib/pull/63))
+- Configure multiple credential helpers with `credHelpers` ([#68](https://github.com/google/jib/pull/68))
+- Configure registry credentials with Maven settings ([#81](https://github.com/google/jib/pull/81))
### Changed
-- Configure multiple credential helpers with `credHelpers` ([#68](https://github.com/google/jib/pull/68))
- Removed configuration `credentialHelperName` ([#68](https://github.com/google/jib/pull/68))
### Fixed
@@ -23,3 +24,4 @@ All notable changes to this project will be documented in this file.
- Infers common credential helper names (for GCR and ECR) ([#64](https://github.com/google/jib/pull/64))
- Cannot use private base image ([#68](https://github.com/google/jib/pull/68))
- Building applications with no resources ([#73](https://github.com/google/jib/pull/73))
+- Pushing to registries like Docker Hub and ACR ([#75](https://github.com/google/jib/issues/75))
diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildImageStepsIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildImageStepsIntegrationTest.java
index 3258f4439a..4af10e95a9 100644
--- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildImageStepsIntegrationTest.java
+++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/builder/BuildImageStepsIntegrationTest.java
@@ -42,11 +42,11 @@ public void testSteps() throws Exception {
SourceFilesConfiguration sourceFilesConfiguration = new TestSourceFilesConfiguration();
BuildConfiguration buildConfiguration =
BuildConfiguration.builder()
- .setBaseImageServerUrl("gcr.io")
- .setBaseImageName("distroless/java")
+ .setBaseImageRegistry("gcr.io")
+ .setBaseImageRepository("distroless/java")
.setBaseImageTag("latest")
- .setTargetServerUrl("localhost:5000")
- .setTargetImageName("testimage")
+ .setTargetRegistry("localhost:5000")
+ .setTargetRepository("testimage")
.setTargetTag("testtag")
.setMainClass("HelloWorld")
.setBuildLogger(new TestBuildLogger())
diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverIntegrationTest.java
index f9818537ca..e6dc2195cb 100644
--- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverIntegrationTest.java
+++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverIntegrationTest.java
@@ -29,7 +29,7 @@ public void testGetRegistryAuthenticator()
RegistryClient registryClient =
new RegistryClient(null, "registry.hub.docker.com", "library/busybox");
RegistryAuthenticator registryAuthenticator = registryClient.getRegistryAuthenticator();
- Authorization authorization = registryAuthenticator.authenticate();
+ Authorization authorization = registryAuthenticator.authenticatePull();
RegistryClient authorizedRegistryClient =
new RegistryClient(authorization, "registry.hub.docker.com", "library/busybox");
diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorIntegrationTest.java
index 9338b60dd3..dec85e914c 100644
--- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorIntegrationTest.java
+++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorIntegrationTest.java
@@ -27,7 +27,7 @@ public class RegistryAuthenticatorIntegrationTest {
public void testAuthenticate() throws RegistryAuthenticationFailedException {
RegistryAuthenticator registryAuthenticator =
RegistryAuthenticators.forDockerHub("library/busybox");
- Authorization authorization = registryAuthenticator.authenticate();
+ Authorization authorization = registryAuthenticator.authenticatePull();
// Checks that some token was received.
Assert.assertTrue(0 < authorization.getToken().length());
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePullStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePullStep.java
index c3c2fb05e2..bed7005302 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePullStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePullStep.java
@@ -19,67 +19,52 @@
import com.google.cloud.tools.jib.Timer;
import com.google.cloud.tools.jib.http.Authorization;
import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException;
+import com.google.cloud.tools.jib.registry.RegistryAuthenticator;
import com.google.cloud.tools.jib.registry.RegistryAuthenticators;
import com.google.cloud.tools.jib.registry.RegistryException;
-import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-import javax.annotation.Nullable;
-/** Retrieves credentials to pull from the base image registry. */
+/**
+ * Authenticates pull from the base image registry using Docker Token Authentication.
+ *
+ * @see https://docs.docker.com/registry/spec/auth/token/
+ */
class AuthenticatePullStep implements Callable {
- private static final String DESCRIPTION = "Authenticating with base image registry";
+ private static final String DESCRIPTION = "Authenticating pull from %s";
private final BuildConfiguration buildConfiguration;
- private final ListenableFuture registryCredentialsFuture;
+ private final ListenableFuture registryCredentialsFuture;
AuthenticatePullStep(
BuildConfiguration buildConfiguration,
- ListenableFuture registryCredentialsFuture) {
+ ListenableFuture registryCredentialsFuture) {
this.buildConfiguration = buildConfiguration;
this.registryCredentialsFuture = registryCredentialsFuture;
}
- /** Depends on nothing. */
+ /** Depends on {@link RetrieveRegistryCredentialsStep}. */
@Override
public Authorization call()
throws RegistryAuthenticationFailedException, IOException, RegistryException,
ExecutionException, InterruptedException {
- try (Timer ignored = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) {
- return RegistryAuthenticators.forOther(
- buildConfiguration.getBaseImageServerUrl(), buildConfiguration.getBaseImageName())
- .setAuthorization(getBaseImageAuthorization())
- .authenticate();
+ try (Timer ignored =
+ new Timer(
+ buildConfiguration.getBuildLogger(),
+ String.format(DESCRIPTION, buildConfiguration.getBaseImageRegistry()))) {
+ Authorization registryCredentials = NonBlockingFutures.get(registryCredentialsFuture);
+ RegistryAuthenticator registryAuthenticator =
+ RegistryAuthenticators.forOther(
+ buildConfiguration.getBaseImageRegistry(),
+ buildConfiguration.getBaseImageRepository());
+ if (registryAuthenticator == null) {
+ return registryCredentials;
+ }
+ return registryAuthenticator.setAuthorization(registryCredentials).authenticatePull();
}
}
-
- /** Attempts to retrieve authorization for pulling the base image. */
- @Nullable
- private Authorization getBaseImageAuthorization()
- throws ExecutionException, InterruptedException {
- RegistryCredentials registryCredentials = NonBlockingFutures.get(registryCredentialsFuture);
- String registry = buildConfiguration.getBaseImageServerUrl();
-
- String credentialHelperSuffix = registryCredentials.getCredentialHelperUsed(registry);
- Authorization authorization = registryCredentials.getAuthorization(registry);
- if (credentialHelperSuffix == null || authorization == null) {
- /*
- * If no credentials found, give an info (not warning because in most cases, the base image is
- * public and does not need extra credentials) and return null.
- */
- buildConfiguration
- .getBuildLogger()
- .info("No credentials could be retrieved for registry " + registry);
- return null;
- }
-
- buildConfiguration
- .getBuildLogger()
- .info(
- "Using docker-credential-" + credentialHelperSuffix + " for pulling from " + registry);
- return authorization;
- }
}
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePushStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePushStep.java
index 2444dbf77c..15e5e4e3c7 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePushStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/AuthenticatePushStep.java
@@ -18,52 +18,56 @@
import com.google.cloud.tools.jib.Timer;
import com.google.cloud.tools.jib.http.Authorization;
-import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
+import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException;
+import com.google.cloud.tools.jib.registry.RegistryAuthenticator;
+import com.google.cloud.tools.jib.registry.RegistryAuthenticators;
+import com.google.cloud.tools.jib.registry.RegistryException;
import com.google.common.util.concurrent.ListenableFuture;
+import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;
-/** Retrieves credentials to push to a target registry. */
+/**
+ * Authenticates push to a target registry using Docker Token Authentication.
+ *
+ * @see https://docs.docker.com/registry/spec/auth/token/
+ */
class AuthenticatePushStep implements Callable {
private static final String DESCRIPTION = "Authenticating with push to %s";
private final BuildConfiguration buildConfiguration;
- private final ListenableFuture registryCredentialsFuture;
+ private final ListenableFuture registryCredentialsFuture;
AuthenticatePushStep(
BuildConfiguration buildConfiguration,
- ListenableFuture registryCredentialsFuture) {
+ ListenableFuture registryCredentialsFuture) {
this.buildConfiguration = buildConfiguration;
this.registryCredentialsFuture = registryCredentialsFuture;
}
- /** Depends on nothing. */
+ /** Depends on {@link RetrieveRegistryCredentialsStep}. */
@Override
@Nullable
- public Authorization call() throws ExecutionException, InterruptedException {
+ public Authorization call()
+ throws ExecutionException, InterruptedException, RegistryAuthenticationFailedException,
+ IOException, RegistryException {
try (Timer ignored =
new Timer(
buildConfiguration.getBuildLogger(),
- String.format(DESCRIPTION, buildConfiguration.getTargetServerUrl()))) {
- RegistryCredentials registryCredentials = NonBlockingFutures.get(registryCredentialsFuture);
- String registry = buildConfiguration.getTargetServerUrl();
-
- String credentialHelperSuffix = registryCredentials.getCredentialHelperUsed(registry);
- Authorization authorization = registryCredentials.getAuthorization(registry);
- if (credentialHelperSuffix == null || authorization == null) {
- // If no credentials found, give a warning and return null.
- buildConfiguration
- .getBuildLogger()
- .warn("No credentials could be retrieved for registry " + registry);
- return null;
+ String.format(DESCRIPTION, buildConfiguration.getTargetRegistry()))) {
+ Authorization registryCredentials = NonBlockingFutures.get(registryCredentialsFuture);
+ RegistryAuthenticator registryAuthenticator =
+ RegistryAuthenticators.forOther(
+ buildConfiguration.getTargetRegistry(), buildConfiguration.getTargetRepository());
+ if (registryAuthenticator == null) {
+ return registryCredentials;
}
- buildConfiguration
- .getBuildLogger()
- .info(
- "Using docker-credential-" + credentialHelperSuffix + " for pushing to " + registry);
- return authorization;
+ return registryAuthenticator
+ .setAuthorization(NonBlockingFutures.get(registryCredentialsFuture))
+ .authenticatePush();
}
}
}
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/BuildAndPushContainerConfigurationStep.java
index 1249212f15..ec6a1eb70d 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/BuildAndPushContainerConfigurationStep.java
@@ -90,8 +90,8 @@ private BlobDescriptor afterBaseImageLayerFuturesFuture()
RegistryClient registryClient =
new RegistryClient(
NonBlockingFutures.get(pushAuthorizationFuture),
- buildConfiguration.getTargetServerUrl(),
- buildConfiguration.getTargetImageName())
+ buildConfiguration.getTargetRegistry(),
+ buildConfiguration.getTargetRepository())
.setTimer(timer);
// Constructs the image.
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java
index 2a3a050f9e..18c456c3f1 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/BuildConfiguration.java
@@ -31,21 +31,23 @@ public class BuildConfiguration {
@VisibleForTesting
enum Fields {
/** The server URL of the registry to pull the base image from. */
- BASE_IMAGE_SERVER_URL(true),
+ BASE_IMAGE_REGISTRY(true),
/** The image name/repository of the base image (also known as the registry namespace). */
- BASE_IMAGE_NAME(true),
+ BASE_IMAGE_REPOSITORY(true),
/** The base image tag. */
BASE_IMAGE_TAG(true),
/** The server URL of the registry to push the built image to. */
- TARGET_SERVER_URL(true),
+ TARGET_REGISTRY(true),
/** The image name/repository of the built image (also known as the registry namespace). */
- TARGET_IMAGE_NAME(true),
+ TARGET_REPOSITORY(true),
/** The image tag of the built image (the part after the colon). */
TARGET_TAG(true),
/** The credential helper names used by {@link RegistryCredentials}. */
CREDENTIAL_HELPER_NAMES(false),
+ /** Known registry credentials to fallback on. */
+ KNOWN_REGISTRY_CREDENTIALS(false),
/** The main class to use when running the application. */
MAIN_CLASS(true),
@@ -74,11 +76,11 @@ public static class Builder {
static final Map FIELD_DESCRIPTIONS =
new EnumMap(Fields.class) {
{
- put(Fields.BASE_IMAGE_SERVER_URL, "base image registry server URL");
- put(Fields.BASE_IMAGE_NAME, "base image name");
+ put(Fields.BASE_IMAGE_REGISTRY, "base image registry server URL");
+ put(Fields.BASE_IMAGE_REPOSITORY, "base image name");
put(Fields.BASE_IMAGE_TAG, "base image tag");
- put(Fields.TARGET_SERVER_URL, "target registry server URL");
- put(Fields.TARGET_IMAGE_NAME, "target image name");
+ put(Fields.TARGET_REGISTRY, "target registry server URL");
+ put(Fields.TARGET_REPOSITORY, "target image name");
put(Fields.TARGET_TAG, "target image tag");
put(Fields.CREDENTIAL_HELPER_NAMES, "credential helper names");
put(Fields.MAIN_CLASS, "main class");
@@ -96,6 +98,7 @@ public static class Builder {
private Builder() {
// Sets default empty values.
values.put(Fields.CREDENTIAL_HELPER_NAMES, Collections.emptyList());
+ values.put(Fields.KNOWN_REGISTRY_CREDENTIALS, RegistryCredentials.none());
values.put(Fields.JVM_FLAGS, Collections.emptyList());
values.put(Fields.ENVIRONMENT, Collections.emptyMap());
}
@@ -105,13 +108,13 @@ public Builder setBuildLogger(BuildLogger buildLogger) {
return this;
}
- public Builder setBaseImageServerUrl(String baseImageServerUrl) {
- values.put(Fields.BASE_IMAGE_SERVER_URL, baseImageServerUrl);
+ public Builder setBaseImageRegistry(String baseImageServerUrl) {
+ values.put(Fields.BASE_IMAGE_REGISTRY, baseImageServerUrl);
return this;
}
- public Builder setBaseImageName(String baseImageName) {
- values.put(Fields.BASE_IMAGE_NAME, baseImageName);
+ public Builder setBaseImageRepository(String baseImageName) {
+ values.put(Fields.BASE_IMAGE_REPOSITORY, baseImageName);
return this;
}
@@ -120,13 +123,13 @@ public Builder setBaseImageTag(String baseImageTag) {
return this;
}
- public Builder setTargetServerUrl(String targetServerUrl) {
- values.put(Fields.TARGET_SERVER_URL, targetServerUrl);
+ public Builder setTargetRegistry(String targetServerUrl) {
+ values.put(Fields.TARGET_REGISTRY, targetServerUrl);
return this;
}
- public Builder setTargetImageName(String targetImageName) {
- values.put(Fields.TARGET_IMAGE_NAME, targetImageName);
+ public Builder setTargetRepository(String targetImageName) {
+ values.put(Fields.TARGET_REPOSITORY, targetImageName);
return this;
}
@@ -142,6 +145,13 @@ public Builder setCredentialHelperNames(List credentialHelperNames) {
return this;
}
+ public Builder setKnownRegistryCredentials(RegistryCredentials knownRegistryCredentials) {
+ if (knownRegistryCredentials != null) {
+ values.put(Fields.KNOWN_REGISTRY_CREDENTIALS, knownRegistryCredentials);
+ }
+ return this;
+ }
+
public Builder setMainClass(String mainClass) {
values.put(Fields.MAIN_CLASS, mainClass);
return this;
@@ -220,30 +230,34 @@ public BuildLogger getBuildLogger() {
return buildLogger;
}
- public String getBaseImageServerUrl() {
- return getFieldValue(Fields.BASE_IMAGE_SERVER_URL);
+ public String getBaseImageRegistry() {
+ return getFieldValue(Fields.BASE_IMAGE_REGISTRY);
}
- public String getBaseImageName() {
- return getFieldValue(Fields.BASE_IMAGE_NAME);
+ public String getBaseImageRepository() {
+ return getFieldValue(Fields.BASE_IMAGE_REPOSITORY);
}
public String getBaseImageTag() {
return getFieldValue(Fields.BASE_IMAGE_TAG);
}
- public String getTargetServerUrl() {
- return getFieldValue(Fields.TARGET_SERVER_URL);
+ public String getTargetRegistry() {
+ return getFieldValue(Fields.TARGET_REGISTRY);
}
- public String getTargetImageName() {
- return getFieldValue(Fields.TARGET_IMAGE_NAME);
+ public String getTargetRepository() {
+ return getFieldValue(Fields.TARGET_REPOSITORY);
}
public String getTargetTag() {
return getFieldValue(Fields.TARGET_TAG);
}
+ public RegistryCredentials getKnownRegistryCredentials() {
+ return getFieldValue(Fields.KNOWN_REGISTRY_CREDENTIALS);
+ }
+
public List getCredentialHelperNames() {
return getFieldValue(Fields.CREDENTIAL_HELPER_NAMES);
}
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 b588f006b6..66b56798f1 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
@@ -23,7 +23,6 @@
import com.google.cloud.tools.jib.cache.CachedLayer;
import com.google.cloud.tools.jib.http.Authorization;
import com.google.cloud.tools.jib.image.Image;
-import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -68,26 +67,31 @@ public void run()
try (Cache cache = Cache.init(cacheDirectory)) {
timer2.lap("Setting up credential retrieval");
- ListenableFuture retrieveRegistryCredentialsFuture =
+ ListenableFuture retrieveTargetRegistryCredentialsFuture =
listeningExecutorService.submit(
- new RetrieveRegistryCredentialsStep(buildConfiguration));
+ new RetrieveRegistryCredentialsStep(
+ buildConfiguration, buildConfiguration.getTargetRegistry()));
+ ListenableFuture retrieveBaseImageRegistryCredentialsFuture =
+ listeningExecutorService.submit(
+ new RetrieveRegistryCredentialsStep(
+ buildConfiguration, buildConfiguration.getBaseImageRegistry()));
timer2.lap("Setting up image push authentication");
// Authenticates push.
ListenableFuture authenticatePushFuture =
- Futures.whenAllSucceed(retrieveRegistryCredentialsFuture)
+ Futures.whenAllSucceed(retrieveTargetRegistryCredentialsFuture)
.call(
new AuthenticatePushStep(
- buildConfiguration, retrieveRegistryCredentialsFuture),
+ buildConfiguration, retrieveTargetRegistryCredentialsFuture),
listeningExecutorService);
timer2.lap("Setting up image pull authentication");
// Authenticates base image pull.
ListenableFuture authenticatePullFuture =
- Futures.whenAllSucceed(retrieveRegistryCredentialsFuture)
+ Futures.whenAllSucceed(retrieveBaseImageRegistryCredentialsFuture)
.call(
new AuthenticatePullStep(
- buildConfiguration, retrieveRegistryCredentialsFuture),
+ buildConfiguration, retrieveBaseImageRegistryCredentialsFuture),
listeningExecutorService);
timer2.lap("Setting up base image pull");
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullAndCacheBaseImageLayerStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullAndCacheBaseImageLayerStep.java
index 09fb63468d..55eb9d8e8f 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullAndCacheBaseImageLayerStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullAndCacheBaseImageLayerStep.java
@@ -64,8 +64,8 @@ public CachedLayer call()
RegistryClient registryClient =
new RegistryClient(
pullAuthorizationFuture.get(),
- buildConfiguration.getBaseImageServerUrl(),
- buildConfiguration.getBaseImageName());
+ buildConfiguration.getBaseImageRegistry(),
+ buildConfiguration.getBaseImageRepository());
// Checks if the layer already exists in the cache.
CachedLayer cachedLayer = new CacheChecker(cache).getLayer(layerDigest);
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullBaseImageStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullBaseImageStep.java
index 010f605482..af4bf33eef 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullBaseImageStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PullBaseImageStep.java
@@ -61,8 +61,8 @@ public Image call()
RegistryClient registryClient =
new RegistryClient(
NonBlockingFutures.get(pullAuthorizationFuture),
- buildConfiguration.getBaseImageServerUrl(),
- buildConfiguration.getBaseImageName());
+ buildConfiguration.getBaseImageRegistry(),
+ buildConfiguration.getBaseImageRepository());
ManifestTemplate manifestTemplate =
registryClient.pullManifest(buildConfiguration.getBaseImageTag());
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushBlobStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushBlobStep.java
index 44f7ffe8e0..7a781632d6 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushBlobStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushBlobStep.java
@@ -56,8 +56,8 @@ public Void call()
RegistryClient registryClient =
new RegistryClient(
pushAuthorizationFuture.get(),
- buildConfiguration.getTargetServerUrl(),
- buildConfiguration.getTargetImageName())
+ buildConfiguration.getTargetRegistry(),
+ buildConfiguration.getTargetRepository())
.setTimer(timer);
if (registryClient.checkBlob(layerDigest) != null) {
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushImageStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushImageStep.java
index 28a2a48670..457391cc3a 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushImageStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/PushImageStep.java
@@ -104,8 +104,8 @@ private Void afterPushBaseImageLayerFuturesFuture()
RegistryClient registryClient =
new RegistryClient(
NonBlockingFutures.get(pushAuthorizationFuture),
- buildConfiguration.getTargetServerUrl(),
- buildConfiguration.getTargetImageName());
+ buildConfiguration.getTargetRegistry(),
+ buildConfiguration.getTargetRepository());
// TODO: Consolidate with BuildAndPushContainerConfigurationStep.
// Constructs the image.
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStep.java b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStep.java
index db3ddbcfff..e3ceb9270d 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStep.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStep.java
@@ -16,29 +16,69 @@
package com.google.cloud.tools.jib.builder;
+import com.google.cloud.tools.jib.Timer;
+import com.google.cloud.tools.jib.http.Authorization;
+import com.google.cloud.tools.jib.registry.DockerCredentialRetriever;
import com.google.cloud.tools.jib.registry.NonexistentDockerCredentialHelperException;
-import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
+import com.google.cloud.tools.jib.registry.NonexistentServerUrlDockerCredentialHelperException;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.Callable;
/** Attempts to retrieve registry credentials. */
-class RetrieveRegistryCredentialsStep implements Callable {
+class RetrieveRegistryCredentialsStep implements Callable {
- private static final String DESCRIPTION = "Retrieving registry credentials";
+ private static final String DESCRIPTION = "Retrieving registry credentials for %s";
private final BuildConfiguration buildConfiguration;
+ private final String registry;
- RetrieveRegistryCredentialsStep(BuildConfiguration buildConfiguration) {
+ RetrieveRegistryCredentialsStep(BuildConfiguration buildConfiguration, String registry) {
this.buildConfiguration = buildConfiguration;
+ this.registry = registry;
}
@Override
- public RegistryCredentials call() throws IOException, NonexistentDockerCredentialHelperException {
- List registries =
- Arrays.asList(
- buildConfiguration.getBaseImageServerUrl(), buildConfiguration.getTargetServerUrl());
- return RegistryCredentials.from(buildConfiguration.getCredentialHelperNames(), registries);
+ public Authorization call() throws IOException, NonexistentDockerCredentialHelperException {
+ try (Timer ignored =
+ new Timer(
+ buildConfiguration.getBuildLogger(),
+ String.format(DESCRIPTION, buildConfiguration.getTargetRegistry()))) {
+ // Tries to get registry credentials from Docker credential helpers.
+ for (String credentialHelperSuffix : buildConfiguration.getCredentialHelperNames()) {
+ // Attempts to retrieve authorization for the registry using
+ // docker-credential-[credentialSource].
+ try {
+ Authorization authorization =
+ new DockerCredentialRetriever(registry, credentialHelperSuffix).retrieve();
+ logGotCredentialsFrom("docker-credential-" + credentialHelperSuffix);
+ return authorization;
+
+ } catch (NonexistentServerUrlDockerCredentialHelperException ex) {
+ // No authorization is found, so continues on to the next credential helper.
+ }
+ }
+
+ // Tries to get registry credentials from known registry credentials.
+ if (buildConfiguration.getKnownRegistryCredentials().has(registry)) {
+ logGotCredentialsFrom(
+ buildConfiguration.getKnownRegistryCredentials().getCredentialSource(registry));
+ return buildConfiguration.getKnownRegistryCredentials().getAuthorization(registry);
+ }
+
+ /*
+ * If no credentials found, give an info (not warning because in most cases, the base image is
+ * public and does not need extra credentials) and return null.
+ */
+ buildConfiguration
+ .getBuildLogger()
+ .info("No credentials could be retrieved for registry " + registry);
+ return null;
+ }
+ }
+
+ private void logGotCredentialsFrom(String credentialSource) {
+ buildConfiguration
+ .getBuildLogger()
+ .info("Using " + credentialSource + " for pulling from " + registry);
}
}
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetriever.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetriever.java
index 1d603464f3..5d26a30820 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetriever.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetriever.java
@@ -44,10 +44,10 @@ public List getAccept() {
}
@Override
- public RegistryAuthenticator handleResponse(Response response) throws RegistryErrorException {
- throw new RegistryErrorExceptionBuilder(getActionDescription())
- .addReason("Did not receive '401 Unauthorized' response")
- .build();
+ @Nullable
+ public RegistryAuthenticator handleResponse(Response response) {
+ // The registry does not require authentication.
+ return null;
}
@Override
@@ -66,6 +66,7 @@ public String getActionDescription() {
}
@Override
+ @Nullable
public RegistryAuthenticator handleHttpResponseException(
HttpResponseException httpResponseException)
throws HttpResponseException, RegistryErrorException {
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticator.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticator.java
index 405c400112..b39875b255 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticator.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticator.java
@@ -34,7 +34,7 @@
import javax.annotation.Nullable;
/**
- * Authenticates pull access with a registry service.
+ * Authenticates push/pull access with a registry service.
*
* @see https://docs.docker.com/registry/spec/auth/token/
@@ -50,9 +50,15 @@ public class RegistryAuthenticator {
* @see https://docs.docker.com/registry/spec/auth/token/#how-to-authenticate
*/
+ @Nullable
static RegistryAuthenticator fromAuthenticationMethod(
String authenticationMethod, String repository)
throws RegistryAuthenticationFailedException, MalformedURLException {
+ // If the authentication method starts with 'Basic ', no registry authentication is needed.
+ if (authenticationMethod.matches("^Basic .*")) {
+ return null;
+ }
+
// Checks that the authentication method starts with 'Bearer '.
if (!authenticationMethod.matches("^Bearer .*")) {
throw newRegistryAuthenticationFailedException(authenticationMethod, "Bearer");
@@ -91,13 +97,12 @@ private static class AuthenticationResponseTemplate extends JsonTemplate {
private String token;
}
- private final URL authenticationUrl;
+ private final String authenticationUrlBase;
@Nullable private Authorization authorization;
RegistryAuthenticator(String realm, String service, String repository)
throws MalformedURLException {
- authenticationUrl =
- new URL(realm + "?service=" + service + "&scope=repository:" + repository + ":pull");
+ authenticationUrlBase = realm + "?service=" + service + "&scope=repository:" + repository + ":";
}
/** Sets an {@code Authorization} header to authenticate with. */
@@ -106,9 +111,30 @@ public RegistryAuthenticator setAuthorization(@Nullable Authorization authorizat
return this;
}
- /** Sends the authentication request and retrieves the Bearer authorization token. */
- public Authorization authenticate() throws RegistryAuthenticationFailedException {
- try (Connection connection = new Connection(authenticationUrl)) {
+ /** Authenticates permissions to pull. */
+ public Authorization authenticatePull() throws RegistryAuthenticationFailedException {
+ return authenticate("pull");
+ }
+
+ /** Authenticates permission to pull and push. */
+ public Authorization authenticatePush() throws RegistryAuthenticationFailedException {
+ return authenticate("pull,push");
+ }
+
+ @VisibleForTesting
+ URL getAuthenticationUrl(String scope) throws MalformedURLException {
+ return new URL(authenticationUrlBase + scope);
+ }
+
+ /**
+ * Sends the authentication request and retrieves the Bearer authorization token.
+ *
+ * @param scope the scope of permissions to authenticate for
+ * @see https://docs.docker.com/registry/spec/auth/token/#how-to-authenticate
+ */
+ private Authorization authenticate(String scope) throws RegistryAuthenticationFailedException {
+ try (Connection connection = new Connection(getAuthenticationUrl(scope))) {
Request.Builder requestBuilder = Request.builder();
if (authorization != null) {
requestBuilder.setAuthorization(authorization);
@@ -124,9 +150,4 @@ public Authorization authenticate() throws RegistryAuthenticationFailedException
throw new RegistryAuthenticationFailedException(ex);
}
}
-
- @VisibleForTesting
- URL getAuthenticationUrl() {
- return authenticationUrl;
- }
}
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticators.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticators.java
index e853662bcb..54c3ed3678 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticators.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryAuthenticators.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.net.MalformedURLException;
+import javax.annotation.Nullable;
/** Static initializers for {@link RegistryAuthenticator}. */
public abstract class RegistryAuthenticators {
@@ -33,6 +34,7 @@ public static RegistryAuthenticator forDockerHub(String repository)
}
}
+ @Nullable
public static RegistryAuthenticator forOther(String serverUrl, String repository)
throws RegistryAuthenticationFailedException, IOException, RegistryException {
try {
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java
index f7513650d5..4a6b9d9113 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryClient.java
@@ -101,13 +101,15 @@ public RegistryClient(@Nullable Authorization authorization, String serverUrl, S
this.registryEndpointProperties = new RegistryEndpointProperties(serverUrl, imageName);
}
- /** Gets the {@link RegistryAuthenticator} to authenticate pulls from the registry. */
+ /**
+ * @return the {@link RegistryAuthenticator} to authenticate pulls/pushes with the registry, or
+ * {@code null} if no token authentication is necessary
+ */
+ @Nullable
public RegistryAuthenticator getRegistryAuthenticator() throws IOException, RegistryException {
// Gets the WWW-Authenticate header (eg. 'WWW-Authenticate: Bearer
// realm="https://gcr.io/v2/token",service="gcr.io"')
- AuthenticationMethodRetriever authenticationMethodRetriever =
- new AuthenticationMethodRetriever(registryEndpointProperties);
- return callRegistryEndpoint(authenticationMethodRetriever);
+ return callRegistryEndpoint(new AuthenticationMethodRetriever(registryEndpointProperties));
}
/**
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryEndpointProvider.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryEndpointProvider.java
index 1d1708fbb1..dd14f2e1a5 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryEndpointProvider.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryEndpointProvider.java
@@ -49,6 +49,7 @@ interface RegistryEndpointProvider {
List getAccept();
/** Handles the response specific to the registry action. */
+ @Nullable
T handleResponse(Response response) throws IOException, RegistryException;
/**
@@ -58,6 +59,7 @@ interface RegistryEndpointProvider {
* @throws HttpResponseException {@code httpResponseException} if {@code httpResponseException}
* could not be handled
*/
+ @Nullable
default T handleHttpResponseException(HttpResponseException httpResponseException)
throws HttpResponseException, RegistryErrorException {
throw httpResponseException;
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryUnauthorizedException.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryUnauthorizedException.java
index d14b016634..68dfc3b5b3 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryUnauthorizedException.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/RegistryUnauthorizedException.java
@@ -24,7 +24,7 @@ public class RegistryUnauthorizedException extends RegistryException {
private final String imageReference;
/** @param imageReference identifies the image registry and repository that denied access */
- RegistryUnauthorizedException(String imageReference, HttpResponseException cause) {
+ public RegistryUnauthorizedException(String imageReference, HttpResponseException cause) {
super(cause);
this.imageReference = imageReference;
}
diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/RegistryCredentials.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/RegistryCredentials.java
index 7e2f5bca89..d98dd55957 100644
--- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/RegistryCredentials.java
+++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/RegistryCredentials.java
@@ -33,6 +33,17 @@
*/
public class RegistryCredentials {
+ /** Instantiates with no credentials. */
+ public static RegistryCredentials none() {
+ return new RegistryCredentials();
+ }
+
+ /** Instantiates with credentials for a single registry. */
+ public static RegistryCredentials of(
+ String registry, String credentialSource, Authorization authorization) {
+ return new RegistryCredentials().store(registry, credentialSource, authorization);
+ }
+
/**
* Retrieves credentials for {@code registries} using the credential helpers referred to by {@code
* credentialHelperSuffixes}.
@@ -48,11 +59,16 @@ public static RegistryCredentials from(
// TODO: These can be done in parallel.
for (String registry : registries) {
for (String credentialHelperSuffix : credentialHelperSuffixes) {
- Authorization authorization = retrieveCredentials(registry, credentialHelperSuffix);
- if (authorization != null) {
-
- registryCredentials.store(registry, credentialHelperSuffix, authorization);
- break;
+ // Attempts to retrieve authorization for the registry using
+ // docker-credential-[credentialSource].
+ try {
+ registryCredentials.store(
+ registry,
+ "docker-credential-" + credentialHelperSuffix,
+ new DockerCredentialRetriever(registry, credentialHelperSuffix).retrieve());
+
+ } catch (NonexistentServerUrlDockerCredentialHelperException ex) {
+ // No authorization is found, so continues on to the next credential helper.
}
}
}
@@ -60,48 +76,54 @@ public static RegistryCredentials from(
}
/**
- * Attempts to retrieve authorization for {@code registry} using docker-credential-{@code
- * credentialHelperSuffix}.
+ * Instantiates from a credential source and a map of registry credentials.
*
- * @return the retrieved credentials, or {@code null} if not found
+ * @param credentialSource the source of the credentials, useful for informing users where the
+ * credentials came from
+ * @param registryCredentialMap a map from registries to their respective credentials
*/
- @Nullable
- private static Authorization retrieveCredentials(String registry, String credentialHelperSuffix)
- throws IOException, NonexistentDockerCredentialHelperException {
- try {
- DockerCredentialRetriever dockerCredentialRetriever =
- new DockerCredentialRetriever(registry, credentialHelperSuffix);
-
- return dockerCredentialRetriever.retrieve();
-
- } catch (NonexistentServerUrlDockerCredentialHelperException ex) {
- // Returns null if no authorization is found.
- return null;
+ public static RegistryCredentials from(
+ String credentialSource, Map registryCredentialMap) {
+ RegistryCredentials registryCredentials = new RegistryCredentials();
+ for (Map.Entry registryCredential : registryCredentialMap.entrySet()) {
+ registryCredentials.store(
+ registryCredential.getKey(), credentialSource, registryCredential.getValue());
}
+ return registryCredentials;
}
- /** Pair of (Docker credential helper name, {@link Authorization}). */
- private static class CredentialHelperAuthorizationPair {
+ /** Pair of (source of credentials, {@link Authorization}). */
+ private static class AuthorizationSourcePair {
+
+ /**
+ * A string representation of where the credentials were retrieved from. This is useful for
+ * letting the user know which credentials were used.
+ */
+ private final String credentialSource;
- private final String credentialHelperSuffix;
private final Authorization authorization;
- private CredentialHelperAuthorizationPair(
- String credentialHelperSuffix, Authorization authorization) {
- this.credentialHelperSuffix = credentialHelperSuffix;
+ private AuthorizationSourcePair(String credentialSource, Authorization authorization) {
+ this.credentialSource = credentialSource;
this.authorization = authorization;
}
}
/** Maps from registry to the credentials for that registry. */
- private final Map credentials = new HashMap<>();
+ private final Map credentials = new HashMap<>();
/** Instantiate using {@link #from}. */
private RegistryCredentials() {};
- private void store(String registry, String credentialHelperSuffix, Authorization authorization) {
- credentials.put(
- registry, new CredentialHelperAuthorizationPair(credentialHelperSuffix, authorization));
+ private RegistryCredentials store(
+ String registry, String credentialSource, Authorization authorization) {
+ credentials.put(registry, new AuthorizationSourcePair(credentialSource, authorization));
+ return this;
+ }
+
+ /** @return {@code true} if there are credentials for {@code registry}; {@code false} otherwise */
+ public boolean has(String registry) {
+ return credentials.containsKey(registry);
}
/**
@@ -110,7 +132,7 @@ private void store(String registry, String credentialHelperSuffix, Authorization
*/
@Nullable
public Authorization getAuthorization(String registry) {
- if (!credentials.containsKey(registry)) {
+ if (!has(registry)) {
return null;
}
return credentials.get(registry).authorization;
@@ -121,10 +143,10 @@ public Authorization getAuthorization(String registry) {
* registry}, or {@code null} if none exists
*/
@Nullable
- public String getCredentialHelperUsed(String registry) {
- if (!credentials.containsKey(registry)) {
+ public String getCredentialSource(String registry) {
+ if (!has(registry)) {
return null;
}
- return credentials.get(registry).credentialHelperSuffix;
+ return credentials.get(registry).credentialSource;
}
}
diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java
index b8ae08bbf8..50c95cb895 100644
--- a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java
+++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/BuildConfigurationTest.java
@@ -41,21 +41,21 @@ public void testBuilder() {
BuildConfiguration buildConfiguration =
BuildConfiguration.builder()
- .setBaseImageServerUrl(expectedBaseImageServerUrl)
- .setBaseImageName(expectedBaseImageName)
+ .setBaseImageRegistry(expectedBaseImageServerUrl)
+ .setBaseImageRepository(expectedBaseImageName)
.setBaseImageTag(expectedBaseImageTag)
- .setTargetServerUrl(expectedTargetServerUrl)
- .setTargetImageName(expectedTargetImageName)
+ .setTargetRegistry(expectedTargetServerUrl)
+ .setTargetRepository(expectedTargetImageName)
.setTargetTag(expectedTargetTag)
.setCredentialHelperNames(expectedCredentialHelperNames)
.setMainClass(expectedMainClass)
.setJvmFlags(expectedJvmFlags)
.build();
- Assert.assertEquals(expectedBaseImageServerUrl, buildConfiguration.getBaseImageServerUrl());
- Assert.assertEquals(expectedBaseImageName, buildConfiguration.getBaseImageName());
+ Assert.assertEquals(expectedBaseImageServerUrl, buildConfiguration.getBaseImageRegistry());
+ Assert.assertEquals(expectedBaseImageName, buildConfiguration.getBaseImageRepository());
Assert.assertEquals(expectedBaseImageTag, buildConfiguration.getBaseImageTag());
- Assert.assertEquals(expectedTargetServerUrl, buildConfiguration.getTargetServerUrl());
- Assert.assertEquals(expectedTargetImageName, buildConfiguration.getTargetImageName());
+ Assert.assertEquals(expectedTargetServerUrl, buildConfiguration.getTargetRegistry());
+ Assert.assertEquals(expectedTargetImageName, buildConfiguration.getTargetRepository());
Assert.assertEquals(expectedTargetTag, buildConfiguration.getTargetTag());
Assert.assertEquals(
expectedCredentialHelperNames, buildConfiguration.getCredentialHelperNames());
diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverTest.java
index d0b1b570b3..65b18696a7 100644
--- a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverTest.java
+++ b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/AuthenticationMethodRetrieverTest.java
@@ -55,15 +55,8 @@ public void testGetAccept() {
@Test
public void testHandleResponse() {
- try {
- testAuthenticationMethodRetriever.handleResponse(Mockito.mock(Response.class));
- Assert.fail("Authentication method retriever should only handle HTTP error responses");
-
- } catch (RegistryErrorException ex) {
- Assert.assertThat(
- ex.getMessage(),
- CoreMatchers.containsString("Did not receive '401 Unauthorized' response"));
- }
+ Assert.assertNull(
+ testAuthenticationMethodRetriever.handleResponse(Mockito.mock(Response.class)));
}
@Test
@@ -155,7 +148,7 @@ public void testHandleHttpResponseException_pass()
testAuthenticationMethodRetriever.handleHttpResponseException(mockHttpResponseException);
Assert.assertEquals(
- new URL("https://somerealm?service=someservice&scope=repository:someImageName:pull"),
- registryAuthenticator.getAuthenticationUrl());
+ new URL("https://somerealm?service=someservice&scope=repository:someImageName:someScope"),
+ registryAuthenticator.getAuthenticationUrl("someScope"));
}
}
diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorTest.java
index 74a7a166ab..7a2e894070 100644
--- a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorTest.java
+++ b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/RegistryAuthenticatorTest.java
@@ -25,15 +25,24 @@
public class RegistryAuthenticatorTest {
@Test
- public void testFromAuthenticationMethod()
+ public void testFromAuthenticationMethod_bearer()
throws MalformedURLException, RegistryAuthenticationFailedException {
RegistryAuthenticator registryAuthenticator =
RegistryAuthenticator.fromAuthenticationMethod(
"Bearer realm=\"https://somerealm\",service=\"someservice\",scope=\"somescope\"",
"someimage");
Assert.assertEquals(
- new URL("https://somerealm?service=someservice&scope=repository:someimage:pull"),
- registryAuthenticator.getAuthenticationUrl());
+ new URL("https://somerealm?service=someservice&scope=repository:someimage:scope"),
+ registryAuthenticator.getAuthenticationUrl("scope"));
+ }
+
+ @Test
+ public void testFromAuthenticationMethod_basic()
+ throws MalformedURLException, RegistryAuthenticationFailedException {
+ Assert.assertNull(
+ RegistryAuthenticator.fromAuthenticationMethod(
+ "Basic realm=\"https://somerealm\",service=\"someservice\",scope=\"somescope\"",
+ "someimage"));
}
@Test
@@ -41,7 +50,7 @@ public void testFromAuthenticationMethod_noBearer() throws MalformedURLException
try {
RegistryAuthenticator.fromAuthenticationMethod(
"realm=\"https://somerealm\",service=\"someservice\",scope=\"somescope\"", "someimage");
- Assert.fail("Authentication method without 'Bearer ' should fail");
+ Assert.fail("Authentication method without 'Bearer ' or 'Basic ' should fail");
} catch (RegistryAuthenticationFailedException ex) {
Assert.assertEquals(
diff --git a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java
index b4bf4c98ef..c4395b1f38 100644
--- a/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java
+++ b/jib-maven-plugin/src/main/java/com/google/cloud/tools/jib/maven/BuildImageMojo.java
@@ -16,15 +16,20 @@
package com.google.cloud.tools.jib.maven;
+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.BuildImageSteps;
import com.google.cloud.tools.jib.builder.SourceFilesConfiguration;
import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException;
+import com.google.cloud.tools.jib.http.Authorization;
+import com.google.cloud.tools.jib.http.Authorizations;
import com.google.cloud.tools.jib.image.ImageReference;
import com.google.cloud.tools.jib.image.InvalidImageReferenceException;
+import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException;
import com.google.cloud.tools.jib.registry.RegistryClient;
import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException;
+import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
@@ -32,11 +37,13 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nullable;
import org.apache.http.conn.HttpHostConnectException;
+import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
@@ -45,6 +52,7 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
+import org.apache.maven.settings.Server;
import org.codehaus.plexus.util.xml.Xpp3Dom;
/** Builds a container image. */
@@ -67,6 +75,9 @@ public class BuildImageMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
+ @Parameter(defaultValue = "${session}", readonly = true)
+ private MavenSession session;
+
@Parameter(defaultValue = "gcr.io/distroless/java", required = true)
private String from;
@@ -108,10 +119,27 @@ public void execute() throws MojoExecutionException, MojoFailureException {
SourceFilesConfiguration sourceFilesConfiguration = getSourceFilesConfiguration();
- // Parse 'from' into image reference.
+ // Parses 'from' into image reference.
ImageReference baseImage = getBaseImageReference();
- // Infer common credential helper names if credHelpers is not set.
+ // Checks Maven settings for registry credentials.
+ session.getSettings().getServer(baseImage.getRegistry());
+ Map registryCredentials = new HashMap<>(2);
+ // Retrieves credentials for the base image registry.
+ Authorization baseImageRegistryCredentials =
+ getRegistryCredentialsFromSettings(baseImage.getRegistry());
+ if (baseImageRegistryCredentials != null) {
+ registryCredentials.put(baseImage.getRegistry(), baseImageRegistryCredentials);
+ }
+ // Retrieves credentials for the target registry.
+ Authorization targetRegistryCredentials = getRegistryCredentialsFromSettings(registry);
+ if (targetRegistryCredentials != null) {
+ registryCredentials.put(registry, targetRegistryCredentials);
+ }
+ RegistryCredentials mavenSettingsCredentials =
+ RegistryCredentials.from("Maven settings", registryCredentials);
+
+ // Infers common credential helper names if credHelpers is not set.
if (credHelpers == null) {
credHelpers = new ArrayList<>(2);
String baseImageRegistryCredHelper = inferCredHelper(baseImage.getRegistry());
@@ -127,13 +155,14 @@ public void execute() throws MojoExecutionException, MojoFailureException {
BuildConfiguration buildConfiguration =
BuildConfiguration.builder()
.setBuildLogger(new MavenBuildLogger(getLog()))
- .setBaseImageServerUrl(baseImage.getRegistry())
- .setBaseImageName(baseImage.getRepository())
+ .setBaseImageRegistry(baseImage.getRegistry())
+ .setBaseImageRepository(baseImage.getRepository())
.setBaseImageTag(baseImage.getTag())
- .setTargetServerUrl(registry)
- .setTargetImageName(repository)
+ .setTargetRegistry(registry)
+ .setTargetRepository(repository)
.setTargetTag(tag)
.setCredentialHelperNames(credHelpers)
+ .setKnownRegistryCredentials(mavenSettingsCredentials)
.setMainClass(mainClass)
.setJvmFlags(jvmFlags)
.setEnvironment(environment)
@@ -179,6 +208,8 @@ void buildImage(BuildImageSteps buildImageSteps) throws MojoExecutionException {
cacheMetadataCorruptedException, "run 'mvn clean' to clear the cache");
} catch (ExecutionException executionException) {
+ BuildConfiguration buildConfiguration = buildImageSteps.getBuildConfiguration();
+
if (executionException.getCause() instanceof HttpHostConnectException) {
// Failed to connect to registry.
throwMojoExecutionExceptionWithHelpMessage(
@@ -186,34 +217,18 @@ void buildImage(BuildImageSteps buildImageSteps) throws MojoExecutionException {
"make sure your Internet is up and that the registry you are pushing to exists");
} else if (executionException.getCause() instanceof RegistryUnauthorizedException) {
- BuildConfiguration buildConfiguration = buildImageSteps.getBuildConfiguration();
-
- RegistryUnauthorizedException registryUnauthorizedException =
- (RegistryUnauthorizedException) executionException.getCause();
- if (registryUnauthorizedException.getHttpResponseException().getStatusCode()
- == HttpStatusCodes.STATUS_CODE_FORBIDDEN) {
- // No permissions for registry/repository.
- throwMojoExecutionExceptionWithHelpMessage(
- registryUnauthorizedException,
- "make sure your have permissions for "
- + registryUnauthorizedException.getImageReference());
-
- } else if (buildConfiguration.getCredentialHelperNames() == null
- || buildConfiguration.getCredentialHelperNames().isEmpty()) {
- // No credential helpers not defined.
- throwMojoExecutionExceptionWithHelpMessage(
- registryUnauthorizedException,
- "set a credential helper name with the configuration 'credHelpers'");
-
- } else {
- // Credential helper probably was not configured correctly or did not have the necessary
- // credentials.
- throwMojoExecutionExceptionWithHelpMessage(
- registryUnauthorizedException,
- "make sure your credential helper for '"
- + registryUnauthorizedException.getImageReference()
- + "' is set up correctly");
- }
+ handleRegistryUnauthorizedException(
+ (RegistryUnauthorizedException) executionException.getCause(), buildConfiguration);
+
+ } else if (executionException.getCause() instanceof RegistryAuthenticationFailedException
+ && executionException.getCause().getCause() instanceof HttpResponseException) {
+ handleRegistryUnauthorizedException(
+ new RegistryUnauthorizedException(
+ buildConfiguration.getTargetRegistry()
+ + "/"
+ + buildConfiguration.getTargetRepository(),
+ (HttpResponseException) executionException.getCause().getCause()),
+ buildConfiguration);
} else {
throwMojoExecutionExceptionWithHelpMessage(executionException.getCause(), null);
@@ -246,6 +261,17 @@ String inferCredHelper(String registry) {
return null;
}
+ /** Attempts to retrieve credentials for {@code registry} from Maven settings. */
+ @Nullable
+ private Authorization getRegistryCredentialsFromSettings(String registry) {
+ Server registryServerSettings = session.getSettings().getServer(registry);
+ if (registryServerSettings == null) {
+ return null;
+ }
+ return Authorizations.withBasicCredentials(
+ registryServerSettings.getUsername(), registryServerSettings.getPassword());
+ }
+
/** Checks validity of plugin parameters. */
private void validateParameters() throws MojoFailureException {
// Validates 'registry'.
@@ -338,6 +364,40 @@ private ImageReference getBaseImageReference() throws MojoFailureException {
}
}
+ private void handleRegistryUnauthorizedException(
+ RegistryUnauthorizedException registryUnauthorizedException,
+ BuildConfiguration buildConfiguration)
+ throws MojoExecutionException {
+ if (registryUnauthorizedException.getHttpResponseException().getStatusCode()
+ == HttpStatusCodes.STATUS_CODE_FORBIDDEN) {
+ // No permissions for registry/repository.
+ throwMojoExecutionExceptionWithHelpMessage(
+ registryUnauthorizedException,
+ "make sure your have permissions for "
+ + registryUnauthorizedException.getImageReference());
+
+ } else if (buildConfiguration.getCredentialHelperNames() == null
+ || buildConfiguration.getCredentialHelperNames().isEmpty()
+ || buildConfiguration.getKnownRegistryCredentials() == null) {
+ // No credential helpers not defined.
+ throwMojoExecutionExceptionWithHelpMessage(
+ registryUnauthorizedException,
+ "set a credential helper name with the configuration 'credHelpers' or "
+ + "set credentials for '"
+ + registryUnauthorizedException.getImageReference()
+ + "' in your Maven settings");
+
+ } else {
+ // Credential helper probably was not configured correctly or did not have the necessary
+ // credentials.
+ throwMojoExecutionExceptionWithHelpMessage(
+ registryUnauthorizedException,
+ "make sure your credentials for '"
+ + registryUnauthorizedException.getImageReference()
+ + "' is set up correctly");
+ }
+ }
+
/**
* Wraps an exception in a {@link MojoExecutionException} and provides a suggestion on how to fix
* the error.
diff --git a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoTest.java b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoTest.java
index 605be6fc4a..99f14a1579 100644
--- a/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoTest.java
+++ b/jib-maven-plugin/src/test/java/com/google/cloud/tools/jib/maven/BuildImageMojoTest.java
@@ -22,6 +22,7 @@
import com.google.cloud.tools.jib.builder.BuildImageSteps;
import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException;
import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException;
+import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
@@ -140,6 +141,8 @@ public void testBuildImage_executionException_registryUnauthorizedException_noCr
HttpResponseException mockHttpResponseException = Mockito.mock(HttpResponseException.class);
Mockito.when(mockRegistryUnauthorizedException.getHttpResponseException())
.thenReturn(mockHttpResponseException);
+ Mockito.when(mockRegistryUnauthorizedException.getImageReference())
+ .thenReturn("someregistry/somerepository");
Mockito.when(mockHttpResponseException.getStatusCode()).thenReturn(-1); // Unknown
Mockito.when(mockBuildImageSteps.getBuildConfiguration())
@@ -155,7 +158,7 @@ public void testBuildImage_executionException_registryUnauthorizedException_noCr
} catch (MojoExecutionException ex) {
Assert.assertEquals(
- "Build image failed, perhaps you should set a credential helper name with the configuration 'credHelpers'",
+ "Build image failed, perhaps you should set a credential helper name with the configuration 'credHelpers' or set credentials for 'someregistry/somerepository' in your Maven settings",
ex.getMessage());
Assert.assertEquals(mockRegistryUnauthorizedException, ex.getCause());
}
@@ -181,6 +184,8 @@ public void testBuildImage_executionException_registryUnauthorizedException_othe
BuildConfiguration mockBuildConfiguration = Mockito.mock(BuildConfiguration.class);
Mockito.when(mockBuildConfiguration.getCredentialHelperNames())
.thenReturn(Collections.singletonList("some-credential-helper"));
+ Mockito.when(mockBuildConfiguration.getKnownRegistryCredentials())
+ .thenReturn(Mockito.mock(RegistryCredentials.class));
Mockito.when(mockBuildImageSteps.getBuildConfiguration()).thenReturn(mockBuildConfiguration);
try {
@@ -189,7 +194,7 @@ public void testBuildImage_executionException_registryUnauthorizedException_othe
} catch (MojoExecutionException ex) {
Assert.assertEquals(
- "Build image failed, perhaps you should make sure your credential helper for 'someregistry/somerepository' is set up correctly",
+ "Build image failed, perhaps you should make sure your credentials for 'someregistry/somerepository' is set up correctly",
ex.getMessage());
Assert.assertEquals(mockRegistryUnauthorizedException, ex.getCause());
}
diff --git a/jib-maven-plugin/src/test/resources/project/pom.xml b/jib-maven-plugin/src/test/resources/project/pom.xml
index f6c5aa8d7d..8651c645a9 100644
--- a/jib-maven-plugin/src/test/resources/project/pom.xml
+++ b/jib-maven-plugin/src/test/resources/project/pom.xml
@@ -41,9 +41,9 @@
gcr.io
qingyangc-sandbox/jibtestimage
built-with-jib
-
- gcr
-
+
+ gcr
+
com.test.HelloWorld