[JENKINS-49779] sequential parallel first parent null (#1787)
* [JENKINS-49779] fix null firstParentId null where it should not be Signed-off-by: olivier lamy <olamy@apache.org> * add a null check * trying to reproduce the non existing edge issue but cannot Signed-off-by: olivier lamy <olamy@apache.org> * checkstyle fix :) Signed-off-by: olivier lamy <olamy@apache.org>
This commit is contained in:
parent
5e1fed1828
commit
628d3a9762
|
@ -96,7 +96,11 @@ function buildSequentialStages(originalNodes, convertedNodes, sequentialNodeKey,
|
|||
|
||||
currentNode.isSequential = true;
|
||||
if (nextSequentialNodeId) {
|
||||
if (originalNodes[sequentialNodeKey].edges.length && originalNodes[nextSequentialNodeId].firstParent == currentNode.id) {
|
||||
if (
|
||||
originalNodes[sequentialNodeKey].edges.length &&
|
||||
originalNodes[nextSequentialNodeId] &&
|
||||
originalNodes[nextSequentialNodeId].firstParent == currentNode.id
|
||||
) {
|
||||
currentNode.nextSibling = convertedNodes[nextSequentialNodeId];
|
||||
|
||||
buildSequentialStages(originalNodes, convertedNodes, currentNode.nextSibling.id, currentNode.nextSibling);
|
||||
|
|
|
@ -125,7 +125,7 @@ public class FlowNodeWrapper {
|
|||
}
|
||||
|
||||
public void addParents(Collection<FlowNodeWrapper> parents){
|
||||
parents.addAll(parents);
|
||||
this.parents.addAll(parents);
|
||||
}
|
||||
|
||||
public @CheckForNull FlowNodeWrapper getFirstParent(){
|
||||
|
|
|
@ -64,4 +64,12 @@ public class PipelineNodeContainerImpl extends BluePipelineNodeContainer {
|
|||
public Link getLink() {
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* for test purpose
|
||||
* @return
|
||||
*/
|
||||
protected List<BluePipelineNode> getNodes(){
|
||||
return nodes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ public class PipelineNodeGraphVisitor extends StandardChunkVisitor implements No
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PipelineNodeGraphVisitor.class);
|
||||
|
||||
private static final boolean isNodeVisitorDumpEnabled = Boolean.getBoolean("NODE-DUMP-ENABLED");
|
||||
private static final boolean isNodeVisitorDumpEnabled = Boolean.getBoolean("NODE-DUMP-ENABLED")
|
||||
&& logger.isDebugEnabled();
|
||||
|
||||
private final Stack<FlowNode> nestedStages = new Stack<>();
|
||||
private final Stack<FlowNode> nestedbranches = new Stack<>();
|
||||
|
|
|
@ -273,4 +273,8 @@ public class PipelineNodeImpl extends BluePipelineNode {
|
|||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PipelineNodeImpl{" + "node=" + node + ", edges=" + edges + ", status=" + status + '}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,10 +96,10 @@ public abstract class PipelineBaseTest{
|
|||
value = "{}";
|
||||
}
|
||||
T r = JsonConverter.om.readValue(value, valueType);
|
||||
LOGGER.info("Response:\n"+JsonConverter.om.writeValueAsString(r));
|
||||
LOGGER.debug("Response:\n"+JsonConverter.om.writeValueAsString(r));
|
||||
return r;
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Failed to parse JSON: "+value+". "+e.getMessage());
|
||||
LOGGER.error("Failed to parse JSON: "+value+". "+e.getMessage());
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ public abstract class PipelineBaseTest{
|
|||
public String writeValue(Object value) {
|
||||
try {
|
||||
String str = JsonConverter.om.writeValueAsString(value);
|
||||
LOGGER.info("Request:\n"+str);
|
||||
LOGGER.debug("Request:\n"+str);
|
||||
return str;
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
|
|
@ -21,6 +21,7 @@ import jenkins.plugins.git.GitSCMSource;
|
|||
import jenkins.plugins.git.GitSampleRepoRule;
|
||||
import jenkins.scm.api.SCMSource;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
|
@ -44,11 +45,15 @@ import org.jvnet.hudson.test.Issue;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.jenkins.blueocean.rest.impl.pipeline.PipelineStepImpl.PARAMETERS_ELEMENT;
|
||||
import static org.hamcrest.CoreMatchers.hasItem;
|
||||
|
@ -2398,6 +2403,55 @@ public class PipelineNodeTest extends PipelineBaseTest {
|
|||
assertEquals(7, nodes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sequentialParallelStagesLongRun() throws Exception {
|
||||
WorkflowJob p = createWorkflowJobWithJenkinsfile( getClass(), "sequential_parallel_stages_long_run_time.jenkinsfile" );
|
||||
Slave s = j.createOnlineSlave();
|
||||
s.setNumExecutors(3);
|
||||
WorkflowRun run = p.scheduleBuild2( 0).waitForStart();
|
||||
j.waitForCompletion( run );
|
||||
|
||||
List<String> watchedStages = Arrays.asList("first-sequential-stage", "second-sequential-stage", "third-sequential-stage");
|
||||
|
||||
run = p.scheduleBuild2( 0).waitForStart();
|
||||
while(run.isBuilding()){
|
||||
PipelineNodeContainerImpl pipelineNodeContainer = new PipelineNodeContainerImpl(run, new Link( "foo" ) );
|
||||
|
||||
List<BluePipelineNode> nodes = pipelineNodeContainer.getNodes();
|
||||
for (BluePipelineNode node : nodes){
|
||||
if( StringUtils.isEmpty(node.getFirstParent())
|
||||
&& watchedStages.contains(node.getDisplayName())){
|
||||
LOGGER.error( "node {} has getFirstParent null", node);
|
||||
fail( "node with getFirstParent null:" + node );
|
||||
}
|
||||
// we try to find edges with id for a non existing node in the list
|
||||
List<BluePipelineNode.Edge> emptyEdges =
|
||||
node.getEdges().stream().filter( edge ->
|
||||
!nodes.stream()
|
||||
.filter( bluePipelineNode -> bluePipelineNode.getId().equals( edge.getId() ) )
|
||||
.findFirst().isPresent()
|
||||
|
||||
).collect( Collectors.toList() );
|
||||
if(!emptyEdges.isEmpty()) {
|
||||
// TODO remove this weird if but it's only to have breakpoint in IDE
|
||||
assertTrue( "edges with unknown nodes" + nodes, !emptyEdges.isEmpty() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOGGER.debug( "nodes size {}", nodes.size() );
|
||||
if(nodes.size()!=9){
|
||||
LOGGER.info( "nodes != 9 {}", nodes);
|
||||
fail( "nodes != 9:"+ nodes);
|
||||
}
|
||||
|
||||
Thread.sleep( 100 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
@Issue("JENKINS-49050")
|
||||
public void sequentialParallelStages() throws Exception {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('first-solo') {
|
||||
steps {
|
||||
sh 'echo \'first-solo\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
stage('parent') {
|
||||
parallel {
|
||||
stage('single-stage') {
|
||||
steps {
|
||||
sh 'echo \'single-stage\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
|
||||
stage('multiple-stages') {
|
||||
stages {
|
||||
stage('first-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'first-sequential-stage\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
stage('second-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'second-sequential-stage\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
stage('third-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'third-sequential-stage\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('other-single-stage') {
|
||||
steps {
|
||||
sh 'echo \'other-single-stage\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('second-solo') {
|
||||
steps {
|
||||
sh 'echo \'second-solo\''
|
||||
sh 'ping -c 5 localhost'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,4 +5,4 @@ java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
|
|||
java.util.logging.ConsoleHandler.level = ALL
|
||||
|
||||
#org.apache.http.wire.level = FINE
|
||||
io.jenkins.blueocean.rest.impl.pipeline.level = FINE
|
||||
#io.jenkins.blueocean.rest.impl.pipeline.level = FINE
|
||||
|
|
Loading…
Reference in New Issue