From c591f145f3ecb10435e71fd2f137c17e4e3a3ad5 Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Fri, 16 Feb 2018 14:33:05 -0500 Subject: [PATCH 1/2] Uses Docker config in RetrieveRegistryCredentialsStep. --- CHANGELOG.md | 2 +- ...kerCredentialRetrieverIntegrationTest.java | 6 +- .../RetrieveRegistryCredentialsStep.java | 37 +++- .../DockerConfigCredentialRetriever.java | 16 -- .../DockerCredentialRetriever.java | 4 +- .../DockerCredentialRetrieverFactory.java | 31 +++ .../credentials/RegistryCredentials.java | 50 ----- .../RetrieveRegistryCredentialsStepTest.java | 177 ++++++++++++++++++ .../DockerConfigCredentialRetrieverTest.java | 6 +- 9 files changed, 251 insertions(+), 78 deletions(-) create mode 100644 jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverFactory.java create mode 100644 jib-core/src/test/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStepTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index eda5f53bd6..0ce32bdfcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to this project will be documented in this file. ## 0.1.2 ### Added -- Use credentials from Docker config if none can be found otherwise ([]()) +- Use credentials from Docker config if none can be found otherwise ([#101](https://github.com/google/jib/issues/101)) ## 0.1.1 ### Added diff --git a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverIntegrationTest.java b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverIntegrationTest.java index 108fc17b08..c0256c06ce 100644 --- a/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverIntegrationTest.java +++ b/jib-core/src/integration-test/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverIntegrationTest.java @@ -44,7 +44,7 @@ public void testRetrieveGCR() process.waitFor(); DockerCredentialRetriever dockerCredentialRetriever = - new DockerCredentialRetriever("myregistry", "gcr"); + new DockerCredentialRetrieverFactory("myregistry").withSuffix("gcr"); Authorization authorization = dockerCredentialRetriever.retrieve(); @@ -57,7 +57,7 @@ public void testRetrieve_nonexistentCredentialHelper() throws IOException, NonexistentServerUrlDockerCredentialHelperException { try { DockerCredentialRetriever fakeDockerCredentialRetriever = - new DockerCredentialRetriever("", "fake-cloud-provider"); + new DockerCredentialRetrieverFactory("").withSuffix("fake-cloud-provider"); fakeDockerCredentialRetriever.retrieve(); @@ -74,7 +74,7 @@ public void testRetrieve_nonexistentServerUrl() throws IOException, NonexistentDockerCredentialHelperException { try { DockerCredentialRetriever fakeDockerCredentialRetriever = - new DockerCredentialRetriever("fake.server.url", "gcr"); + new DockerCredentialRetrieverFactory("fake.server.url").withSuffix("gcr"); fakeDockerCredentialRetriever.retrieve(); 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 46a6d77666..fb1395bd7e 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 @@ -18,12 +18,15 @@ import com.google.cloud.tools.jib.Timer; import com.google.cloud.tools.jib.http.Authorization; -import com.google.cloud.tools.jib.registry.credentials.DockerCredentialRetriever; +import com.google.cloud.tools.jib.registry.credentials.DockerConfigCredentialRetriever; +import com.google.cloud.tools.jib.registry.credentials.DockerCredentialRetrieverFactory; import com.google.cloud.tools.jib.registry.credentials.NonexistentDockerCredentialHelperException; import com.google.cloud.tools.jib.registry.credentials.NonexistentServerUrlDockerCredentialHelperException; +import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableMap; import java.io.IOException; import java.util.concurrent.Callable; +import javax.annotation.Nullable; /** Attempts to retrieve registry credentials. */ class RetrieveRegistryCredentialsStep implements Callable { @@ -39,10 +42,27 @@ class RetrieveRegistryCredentialsStep implements Callable { private final BuildConfiguration buildConfiguration; private final String registry; + private final DockerCredentialRetrieverFactory dockerCredentialRetrieverFactory; + private final DockerConfigCredentialRetriever dockerConfigCredentialRetriever; RetrieveRegistryCredentialsStep(BuildConfiguration buildConfiguration, String registry) { + this( + buildConfiguration, + registry, + new DockerCredentialRetrieverFactory(registry), + new DockerConfigCredentialRetriever(registry)); + } + + @VisibleForTesting + RetrieveRegistryCredentialsStep( + BuildConfiguration buildConfiguration, + String registry, + DockerCredentialRetrieverFactory dockerCredentialRetrieverFactory, + DockerConfigCredentialRetriever dockerConfigCredentialRetriever) { this.buildConfiguration = buildConfiguration; this.registry = registry; + this.dockerCredentialRetrieverFactory = dockerCredentialRetrieverFactory; + this.dockerConfigCredentialRetriever = dockerConfigCredentialRetriever; } @Override @@ -66,6 +86,15 @@ public Authorization call() throws IOException, NonexistentDockerCredentialHelpe return buildConfiguration.getKnownRegistryCredentials().getAuthorization(registry); } + // Tries to get registry credentials from the Docker config. + Authorization dockerConfigAuthorization = dockerConfigCredentialRetriever.retrieve(); + if (dockerConfigAuthorization != null) { + buildConfiguration + .getBuildLogger() + .info("Using credentials from Docker config for " + registry); + return dockerConfigAuthorization; + } + // Tries to infer common credential helpers for known registries. for (String registrySuffix : COMMON_CREDENTIAL_HELPERS.keySet()) { if (registry.endsWith(registrySuffix)) { @@ -99,11 +128,13 @@ public Authorization call() throws IOException, NonexistentDockerCredentialHelpe * Attempts to retrieve authorization for the registry using {@code * docker-credential-[credentialHelperSuffix]}. */ - private Authorization retrieveFromCredentialHelper(String credentialHelperSuffix) + @VisibleForTesting + @Nullable + Authorization retrieveFromCredentialHelper(String credentialHelperSuffix) throws NonexistentDockerCredentialHelperException, IOException { try { Authorization authorization = - new DockerCredentialRetriever(registry, credentialHelperSuffix).retrieve(); + dockerCredentialRetrieverFactory.withSuffix(credentialHelperSuffix).retrieve(); logGotCredentialsFrom("docker-credential-" + credentialHelperSuffix); return authorization; diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetriever.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetriever.java index 7111c454d1..13fa40bcd1 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetriever.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetriever.java @@ -50,22 +50,6 @@ public class DockerConfigCredentialRetriever { private static final Path DOCKER_CONFIG_FILE = Paths.get(System.getProperty("user.home")).resolve(".docker").resolve("config.json"); - /** Factory class for constructing {@link DockerCredentialRetriever}. */ - @VisibleForTesting - static class DockerCredentialRetrieverFactory { - - private final String registry; - - private DockerCredentialRetrieverFactory(String registry) { - this.registry = registry; - } - - @VisibleForTesting - DockerCredentialRetriever withSuffix(String credentialHelperSuffix) { - return new DockerCredentialRetriever(registry, credentialHelperSuffix); - } - } - private final String registry; private final Path dockerConfigFile; private final DockerCredentialRetrieverFactory dockerCredentialRetrieverFactory; diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetriever.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetriever.java index ef5672f10c..4294c93180 100644 --- a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetriever.java +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetriever.java @@ -47,10 +47,12 @@ private static class DockerCredentialsTemplate extends JsonTemplate { } /** + * Construct with {@link DockerCredentialRetrieverFactory}. + * * @param serverUrl the server URL to pass into the credential helper * @param credentialHelperSuffix the credential helper CLI suffix */ - public DockerCredentialRetriever(String serverUrl, String credentialHelperSuffix) { + DockerCredentialRetriever(String serverUrl, String credentialHelperSuffix) { this.serverUrl = serverUrl; this.credentialHelperSuffix = credentialHelperSuffix; } diff --git a/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverFactory.java b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverFactory.java new file mode 100644 index 0000000000..4b2eb6df79 --- /dev/null +++ b/jib-core/src/main/java/com/google/cloud/tools/jib/registry/credentials/DockerCredentialRetrieverFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2018 Google Inc. + * + * 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.registry.credentials; + +/** Factory class for constructing {@link DockerCredentialRetriever}. */ +public class DockerCredentialRetrieverFactory { + + private final String registry; + + public DockerCredentialRetrieverFactory(String registry) { + this.registry = registry; + } + + public DockerCredentialRetriever withSuffix(String credentialHelperSuffix) { + return new DockerCredentialRetriever(registry, credentialHelperSuffix); + } +} 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 084a9c2e42..fa5f84404d 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 @@ -17,9 +17,7 @@ package com.google.cloud.tools.jib.registry.credentials; import com.google.cloud.tools.jib.http.Authorization; -import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -41,54 +39,6 @@ public static RegistryCredentials of( return new RegistryCredentials().store(registry, credentialSource, authorization); } - /** - * Retrieves credentials for {@code registries} using the credential helpers referred to by {@code - * credentialHelperSuffixes}. - * - *

