Skip to content

Commit 68c1fd6

Browse files
authored
Merge pull request jenkinsci#86 from jglick/hang-JENKINS-37719
[JENKINS-37719] Apply a timeout to all Docker CLI operations
2 parents 916bdb7 + 3f8a83f commit 68c1fd6

File tree

5 files changed

+51
-13
lines changed

5 files changed

+51
-13
lines changed

pom.xml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<parent>
55
<groupId>org.jenkins-ci.plugins</groupId>
66
<artifactId>plugin</artifactId>
7-
<version>2.17</version>
7+
<version>2.21</version>
88
<relativePath/>
99
</parent>
1010
<artifactId>docker-workflow</artifactId>
@@ -52,37 +52,37 @@
5252
<dependency>
5353
<groupId>org.jenkins-ci.plugins.workflow</groupId>
5454
<artifactId>workflow-cps</artifactId>
55-
<version>2.17</version>
55+
<version>2.25</version>
5656
</dependency>
5757
<dependency>
5858
<groupId>org.jenkins-ci.plugins.workflow</groupId>
5959
<artifactId>workflow-durable-task-step</artifactId>
60-
<version>2.4</version>
60+
<version>2.8</version>
6161
</dependency>
6262
<dependency> <!-- StepConfigTester -->
6363
<groupId>org.jenkins-ci.plugins.workflow</groupId>
6464
<artifactId>workflow-step-api</artifactId>
6565
<classifier>tests</classifier>
66-
<version>2.2</version>
66+
<version>2.7</version>
6767
<scope>test</scope>
6868
</dependency>
6969
<dependency> <!-- SemaphoreStep -->
7070
<groupId>org.jenkins-ci.plugins.workflow</groupId>
7171
<artifactId>workflow-support</artifactId>
7272
<classifier>tests</classifier>
73-
<version>2.1</version>
73+
<version>2.12</version>
7474
<scope>test</scope>
7575
</dependency>
7676
<dependency>
7777
<groupId>org.jenkins-ci.plugins.workflow</groupId>
7878
<artifactId>workflow-job</artifactId>
79-
<version>2.3</version>
79+
<version>2.9</version>
8080
<scope>test</scope>
8181
</dependency>
8282
<dependency>
8383
<groupId>org.jenkins-ci.plugins.workflow</groupId>
8484
<artifactId>workflow-basic-steps</artifactId>
85-
<version>2.0</version>
85+
<version>2.3</version>
8686
<scope>test</scope>
8787
</dependency>
8888
<dependency>

src/main/java/org/jenkinsci/plugins/docker/workflow/WithContainerStep.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import javax.annotation.Nonnull;
5858

