Skip to content

Commit

Permalink
JENKINS-48820# Fix for stack overflow in flownode sse event generation
Browse files Browse the repository at this point in the history
  • Loading branch information
vivek committed Feb 20, 2018
1 parent 7375e01 commit e7e49af
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 54 deletions.
Expand Up @@ -5,6 +5,15 @@
import hudson.model.Run;
import io.jenkins.blueocean.rest.impl.pipeline.PipelineInputStepListener;
import io.jenkins.blueocean.rest.impl.pipeline.PipelineNodeUtil;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import org.jenkinsci.plugins.pubsub.Events;
import org.jenkinsci.plugins.pubsub.Message;
import org.jenkinsci.plugins.pubsub.MessageException;
Expand All @@ -15,29 +24,19 @@
import org.jenkinsci.plugins.workflow.actions.QueueItemAction;
import org.jenkinsci.plugins.workflow.cps.nodes.StepAtomNode;
import org.jenkinsci.plugins.workflow.cps.nodes.StepEndNode;
import org.jenkinsci.plugins.workflow.graph.StepNode;
import org.jenkinsci.plugins.workflow.cps.nodes.StepStartNode;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionListener;
import org.jenkinsci.plugins.workflow.flow.GraphListener;
import org.jenkinsci.plugins.workflow.graph.FlowEndNode;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.graph.StepNode;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.support.steps.ExecutorStep;
import org.jenkinsci.plugins.workflow.support.steps.input.InputAction;
import org.jenkinsci.plugins.workflow.support.steps.input.InputStep;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;


/**
* Listen for run events, filter and publish stage/branch events
Expand All @@ -58,77 +57,63 @@ public class PipelineEventListener implements GraphListener {
public void onNewHead(FlowNode flowNode) {
// test whether we have a stage node
if (PipelineNodeUtil.isStage(flowNode)) {
List<String> branch = getBranch(flowNode);
ArrayDeque<String> branch = getBranch(flowNode);
currentStageName.put(flowNode.getExecution(), flowNode.getDisplayName());
currentStageId.put(flowNode.getExecution(), flowNode.getId());
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_stage, flowNode, branch));
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_stage, flowNode, branch.iterator()));
} else if (flowNode instanceof StepStartNode) {
if (flowNode.getAction(BodyInvocationAction.class) != null) {
List<String> branch = getBranch(flowNode);
ArrayDeque<String> branch = getBranch(flowNode);
branch.add(flowNode.getId());
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_block_start, flowNode, branch));
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_block_start, flowNode, branch.iterator()));
} else if (flowNode.getPersistentAction(QueueItemAction.class) != null) {
// Make sure we fire an event for the start of node blocks.
List<String> branch = getBranch(flowNode);
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_step, flowNode, branch));
ArrayDeque<String> branch = getBranch(flowNode);
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_step, flowNode, branch.iterator()));
}
} else if (flowNode instanceof StepAtomNode) {
List<String> branch = getBranch(flowNode);
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_step, flowNode, branch));
ArrayDeque<String> branch = getBranch(flowNode);
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_step, flowNode, branch.iterator()));
} else if (flowNode instanceof StepEndNode) {
if (flowNode.getAction(BodyInvocationAction.class) != null) {
FlowNode startNode = ((StepEndNode) flowNode).getStartNode();
String startNodeId = startNode.getId();

List<String> branch = getBranch(startNode);
ArrayDeque<String> branch = getBranch(startNode);

branch.add(startNodeId);
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_block_end, flowNode, branch));
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_block_end, flowNode, branch.iterator()));
}
} else if (flowNode instanceof FlowEndNode) {
publishEvent(newMessage(PipelineEventChannel.Event.pipeline_end, flowNode.getExecution()));
}
}

private List<String> getBranch(FlowNode flowNode) {
List<String> branch = new ArrayList<>();
FlowNode parentBlock = getParentBlock(flowNode);

while (parentBlock != null) {
branch.add(0, parentBlock.getId());
parentBlock = getParentBlock(parentBlock);
}

return branch;
}

private FlowNode getParentBlock(FlowNode flowNode) {
List<FlowNode> parents = flowNode.getParents();
private ArrayDeque<String> getBranch(FlowNode flowNode) {
ArrayDeque<String> branch = new ArrayDeque<>();
String parentBlockId = flowNode.getEnclosingId();

for (FlowNode parent : parents) {
if (parent instanceof StepStartNode) {
if (parent.getAction(BodyInvocationAction.class) != null) {
return parent;
while (parentBlockId != null) {
branch.addFirst(parentBlockId);
try {
FlowNode parentBlock = flowNode.getExecution().getNode(parentBlockId);
if(parentBlock == null){
return branch;
}
parentBlockId = parentBlock.getEnclosingId();
} catch (IOException e) {
LOGGER.log(Level.WARNING, String.format("Failed to load enclosing FlowNode: %s of node: %s",
parentBlockId, flowNode), e);
return branch;
}
}

for (FlowNode parent : parents) {
if (parent instanceof StepEndNode) {
continue;
}
FlowNode grandparent = getParentBlock(parent);
if (grandparent != null) {
return grandparent;
}
}

return null;
return branch;
}

private String toPath(List<String> branch) {
private String toPath(Iterator<String> branch) {
StringBuilder builder = new StringBuilder();
for (String leaf : branch) {
while (branch.hasNext()) {
String leaf = branch.next();
if(builder.length() > 0) {
builder.append("/");
}
Expand Down Expand Up @@ -164,7 +149,7 @@ private static Message newMessage(PipelineEventChannel.Event event, FlowExecutio
return message;
}

private Message newMessage(PipelineEventChannel.Event event, FlowNode flowNode, List<String> branch) {
private Message newMessage(PipelineEventChannel.Event event, FlowNode flowNode, Iterator<String> branch) {
Message message = newMessage(event, flowNode.getExecution());

message.set(PipelineEventChannel.EventProps.pipeline_step_flownode_id, flowNode.getId());
Expand Down
Expand Up @@ -4,6 +4,7 @@
import hudson.model.Result;
import io.jenkins.blueocean.rest.model.BlueRun.BlueRunResult;
import io.jenkins.blueocean.rest.model.BlueRun.BlueRunState;
import java.util.Collections;
import org.jenkinsci.plugins.workflow.actions.ErrorAction;
import org.jenkinsci.plugins.workflow.graph.AtomNode;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
Expand Down Expand Up @@ -203,6 +204,9 @@ public void setBlockErrorAction(ErrorAction blockErrorAction) {
* Filters by class to mimic Item.getActions(class).
*/
public <T extends Action> Collection<T> getPipelineActions(Class<T> clazz) {
if(pipelineActions == null){
return Collections.emptyList();
}
ArrayList<T> filtered = new ArrayList<>();
for (Action a:pipelineActions) {
if (clazz.isInstance(a)) {
Expand Down

0 comments on commit e7e49af

Please sign in to comment.