Skip to content

Commit

Permalink
Uses matched registry alias for credential helper. (#671)
Browse files Browse the repository at this point in the history
  • Loading branch information
coollog authored Jul 19, 2018
1 parent c07f4f2 commit 59b2df9
Show file tree
Hide file tree
Showing 10 changed files with 257 additions and 138 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public void testRetrieveGCR()
.run(Files.readAllBytes(Paths.get(Resources.getResource("credentials.json").toURI())));

DockerCredentialHelper dockerCredentialHelper =
new DockerCredentialHelperFactory("myregistry").withCredentialHelperSuffix("gcr");
new DockerCredentialHelperFactory().newDockerCredentialHelper("myregistry", "gcr");

Authorization authorization = dockerCredentialHelper.retrieve();

Expand All @@ -52,7 +52,7 @@ public void testRetrieve_nonexistentCredentialHelper()
throws IOException, NonexistentServerUrlDockerCredentialHelperException {
try {
DockerCredentialHelper fakeDockerCredentialHelper =
new DockerCredentialHelperFactory("").withCredentialHelperSuffix("fake-cloud-provider");
new DockerCredentialHelperFactory().newDockerCredentialHelper("", "fake-cloud-provider");

fakeDockerCredentialHelper.retrieve();

Expand All @@ -69,7 +69,7 @@ public void testRetrieve_nonexistentServerUrl()
throws IOException, NonexistentDockerCredentialHelperException {
try {
DockerCredentialHelper fakeDockerCredentialHelper =
new DockerCredentialHelperFactory("fake.server.url").withCredentialHelperSuffix("gcr");
new DockerCredentialHelperFactory().newDockerCredentialHelper("fake.server.url", "gcr");

fakeDockerCredentialHelper.retrieve();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private RetrieveRegistryCredentialsStep(
registry,
credentialHelperSuffix,
knownRegistryCredentials,
new DockerCredentialHelperFactory(registry),
new DockerCredentialHelperFactory(),
new DockerConfigCredentialRetriever(registry));
}

Expand Down Expand Up @@ -195,7 +195,7 @@ Authorization retrieveFromCredentialHelper(String credentialHelperSuffix)
try {
Authorization authorization =
dockerCredentialHelperFactory
.withCredentialHelperSuffix(credentialHelperSuffix)
.newDockerCredentialHelper(registry, credentialHelperSuffix)
.retrieve();
logGotCredentialsFrom("docker-credential-" + credentialHelperSuffix);
return authorization;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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.registry.credentials;

import com.google.cloud.tools.jib.registry.credentials.json.DockerConfigTemplate;
import com.google.cloud.tools.jib.registry.credentials.json.DockerConfigTemplate.AuthTemplate;
import com.google.common.annotations.VisibleForTesting;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import javax.annotation.Nullable;

/** Handles getting useful information from a {@link DockerConfigTemplate}. */
class DockerConfig {

/**
* Returns the first entry matching the given key predicates (short-circuiting in the order of
* predicates).
*/
@Nullable
private static <K, T> Map.Entry<K, T> findFirstInMapByKey(
Map<K, T> map, List<Predicate<K>> keyMatches) {
return keyMatches
.stream()
.map(keyMatch -> findFirstInMapByKey(map, keyMatch))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}

/** Returns the first entry matching the given key predicate. */
@Nullable
private static <K, T> Map.Entry<K, T> findFirstInMapByKey(Map<K, T> map, Predicate<K> keyMatch) {
return map.entrySet()
.stream()
.filter(entry -> keyMatch.test(entry.getKey()))
.findFirst()
.orElse(null);
}

private final DockerConfigTemplate dockerConfigTemplate;

DockerConfig(DockerConfigTemplate dockerConfigTemplate) {
this.dockerConfigTemplate = dockerConfigTemplate;
}

/**
* Returns the base64-encoded {@code Basic} authorization for {@code registry}, or {@code null} if
* none exists. The order of lookup preference:
*
* <ol>
* <li>Exact registry name
* <li>https:// + registry name
* <li>registry name + / + arbitrary suffix
* <li>https:// + registry name + / arbitrary suffix
* </ol>
*
* @param registry the registry to get the authorization for
* @return the base64-encoded {@code Basic} authorization for {@code registry}, or {@code null} if
* none exists
*/
@Nullable
String getAuthFor(String registry) {
Map.Entry<String, AuthTemplate> authEntry =
findFirstInMapByKey(dockerConfigTemplate.getAuths(), getRegistryMatchersFor(registry));
return authEntry != null ? authEntry.getValue().getAuth() : null;
}

@VisibleForTesting
@Nullable
DockerCredentialHelper getCredentialHelperFor(String registry) {
return getCredentialHelperFor(new DockerCredentialHelperFactory(), registry);
}

/**
* Determines a {@link DockerCredentialHelper} to use for {@code registry}.
*
* <p>If there exists a matching registry entry (or its aliases) in {@code auths} for {@code
* registry}, the credential helper is {@code credStore}; otherwise, if there exists a matching
* registry entry (or its aliases) in {@code credHelpers}, the corresponding credential helper
* suffix is used.
*
* <p>See {@link #getRegistryMatchersFor} for the alias lookup order.
*
* @param registry the registry to get the credential helpers for
* @return the {@link DockerCredentialHelper} or {@code null} if none is found for the given
* registry
*/
@Nullable
DockerCredentialHelper getCredentialHelperFor(
DockerCredentialHelperFactory dockerCredentialHelperFactory, String registry) {
List<Predicate<String>> registryMatchers = getRegistryMatchersFor(registry);
Map.Entry<String, AuthTemplate> firstMatchInAuths =
findFirstInMapByKey(dockerConfigTemplate.getAuths(), registryMatchers);
if (dockerConfigTemplate.getCredsStore() != null && firstMatchInAuths != null) {
return dockerCredentialHelperFactory.newDockerCredentialHelper(
firstMatchInAuths.getKey(), dockerConfigTemplate.getCredsStore());
}
Map.Entry<String, String> firstMatchInCredHelpers =
findFirstInMapByKey(dockerConfigTemplate.getCredHelpers(), registryMatchers);
if (firstMatchInCredHelpers == null) {
return null;
}
return dockerCredentialHelperFactory.newDockerCredentialHelper(
firstMatchInCredHelpers.getKey(), firstMatchInCredHelpers.getValue());
}

/**
* Registry alias matches in the following order:
*
* <ol>
* <li>Exact registry name
* <li>https:// + registry name
* <li>registry name + / + arbitrary suffix
* <li>https:// + registry name + / + arbitrary suffix
* </ol>
*
* @param registry the registry to get matchers for
* @return the list of predicates to match possible aliases
*/
private List<Predicate<String>> getRegistryMatchersFor(String registry) {
Predicate<String> exactMatch = registry::equals;
Predicate<String> withHttps = ("https://" + registry)::equals;
Predicate<String> withSuffix = name -> name.startsWith(registry + "/");
Predicate<String> withHttpsAndSuffix = name -> name.startsWith("https://" + registry + "/");
return Arrays.asList(exactMatch, withHttps, withSuffix, withHttpsAndSuffix);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public DockerConfigCredentialRetriever(String registry) {
DockerConfigCredentialRetriever(String registry, Path dockerConfigFile) {
this.registry = registry;
this.dockerConfigFile = dockerConfigFile;
this.dockerCredentialHelperFactory = new DockerCredentialHelperFactory(registry);
this.dockerCredentialHelperFactory = new DockerCredentialHelperFactory();
}

@VisibleForTesting
Expand All @@ -77,6 +77,8 @@ public DockerConfigCredentialRetriever(String registry) {
}

/**
* Retrieves credentials for a registry. Tries all possible known aliases.
*
* @return {@link Authorization} found for {@code registry}, or {@code null} if not found
* @throws IOException if failed to parse the config JSON
*/
Expand All @@ -87,30 +89,39 @@ public Authorization retrieve() throws IOException {
return null;
}

for (String registry : RegistryAliasGroup.getAliasesGroup(registry)) {
Authorization authorization = retrieve(dockerConfigTemplate, registry);
DockerConfig dockerConfig = new DockerConfig(dockerConfigTemplate);

for (String registryAlias : RegistryAliasGroup.getAliasesGroup(registry)) {
Authorization authorization = retrieve(dockerConfig, registryAlias);
if (authorization != null) {
return authorization;
}
}
return null;
}

/**
* Retrieves credentials for a registry alias from a {@link DockerConfig}.
*
* @param dockerConfig the {@link DockerConfig} to retrieve from
* @param registryAlias the registry alias to use
* @return the retrieved credentials, or {@code null} if none are found
*/
@Nullable
private Authorization retrieve(DockerConfigTemplate dockerConfigTemplate, String registry) {
private Authorization retrieve(DockerConfig dockerConfig, String registryAlias) {
// First, tries to find defined auth.
String auth = dockerConfigTemplate.getAuthFor(registry);
String auth = dockerConfig.getAuthFor(registryAlias);
if (auth != null) {
return Authorizations.withBasicToken(auth);
}

// Then, tries to use a defined credHelpers credential helper.
String credentialHelperSuffix = dockerConfigTemplate.getCredentialHelperFor(registry);
if (credentialHelperSuffix != null) {
DockerCredentialHelper dockerCredentialHelper =
dockerConfig.getCredentialHelperFor(dockerCredentialHelperFactory, registryAlias);
if (dockerCredentialHelper != null) {
try {
return dockerCredentialHelperFactory
.withCredentialHelperSuffix(credentialHelperSuffix)
.retrieve();
// Tries with the given registry alias (NOT the original registry).
return dockerCredentialHelper.retrieve();

} catch (IOException
| NonexistentServerUrlDockerCredentialHelperException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.cloud.tools.jib.http.Authorizations;
import com.google.cloud.tools.jib.json.JsonTemplate;
import com.google.cloud.tools.jib.json.JsonTemplateMapper;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.CharStreams;
import java.io.IOException;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -131,4 +132,14 @@ public Authorization retrieve()
throw ex;
}
}

@VisibleForTesting
String getServerUrl() {
return serverUrl;
}

@VisibleForTesting
String getCredentialHelperSuffix() {
return credentialHelperSuffix;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,15 @@
/** Factory class for constructing {@link DockerCredentialHelper}. */
public class DockerCredentialHelperFactory {

private final String registry;

public DockerCredentialHelperFactory(String registry) {
this.registry = registry;
}
public DockerCredentialHelperFactory() {}

/**
* @param registry the {@code ServerURL} stored by the credential helper
* @param credentialHelperSuffix the suffix of the docker-credential-[suffix] command to be run.
* @return a {@link DockerCredentialHelper} retrieved from the command.
*/
public DockerCredentialHelper withCredentialHelperSuffix(String credentialHelperSuffix) {
public DockerCredentialHelper newDockerCredentialHelper(
String registry, String credentialHelperSuffix) {
return new DockerCredentialHelper(registry, credentialHelperSuffix);
}
}
Loading

0 comments on commit 59b2df9

Please sign in to comment.