[JENKINS-49779] Feature/sequentiel add more tests (#1779)
* [JENKINS-49779] Feature/sequentiel add more tests Signed-off-by: olivier lamy <olamy@apache.org>
This commit is contained in:
parent
64ee5d460e
commit
170bd6480f
|
@ -260,15 +260,19 @@ public class PipelineNodeGraphVisitor extends StandardChunkVisitor implements No
|
|||
}
|
||||
}else{
|
||||
if(parallelNestedStages) {
|
||||
String endId = parallelBranchEndNodes.peek().getId();
|
||||
Stack<FlowNodeWrapper> stack = stackPerEnd.get(endId);
|
||||
if(stack==null){
|
||||
stack=new Stack<>();
|
||||
stackPerEnd.put(endId, stack);
|
||||
if(parallelBranchEndNodes.isEmpty()){
|
||||
logger.debug("skip parsing stage {} but parallelBranchEndNodes is empty", stage);
|
||||
} else {
|
||||
String endId = parallelBranchEndNodes.peek().getId();
|
||||
Stack<FlowNodeWrapper> stack = stackPerEnd.get(endId);
|
||||
if(stack==null){
|
||||
stack=new Stack<>();
|
||||
stackPerEnd.put(endId, stack);
|
||||
}
|
||||
stack.add(stage);
|
||||
}
|
||||
stack.add(stage);
|
||||
}
|
||||
if(nextStage != null) {
|
||||
if(nextStage != null&&!parallelNestedStages) {
|
||||
nextStage.addParent(stage);
|
||||
stage.addEdge(nextStage);
|
||||
}
|
||||
|
@ -349,7 +353,7 @@ public class PipelineNodeGraphVisitor extends StandardChunkVisitor implements No
|
|||
}
|
||||
|
||||
branch.setPipelineActions(branchActions);
|
||||
// do we have nested sequential stages for this parallel branch?
|
||||
// do we have sequential stages for this parallel branch?
|
||||
Stack<FlowNodeWrapper> stack = stackPerEnd.get(endNode.getId());
|
||||
if(stack!=null&&!stack.isEmpty()){
|
||||
// yes so we can rebuild the graph here
|
||||
|
@ -373,11 +377,15 @@ public class PipelineNodeGraphVisitor extends StandardChunkVisitor implements No
|
|||
branch.addEdge(flowNodeWrapper);
|
||||
flowNodeWrapper.addParent(branch);
|
||||
nodes.add(flowNodeWrapper);
|
||||
stack.stream().forEach(nodeWrapper->{
|
||||
while(!stack.isEmpty()){
|
||||
FlowNodeWrapper nodeWrapper = stack.pop();
|
||||
nodes.peekLast().addEdge(nodeWrapper);
|
||||
nodeWrapper.addParent(nodes.peekLast());
|
||||
nodes.add( nodeWrapper );
|
||||
});
|
||||
if(stack.isEmpty()&&nextStage!=null){
|
||||
nodeWrapper.addEdge(nextStage);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(nextStage!=null) {
|
||||
branch.addEdge(nextStage);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package io.jenkins.blueocean.rest.impl.pipeline;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.model.Action;
|
||||
import hudson.model.Queue;
|
||||
import io.jenkins.blueocean.commons.JsonConverter;
|
||||
|
@ -34,13 +35,16 @@ import org.kohsuke.stapler.export.Exported;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implementation of {@link BluePipelineNode}.
|
||||
|
@ -93,6 +97,12 @@ public class PipelineNodeImpl extends BluePipelineNode {
|
|||
return status.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
|
||||
public @CheckForNull String getFirstParent() {
|
||||
return node.getFirstParent() == null ? null : node.getFirstParent().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getStartTime() {
|
||||
long nodeTime = node.getTiming().getStartTimeMillis();
|
||||
|
@ -174,9 +184,9 @@ public class PipelineNodeImpl extends BluePipelineNode {
|
|||
public boolean isRestartable() {
|
||||
RestartDeclarativePipelineAction restartDeclarativePipelineAction =
|
||||
this.run.getAction( RestartDeclarativePipelineAction.class );
|
||||
if ( restartDeclarativePipelineAction != null ) {
|
||||
if (restartDeclarativePipelineAction != null) {
|
||||
List<String> restartableStages = restartDeclarativePipelineAction.getRestartableStages();
|
||||
if ( restartableStages != null ) {
|
||||
if (restartableStages != null) {
|
||||
return restartableStages.contains(this.getDisplayName())
|
||||
&& this.getStateObj() == BlueRun.BlueRunState.FINISHED;
|
||||
}
|
||||
|
@ -254,13 +264,9 @@ public class PipelineNodeImpl extends BluePipelineNode {
|
|||
}
|
||||
|
||||
private List<Edge> buildEdges(List<FlowNodeWrapper> nodes) {
|
||||
List<Edge> edges = new ArrayList<>();
|
||||
if (!nodes.isEmpty()) {
|
||||
for (FlowNodeWrapper edge : nodes) {
|
||||
edges.add(new EdgeImpl(edge));
|
||||
}
|
||||
}
|
||||
return edges;
|
||||
return nodes.isEmpty()? Collections.emptyList():
|
||||
nodes.stream().map( nodeWrapper -> new EdgeImpl( nodeWrapper ) ).collect( Collectors.toList() );
|
||||
|
||||
}
|
||||
|
||||
FlowNodeWrapper getFlowNodeWrapper() {
|
||||
|
|
|
@ -47,6 +47,7 @@ import java.net.URL;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static io.jenkins.blueocean.rest.impl.pipeline.PipelineStepImpl.PARAMETERS_ELEMENT;
|
||||
|
@ -2397,6 +2398,106 @@ public class PipelineNodeTest extends PipelineBaseTest {
|
|||
assertEquals(7, nodes.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Issue("JENKINS-49050")
|
||||
public void sequentialParallelStages() throws Exception {
|
||||
WorkflowJob p = createWorkflowJobWithJenkinsfile( getClass(), "sequentialParallel.jenkinsfile");
|
||||
Slave s = j.createOnlineSlave();
|
||||
s.setNumExecutors(2);
|
||||
|
||||
// Run until completed
|
||||
WorkflowRun run = p.scheduleBuild2( 0).waitForStart();
|
||||
j.waitForCompletion( run );
|
||||
|
||||
PipelineNodeGraphVisitor pipelineNodeGraphVisitor = new PipelineNodeGraphVisitor( run );
|
||||
assertTrue( pipelineNodeGraphVisitor.isDeclarative() );
|
||||
|
||||
List<FlowNodeWrapper> wrappers = pipelineNodeGraphVisitor.getPipelineNodes();
|
||||
|
||||
assertEquals(9, wrappers.size());
|
||||
|
||||
Optional<FlowNodeWrapper> optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getDisplayName().equals( "first-sequential-stage" ) )
|
||||
.findFirst();
|
||||
|
||||
// we ensure "multiple-stages" is parent of "first-sequential-stage"
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
assertEquals( 1, optionalFlowNodeWrapper.get().edges.size() );
|
||||
assertEquals( "second-sequential-stage", optionalFlowNodeWrapper.get().edges.get( 0 ).getDisplayName() );
|
||||
|
||||
final String parentId = optionalFlowNodeWrapper.get().getFirstParent().getId();
|
||||
|
||||
optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getId().equals( parentId ) )
|
||||
.findFirst();
|
||||
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
assertEquals( "multiple-stages", optionalFlowNodeWrapper.get().getDisplayName() );
|
||||
assertEquals( 1, optionalFlowNodeWrapper.get().edges.size() );
|
||||
|
||||
optionalFlowNodeWrapper.get().edges.stream()
|
||||
.filter( nodeWrapper -> nodeWrapper.getDisplayName().equals( "first-sequential-stage" ) )
|
||||
.findFirst();
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
|
||||
|
||||
optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getDisplayName().equals( "other-single-stage" ) )
|
||||
.findFirst();
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
|
||||
final String otherParentId = optionalFlowNodeWrapper.get().getFirstParent().getId();
|
||||
|
||||
optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getId().equals( otherParentId ) )
|
||||
.findFirst();
|
||||
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
assertEquals( "parent", optionalFlowNodeWrapper.get().getDisplayName() );
|
||||
assertEquals( 3, optionalFlowNodeWrapper.get().edges.size() );
|
||||
|
||||
optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getDisplayName().equals( "second-sequential-stage" ) )
|
||||
.findFirst();
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
|
||||
assertEquals(1, optionalFlowNodeWrapper.get().edges.size() );
|
||||
assertEquals( "third-sequential-stage", optionalFlowNodeWrapper.get().edges.get( 0 ).getDisplayName() );
|
||||
|
||||
optionalFlowNodeWrapper =
|
||||
wrappers.stream().filter( nodeWrapper -> nodeWrapper.getDisplayName().equals( "third-sequential-stage" ) )
|
||||
.findFirst();
|
||||
assertTrue( optionalFlowNodeWrapper.isPresent() );
|
||||
|
||||
assertEquals(1, optionalFlowNodeWrapper.get().edges.size() );
|
||||
assertEquals( "second-solo", optionalFlowNodeWrapper.get().edges.get( 0 ).getDisplayName() );
|
||||
|
||||
List<Map> nodes = get("/organizations/jenkins/pipelines/" + p.getName() + "/runs/1/nodes/", List.class);
|
||||
assertEquals(9, nodes.size());
|
||||
|
||||
|
||||
Optional<Map> firstSeqStage = nodes.stream()
|
||||
.filter( map -> map.get( "displayName" )
|
||||
.equals( "first-sequential-stage" ) ).findFirst();
|
||||
|
||||
assertTrue( firstSeqStage.isPresent() );
|
||||
String firstParentId = (String) firstSeqStage.get().get( "firstParent" );
|
||||
|
||||
Optional<Map> parentStage = nodes.stream()
|
||||
.filter( map -> map.get( "id" )
|
||||
.equals( firstParentId ) ).findFirst();
|
||||
assertTrue( parentStage.isPresent() );
|
||||
assertEquals( "multiple-stages", parentStage.get().get( "displayName" ) );
|
||||
|
||||
// ensure no issue getting steps for each node
|
||||
for(Map<String,String> node:nodes){
|
||||
String id = node.get( "id" );
|
||||
List<Map> steps =
|
||||
get( "/organizations/jenkins/pipelines/" + p.getName() + "/runs/1/nodes/" + id +"/steps/", List.class );
|
||||
assertFalse( steps.get( 0 ).isEmpty() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void nestedStagesGroups() throws Exception {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('first-solo') {
|
||||
steps {
|
||||
sh 'echo \'dummy text first-solo\''
|
||||
}
|
||||
}
|
||||
stage('parent') {
|
||||
parallel {
|
||||
stage('single-stage') {
|
||||
steps {
|
||||
sh 'echo \'dummy text single-stage\''
|
||||
}
|
||||
}
|
||||
|
||||
stage('multiple-stages') {
|
||||
stages {
|
||||
stage('first-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'dummy text first-sequential-stage\''
|
||||
}
|
||||
}
|
||||
stage('second-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'dummy text second-sequential-stage\''
|
||||
}
|
||||
}
|
||||
stage('third-sequential-stage') {
|
||||
steps {
|
||||
sh 'echo \'dummy text third-sequential-stage\''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('other-single-stage') {
|
||||
steps {
|
||||
sh 'echo \'dummy text other-single-stage\''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('second-solo') {
|
||||
steps {
|
||||
sh 'echo \'dummy text second-solo\''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.kohsuke.stapler.export.Exported;
|
|||
import org.kohsuke.stapler.export.ExportedBean;
|
||||
import org.kohsuke.stapler.verb.POST;
|
||||
|
||||
import javax.annotation.CheckForNull;
|
||||
import java.util.List;
|
||||
|
||||
import static io.jenkins.blueocean.rest.model.KnownCapabilities.BLUE_PIPELINE_NODE;
|
||||
|
@ -79,6 +80,12 @@ public abstract class BluePipelineNode extends BluePipelineStep {
|
|||
@Exported
|
||||
public abstract boolean isRestartable();
|
||||
|
||||
/**
|
||||
* @return the id of the first immediate parent
|
||||
*/
|
||||
@Exported
|
||||
public @CheckForNull abstract String getFirstParent();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request To restart the content must be simple json body with a field <code>restart</code> will value <code>true</code>
|
||||
|
|
Loading…
Reference in New Issue