From 75e190fc7ad079d1091b675f3204b7168611e2ac Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Wed, 1 Aug 2018 13:55:30 -0400 Subject: [PATCH 1/9] Added support to run Docker stages on Windows slaves --- .../docker/workflow/WithContainerStep.java | 20 ++- .../workflow/client/WindowsDockerClient.java | 129 ++++++++++++++++++ .../docker/workflow/DockerTestUtil.java | 20 +++ .../workflow/client/DockerClientTest.java | 22 +-- .../client/WindowsDockerClientTest.java | 57 ++++++++ 5 files changed, 222 insertions(+), 26 deletions(-) create mode 100644 src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java create mode 100644 src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java index 5569208e6..64c6afeac 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java @@ -24,6 +24,9 @@ package org.jenkinsci.plugins.docker.workflow; import com.google.common.base.Optional; +import hudson.slaves.NodeProperty; +import hudson.slaves.NodePropertyDescriptor; +import hudson.util.DescribableList; import org.jenkinsci.plugins.docker.workflow.client.DockerClient; import com.google.inject.Inject; import hudson.AbortException; @@ -63,6 +66,7 @@ import javax.annotation.CheckForNull; import org.jenkinsci.plugins.docker.commons.fingerprint.DockerFingerprints; import org.jenkinsci.plugins.docker.commons.tools.DockerTool; +import org.jenkinsci.plugins.docker.workflow.client.WindowsDockerClient; import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl; import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; @@ -111,7 +115,6 @@ private static void destroy(String container, Launcher launcher, Node node, EnvV // TODO switch to GeneralNonBlockingStepExecution public static class Execution extends AbstractStepExecutionImpl { - private static final long serialVersionUID = 1; @Inject(optional=true) private transient WithContainerStep step; @StepContextParameter private transient Launcher launcher; @@ -138,7 +141,9 @@ public static class Execution extends AbstractStepExecutionImpl { workspace.mkdirs(); // otherwise it may be owned by root when created for -v String ws = workspace.getRemote(); toolName = step.toolName; - DockerClient dockerClient = new DockerClient(launcher, node, toolName); + DockerClient dockerClient = launcher.isUnix() + ? new DockerClient(launcher, node, toolName) + : new WindowsDockerClient(launcher, node, toolName); VersionNumber dockerVersion = dockerClient.version(); if (dockerVersion != null) { @@ -166,7 +171,11 @@ public static class Execution extends AbstractStepExecutionImpl { // check if there is any volume which contains the directory boolean found = false; for (String vol : mountedVolumes) { - if (dir.startsWith(vol)) { + boolean dirStartsWithVol = launcher.isUnix() + ? dir.startsWith(vol) // Linux + : dir.toLowerCase().startsWith(vol.toLowerCase()); // Windows + + if (dirStartsWithVol) { volumesFromContainers.add(containerId.get()); found = true; break; @@ -183,9 +192,10 @@ public static class Execution extends AbstractStepExecutionImpl { volumes.put(tmp, tmp); } - container = dockerClient.run(env, step.image, step.args, ws, volumes, volumesFromContainers, envReduced, dockerClient.whoAmI(), /* expected to hang until killed */ "cat"); + String command = launcher.isUnix() ? "cat" : "cmd.exe"; + container = dockerClient.run(env, step.image, step.args, ws, volumes, volumesFromContainers, envReduced, dockerClient.whoAmI(), /* expected to hang until killed */ command); final List ps = dockerClient.listProcess(env, container); - if (!ps.contains("cat")) { + if (!ps.contains(command)) { listener.error( "The container started but didn't run the expected command. " + "Please double check your ENTRYPOINT does execute the command passed as docker run argument, " + diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java new file mode 100644 index 000000000..81a1cab53 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -0,0 +1,129 @@ +package org.jenkinsci.plugins.docker.workflow.client; + +import com.google.common.base.Optional; +import hudson.EnvVars; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.Node; +import hudson.util.ArgumentListBuilder; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import java.io.*; +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class WindowsDockerClient extends DockerClient { + private static final Logger LOGGER = Logger.getLogger(WindowsDockerClient.class.getName()); + + private final Launcher launcher; + private final Node node; + + public WindowsDockerClient(@Nonnull Launcher launcher, @CheckForNull Node node, @CheckForNull String toolName) { + super(launcher, node, toolName); + this.launcher = launcher; + this.node = node; + } + + @Override + public String run(@Nonnull EnvVars launchEnv, @Nonnull String image, @CheckForNull String args, @CheckForNull String workdir, @Nonnull Map volumes, @Nonnull Collection volumesFromContainers, @Nonnull EnvVars containerEnv, @Nonnull String user, @Nonnull String... command) throws IOException, InterruptedException { + ArgumentListBuilder argb = new ArgumentListBuilder("docker", "run", "-d", "-t"); + if (args != null) { + argb.addTokenized(args); + } + + if (workdir != null) { + argb.add("-w", workdir); + } + for (Map.Entry volume : volumes.entrySet()) { + argb.add("-v", volume.getKey() + ":" + volume.getValue()); + } + for (String containerId : volumesFromContainers) { + argb.add("--volumes-from", containerId); + } + for (Map.Entry variable : containerEnv.entrySet()) { + argb.add("-e"); + argb.addMasked(variable.getKey()+"="+variable.getValue()); + } + argb.add(image).add(command); + + LaunchResult result = launch(launchEnv, false, null, argb); + if (result.getStatus() == 0) { + return result.getOut(); + } else { + throw new IOException(String.format("Failed to run image '%s'. Error: %s", image, result.getErr())); + } + } + + @Override + public List listProcess(@Nonnull EnvVars launchEnv, @Nonnull String containerId) throws IOException, InterruptedException { + LaunchResult result = launch(launchEnv, false, null, "docker", "top", containerId); + if (result.getStatus() != 0) { + throw new IOException(String.format("Failed to run top '%s'. Error: %s", containerId, result.getErr())); + } + List processes = new ArrayList<>(); + try (Reader r = new StringReader(result.getOut()); + BufferedReader in = new BufferedReader(r)) { + String line; + in.readLine(); // ps header + while ((line = in.readLine()) != null) { + final StringTokenizer stringTokenizer = new StringTokenizer(line, " "); + if (stringTokenizer.countTokens() < 1) { + throw new IOException("Unexpected `docker top` output : "+line); + } + + processes.add(stringTokenizer.nextToken()); // COMMAND + } + } + return processes; + } + + @Override + public Optional getContainerIdIfContainerized() throws IOException, InterruptedException { + if (node == null || + launch(new EnvVars(), true, null, "sc.exe", "query", "cexecsvc").getStatus() != 0) { + return Optional.absent(); + } + + LaunchResult getComputerName = launch(new EnvVars(), true, null, "hostname"); + if(getComputerName.getStatus() != 0) { + throw new IOException("Failed to get hostname."); + } + + String shortID = getComputerName.getOut().toLowerCase(); + LaunchResult getLongIdResult = launch(new EnvVars(), true, null, "docker", "inspect", shortID, "--format={{.Id}}"); + if(getLongIdResult.getStatus() != 0) { + LOGGER.log(Level.INFO, "Running inside of a container but cannot determine container ID from current environment."); + return Optional.absent(); + } + + return Optional.of(getLongIdResult.getOut()); + } + + private LaunchResult launch(@Nonnull EnvVars env, boolean quiet, FilePath workDir, String... args) throws IOException, InterruptedException { + return launch(env, quiet, workDir, new ArgumentListBuilder(args)); + } + private LaunchResult launch(@CheckForNull @Nonnull EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.log(Level.FINE, "Executing command \"{0}\"", argb); + } + + Launcher.ProcStarter procStarter = launcher.launch(); + if (workDir != null) { + procStarter.pwd(workDir); + } + + LaunchResult result = new LaunchResult(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream err = new ByteArrayOutputStream(); + result.setStatus(procStarter.quiet(quiet).cmds(argb).envs(env).stdout(out).stderr(err).start().joinWithTimeout(CLIENT_TIMEOUT, TimeUnit.SECONDS, launcher.getListener())); + final String charsetName = Charset.defaultCharset().name(); + result.setOut(out.toString(charsetName)); + result.setErr(err.toString(charsetName)); + + return result; + } +} diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java index 752e307f7..4f3d45e61 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java @@ -23,6 +23,7 @@ */ package org.jenkinsci.plugins.docker.workflow; +import hudson.EnvVars; import org.jenkinsci.plugins.docker.workflow.client.DockerClient; import hudson.Launcher; import hudson.util.StreamTaskListener; @@ -60,6 +61,25 @@ public static void assumeNotWindows() throws Exception { Assume.assumeFalse(System.getProperty("os.name").toLowerCase().contains("windows")); } + public static EnvVars newDockerLaunchEnv() { + // Create the KeyMaterial for connecting to the docker host/server. + // E.g. currently need to add something like the following to your env + // -DDOCKER_HOST_FOR_TEST="tcp://192.168.x.y:2376" + // -DDOCKER_HOST_KEY_DIR_FOR_TEST="/Users/tfennelly/.boot2docker/certs/boot2docker-vm" + final String docker_host_for_test = System.getProperty("DOCKER_HOST_FOR_TEST"); + final String docker_host_key_dir_for_test = System.getProperty("DOCKER_HOST_KEY_DIR_FOR_TEST"); + + EnvVars env = new EnvVars(); + if (docker_host_for_test != null) { + env.put("DOCKER_HOST", docker_host_for_test); + } + if (docker_host_key_dir_for_test != null) { + env.put("DOCKER_TLS_VERIFY", "1"); + env.put("DOCKER_CERT_PATH", docker_host_key_dir_for_test); + } + return env; + } + private DockerTestUtil() {} } diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/DockerClientTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/DockerClientTest.java index 3052aef0c..2351e8eef 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/DockerClientTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/DockerClientTest.java @@ -57,7 +57,7 @@ public void setup() throws Exception { @Test public void test_run() throws IOException, InterruptedException { - EnvVars launchEnv = newLaunchEnv(); + EnvVars launchEnv = DockerTestUtil.newDockerLaunchEnv(); String containerId = dockerClient.run(launchEnv, "learn/tutorial", null, null, Collections.emptyMap(), Collections.emptyList(), new EnvVars(), dockerClient.whoAmI(), "cat"); @@ -87,24 +87,4 @@ public void test_valid_version() { public void test_invalid_version() { Assert.assertNull(DockerClient.parseVersionNumber("xxx")); } - - - private EnvVars newLaunchEnv() { - // Create the KeyMaterial for connecting to the docker host/server. - // E.g. currently need to add something like the following to your env - // -DDOCKER_HOST_FOR_TEST="tcp://192.168.x.y:2376" - // -DDOCKER_HOST_KEY_DIR_FOR_TEST="/Users/tfennelly/.boot2docker/certs/boot2docker-vm" - final String docker_host_for_test = System.getProperty("DOCKER_HOST_FOR_TEST"); - final String docker_host_key_dir_for_test = System.getProperty("DOCKER_HOST_KEY_DIR_FOR_TEST"); - - EnvVars env = new EnvVars(); - if (docker_host_for_test != null) { - env.put("DOCKER_HOST", docker_host_for_test); - } - if (docker_host_key_dir_for_test != null) { - env.put("DOCKER_TLS_VERIFY", "1"); - env.put("DOCKER_CERT_PATH", docker_host_key_dir_for_test); - } - return env; - } } diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java new file mode 100644 index 000000000..909f6c383 --- /dev/null +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java @@ -0,0 +1,57 @@ +package org.jenkinsci.plugins.docker.workflow.client; + +import hudson.EnvVars; +import hudson.Launcher; +import hudson.model.TaskListener; +import hudson.util.StreamTaskListener; +import org.jenkinsci.plugins.docker.commons.fingerprint.ContainerRecord; +import org.jenkinsci.plugins.docker.workflow.DockerTestUtil; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; + +public class WindowsDockerClientTest { + + private DockerClient dockerClient; + + @Before + public void setup() throws Exception { + DockerTestUtil.assumeDocker(); + + TaskListener taskListener = StreamTaskListener.fromStderr(); + Launcher.LocalLauncher launcher = new Launcher.LocalLauncher(taskListener); + + dockerClient = new WindowsDockerClient(launcher, null, null); + } + + @Test + public void test_run() throws IOException, InterruptedException { + EnvVars launchEnv = DockerTestUtil.newDockerLaunchEnv(); + String containerId = dockerClient.run( + launchEnv, + "microsoft/nanoserver", + null, + null, + Collections.emptyMap(), + Collections.emptyList(), + new EnvVars(), + dockerClient.whoAmI(), + "cmd"); + + Assert.assertEquals(64, containerId.length()); + ContainerRecord containerRecord = dockerClient.getContainerRecord(launchEnv, containerId); + Assert.assertEquals(dockerClient.inspect(launchEnv, "microsoft/nanoserver", ".Id"), containerRecord.getImageId()); + Assert.assertTrue(containerRecord.getContainerName().length() > 0); + Assert.assertTrue(containerRecord.getHost().length() > 0); + Assert.assertTrue(containerRecord.getCreated() > 1000000000000L); + Assert.assertEquals(Collections.emptyList(), dockerClient.getVolumes(launchEnv, containerId)); + + // Also test that the stop works and cleans up after itself + Assert.assertNotNull(dockerClient.inspect(launchEnv, containerId, ".Name")); + dockerClient.stop(launchEnv, containerId); + Assert.assertNull(dockerClient.inspect(launchEnv, containerId, ".Name")); + } +} From 599057fd7dd9623f15c60d6fd38e10d8f174783d Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Thu, 2 Aug 2018 10:09:36 -0400 Subject: [PATCH 2/9] Updated changes to support docker.inside in reference to PR-98 --- .../docker/workflow/WithContainerStep.java | 3 +++ .../plugins/docker/workflow/Docker.groovy | 22 ++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java index 64c6afeac..8622a31a6 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java @@ -151,6 +151,9 @@ public static class Execution extends AbstractStepExecutionImpl { throw new AbortException("The docker version is less than v1.7. Pipeline functions requiring 'docker exec' (e.g. 'docker.inside') or SELinux labeling will not work."); } else if (dockerVersion.isOlderThan(new VersionNumber("1.8"))) { listener.error("The docker version is less than v1.8. Running a 'docker.inside' from inside a container will not work."); + } else if (dockerVersion.isOlderThan(new VersionNumber("1.13"))) { + if (!launcher.isUnix()) + throw new AbortException("The docker version is less than v1.13. Running a 'docker.inside' from inside a Windows container will not work."); } } else { listener.error("Failed to parse docker version. Please note there is a minimum docker version requirement of v1.7."); diff --git a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy index 3c8e01a67..6009e5251 100644 --- a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy +++ b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy @@ -75,6 +75,12 @@ class Docker implements Serializable { new Image(this, id) } + String shell() { + node { + script.isUnix() ? "sh" : "bat" + } + } + public Image build(String image, String args = '.') { node { def parsedArgs = args.split(/ (?=([^"']*["'][^"']*["'])*[^"']*$)/) @@ -121,11 +127,11 @@ class Docker implements Serializable { public V inside(String args = '', Closure body) { docker.node { def toRun = imageName() - if (toRun != id && docker.script.sh(script: "docker inspect -f . ${id}", returnStatus: true) == 0) { + if (toRun != id && docker.script."${shell()}"(script: "docker inspect -f . ${id}", returnStatus: true) == 0) { // Can run it without registry prefix, because it was locally built. toRun = id } else { - if (docker.script.sh(script: "docker inspect -f . ${toRun}", returnStatus: true) != 0) { + if (docker.script."${shell()}"(script: "docker inspect -f . ${toRun}", returnStatus: true) != 0) { // Not yet present locally. // withDockerContainer requires the image to be available locally, since its start phase is not a durable task. pull() @@ -139,13 +145,13 @@ class Docker implements Serializable { public void pull() { docker.node { - docker.script.sh "docker pull ${imageName()}" + docker.script."${shell()}" "docker pull ${imageName()}" } } public Container run(String args = '', String command = "") { docker.node { - def container = docker.script.sh(script: "docker run -d${args != '' ? ' ' + args : ''} ${id}${command != '' ? ' ' + command : ''}", returnStdout: true).trim() + def container = docker.script."${shell()}"(script: "docker run -d${args != '' ? ' ' + args : ''} ${id}${command != '' ? ' ' + command : ''}", returnStdout: true).trim() docker.script.dockerFingerprintRun containerId: container, toolName: docker.script.env.DOCKER_TOOL_NAME new Container(docker, container) } @@ -165,7 +171,7 @@ class Docker implements Serializable { public void tag(String tagName = parsedId.tag, boolean force = true) { docker.node { def taggedImageName = toQualifiedImageName(parsedId.userAndRepo + ':' + tagName) - docker.script.sh "docker tag ${id} ${taggedImageName}" + docker.script."${shell()}" "docker tag ${id} ${taggedImageName}" return taggedImageName; } } @@ -175,7 +181,7 @@ class Docker implements Serializable { // The image may have already been tagged, so the tagging may be a no-op. // That's ok since tagging is cheap. def taggedImageName = tag(tagName, force) - docker.script.sh "docker push ${taggedImageName}" + docker.script."${shell()}" "docker push ${taggedImageName}" } } @@ -192,11 +198,11 @@ class Docker implements Serializable { } public void stop() { - docker.script.sh "docker stop ${id} && docker rm -f ${id}" + docker.script."${shell()}" "docker stop ${id} && docker rm -f ${id}" } public String port(int port) { - docker.script.sh(script: "docker port ${id} ${port}", returnStdout: true).trim() + docker.script."${shell()}"(script: "docker port ${id} ${port}", returnStdout: true).trim() } } From 96d6a58d9e40c43580a77d7d8064477c5cad8094 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Thu, 2 Aug 2018 10:11:29 -0400 Subject: [PATCH 3/9] Cleaning up imports --- .../docker/workflow/WithContainerStep.java | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java index 8622a31a6..d5d6be46b 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java @@ -24,45 +24,36 @@ package org.jenkinsci.plugins.docker.workflow; import com.google.common.base.Optional; -import hudson.slaves.NodeProperty; -import hudson.slaves.NodePropertyDescriptor; -import hudson.util.DescribableList; -import org.jenkinsci.plugins.docker.workflow.client.DockerClient; import com.google.inject.Inject; -import hudson.AbortException; -import hudson.EnvVars; -import hudson.Extension; -import hudson.FilePath; -import hudson.Launcher; -import hudson.LauncherDecorator; -import hudson.Proc; -import hudson.Util; +import hudson.*; import hudson.model.Computer; import hudson.model.Node; import hudson.model.Run; import hudson.model.TaskListener; import hudson.slaves.WorkspaceList; +import hudson.util.VersionNumber; +import org.jenkinsci.plugins.docker.commons.fingerprint.DockerFingerprints; +import org.jenkinsci.plugins.docker.commons.tools.DockerTool; +import org.jenkinsci.plugins.docker.workflow.client.DockerClient; +import org.jenkinsci.plugins.docker.workflow.client.WindowsDockerClient; +import org.jenkinsci.plugins.workflow.steps.*; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.Serializable; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.Nonnull; import hudson.util.VersionNumber; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import javax.annotation.CheckForNull; import org.jenkinsci.plugins.docker.commons.fingerprint.DockerFingerprints; import org.jenkinsci.plugins.docker.commons.tools.DockerTool; From c91f4f8860ee18b03f1eef9329084ceb77bf5112 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Thu, 2 Aug 2018 10:48:23 -0400 Subject: [PATCH 4/9] Fixing build issues - Fixing haphazard changes to Docker.groovy - Fixing FindBugs issue with annotations on WindowsDockerClient.launch() --- .../workflow/client/WindowsDockerClient.java | 2 +- .../plugins/docker/workflow/Docker.groovy | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index 81a1cab53..1c92a6972 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -106,7 +106,7 @@ public Optional getContainerIdIfContainerized() throws IOException, Inte private LaunchResult launch(@Nonnull EnvVars env, boolean quiet, FilePath workDir, String... args) throws IOException, InterruptedException { return launch(env, quiet, workDir, new ArgumentListBuilder(args)); } - private LaunchResult launch(@CheckForNull @Nonnull EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException { + private LaunchResult launch(@Nonnull EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Executing command \"{0}\"", argb); } diff --git a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy index 6009e5251..86940be96 100644 --- a/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy +++ b/src/main/resources/org/jenkinsci/plugins/docker/workflow/Docker.groovy @@ -127,11 +127,11 @@ class Docker implements Serializable { public V inside(String args = '', Closure body) { docker.node { def toRun = imageName() - if (toRun != id && docker.script."${shell()}"(script: "docker inspect -f . ${id}", returnStatus: true) == 0) { + if (toRun != id && docker.script."${docker.shell()}"(script: "docker inspect -f . ${id}", returnStatus: true) == 0) { // Can run it without registry prefix, because it was locally built. toRun = id } else { - if (docker.script."${shell()}"(script: "docker inspect -f . ${toRun}", returnStatus: true) != 0) { + if (docker.script."${docker.shell()}"(script: "docker inspect -f . ${toRun}", returnStatus: true) != 0) { // Not yet present locally. // withDockerContainer requires the image to be available locally, since its start phase is not a durable task. pull() @@ -145,13 +145,13 @@ class Docker implements Serializable { public void pull() { docker.node { - docker.script."${shell()}" "docker pull ${imageName()}" + docker.script."${docker.shell()}" "docker pull ${imageName()}" } } public Container run(String args = '', String command = "") { docker.node { - def container = docker.script."${shell()}"(script: "docker run -d${args != '' ? ' ' + args : ''} ${id}${command != '' ? ' ' + command : ''}", returnStdout: true).trim() + def container = docker.script."${docker.shell()}"(script: "docker run -d${args != '' ? ' ' + args : ''} ${id}${command != '' ? ' ' + command : ''}", returnStdout: true).trim() docker.script.dockerFingerprintRun containerId: container, toolName: docker.script.env.DOCKER_TOOL_NAME new Container(docker, container) } @@ -171,7 +171,7 @@ class Docker implements Serializable { public void tag(String tagName = parsedId.tag, boolean force = true) { docker.node { def taggedImageName = toQualifiedImageName(parsedId.userAndRepo + ':' + tagName) - docker.script."${shell()}" "docker tag ${id} ${taggedImageName}" + docker.script."${docker.shell()}" "docker tag ${id} ${taggedImageName}" return taggedImageName; } } @@ -181,7 +181,7 @@ class Docker implements Serializable { // The image may have already been tagged, so the tagging may be a no-op. // That's ok since tagging is cheap. def taggedImageName = tag(tagName, force) - docker.script."${shell()}" "docker push ${taggedImageName}" + docker.script."${docker.shell()}" "docker push ${taggedImageName}" } } @@ -198,11 +198,11 @@ class Docker implements Serializable { } public void stop() { - docker.script."${shell()}" "docker stop ${id} && docker rm -f ${id}" + docker.script."${docker.shell()}" "docker stop ${id} && docker rm -f ${id}" } public String port(int port) { - docker.script."${shell()}"(script: "docker port ${id} ${port}", returnStdout: true).trim() + docker.script."${docker.shell()}"(script: "docker port ${id} ${port}", returnStdout: true).trim() } } From 45eee83861a61cfc4bc47fb1ca2037aa05ac9d60 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Thu, 2 Aug 2018 11:03:41 -0400 Subject: [PATCH 5/9] Removing annotations --- .../plugins/docker/workflow/client/WindowsDockerClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index 1c92a6972..5046fc4c5 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -103,10 +103,10 @@ public Optional getContainerIdIfContainerized() throws IOException, Inte return Optional.of(getLongIdResult.getOut()); } - private LaunchResult launch(@Nonnull EnvVars env, boolean quiet, FilePath workDir, String... args) throws IOException, InterruptedException { + private LaunchResult launch(EnvVars env, boolean quiet, FilePath workDir, String... args) throws IOException, InterruptedException { return launch(env, quiet, workDir, new ArgumentListBuilder(args)); } - private LaunchResult launch(@Nonnull EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException { + private LaunchResult launch(EnvVars env, boolean quiet, FilePath workDir, ArgumentListBuilder argb) throws IOException, InterruptedException { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Executing command \"{0}\"", argb); } From 6da111f5bb4cbf099446c7b770f085b5224a2d30 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Thu, 2 Aug 2018 11:15:32 -0400 Subject: [PATCH 6/9] Fixing DockerWindowsClient test_run() --- .../docker/workflow/client/WindowsDockerClientTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java index 909f6c383..c390e0735 100644 --- a/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java +++ b/src/test/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClientTest.java @@ -32,18 +32,18 @@ public void test_run() throws IOException, InterruptedException { EnvVars launchEnv = DockerTestUtil.newDockerLaunchEnv(); String containerId = dockerClient.run( launchEnv, - "microsoft/nanoserver", + "learn/tutorial", null, null, Collections.emptyMap(), Collections.emptyList(), new EnvVars(), dockerClient.whoAmI(), - "cmd"); + "cat"); Assert.assertEquals(64, containerId.length()); ContainerRecord containerRecord = dockerClient.getContainerRecord(launchEnv, containerId); - Assert.assertEquals(dockerClient.inspect(launchEnv, "microsoft/nanoserver", ".Id"), containerRecord.getImageId()); + Assert.assertEquals(dockerClient.inspect(launchEnv, "learn/tutorial", ".Id"), containerRecord.getImageId()); Assert.assertTrue(containerRecord.getContainerName().length() > 0); Assert.assertTrue(containerRecord.getHost().length() > 0); Assert.assertTrue(containerRecord.getCreated() > 1000000000000L); From a9602189ab8796576b52b330611097d98151ea95 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Tue, 9 Oct 2018 11:06:24 -0400 Subject: [PATCH 7/9] Updated whoAmI to properly get user on Windows --- .../docker/workflow/client/WindowsDockerClient.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index 5046fc4c5..fba56fe24 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -103,6 +103,14 @@ public Optional getContainerIdIfContainerized() throws IOException, Inte return Optional.of(getLongIdResult.getOut()); } + @Override + public String whoAmI() throws IOException, InterruptedException { + try (ByteArrayOutputStream userId = new ByteArrayOutputStream()) { + launcher.launch().cmds("whoami").quiet(true).stdout(userId).start().joinWithTimeout(CLIENT_TIMEOUT, TimeUnit.SECONDS, launcher.getListener()); + return userId.toString(); + } + } + private LaunchResult launch(EnvVars env, boolean quiet, FilePath workDir, String... args) throws IOException, InterruptedException { return launch(env, quiet, workDir, new ArgumentListBuilder(args)); } From b7974345452f479f4480e6cc39d97eaec5674e93 Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Tue, 9 Oct 2018 12:10:38 -0400 Subject: [PATCH 8/9] Fixing findBugs issues with default charset --- .../plugins/docker/workflow/client/WindowsDockerClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java index fba56fe24..77b117e9d 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/client/WindowsDockerClient.java @@ -107,7 +107,7 @@ public Optional getContainerIdIfContainerized() throws IOException, Inte public String whoAmI() throws IOException, InterruptedException { try (ByteArrayOutputStream userId = new ByteArrayOutputStream()) { launcher.launch().cmds("whoami").quiet(true).stdout(userId).start().joinWithTimeout(CLIENT_TIMEOUT, TimeUnit.SECONDS, launcher.getListener()); - return userId.toString(); + return userId.toString(Charset.defaultCharset().name()).trim(); } } From 6d22762113a1fc71401b394400acb24644c8d19f Mon Sep 17 00:00:00 2001 From: Ryan Butcher Date: Tue, 2 Jul 2019 15:01:14 -0400 Subject: [PATCH 9/9] Fixed imports in WithContainerStep.java --- .../docker/workflow/WithContainerStep.java | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java index d5d6be46b..2d3e0ce53 100644 --- a/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java +++ b/src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java @@ -50,23 +50,7 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; -import javax.annotation.Nonnull; - -import hudson.util.VersionNumber; -import java.util.concurrent.TimeUnit; -import javax.annotation.CheckForNull; -import org.jenkinsci.plugins.docker.commons.fingerprint.DockerFingerprints; -import org.jenkinsci.plugins.docker.commons.tools.DockerTool; -import org.jenkinsci.plugins.docker.workflow.client.WindowsDockerClient; -import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl; -import org.jenkinsci.plugins.workflow.steps.AbstractStepExecutionImpl; -import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl; -import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback; -import org.jenkinsci.plugins.workflow.steps.BodyInvoker; -import org.jenkinsci.plugins.workflow.steps.StepContext; -import org.jenkinsci.plugins.workflow.steps.StepContextParameter; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.DataBoundSetter; +import java.util.stream.Collectors; public class WithContainerStep extends AbstractStepImpl {