Skip to content

Commit

Permalink
Merge pull request #54 from Vlatombe/JENKINS-60995
Browse files Browse the repository at this point in the history
[JENKINS-60995] Implement a specific UpstreamCause class containing the nodeId that triggered the build
  • Loading branch information
bitwiseman committed Jun 24, 2021
2 parents d6d637e + 06eb2da commit 8cc9f8a
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public boolean start() throws Exception {
}

List<Action> actions = new ArrayList<>();
actions.add(new CauseAction(new Cause.UpstreamCause(invokingRun)));
actions.add(new CauseAction(new BuildUpstreamCause(node, invokingRun)));
actions.add(new BuildUpstreamNodeAction(node, invokingRun));

if (item instanceof ParameterizedJobMixIn.ParameterizedJob) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.jenkinsci.plugins.workflow.support.steps.build;

import hudson.model.Cause;
import hudson.model.Run;
import java.util.Objects;
import org.jenkinsci.plugins.workflow.graph.FlowNode;

public class BuildUpstreamCause extends Cause.UpstreamCause {
private final String nodeId;

public BuildUpstreamCause(FlowNode node, Run<?, ?> invokingRun) {
super(invokingRun);
this.nodeId = node.getId();
}

public String getNodeId() {
return nodeId;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
BuildUpstreamCause that = (BuildUpstreamCause) o;
return Objects.equals(nodeId, that.nodeId);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), nodeId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import jenkins.branch.MultiBranchProjectFactory;
import jenkins.branch.MultiBranchProjectFactoryDescriptor;
import jenkins.branch.OrganizationFolder;
Expand All @@ -49,17 +52,21 @@
import jenkins.scm.impl.mock.MockSCMNavigator;
import jenkins.security.QueueItemAuthenticatorConfiguration;
import org.apache.commons.lang.StringUtils;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution;
import org.jenkinsci.plugins.workflow.cps.SnippetizerTester;
import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
Expand All @@ -80,6 +87,8 @@
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeThat;

public class BuildTriggerStepTest {
Expand Down Expand Up @@ -115,6 +124,57 @@ public class BuildTriggerStepTest {
j.assertLogContains("ds.result=FAILURE", j.buildAndAssertSuccess(us));
}

@Issue("JENKINS-60995")
@Test public void upstreamCause() throws Exception {
FreeStyleProject downstream = j.createFreeStyleProject("downstream");
WorkflowJob upstream = j.jenkins.createProject(WorkflowJob.class, "upstream");

upstream.setDefinition(new CpsFlowDefinition("build 'downstream'", true));
int numberOfUpstreamBuilds = 3;
for (int i = 0; i < numberOfUpstreamBuilds; i++) {
upstream.scheduleBuild2(0);
// Wait a bit before scheduling next one
Thread.sleep(100);
}
for (WorkflowRun upstreamRun : upstream.getBuilds()) {
j.waitForCompletion(upstreamRun);
j.assertBuildStatus(Result.SUCCESS, upstreamRun);
}
// Wait for all downstream builds to complete
j.waitUntilNoActivity();

List<BuildUpstreamCause> upstreamCauses = new ArrayList<>();
for (Run<FreeStyleProject, FreeStyleBuild> downstreamRun : downstream.getBuilds()) {
upstreamCauses.addAll(downstreamRun.getCauses().stream()
.filter(BuildUpstreamCause.class::isInstance)
.map(BuildUpstreamCause.class::cast)
.collect(Collectors.toList()));
}
assertThat("There should be as many upstream causes as upstream builds", upstreamCauses, hasSize(numberOfUpstreamBuilds));

Set<WorkflowRun> ups = new HashSet<>();
for (BuildUpstreamCause up : upstreamCauses) {
WorkflowRun upstreamRun = (WorkflowRun) up.getUpstreamRun();
ups.add(upstreamRun);
FlowExecution execution = upstreamRun.getExecution();
FlowNode buildTriggerNode = findFirstNodeWithDescriptor(execution, BuildTriggerStep.DescriptorImpl.class);
assertEquals("node id should be build trigger node", buildTriggerNode, execution.getNode(up.getNodeId()));
}
assertEquals("There should be as many upstream causes as referenced upstream builds", numberOfUpstreamBuilds, ups.size());
}

private static FlowNode findFirstNodeWithDescriptor(FlowExecution execution, Class<BuildTriggerStep.DescriptorImpl> cls) {
for (FlowNode node : new FlowGraphWalker(execution)) {
if (node instanceof StepAtomNode) {
StepAtomNode stepAtomNode = (StepAtomNode) node;
if (cls.isInstance(stepAtomNode.getDescriptor())) {
return stepAtomNode;
}
}
}
return null;
}

@Issue("JENKINS-38339")
@Test public void upstreamNodeAction() throws Exception {
FreeStyleProject downstream = j.createFreeStyleProject("downstream");
Expand All @@ -127,16 +187,15 @@ public class BuildTriggerStepTest {
FreeStyleBuild lastDownstreamRun = downstream.getLastBuild();

final FlowExecution execution = lastUpstreamRun.getExecution();
List<FlowNode> nodes = execution.getCurrentHeads();
assertEquals("node count", 1, nodes.size());
FlowNode headNode = nodes.get(0);
FlowNode buildTriggerNode = findFirstNodeWithDescriptor(execution, BuildTriggerStep.DescriptorImpl.class);
assertNotNull(buildTriggerNode);

List<BuildUpstreamNodeAction> actions = lastDownstreamRun.getActions(BuildUpstreamNodeAction.class);
assertEquals("action count", 1, actions.size());

BuildUpstreamNodeAction action = actions.get(0);
assertEquals("correct upstreamRunId", action.getUpstreamRunId(), lastUpstreamRun.getExternalizableId());
assertNotNull("valid upstreamNodeId", execution.getNode(action.getUpstreamNodeId()));
assertEquals("valid upstreamNodeId", buildTriggerNode, execution.getNode(action.getUpstreamNodeId()));
}

@SuppressWarnings("deprecation")
Expand Down

0 comments on commit 8cc9f8a

Please sign in to comment.