5959
import hudson.util.VersionNumber;
60+
import java.util.concurrent.TimeUnit;
6061
import javax.annotation.CheckForNull;
6162
import org.jenkinsci.plugins.docker.commons.fingerprint.DockerFingerprints;
6263
import org.jenkinsci.plugins.docker.commons.tools.DockerTool;
@@ -247,7 +248,7 @@ private static class Decorator extends LauncherDecorator implements Serializable
247248
@Override public void kill(Map<String,String> modelEnvVars) throws IOException, InterruptedException {
248249
ByteArrayOutputStream baos = new ByteArrayOutputStream();
249250
String executable = getExecutable();
250-
if (getInner().launch().cmds(executable, "exec", container, "ps", "-A", "-o", "pid,command", "e").stdout(baos).quiet(true).join() != 0) {
251+
if (getInner().launch().cmds(executable, "exec", container, "ps", "-A", "-o", "pid,command", "e").stdout(baos).quiet(true).start().joinWithTimeout(10, TimeUnit.SECONDS, listener) != 0) {
251252
throw new IOException("failed to run ps");
252253
}
253254
List<String> pids = new ArrayList<String>();
@@ -269,7 +270,7 @@ private static class Decorator extends LauncherDecorator implements Serializable
269270
if (!pids.isEmpty()) {
270271
List<String> cmds = new ArrayList<>(Arrays.asList(executable, "exec", container, "kill"));
271272
cmds.addAll(pids);
272-
if (getInner().launch().cmds(cmds).quiet(true).join() != 0) {
273+
if (getInner().launch().cmds(cmds).quiet(true).start().joinWithTimeout(10, TimeUnit.SECONDS, listener) != 0) {
273274
throw new IOException("failed to run kill");
274275
}
275276
}

src/main/java/org/jenkinsci/plugins/docker/workflow/client/DockerClient.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import java.util.Map;
4646
import java.util.List;
4747
import java.util.Arrays;
48+
import java.util.concurrent.TimeUnit;
4849
import java.util.logging.Level;
4950
import java.util.logging.Logger;
5051
import java.util.regex.Matcher;
@@ -264,7 +265,7 @@ private LaunchResult launch(@CheckForNull @Nonnull EnvVars launchEnv, boolean qu
264265
LaunchResult result = new LaunchResult();
265266
ByteArrayOutputStream out = new ByteArrayOutputStream();
266267
ByteArrayOutputStream err = new ByteArrayOutputStream();
267-
result.setStatus(procStarter.quiet(quiet).cmds(args).envs(launchEnv).stdout(out).stderr(err).join());
268+
result.setStatus(procStarter.quiet(quiet).cmds(args).envs(launchEnv).stdout(out).stderr(err).start().joinWithTimeout(10, TimeUnit.SECONDS, launcher.getListener()));
268269
final String charsetName = Charset.defaultCharset().name();
269270
result.setOut(out.toString(charsetName));
270271
result.setErr(err.toString(charsetName));
@@ -278,10 +279,10 @@ private LaunchResult launch(@CheckForNull @Nonnull EnvVars launchEnv, boolean qu
278279
*/
279280
public String whoAmI() throws IOException, InterruptedException {
280281
ByteArrayOutputStream userId = new ByteArrayOutputStream();
281-
launcher.launch().cmds("id", "-u").quiet(true).stdout(userId).join();
282+
launcher.launch().cmds("id", "-u").quiet(true).stdout(userId).start().joinWithTimeout(10, TimeUnit.SECONDS, launcher.getListener());
282283

283284
ByteArrayOutputStream groupId = new ByteArrayOutputStream();
284-
launcher.launch().cmds("id", "-g").quiet(true).stdout(groupId).join();
285+
launcher.launch().cmds("id", "-g").quiet(true).stdout(groupId).start().joinWithTimeout(10, TimeUnit.SECONDS, launcher.getListener());
285286

286287
final String charsetName = Charset.defaultCharset().name();
287288
return String.format("%s:%s", userId.toString(charsetName).trim(), groupId.toString(charsetName).trim());

src/test/java/org/jenkinsci/plugins/docker/workflow/DockerTestUtil.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.junit.Assume;
3131

3232
import java.io.IOException;
33+
import java.util.concurrent.TimeUnit;
3334

3435
import static org.hamcrest.Matchers.is;
3536
import org.jenkinsci.plugins.docker.commons.tools.DockerTool;
@@ -42,12 +43,14 @@ public class DockerTestUtil {
4243
public static void assumeDocker() throws Exception {
4344
Launcher.LocalLauncher localLauncher = new Launcher.LocalLauncher(StreamTaskListener.NULL);
4445
try {
45-
Assume.assumeThat("Docker working", localLauncher.launch().cmds(DockerTool.getExecutable(null, null, null, null), "ps").join(), is(0));
46+
Assume.assumeThat("Docker working", localLauncher.launch().cmds(DockerTool.getExecutable(null, null, null, null), "ps").start().joinWithTimeout(10, TimeUnit.SECONDS, localLauncher.getListener()), is(0));
4647
} catch (IOException x) {
4748
Assume.assumeNoException("have Docker installed", x);
4849
}
4950
DockerClient dockerClient = new DockerClient(localLauncher, null, null);
5051
Assume.assumeFalse("Docker version not < 1.3", dockerClient.version().isOlderThan(new VersionNumber("1.3")));
5152
}
53+
54+
private DockerTestUtil() {}
5255

5356
}

src/test/java/org/jenkinsci/plugins/docker/workflow/WithContainerStepTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@
3131
import hudson.tools.ToolProperty;
3232
import java.io.File;
3333
import java.util.Collections;
34+
import java.util.logging.Level;
3435
import org.apache.commons.fileupload.FileItem;
3536
import org.apache.commons.io.FileUtils;
37+
import org.hamcrest.Matchers;
3638
import org.jenkinsci.lib.configprovider.ConfigProvider;
3739
import org.jenkinsci.lib.configprovider.model.Config;
3840
import org.jenkinsci.plugins.configfiles.custom.CustomConfig;
@@ -43,6 +45,7 @@
4345
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
4446
import org.jenkinsci.plugins.workflow.steps.StepConfigTester;
4547
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
48+
import org.junit.Assume;
4649
import org.junit.ClassRule;
4750
import org.junit.Ignore;
4851
import org.junit.Test;
@@ -51,13 +54,15 @@
5154
import org.junit.runners.model.Statement;
5255
import org.jvnet.hudson.test.BuildWatcher;
5356
import org.jvnet.hudson.test.Issue;
57+
import org.jvnet.hudson.test.LoggerRule;
5458
import org.jvnet.hudson.test.RestartableJenkinsRule;
5559

5660
public class WithContainerStepTest {
5761

5862
@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();
5963
@Rule public RestartableJenkinsRule story = new RestartableJenkinsRule();
6064
@Rule public TemporaryFolder tmp = new TemporaryFolder();
65+
@Rule public LoggerRule logging = new LoggerRule();
6166

6267
@Test public void configRoundTrip() {
6368
story.addStep(new Statement() {
@@ -90,6 +95,34 @@ public class WithContainerStepTest {
9095
});
9196
}
9297

98+
@Issue("JENKINS-37719")
99+
@Test public void hungDaemon() {
100+
story.addStep(new Statement() {
101+
@Override public void evaluate() throws Throwable {
102+
DockerTestUtil.assumeDocker();
103+
Assume.assumeThat("we are in an interactive environment and can pause dockerd", new ProcessBuilder("sudo", "pgrep", "dockerd").inheritIO().start().waitFor(), Matchers.is(0));
104+
logging.record("org.jenkinsci.plugins.workflow.support.concurrent.Timeout", Level.FINE); // TODO use Timeout.class when workflow-support 2.13+
105+
WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "prj");
106+
p.setDefinition(new CpsFlowDefinition(
107+
"node {\n" +
108+
" timeout(time: 20, unit: 'SECONDS') {\n" +
109+
" withDockerContainer('httpd:2.4.12') {\n" +
110+
" sh 'sleep infinity'\n" +
111+
" }\n" +
112+
" }\n" +
113+
"}", true));
114+
WorkflowRun b = p.scheduleBuild2(0).waitForStart();
115+
story.j.waitForMessage("+ sleep infinity", b);
116+
Assume.assumeThat(new ProcessBuilder("sudo", "killall", "-STOP", "dockerd").inheritIO().start().waitFor(), Matchers.is(0));
117+
try {
118+
story.j.assertBuildStatus(Result.ABORTED, story.j.waitForCompletion(b));
119+
} finally {
120+
Assume.assumeThat(new ProcessBuilder("sudo", "killall", "-CONT", "dockerd").inheritIO().start().waitFor(), Matchers.is(0));
121+
}
122+
}
123+
});
124+
}
125+
93126
@Test public void stop() {
94127
story.addStep(new Statement() {
95128
@Override public void evaluate() throws Throwable {

0 commit comments

Comments
 (0)