use of org.jenkinsci.plugins.workflow.pipelinegraphanalysis.GenericStatus in project blueocean-plugin by jenkinsci.
the class PipelineNodeGraphVisitor method parallelStart.
@Override
public void parallelStart(@Nonnull FlowNode parallelStartNode, @Nonnull FlowNode branchNode, @Nonnull ForkScanner scanner) {
if (isNodeVisitorDumpEnabled) {
dump(String.format("parallelStart=> id: %s, name: %s, function: %s", parallelStartNode.getId(), parallelStartNode.getDisplayName(), parallelStartNode.getDisplayFunctionName()));
dump(String.format("\tbranch=> id: %s, name: %s, function: %s", branchNode.getId(), branchNode.getDisplayName(), branchNode.getDisplayFunctionName()));
}
if (nestedbranches.size() != parallelBranchEndNodes.size()) {
logger.debug(String.format("nestedBranches size: %s not equal to parallelBranchEndNodes: %s", nestedbranches.size(), parallelBranchEndNodes.size()));
if (!parallelEnds.isEmpty()) {
parallelEnds.pop();
}
return;
}
if (!pendingBranchEndNodes.isEmpty()) {
logger.debug("Found parallelBranchEndNodes, but the corresponding branchStartNode yet");
if (!parallelEnds.isEmpty()) {
parallelEnds.pop();
}
return;
}
while (!nestedbranches.empty() && !parallelBranchEndNodes.empty()) {
FlowNode branchStartNode = nestedbranches.pop();
FlowNode endNode = parallelBranchEndNodes.pop();
if (isNodeVisitorDumpEnabled) {
dump(String.format("\tBranch with start node id: %s", branchStartNode.getId()));
}
TimingInfo times;
NodeRunStatus status;
if (endNode != null) {
// Branch has completed
times = StatusAndTiming.computeChunkTiming(run, chunk.getPauseTimeMillis(), branchStartNode, endNode, chunk.getNodeAfter());
if (endNode instanceof StepAtomNode) {
if (PipelineNodeUtil.isPausedForInputStep((StepAtomNode) endNode, inputAction)) {
status = new NodeRunStatus(BlueRun.BlueRunResult.UNKNOWN, BlueRun.BlueRunState.PAUSED);
} else {
status = new NodeRunStatus(endNode);
}
} else {
FlowNode parallelEnd = null;
if (!parallelEnds.isEmpty()) {
parallelEnd = parallelEnds.peek();
}
GenericStatus genericStatus = StatusAndTiming.computeChunkStatus2(run, parallelStartNode, branchStartNode, endNode, parallelEnd);
status = new NodeRunStatus(genericStatus);
}
} else {
// Branch still running / paused
long startTime = System.currentTimeMillis();
if (branchStartNode.getAction(TimingAction.class) != null) {
startTime = TimingAction.getStartTime(branchStartNode);
}
times = new TimingInfo(System.currentTimeMillis() - startTime, chunk.getPauseTimeMillis(), startTime);
status = new NodeRunStatus(BlueRun.BlueRunResult.UNKNOWN, BlueRun.BlueRunState.RUNNING);
}
// keep FB happy
assert times != null;
FlowNodeWrapper branch = new FlowNodeWrapper(branchStartNode, status, times, run);
// Collect any pending actions (required for most-recently-handled branch)
ArrayList<Action> branchActions = new ArrayList<>(drainPipelineActions());
// Add actions for this branch, which are collected when changing branches
if (pendingActionsForBranches.containsKey(branchStartNode)) {
branchActions.addAll(pendingActionsForBranches.get(branchStartNode));
pendingActionsForBranches.remove(branchStartNode);
}
if (isNodeVisitorDumpEnabled && branchActions.size() > 0) {
dump("\t\tAdding " + branchActions.size() + " actions to branch id: " + branch.getId());
}
// Check the stack for this branch, for declarative pipelines it should be non-empty even if only 1 stage
Stack<FlowNodeWrapper> stack = stackPerEnd.get(endNode.getId());
if (stack != null && !stack.isEmpty()) {
if (isNodeVisitorDumpEnabled) {
dump(String.format("\t\t\"Complex\" stages detected (%d)", stack.size()));
}
if (stack.size() == 1) {
FlowNodeWrapper firstNodeWrapper = stack.pop();
if (isNodeVisitorDumpEnabled) {
dump("\t\tSingle-stage branch");
}
// ...but first we need to poach any actions from the stage node we'll be discarding
branchActions.addAll(firstNodeWrapper.getPipelineActions());
if (nextStage != null) {
branch.addEdge(nextStage);
}
} else {
if (isDeclarative()) {
// Declarative parallel pipeline scenario
// We've got nested stages for sequential stage branches and/or branch labelling purposes
FlowNodeWrapper firstNodeWrapper = stack.pop();
// Usually ignore firstNodeWrapper, but if the first stage has a different name...
if (!StringUtils.equals(firstNodeWrapper.getDisplayName(), branch.getDisplayName())) {
// we record this node so the UI can show the label for the branch...
if (isNodeVisitorDumpEnabled) {
dump("\t\tNested labelling stage detected");
}
branch.addEdge(firstNodeWrapper);
firstNodeWrapper.addParent(branch);
nodes.add(firstNodeWrapper);
// Note that there's no edge from this labelling node to the rest of the branch stages
}
}
// Declarative and scripted parallel pipeline scenario
FlowNodeWrapper previousNode = branch;
while (!stack.isEmpty()) {
// Grab next, link to prev, add to result
FlowNodeWrapper currentStage = stack.pop();
previousNode.addEdge(currentStage);
currentStage.addParent(previousNode);
nodes.add(currentStage);
previousNode = currentStage;
}
if (nextStage != null) {
previousNode.addEdge(nextStage);
}
}
} else {
if (isNodeVisitorDumpEnabled) {
dump("\t\tSimple branch (classic pipeline)");
}
if (nextStage != null) {
branch.addEdge(nextStage);
}
}
branch.setPipelineActions(branchActions);
parallelBranches.push(branch);
}
FlowNodeWrapper[] sortedBranches = parallelBranches.toArray(new FlowNodeWrapper[0]);
Arrays.sort(sortedBranches, Comparator.comparing(FlowNodeWrapper::getDisplayName));
parallelBranches.clear();
for (FlowNodeWrapper sortedBranch : sortedBranches) {
parallelBranches.push(sortedBranch);
}
for (FlowNodeWrapper p : parallelBranches) {
nodes.push(p);
nodeMap.put(p.getId(), p);
}
// Removes parallelEnd node for next parallel block
if (!parallelEnds.isEmpty()) {
parallelEnds.pop();
}
}
Aggregations