Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Uses Docker config in RetrieveRegistryCredentialsStep. #104

Merged
merged 3 commits into from
Feb 20, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void testRetrieveGCR()
process.waitFor();

DockerCredentialRetriever dockerCredentialRetriever =
new DockerCredentialRetriever("myregistry", "gcr");
new DockerCredentialRetrieverFactory("myregistry").withSuffix("gcr");

Authorization authorization = dockerCredentialRetriever.retrieve();

Expand All @@ -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();

Expand All @@ -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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Authorization> {
Expand All @@ -39,10 +42,27 @@ class RetrieveRegistryCredentialsStep implements Callable<Authorization> {

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
Expand All @@ -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)) {
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: maybe this should be called withCredentialHelperSuffix? or the class called DockerCredentialHelperRetreiverFactory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll rename DockerCredentialRetriever to DockerCredentialHelper and refactor this factory and method naming in a followup refactoring CL.

return new DockerCredentialRetriever(registry, credentialHelperSuffix);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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"));

Expand Down