diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProc.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProc.java index 291cf09ba..ed18f7820 100755 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProc.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProc.java @@ -89,7 +89,15 @@ public void kill() throws IOException, InterruptedException { } catch (IOException e) { LOGGER.log(Level.FINE, "Proc kill failed, ignoring", e); } finally { - close(); + try { + close(); + } finally { + // Force finish finished latch count down to ensure join unblocks. + // In the wild it has been observed that sometimes the countdown + // latch is not triggered resulting in joinWithTimeout never + // completing even after the timeout interruption has been triggered. + finished.countDown(); + } } } diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProcTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProcTest.java new file mode 100644 index 000000000..d0ccd1078 --- /dev/null +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/ContainerExecProcTest.java @@ -0,0 +1,40 @@ +package org.csanchez.jenkins.plugins.kubernetes.pipeline; + +import hudson.model.TaskListener; +import io.fabric8.kubernetes.client.dsl.ExecWatch; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.jvnet.hudson.test.JenkinsRule; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ContainerExecProcTest { + + @Mock + private ExecWatch execWatch; + + @Rule + public JenkinsRule j = new JenkinsRule(); + + /** + * This test validates that {@link hudson.Proc#joinWithTimeout(long, TimeUnit, TaskListener)} is + * terminated properly when the finished count down latch is never triggered by the watch. + */ + @Test(timeout = 20000) + public void testJoinWithTimeoutFinishCountDown() throws Exception { + AtomicBoolean alive = new AtomicBoolean(true); + CountDownLatch finished = new CountDownLatch(1); + ByteArrayOutputStream stdin = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(new ByteArrayOutputStream()); + TaskListener listener = TaskListener.NULL; + ContainerExecProc proc = new ContainerExecProc(execWatch, alive, finished, stdin, ps); + proc.joinWithTimeout(1, TimeUnit.SECONDS, listener); + } +}