[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:
Olivier Lamy 2018-08-11 13:19:26 +10:00 committed by GitHub
parent 5e1fed1828
commit 628d3a9762
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 135 additions and 7 deletions

View File

@ -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);

View File

@ -125,7 +125,7 @@ public class FlowNodeWrapper {
}
public void addParents(Collection<FlowNodeWrapper> parents){
parents.addAll(parents);
this.parents.addAll(parents);
}
public @CheckForNull FlowNodeWrapper getFirstParent(){

View File

@ -64,4 +64,12 @@ public class PipelineNodeContainerImpl extends BluePipelineNodeContainer {
public Link getLink() {
return self;
}
/**
* for test purpose
* @return
*/
protected List<BluePipelineNode> getNodes(){
return nodes;
}
}

View File

@ -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<>();

View File

@ -273,4 +273,8 @@ public class PipelineNodeImpl extends BluePipelineNode {
return node;
}
@Override
public String toString() {
return "PipelineNodeImpl{" + "node=" + node + ", edges=" + edges + ", status=" + status + '}';
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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'
}
}
}
}

View File

@ -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