Skip to content

Commit

Permalink
[FIXED JENKINS-39631] Last step in failed stage has an error.
Browse files Browse the repository at this point in the history
  • Loading branch information
abayer committed Nov 10, 2016
1 parent 008a2fd commit 1d397c2
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 12 deletions.
7 changes: 7 additions & 0 deletions pipeline-model-definition/pom.xml
Expand Up @@ -152,6 +152,13 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>pipeline-graph-analysis</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
Expand Down
Expand Up @@ -24,6 +24,7 @@

package org.jenkinsci.plugins.pipeline.modeldefinition

import com.google.common.base.Predicate
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache;
Expand All @@ -38,9 +39,18 @@ import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStages
import org.jenkinsci.plugins.pipeline.modeldefinition.model.MethodsToList
import org.jenkinsci.plugins.pipeline.modeldefinition.parser.Converter
import org.jenkinsci.plugins.structs.SymbolLookup
import org.jenkinsci.plugins.workflow.actions.ErrorAction
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution
import org.jenkinsci.plugins.workflow.cps.CpsScript
import org.jenkinsci.plugins.workflow.cps.CpsThread
import org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode
import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode
import org.jenkinsci.plugins.workflow.graph.FlowNode
import org.jenkinsci.plugins.workflow.graphanalysis.LinearBlockHoppingScanner
import org.jenkinsci.plugins.workflow.job.WorkflowRun
import org.jenkinsci.plugins.workflow.support.steps.StageStep

import javax.annotation.Nullable
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -183,6 +193,48 @@ public class Utils {
r.addAction(new ExecutionModelAction(stages))
}

static void attachErrorToStep(Throwable e) {
CpsThread thread = CpsThread.current()
CpsFlowExecution execution = thread.execution
FlowNode currentNode = execution.currentHeads?.get(0)
if (currentNode?.getError() == null) {
ErrorAction errorAction = new ErrorAction(e)
currentNode.addAction(errorAction)
}
}

static Predicate<FlowNode> endNodeForStage(final StepStartNode startNode) {
return new Predicate<FlowNode>() {
@Override
boolean apply(@Nullable FlowNode input) {
return input != null &&
input instanceof StepEndNode &&
input.getStartNode().equals(startNode)
}
}
}

static Predicate<FlowNode> isStageWithOptionalName(final String stageName = null) {
return new Predicate<FlowNode>() {
@Override
boolean apply(@Nullable FlowNode input) {
return input != null &&
input instanceof StepStartNode &&
((StepStartNode) input).descriptor instanceof StageStep.DescriptorImpl &&
(stageName == null || input.displayName?.equals(stageName))
}
}
}

private static FlowNode findStageFlowNode(String stageName) {
CpsThread thread = CpsThread.current()
CpsFlowExecution execution = thread.execution

LinearBlockHoppingScanner scanner = new LinearBlockHoppingScanner();

return scanner.findFirstMatch(execution.currentHeads, null, isStageWithOptionalName(stageName))
}

public static String stringToSHA1(String s) {
return DigestUtils.sha1Hex(s)
}
Expand Down
Expand Up @@ -107,6 +107,7 @@ public class ModelInterpreter implements Serializable {
} catch (Exception e) {
script.echo "Error in stages execution: ${e.getMessage()}"
script.getProperty("currentBuild").result = Result.FAILURE
Utils.attachErrorToStep(e)
if (firstError == null) {
firstError = e
}
Expand All @@ -124,6 +125,7 @@ public class ModelInterpreter implements Serializable {
} catch (Exception e) {
script.echo "Error in stage post: ${e.getMessage()}"
script.getProperty("currentBuild").result = Result.FAILURE
Utils.attachErrorToStep(e)
if (firstError == null) {
firstError = e
}
Expand Down Expand Up @@ -154,6 +156,7 @@ public class ModelInterpreter implements Serializable {
} catch (Exception e) {
script.echo "Error in postBuild execution: ${e.getMessage()}"
script.getProperty("currentBuild").result = Result.FAILURE
Utils.attachErrorToStep(e)
if (firstError == null) {
firstError = e
}
Expand All @@ -162,24 +165,25 @@ public class ModelInterpreter implements Serializable {
}.call()
}.call()

try {
// And finally, run the notifications.
List<Closure> notificationClosures = root.satisfiedNotifications(script.getProperty("currentBuild"))
// And finally, run the notifications.
List<Closure> notificationClosures = root.satisfiedNotifications(script.getProperty("currentBuild"))

catchRequiredContextForNode(root.agent, true) {
if (notificationClosures.size() > 0) {
script.stage("Notifications") {
if (notificationClosures.size() > 0) {
script.stage("Notifications") {
try {
catchRequiredContextForNode(root.agent, true) {
for (int i = 0; i < notificationClosures.size(); i++) {
setUpDelegate(notificationClosures.get(i)).call()
}
}.call()
} catch (Exception e) {
script.echo "Error in notifications execution: ${e.getMessage()}"
script.getProperty("currentBuild").result = Result.FAILURE
Utils.attachErrorToStep(e)
if (firstError == null) {
firstError = e
}
}
}.call()
} catch (Exception e) {
script.echo "Error in notifications execution: ${e.getMessage()}"
script.getProperty("currentBuild").result = Result.FAILURE
if (firstError == null) {
firstError = e
}
}
}.call()
Expand Down
Expand Up @@ -36,7 +36,13 @@
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStages;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTStep;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTTreeStep;
import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.GenericStatus;
import org.jenkinsci.plugins.workflow.pipelinegraphanalysis.StatusAndTiming;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
Expand Down Expand Up @@ -142,6 +148,16 @@ public void allStagesExist() throws Exception {
j.assertLogContains("[Pipeline] { (foo)", b);
j.assertLogContains("hello", b);
j.assertLogContains("[Pipeline] { (bar)", b);

FlowExecution execution = b.getExecution();
List<FlowNode> heads = execution.getCurrentHeads();
DepthFirstScanner scanner = new DepthFirstScanner();
FlowNode startFoo = scanner.findFirstMatch(heads, null, Utils.isStageWithOptionalName("foo"));
assertNotNull(startFoo);
assertTrue(startFoo instanceof StepStartNode);
FlowNode endFoo = scanner.findFirstMatch(heads, null, Utils.endNodeForStage((StepStartNode)startFoo));
assertNotNull(endFoo);
assertEquals(GenericStatus.FAILURE, StatusAndTiming.computeChunkStatus(b, null, startFoo, endFoo, null));
}

@Test
Expand Down

0 comments on commit 1d397c2

Please sign in to comment.