Skip to content

Commit

Permalink
[JENKINS-51397] Define API for passing environment to docker login co…
Browse files Browse the repository at this point in the history
…mmand.
  • Loading branch information
jglick committed May 17, 2018
1 parent 5bd5ad3 commit 4f7f777
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 19 deletions.
Expand Up @@ -60,6 +60,7 @@

import static com.cloudbees.plugins.credentials.CredentialsMatchers.*;
import hudson.AbortException;
import hudson.EnvVars;
import hudson.Launcher;
import hudson.model.TaskListener;
import hudson.slaves.WorkspaceList;
Expand Down Expand Up @@ -207,7 +208,7 @@ public KeyMaterialFactory newKeyMaterialFactory(Item context, @Nonnull VirtualCh
}

/**
* @deprecated Call {@link #newKeyMaterialFactory(Item, FilePath, Launcher, TaskListener, String)}
* @deprecated Call {@link #newKeyMaterialFactory(Item, FilePath, Launcher, EnvVars, TaskListener, String)}
*/
@Deprecated
public KeyMaterialFactory newKeyMaterialFactory(@CheckForNull Item context, @Nonnull VirtualChannel target, @CheckForNull Launcher launcher, @Nonnull TaskListener listener) throws IOException, InterruptedException {
Expand All @@ -221,21 +222,29 @@ public KeyMaterialFactory newKeyMaterialFactory(@CheckForNull Item context, @Non
return token.newKeyMaterialFactory(getEffectiveUrl(), target, launcher, listener);
}

/**
* @deprecated Call {@link #newKeyMaterialFactory(Item, FilePath, Launcher, EnvVars, TaskListener, String)}
*/
@Deprecated
public KeyMaterialFactory newKeyMaterialFactory(@CheckForNull Item context, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws IOException, InterruptedException {
return newKeyMaterialFactory(context, workspace, launcher, new EnvVars(), listener, dockerExecutable);
}

/**
* Makes the credentials available locally and returns {@link KeyMaterialFactory} that gives you the parameters
* needed to access it.
* @param workspace a workspace being used for operations ({@link WorkspaceList#tempDir} will be applied)
* @param dockerExecutable as in {@link DockerTool#getExecutable}, with a 1.8+ client
*/
public KeyMaterialFactory newKeyMaterialFactory(@CheckForNull Item context, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws IOException, InterruptedException {
public KeyMaterialFactory newKeyMaterialFactory(@CheckForNull Item context, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws IOException, InterruptedException {
if (credentialsId == null) {
return KeyMaterialFactory.NULL; // nothing needed to be done
}
DockerRegistryToken token = getToken(context);
if (token == null) {
throw new AbortException("Could not find credentials matching " + credentialsId);
}
return token.newKeyMaterialFactory(getEffectiveUrl(), workspace, launcher, listener, dockerExecutable);
return token.newKeyMaterialFactory(getEffectiveUrl(), workspace, launcher, env, listener, dockerExecutable);
}

/**
Expand Down
Expand Up @@ -24,6 +24,7 @@
package org.jenkinsci.plugins.docker.commons.credentials;

import com.cloudbees.plugins.credentials.Credentials;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.TaskListener;
Expand Down Expand Up @@ -71,30 +72,44 @@ public String getToken() {
}

/**
* @deprecated use {@link #newKeyMaterialFactory(URL, FilePath, Launcher, TaskListener, String)}
* @deprecated use {@link #newKeyMaterialFactory(URL, FilePath, Launcher, EnvVars, TaskListener, String)}
*/
@Deprecated
public KeyMaterialFactory newKeyMaterialFactory(final URL endpoint, @Nonnull VirtualChannel target) throws InterruptedException, IOException {
return newKeyMaterialFactory(endpoint, target, null, TaskListener.NULL);
}

/**
* @deprecated use {@link #newKeyMaterialFactory(URL, FilePath, Launcher, EnvVars, TaskListener, String)}
*/
@Deprecated
public KeyMaterialFactory newKeyMaterialFactory(@Nonnull URL endpoint, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws InterruptedException, IOException {
return newKeyMaterialFactory(endpoint, workspace, launcher, new EnvVars(), listener, dockerExecutable);
}

/** Kill switch in case {@code docker login} via {@link RegistryKeyMaterialFactory} does not work. */
@SuppressWarnings("FieldMayBeFinal")
private static /* not final */ boolean USE_CUSTOM_LOGIN = Boolean.getBoolean(DockerRegistryToken.class.getName() + ".USE_CUSTOM_LOGIN");

/**
* Sets up an environment logged in to the specified Docker registry.
* @param dockerExecutable as in {@link DockerTool#getExecutable}, with a 1.8+ client
*/
public KeyMaterialFactory newKeyMaterialFactory(@Nonnull URL endpoint, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws InterruptedException, IOException {
try {
// see UsernamePasswordDockerRegistryTokenSource for example
String usernameColonPassword = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8);
int colon = usernameColonPassword.indexOf(':');
if (colon > 0) {
return new RegistryKeyMaterialFactory(usernameColonPassword.substring(0, colon), usernameColonPassword.substring(colon + 1), endpoint, launcher, listener, dockerExecutable).
contextualize(new KeyMaterialContext(WorkspaceList.tempDir(workspace)));
public KeyMaterialFactory newKeyMaterialFactory(@Nonnull URL endpoint, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) throws InterruptedException, IOException {
if (!USE_CUSTOM_LOGIN) {
try {
// see UsernamePasswordDockerRegistryTokenSource for example
String usernameColonPassword = new String(Base64.getDecoder().decode(token), StandardCharsets.UTF_8);
int colon = usernameColonPassword.indexOf(':');
if (colon > 0) {
return new RegistryKeyMaterialFactory(usernameColonPassword.substring(0, colon), usernameColonPassword.substring(colon + 1), endpoint, launcher, env, listener, dockerExecutable).
contextualize(new KeyMaterialContext(WorkspaceList.tempDir(workspace)));
}
} catch (IllegalArgumentException x) {
// not Base64-encoded
}
} catch (IllegalArgumentException x) {
// not Base64-encoded
listener.getLogger().println("Warning: authentication token does not look like a username:password; falling back to direct manipulation of Docker configuration files");
}
listener.getLogger().println("Warning: authentication token does not look like a username:password; falling back to direct manipulation of Docker configuration files");
return newKeyMaterialFactory(endpoint, workspace.getChannel(), launcher, listener);
}

Expand Down
Expand Up @@ -48,14 +48,16 @@ public class RegistryKeyMaterialFactory extends KeyMaterialFactory {
private final @Nonnull String password;
private final @Nonnull URL endpoint;
private final @Nonnull Launcher launcher;
private final @Nonnull EnvVars env;
private final @Nonnull TaskListener listener;
private final @Nonnull String dockerExecutable;

public RegistryKeyMaterialFactory(@Nonnull String username, @Nonnull String password, @Nonnull URL endpoint, @Nonnull Launcher launcher, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) {
public RegistryKeyMaterialFactory(@Nonnull String username, @Nonnull String password, @Nonnull URL endpoint, @Nonnull Launcher launcher, @Nonnull EnvVars env, @Nonnull TaskListener listener, @Nonnull String dockerExecutable) {
this.username = username;
this.password = password;
this.endpoint = endpoint;
this.launcher = launcher;
this.env = env;
this.listener = listener;
this.dockerExecutable = dockerExecutable;
}
Expand All @@ -64,7 +66,10 @@ public RegistryKeyMaterialFactory(@Nonnull String username, @Nonnull String pass
public KeyMaterial materialize() throws IOException, InterruptedException {
FilePath dockerConfig = createSecretsDirectory();
try {
if (launcher.launch().cmds(new ArgumentListBuilder(dockerExecutable, "login", "-u", username, "-p").add(password, true).add(endpoint)).envs("DOCKER_CONFIG=" + dockerConfig).stdout(listener).join() != 0) {
// TODO on Docker 17.07+ use --password-stdin
EnvVars envWithConfig = new EnvVars(env);
envWithConfig.put("DOCKER_CONFIG", dockerConfig.getRemote());
if (launcher.launch().cmds(new ArgumentListBuilder(dockerExecutable, "login", "-u", username, "-p").add(password, true).add(endpoint)).envs(envWithConfig).stdout(listener).join() != 0) {
throw new AbortException("docker login failed");
}
} catch (IOException | InterruptedException x) {
Expand Down
Expand Up @@ -23,6 +23,7 @@
*/
package org.jenkinsci.plugins.docker.commons.util;

import hudson.EnvVars;
import org.jenkinsci.plugins.docker.commons.tools.DockerTool;
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterialFactory;
import org.jenkinsci.plugins.docker.commons.credentials.KeyMaterial;
Expand Down Expand Up @@ -74,9 +75,10 @@ public String getToolName() {

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
EnvVars env = build.getEnvironment(listener);
// prepare the credentials to talk to this docker and make it available for docker you'll be forking
String dockerExecutable = DockerTool.getExecutable(toolName, build.getBuiltOn(), listener, build.getEnvironment(listener));
KeyMaterialFactory keyMaterialFactory = server.newKeyMaterialFactory(build).plus(registry.newKeyMaterialFactory(build.getParent(), build.getWorkspace(), launcher, listener, dockerExecutable));
String dockerExecutable = DockerTool.getExecutable(toolName, build.getBuiltOn(), listener, env);
KeyMaterialFactory keyMaterialFactory = server.newKeyMaterialFactory(build).plus(registry.newKeyMaterialFactory(build.getParent(), build.getWorkspace(), launcher, env, listener, dockerExecutable));
try (KeyMaterial key = keyMaterialFactory.materialize()) {
// fork docker with appropriate environment to interact with this docker daemon
return launcher.launch().cmds(dockerExecutable, "info").envs(key.env()).join() == 0;
Expand Down

0 comments on commit 4f7f777

Please sign in to comment.