Skip to content

Commit

Permalink
fix(pipelinetemplate): Child stages of conditional stages should be p…
Browse files Browse the repository at this point in the history
…reserved

- Fixed an issue where children of a rendered conditional stage are dropped off the stages graph
  • Loading branch information
jeyrschabu committed Apr 19, 2017
1 parent 0bd8410 commit dfaf809
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 23 deletions.
Expand Up @@ -25,6 +25,7 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;

public class ConditionalStanzaTransform implements PipelineTemplateVisitor {

Expand All @@ -46,25 +47,18 @@ public void visitPipelineTemplate(PipelineTemplate pipelineTemplate) {
}

private <T extends Conditional> void trimConditionals(List<T> stages, PipelineTemplate template) {
if (stages == null) {
return;
}

for (T el : stages) {
if (el.getWhen() == null || el.getWhen().isEmpty()) {
continue;
}

RenderContext context = new DefaultRenderContext(templateConfiguration.getPipeline().getApplication(), template, trigger);
context.getVariables().putAll(templateConfiguration.getPipeline().getVariables());

for (String conditional : el.getWhen()) {
String rendered = renderer.render(conditional, context);
if (!Boolean.parseBoolean(rendered)) {
stages.remove(el);
return;
Optional.ofNullable(stages).ifPresent( allStages -> allStages
.stream()
.filter(stage -> stage.getWhen() != null && !stage.getWhen().isEmpty())
.forEach(stage -> {
RenderContext context = new DefaultRenderContext(templateConfiguration.getPipeline().getApplication(), template, trigger);
context.getVariables().putAll(templateConfiguration.getPipeline().getVariables());
for (String conditional : stage.getWhen()) {
String rendered = renderer.render(conditional, context);
if (!Boolean.parseBoolean(rendered)) {
stage.setRemove();
}
}
}
}
}));
}
}
Expand Up @@ -55,6 +55,7 @@ public ConfigStageInjectionTransform(TemplateConfiguration templateConfiguration
public void visitPipelineTemplate(PipelineTemplate pipelineTemplate) {
replaceStages(pipelineTemplate);
injectStages(pipelineTemplate);
trimConditionals(pipelineTemplate);
}

private void replaceStages(PipelineTemplate pipelineTemplate) {
Expand Down Expand Up @@ -85,6 +86,24 @@ private void injectStages(PipelineTemplate pipelineTemplate) {
injectStages(templateConfiguration.getStages(), pipelineTemplate.getStages());
}

private void trimConditionals(PipelineTemplate pipelineTemplate) {
// if stage is conditional, ensure children get linked to parents of conditional stage accordingly
pipelineTemplate.getStages()
.stream()
.filter(StageDefinition::getRemoved)
.forEach(conditionalStage -> pipelineTemplate.getStages()
.stream()
.filter(childStage -> childStage.getDependsOn().removeIf(conditionalStage.getId()::equals))
.forEach(childStage -> childStage.getDependsOn().addAll(conditionalStage.getDependsOn())));

pipelineTemplate.setStages(
pipelineTemplate.getStages()
.stream()
.filter(stage -> !stage.getRemoved())
.collect(Collectors.toList())
);
}

private enum Status {
VISITING,
VISITED
Expand Down
Expand Up @@ -18,6 +18,6 @@
import java.util.List;

public interface Conditional {

List<String> getWhen();
void setRemove();
}
Expand Up @@ -33,6 +33,7 @@ public class StageDefinition implements Identifiable, Conditional {
private String comments;
private List<String> when;
private InheritanceControl inheritanceControl;
private Boolean removed = false;

private List<String> requisiteStageRefIds = new ArrayList<>();

Expand Down Expand Up @@ -218,6 +219,16 @@ public List<String> getWhen() {
return when;
}

@Override
public void setRemove() {
this.removed = true;
}

public Boolean getRemoved() {
return removed;
}


public void setWhen(List<String> when) {
this.when = when;
}
Expand Down
Expand Up @@ -175,6 +175,24 @@ class PipelineTemplatePipelinePreprocessorSpec extends Specification {
false || ['wait']
}

@Unroll
def 'should preserve children stages of conditional stage'() {
when:
def result = subject.process(createTemplateRequest('conditional-stages-with-children-001.yml', [includeWait: includeWait]))

then:
result.stages*.name == expectedStageNames

and:
result.stages.find { it.name == 'childOfConditionalStage' }.requisiteStageRefIds == childStageRequisiteRefIds

where:
includeWait || childStageRequisiteRefIds || expectedStageNames
true || ['conditionalWait'] || ['wait', 'conditionalWait', 'childOfConditionalStage']
false || ['wait'] || ['wait', 'childOfConditionalStage']
}


Map<String, Object> createTemplateRequest(String templatePath, Map<String, Object> variables = [:], List<Map<String, Object>> stages = [], boolean plan = false) {
return [
type: 'templatedPipeline',
Expand Down
Expand Up @@ -62,8 +62,7 @@ class ConditionalStanzaTransformSpec extends Specification {

then:
noExceptionThrown()
template.stages.size() == 1
template.stages.find { it.id == 's2' } == null

!template.stages.find { it.id == 's1' }.removed
template.stages.find { it.id == 's2' }.removed
}
}
@@ -0,0 +1,25 @@
schema: "1"
id: simpleTemplate
variables:
- name: includeWait
type: boolean
stages:
- id: wait
type: wait
config:
waitTime: 5
- id: conditionalWait
type: wait
dependsOn:
- wait
config:
waitTime: 5
when:
# This could also just be '{{ includeWait }}', but examples.
- '{{ includeWait == true }}'
- id: childOfConditionalStage
type: conditionalWait
dependsOn:
- conditionalWait
config:
waitTime: 5

0 comments on commit dfaf809

Please sign in to comment.