diff --git a/pom.xml b/pom.xml index 5be9d2e374..cd5142c64a 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,7 @@ false true jenkinsci/${project.artifactId}-plugin + 2.2109.vb_27c2f52c324 @@ -119,6 +120,7 @@ org.jenkins-ci.plugins.workflow workflow-cps + 2725.v7b_c717eb_12ce true @@ -136,13 +138,14 @@ org.jenkins-ci.plugins.workflow workflow-durable-task-step - 1174.v73a_9a_17edce0 + 1217.v0a_5c299d0a_59 org.jenkins-ci.plugins.workflow workflow-job + 1186.v8def1a_5f3944 test @@ -165,6 +168,7 @@ org.jenkins-ci.plugins.workflow workflow-cps tests + 2725.v7b_c717eb_12ce test @@ -220,6 +224,12 @@ io.jenkins.configuration-as-code test-harness test + + + org.jenkins-ci.main + jenkins-test-harness + + org.jenkins-ci.plugins @@ -257,6 +267,37 @@ import pom + + org.jenkins-ci.plugins + script-security + 1172.v35f6a_0b_8207e + + + org.jenkinsci.plugins + pipeline-model-api + ${pipeline-model-definition-plugin.version} + + + org.jenkinsci.plugins + pipeline-model-definition + ${pipeline-model-definition-plugin.version} + + + org.jenkinsci.plugins + pipeline-model-definition + ${pipeline-model-definition-plugin.version} + tests + + + org.jenkinsci.plugins + pipeline-model-extensions + ${pipeline-model-definition-plugin.version} + + + org.jenkinsci.plugins + pipeline-stage-tags-metadata + ${pipeline-model-definition-plugin.version} + org.jenkins-ci.plugins diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java index d3a397b359..103962e3ea 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent.java @@ -13,7 +13,6 @@ import org.csanchez.jenkins.plugins.kubernetes.pod.yaml.YamlMergeStrategy; import org.csanchez.jenkins.plugins.kubernetes.volumes.workspace.WorkspaceVolume; import org.jenkinsci.Symbol; -import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgent; import org.jenkinsci.plugins.pipeline.modeldefinition.agent.DeclarativeAgentDescriptor; import org.jenkinsci.plugins.variant.OptionalExtension; import org.kohsuke.accmod.Restricted; @@ -30,8 +29,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import org.jenkinsci.plugins.pipeline.modeldefinition.agent.RetryableDeclarativeAgent; -public class KubernetesDeclarativeAgent extends DeclarativeAgent { +public class KubernetesDeclarativeAgent extends RetryableDeclarativeAgent { private static final Logger LOGGER = Logger.getLogger(KubernetesDeclarativeAgent.class.getName()); diff --git a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/SecretsMasker.java b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/SecretsMasker.java index 60f51458fd..d65d1edbc6 100644 --- a/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/SecretsMasker.java +++ b/src/main/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/SecretsMasker.java @@ -84,7 +84,13 @@ protected Class type() { @Override protected TaskListenerDecorator get(DelegatedContext context) throws IOException, InterruptedException { - KubernetesComputer c = context.get(KubernetesComputer.class); + KubernetesComputer c; + try { + c = context.get(KubernetesComputer.class); + } catch (IOException | InterruptedException x) { + LOGGER.log(Level.FINE, "Unable to look up KubernetesComputer", x); + return null; + } if (c == null) { return null; } diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly index c023738226..78a4c0047d 100644 --- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgent/config.jelly @@ -1,5 +1,5 @@ - + @@ -49,5 +49,6 @@ + diff --git a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy index 8023becaeb..fe316e2e19 100644 --- a/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy +++ b/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentScript.groovy @@ -49,27 +49,36 @@ public class KubernetesDeclarativeAgentScript extends DeclarativeAgentScript 1) { + script.retry(count: describable.retries, conditions: [script.kubernetesAgent(), script.nonresumable()]) { + run.call() + } + } else { + run.call() } } } diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentTest.java index e18d27eeae..b965e225f4 100644 --- a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentTest.java +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/KubernetesDeclarativeAgentTest.java @@ -33,6 +33,8 @@ import hudson.model.Result; import jenkins.plugins.git.GitSampleRepoRule; import jenkins.plugins.git.GitStep; +import static org.csanchez.jenkins.plugins.kubernetes.KubernetesTestUtil.deletePods; +import static org.csanchez.jenkins.plugins.kubernetes.KubernetesTestUtil.getLabels; import org.csanchez.jenkins.plugins.kubernetes.pod.retention.OnFailure; import org.jenkinsci.plugins.structs.describable.UninstantiatedDescribable; import org.jenkinsci.plugins.workflow.actions.ArgumentsAction; @@ -194,4 +196,18 @@ public void declarativeShowRawYamlFalse() throws Exception { // check yaml metadata labels not logged r.assertLogNotContains("class: KubernetesDeclarativeAgentTest", b); } + + @Issue("JENKINS-49707") + @Test + public void declarativeRetries() throws Exception { + assertNotNull(createJobThenScheduleRun()); + r.waitForMessage("+ sleep", b); + deletePods(cloud.connect(), getLabels(this, name), false); + r.waitForMessage("busybox --", b); + r.waitForMessage("jnlp --", b); + r.waitForMessage("was deleted; cancelling node body", b); + r.waitForMessage("Retrying", b); + r.assertBuildStatusSuccess(r.waitForCompletion(b)); + } + } diff --git a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/RestartPipelineTest.java b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/RestartPipelineTest.java index 3869594b52..2bc9ffa2be 100644 --- a/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/RestartPipelineTest.java +++ b/src/test/java/org/csanchez/jenkins/plugins/kubernetes/pipeline/RestartPipelineTest.java @@ -45,9 +45,11 @@ import org.csanchez.jenkins.plugins.kubernetes.model.KeyValueEnvVar; import org.csanchez.jenkins.plugins.kubernetes.model.SecretEnvVar; import org.csanchez.jenkins.plugins.kubernetes.model.TemplateEnvVar; +import org.csanchez.jenkins.plugins.kubernetes.pod.retention.Reaper; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; -import org.jenkinsci.plugins.workflow.support.steps.ExecutorStepExecution; +import org.jenkinsci.plugins.workflow.steps.durable_task.DurableTaskStep; +import org.jenkinsci.plugins.workflow.support.steps.ExecutorStepDynamicContext; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; @@ -245,12 +247,14 @@ public void terminatedPodAfterRestart() throws Exception { projectName.set(b.getParent().getFullName()); r.waitForMessage("+ sleep", b); }); + logs.record(DurableTaskStep.class, Level.FINE).record(Reaper.class, Level.FINE).record(ExecutorStepDynamicContext.class, Level.FINE); story.then(r -> { WorkflowRun b = r.jenkins.getItemByFullName(projectName.get(), WorkflowJob.class).getBuildByNumber(1); r.waitForMessage("Ready to run", b); deletePods(cloud.connect(), getLabels(this, name), false); - r.assertBuildStatus(Result.ABORTED, r.waitForCompletion(b)); - r.waitForMessage(new ExecutorStepExecution.RemovedNodeCause().getShortDescription(), b); + r.waitForMessage("Agent was removed", b); + r.waitForMessage("Retrying", b); + r.assertBuildStatusSuccess(r.waitForCompletion(b)); }); } diff --git a/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/declarativeRetries.groovy b/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/declarativeRetries.groovy new file mode 100644 index 0000000000..bf63cb20fc --- /dev/null +++ b/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/declarativeRetries.groovy @@ -0,0 +1,26 @@ +pipeline { + agent { + kubernetes { + yaml ''' +spec: + containers: + - name: busybox + image: busybox + command: + - sleep + - 99d + terminationGracePeriodSeconds: 3 + ''' + defaultContainer 'busybox' + retries 2 + } + } + stages { + stage('Run') { + steps { + sh 'echo hello world' + sh 'sleep 15' + } + } + } +} diff --git a/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/terminatedPodAfterRestart.groovy b/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/terminatedPodAfterRestart.groovy index d1eca4791b..b4b5ef61ab 100644 --- a/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/terminatedPodAfterRestart.groovy +++ b/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/terminatedPodAfterRestart.groovy @@ -1,11 +1,20 @@ package org.csanchez.jenkins.plugins.kubernetes.pipeline -podTemplate(label: '$NAME', containers: [ - containerTemplate(name: 'busybox', image: 'busybox', ttyEnabled: true, command: '/bin/cat'), -]) { - node ('$NAME') { +podTemplate(yaml: ''' +spec: + containers: + - name: busybox + image: busybox + command: + - sleep + - 99d + terminationGracePeriodSeconds: 3 +''') { + retry(count: 2, conditions: [kubernetesAgent()]) { + node(POD_LABEL) { container('busybox') { - sh 'sleep 9999999' + sh 'sleep 15' } } + } }