This obtains the registry credentials, not the Docker authentication token. - */ - public static RegistryCredentials from( - List credentialHelperSuffixes, List registries) - throws IOException, NonexistentDockerCredentialHelperException { - RegistryCredentials registryCredentials = new RegistryCredentials(); - - // TODO: These can be done in parallel. - for (String registry : registries) { - for (String credentialHelperSuffix : credentialHelperSuffixes) { - // 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. - } - } - } - return registryCredentials; - } - - /** - * Instantiates from a credential source and a map of registry credentials. - * - * @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 - */ - 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 (source of credentials, {@link Authorization}). */ private static class AuthorizationSourcePair { diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStepTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStepTest.java new file mode 100644 index 0000000000..60d6798c6f --- /dev/null +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/builder/RetrieveRegistryCredentialsStepTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2018 Google Inc. + * + * 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.http.Authorization; +import com.google.cloud.tools.jib.registry.credentials.DockerConfigCredentialRetriever; +import com.google.cloud.tools.jib.registry.credentials.DockerCredentialRetriever; +import com.google.cloud.tools.jib.registry.credentials.DockerCredentialRetrieverFactory; +import com.google.cloud.tools.jib.registry.credentials.NonexistentDockerCredentialHelperException; +import com.google.cloud.tools.jib.registry.credentials.NonexistentServerUrlDockerCredentialHelperException; +import com.google.cloud.tools.jib.registry.credentials.RegistryCredentials; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Assert; +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 RetrieveRegistryCredentialsStep}. */ +@RunWith(MockitoJUnitRunner.class) +public class RetrieveRegistryCredentialsStepTest { + + @Mock private BuildConfiguration mockBuildConfiguration; + @Mock private BuildLogger mockBuildLogger; + + @Mock private DockerCredentialRetrieverFactory mockDockerCredentialRetrieverFactory; + @Mock private DockerCredentialRetriever mockDockerCredentialRetriever; + /** + * A {@link DockerCredentialRetriever} that throws {@link + * NonexistentServerUrlDockerCredentialHelperException}. + */ + @Mock private DockerCredentialRetriever mockNonexistentServerUrlDockerCredentialRetriever; + /** + * A {@link DockerCredentialRetriever} that throws {@link + * NonexistentDockerCredentialHelperException}. + */ + @Mock private DockerCredentialRetriever mockNonexistentDockerCredentialRetriever; + + @Mock private Authorization mockAuthorization; + + @Mock private DockerConfigCredentialRetriever mockDockerConfigCredentialRetriever; + + @Mock + private NonexistentServerUrlDockerCredentialHelperException + mockNonexistentServerUrlDockerCredentialHelperException; + + @Mock + private NonexistentDockerCredentialHelperException mockNonexistentDockerCredentialHelperException; + + private static final String fakeTargetRegistry = "someRegistry"; + + @Before + public void setUpMocks() + throws NonexistentServerUrlDockerCredentialHelperException, + NonexistentDockerCredentialHelperException, IOException { + Mockito.when(mockBuildConfiguration.getBuildLogger()).thenReturn(mockBuildLogger); + + Mockito.when(mockDockerCredentialRetriever.retrieve()).thenReturn(mockAuthorization); + Mockito.when(mockNonexistentServerUrlDockerCredentialRetriever.retrieve()) + .thenThrow(mockNonexistentServerUrlDockerCredentialHelperException); + Mockito.when(mockNonexistentDockerCredentialRetriever.retrieve()) + .thenThrow(mockNonexistentDockerCredentialHelperException); + } + + @Test + public void testCall_useCredentialHelper() + throws IOException, NonexistentDockerCredentialHelperException, + NonexistentServerUrlDockerCredentialHelperException { + Mockito.when(mockBuildConfiguration.getCredentialHelperNames()) + .thenReturn(Arrays.asList("someCredentialHelper", "someOtherCredentialHelper")); + Mockito.when(mockDockerCredentialRetrieverFactory.withSuffix("someCredentialHelper")) + .thenReturn(mockNonexistentServerUrlDockerCredentialRetriever); + Mockito.when(mockDockerCredentialRetrieverFactory.withSuffix("someOtherCredentialHelper")) + .thenReturn(mockDockerCredentialRetriever); + + Assert.assertEquals( + mockAuthorization, makeRetrieveRegistryCredentialsStep(fakeTargetRegistry).call()); + + Mockito.verify(mockBuildLogger) + .info("Using docker-credential-someOtherCredentialHelper for " + fakeTargetRegistry); + } + + @Test + public void testCall_useKnownRegistryCredentials() + throws IOException, NonexistentDockerCredentialHelperException, + NonexistentServerUrlDockerCredentialHelperException { + // Has no credential helpers be defined. + Mockito.when(mockBuildConfiguration.getCredentialHelperNames()) + .thenReturn(Collections.emptyList()); + + Mockito.when(mockBuildConfiguration.getKnownRegistryCredentials()) + .thenReturn( + RegistryCredentials.of(fakeTargetRegistry, "credentialSource", mockAuthorization)); + + Assert.assertEquals( + mockAuthorization, makeRetrieveRegistryCredentialsStep(fakeTargetRegistry).call()); + + Mockito.verify(mockBuildLogger).info("Using credentialSource for " + fakeTargetRegistry); + } + + @Test + public void testCall_useDockerConfig() + throws IOException, NonexistentDockerCredentialHelperException, + NonexistentServerUrlDockerCredentialHelperException { + // Has no credential helpers be defined. + Mockito.when(mockBuildConfiguration.getCredentialHelperNames()) + .thenReturn(Collections.emptyList()); + // Has known credentials be empty. + Mockito.when(mockBuildConfiguration.getKnownRegistryCredentials()) + .thenReturn(RegistryCredentials.none()); + + Mockito.when(mockDockerConfigCredentialRetriever.retrieve()).thenReturn(mockAuthorization); + + Assert.assertEquals( + mockAuthorization, makeRetrieveRegistryCredentialsStep(fakeTargetRegistry).call()); + + Mockito.verify(mockBuildLogger) + .info("Using credentials from Docker config for " + fakeTargetRegistry); + } + + @Test + public void testCall_inferCommonCredentialHelpers() + throws IOException, NonexistentDockerCredentialHelperException, + NonexistentServerUrlDockerCredentialHelperException { + // Has no credential helpers be defined. + Mockito.when(mockBuildConfiguration.getCredentialHelperNames()) + .thenReturn(Collections.emptyList()); + // Has known credentials be empty. + Mockito.when(mockBuildConfiguration.getKnownRegistryCredentials()) + .thenReturn(RegistryCredentials.none()); + // Has no Docker config. + Mockito.when(mockDockerConfigCredentialRetriever.retrieve()).thenReturn(null); + + Mockito.when(mockDockerCredentialRetrieverFactory.withSuffix("gcr")) + .thenReturn(mockDockerCredentialRetriever); + Mockito.when(mockDockerCredentialRetrieverFactory.withSuffix("ecr-login")) + .thenReturn(mockNonexistentDockerCredentialRetriever); + + Assert.assertEquals( + mockAuthorization, makeRetrieveRegistryCredentialsStep("something.gcr.io").call()); + Mockito.verify(mockBuildLogger).info("Using docker-credential-gcr for something.gcr.io"); + + Mockito.when(mockNonexistentDockerCredentialHelperException.getMessage()).thenReturn("warning"); + Assert.assertEquals( + null, makeRetrieveRegistryCredentialsStep("something.amazonaws.com").call()); + Mockito.verify(mockBuildLogger).warn("warning"); + } + + /** Creates a fake {@link RetrieveRegistryCredentialsStep} for {@code registry}. */ + private RetrieveRegistryCredentialsStep makeRetrieveRegistryCredentialsStep(String registry) { + Mockito.when(mockBuildConfiguration.getTargetRegistry()).thenReturn(fakeTargetRegistry); + + return new RetrieveRegistryCredentialsStep( + mockBuildConfiguration, + registry, + mockDockerCredentialRetrieverFactory, + mockDockerConfigCredentialRetriever); + } +} diff --git a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetrieverTest.java b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetrieverTest.java index 70cbb2a0c8..e2104247a3 100644 --- a/jib-core/src/test/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetrieverTest.java +++ b/jib-core/src/test/java/com/google/cloud/tools/jib/registry/credentials/DockerConfigCredentialRetrieverTest.java @@ -37,9 +37,7 @@ public class DockerConfigCredentialRetrieverTest { @Mock private Authorization mockAuthorization; @Mock private DockerCredentialRetriever mockDockerCredentialRetriever; - @Mock - private DockerConfigCredentialRetriever.DockerCredentialRetrieverFactory - mockDockerCredentialRetrieverFactory; + @Mock private DockerCredentialRetrieverFactory mockDockerCredentialRetrieverFactory; private Path dockerConfigFile; @@ -53,7 +51,7 @@ public void setUp() } @Test - public void testRetrieve_nonexistentDockerConfigFile() throws URISyntaxException { + public void testRetrieve_nonexistentDockerConfigFile() { DockerConfigCredentialRetriever dockerConfigCredentialRetriever = new DockerConfigCredentialRetriever("some registry", Paths.get("fake/path")); From dc786e37555dab85672cecb4eab75e098029345f Mon Sep 17 00:00:00 2001 From: Qingyang Chen Date: Fri, 16 Feb 2018 14:47:18 -0500 Subject: [PATCH 2/2] Reverts changes to RegistryCredentials. --- .../credentials/RegistryCredentials.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) 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 fa5f84404d..084a9c2e42 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 @@ -17,7 +17,9 @@ package com.google.cloud.tools.jib.registry.credentials; import com.google.cloud.tools.jib.http.Authorization; +import java.io.IOException; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -39,6 +41,54 @@ public static RegistryCredentials of( return new RegistryCredentials().store(registry, credentialSource, authorization); } + /** + * Retrieves credentials for {@code registries} using the credential helpers referred to by {@code + * credentialHelperSuffixes}. + * + *

This obtains the registry credentials, not the Docker authentication token. + */ + public static RegistryCredentials from( + List credentialHelperSuffixes, List registries) + throws IOException, NonexistentDockerCredentialHelperException { + RegistryCredentials registryCredentials = new RegistryCredentials(); + + // TODO: These can be done in parallel. + for (String registry : registries) { + for (String credentialHelperSuffix : credentialHelperSuffixes) { + // 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. + } + } + } + return registryCredentials; + } + + /** + * Instantiates from a credential source and a map of registry credentials. + * + * @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 + */ + 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 (source of credentials, {@link Authorization}). */ private static class AuthorizationSourcePair {