diff --git a/orca-bakery/src/main/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStage.groovy b/orca-bakery/src/main/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStage.groovy index db01c68093..a66848a390 100644 --- a/orca-bakery/src/main/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStage.groovy +++ b/orca-bakery/src/main/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStage.groovy @@ -16,6 +16,7 @@ package com.netflix.spinnaker.orca.bakery.pipeline +import javax.annotation.Nonnull import com.netflix.spinnaker.orca.ExecutionStatus import com.netflix.spinnaker.orca.RestartableStage import com.netflix.spinnaker.orca.Task @@ -23,7 +24,7 @@ import com.netflix.spinnaker.orca.TaskResult import com.netflix.spinnaker.orca.bakery.tasks.CompletedBakeTask import com.netflix.spinnaker.orca.bakery.tasks.CreateBakeTask import com.netflix.spinnaker.orca.bakery.tasks.MonitorBakeTask -import com.netflix.spinnaker.orca.pipeline.BranchingStageDefinitionBuilder +import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.TaskNode import com.netflix.spinnaker.orca.pipeline.model.Execution import com.netflix.spinnaker.orca.pipeline.model.Stage @@ -31,31 +32,33 @@ import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.stereotype.Component +import static com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner.STAGE_BEFORE @Slf4j @Component @CompileStatic -class BakeStage implements BranchingStageDefinitionBuilder, RestartableStage { +class BakeStage implements StageDefinitionBuilder, RestartableStage { public static final String PIPELINE_CONFIG_TYPE = "bake" @Override > void taskGraph(Stage stage, TaskNode.Builder builder) { builder - .withTask("createBake", CreateBakeTask) - .withTask("monitorBake", MonitorBakeTask) - .withTask("completedBake", CompletedBakeTask) + .withTask("completeParallel", CompleteParallelBakeTask) } @Override - void postBranchGraph(Stage stage, TaskNode.Builder builder) { - builder - .withTask("completeParallel", CompleteParallelBakeTask) + @Nonnull + > List> parallelStages( + @Nonnull Stage stage + ) { + parallelContexts(stage).collect { context -> + newStage(stage.execution, "${type}.parallel", "Bake in ${context.region}", context, stage, STAGE_BEFORE) + } } - @Override @CompileDynamic - public > Collection> parallelContexts(Stage stage) { + > Collection> parallelContexts(Stage stage) { Set deployRegions = stage.context.region ? [stage.context.region] as Set : [] deployRegions.addAll(stage.context.regions as Set ?: []) @@ -100,9 +103,21 @@ class BakeStage implements BranchingStageDefinitionBuilder, RestartableStage { } } - @Override - String parallelStageName(Stage stage, boolean hasParallelFlows) { - return hasParallelFlows ? "Multi-region Bake" : stage.name + @Component + @CompileStatic + static class Parallel implements StageDefinitionBuilder { + @Override + > void taskGraph(Stage stage, TaskNode.Builder builder) { + builder + .withTask("createBake", CreateBakeTask) + .withTask("monitorBake", MonitorBakeTask) + .withTask("completedBake", CompletedBakeTask) + } + + @Override + String getType() { + return "${PIPELINE_CONFIG_TYPE}.parallel" + } } @Component diff --git a/orca-bakery/src/test/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStageSpec.groovy b/orca-bakery/src/test/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStageSpec.groovy index ac6f1bafa3..bbeebe1f2e 100644 --- a/orca-bakery/src/test/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStageSpec.groovy +++ b/orca-bakery/src/test/groovy/com/netflix/spinnaker/orca/bakery/pipeline/BakeStageSpec.groovy @@ -120,23 +120,6 @@ class BakeStageSpec extends Specification { ] } - @Unroll - def "should return a different stage name when parallel flows are present"() { - given: - def stage = stage { - type = "type" - name = stageName - } - - expect: - new BakeStage().parallelStageName(stage, hasParallelFlows) == expectedStageName - - where: - stageName | hasParallelFlows || expectedStageName - "Default" | false || "Default" - "Default" | true || "Multi-region Bake" - } - private static List deployAz(String cloudProvider, String prefix, String... regions) { if (prefix == "clusters") { diff --git a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStage.groovy b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStage.groovy index 8d4c24fa5a..9494274979 100644 --- a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStage.groovy +++ b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStage.groovy @@ -16,12 +16,13 @@ package com.netflix.spinnaker.orca.kato.pipeline +import javax.annotation.Nonnull import com.netflix.spinnaker.orca.ExecutionStatus import com.netflix.spinnaker.orca.Task import com.netflix.spinnaker.orca.TaskResult import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.CloneServerGroupStage import com.netflix.spinnaker.orca.clouddriver.pipeline.servergroup.CreateServerGroupStage -import com.netflix.spinnaker.orca.pipeline.BranchingStageDefinitionBuilder +import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.TaskNode import com.netflix.spinnaker.orca.pipeline.model.Execution import com.netflix.spinnaker.orca.pipeline.model.Pipeline @@ -30,11 +31,12 @@ import groovy.transform.CompileDynamic import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.stereotype.Component +import static com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner.STAGE_BEFORE @Component @Slf4j @CompileStatic -class ParallelDeployStage implements BranchingStageDefinitionBuilder { +class ParallelDeployStage implements StageDefinitionBuilder { @Deprecated public static final String PIPELINE_CONFIG_TYPE = "deploy" @@ -46,16 +48,15 @@ class ParallelDeployStage implements BranchingStageDefinitionBuilder { @Override > void taskGraph(Stage stage, TaskNode.Builder builder) { - } - - @Override - void postBranchGraph(Stage stage, TaskNode.Builder builder) { builder.withTask("completeParallelDeploy", CompleteParallelDeployTask) } - @Override - String getChildStageType(Stage childStage) { - return isClone(childStage) ? CloneServerGroupStage.PIPELINE_CONFIG_TYPE : PIPELINE_CONFIG_TYPE + @Nonnull > List> parallelStages( + @Nonnull Stage stage) { + parallelContexts(stage).collect { context -> + def type = isClone(stage) ? CloneServerGroupStage.PIPELINE_CONFIG_TYPE : CreateServerGroupStage.PIPELINE_CONFIG_TYPE + newStage(stage.execution, type, context.name as String, context, stage, STAGE_BEFORE) + } } @CompileDynamic @@ -77,9 +78,8 @@ class ParallelDeployStage implements BranchingStageDefinitionBuilder { ] } - @Override @CompileDynamic - > Collection> parallelContexts(Stage stage) { + protected > Collection> parallelContexts(Stage stage) { if (stage.execution instanceof Pipeline) { Map trigger = ((Pipeline) stage.execution).trigger if (trigger.parameters?.strategy == true) { @@ -145,11 +145,6 @@ class ParallelDeployStage implements BranchingStageDefinitionBuilder { } } - @Override - String parallelStageName(Stage stage, boolean hasParallelFlows) { - return isClone(stage) ? "Clone" : stage.name - } - @CompileDynamic private > boolean isClone(Stage stage) { if (stage.execution instanceof Pipeline) { @@ -166,7 +161,7 @@ class ParallelDeployStage implements BranchingStageDefinitionBuilder { @Component @Slf4j @CompileStatic - public static class CompleteParallelDeployTask implements Task { + static class CompleteParallelDeployTask implements Task { TaskResult execute(Stage stage) { log.info("Completed Parallel Deploy") new TaskResult(ExecutionStatus.SUCCEEDED, [:], [:]) diff --git a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStageSpec.groovy b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStageSpec.groovy index ac4e9212b7..101c382840 100644 --- a/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStageSpec.groovy +++ b/orca-clouddriver/src/test/groovy/com/netflix/spinnaker/orca/kato/pipeline/ParallelDeployStageSpec.groovy @@ -45,20 +45,6 @@ class ParallelDeployStageSpec extends Specification { [account: "prod", restrictedExecutionWindow: [:], cluster: [availabilityZones: ["europe-west1-b": []], cloudProvider: "gce"], type: "createServerGroup", name: "Deploy in europe-west1-b"]] } - @Unroll - def "should return stage name regardless of whether parallel flows are present"() { - given: - def stage = new Stage<>(new Pipeline("orca"), "type", stageName, [:]) - - expect: - new ParallelDeployStage().parallelStageName(stage, hasParallelFlows) == expectedStageName - - where: - stageName | hasParallelFlows || expectedStageName - "Default" | false || "Default" - "Default" | true || "Default" - } - Map deployStageContext(String account, String cloudProvider, String... availabilityZones) { def context = ["account": account, restrictedExecutionWindow: [:]] if (availabilityZones.size() == 1) { diff --git a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/BranchingStageDefinitionBuilder.java b/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/BranchingStageDefinitionBuilder.java deleted file mode 100644 index 9988200b4f..0000000000 --- a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/BranchingStageDefinitionBuilder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2016 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License") - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.netflix.spinnaker.orca.pipeline; - -import java.util.Collection; -import java.util.Map; -import com.netflix.spinnaker.orca.pipeline.model.Execution; -import com.netflix.spinnaker.orca.pipeline.model.Stage; -import static com.netflix.spinnaker.orca.pipeline.TaskNode.Builder; -import static com.netflix.spinnaker.orca.pipeline.TaskNode.GraphType.HEAD; -import static com.netflix.spinnaker.orca.pipeline.TaskNode.GraphType.TAIL; - -/** - * Implement for stages that will create parallel branches to perform the same - * tasks for multiple contexts. For example, a multi-region bake or deploy. - */ -public interface BranchingStageDefinitionBuilder extends StageDefinitionBuilder { - - /** - * Produce the different contexts for each parallel branch. - */ - > Collection> parallelContexts(Stage stage); - - default TaskNode.TaskGraph buildPreGraph(Stage stage) { - TaskNode.Builder graphBuilder = Builder(HEAD); - preBranchGraph(stage, graphBuilder); - return graphBuilder.build(); - } - - /** - * Define any tasks that should run _before_ the parallel split. - */ - default void preBranchGraph(Stage stage, TaskNode.Builder builder) { - } - - default TaskNode.TaskGraph buildPostGraph(Stage stage) { - Builder graphBuilder = Builder(TAIL); - postBranchGraph(stage, graphBuilder); - return graphBuilder.build(); - } - - /** - * Define any tasks that should run _after_ the parallel split. - */ - default void postBranchGraph(Stage stage, TaskNode.Builder builder) { - } - - /** - * Override this to rename the stage if it has parallel flows. - * This affects the base stage not the individual parallel synthetic stages. - */ - default String parallelStageName(Stage stage, boolean hasParallelFlows) { - return stage.getName(); - } - - /** - * Determines the type of child stage. - */ - default String getChildStageType(Stage childStage) { - return childStage.getType(); - } -} - diff --git a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/CheckPreconditionsStage.groovy b/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/CheckPreconditionsStage.groovy index b24cdd654d..a8d0d1ccc4 100644 --- a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/CheckPreconditionsStage.groovy +++ b/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/CheckPreconditionsStage.groovy @@ -16,42 +16,30 @@ package com.netflix.spinnaker.orca.pipeline +import javax.annotation.Nonnull import com.netflix.spinnaker.orca.Task import com.netflix.spinnaker.orca.pipeline.model.Execution import com.netflix.spinnaker.orca.pipeline.model.Stage import com.netflix.spinnaker.orca.pipeline.tasks.PreconditionTask +import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component +import static com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner.STAGE_BEFORE @Component -class CheckPreconditionsStage implements BranchingStageDefinitionBuilder { +@CompileStatic +class CheckPreconditionsStage implements StageDefinitionBuilder { static final String PIPELINE_CONFIG_TYPE = "checkPreconditions" - private final List preconditionTasks - - @Autowired - CheckPreconditionsStage(List preconditionTasks) { - this.preconditionTasks = preconditionTasks - } - - @Override - def > void taskGraph(Stage stage, TaskNode.Builder builder) { - String preconditionType = stage.context.preconditionType - if (!preconditionType) { - throw new IllegalStateException("no preconditionType specified for stage $stage.id") - } - Task preconditionTask = preconditionTasks.find { - it.preconditionType == preconditionType + @Nonnull > List> parallelStages( + @Nonnull Stage stage) { + parallelContexts(stage).collect { context -> + newStage(stage.execution, "${type}.parallel", "Check precondition (${context.preconditionType})", context, stage, STAGE_BEFORE) } - if (!preconditionTask) { - throw new IllegalStateException("no Precondition implementation for type $preconditionType") - } - builder.withTask("checkPrecondition", preconditionTask.getClass() as Class) } - @Override - def > Collection> parallelContexts(Stage stage) { + private > Collection> parallelContexts(Stage stage) { stage.resolveStrategyParams() def baseContext = new HashMap(stage.context) List preconditions = baseContext.remove('preconditions') as List @@ -67,8 +55,37 @@ class CheckPreconditionsStage implements BranchingStageDefinitionBuilder { context['context'][it] = context['context'][it] ?: baseContext[it] } - context.name = context.name ?: "Check precondition (${context.preconditionType})".toString() return context } } + + @Component + static class Parallel implements StageDefinitionBuilder { + private final List preconditionTasks + + @Autowired + Parallel(List preconditionTasks) { + this.preconditionTasks = preconditionTasks + } + + @Override + def > void taskGraph(Stage stage, TaskNode.Builder builder) { + String preconditionType = stage.context.preconditionType + if (!preconditionType) { + throw new IllegalStateException("no preconditionType specified for stage $stage.id") + } + Task preconditionTask = preconditionTasks.find { + it.preconditionType == preconditionType + } + if (!preconditionTask) { + throw new IllegalStateException("no Precondition implementation for type $preconditionType") + } + builder.withTask("checkPrecondition", preconditionTask.getClass() as Class) + } + + @Override + String getType() { + return "${PIPELINE_CONFIG_TYPE}.parallel" + } + } } diff --git a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/StageDefinitionBuilder.java b/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/StageDefinitionBuilder.java index 0effba8710..3277acbc5d 100644 --- a/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/StageDefinitionBuilder.java +++ b/orca-core/src/main/groovy/com/netflix/spinnaker/orca/pipeline/StageDefinitionBuilder.java @@ -20,6 +20,7 @@ import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import com.netflix.spinnaker.orca.pipeline.TaskNode.TaskGraph; import com.netflix.spinnaker.orca.pipeline.model.Execution; import com.netflix.spinnaker.orca.pipeline.model.Stage; import com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner; @@ -29,7 +30,7 @@ public interface StageDefinitionBuilder { - default @Nonnull TaskNode.TaskGraph buildTaskGraph(@Nonnull Stage stage) { + default @Nonnull TaskGraph buildTaskGraph(@Nonnull Stage stage) { Builder graphBuilder = Builder(FULL); taskGraph(stage, graphBuilder); return graphBuilder.build(); @@ -44,6 +45,11 @@ default > void taskGraph( return emptyList(); } + default @Nonnull > List> parallelStages( + @Nonnull Stage stage) { + return emptyList(); + } + /** * @return the stage type this builder handles. */ diff --git a/orca-mine/src/main/groovy/com/netflix/spinnaker/orca/mine/pipeline/DeployCanaryStage.groovy b/orca-mine/src/main/groovy/com/netflix/spinnaker/orca/mine/pipeline/DeployCanaryStage.groovy index 0ff5e65efc..5c360bd043 100644 --- a/orca-mine/src/main/groovy/com/netflix/spinnaker/orca/mine/pipeline/DeployCanaryStage.groovy +++ b/orca-mine/src/main/groovy/com/netflix/spinnaker/orca/mine/pipeline/DeployCanaryStage.groovy @@ -65,13 +65,13 @@ class DeployCanaryStage extends ParallelDeployStage implements CloudProviderAwar } @Override - void postBranchGraph(Stage stage, TaskNode.Builder builder) { + > void taskGraph(Stage stage, TaskNode.Builder builder) { builder.withTask("completeDeployCanary", CompleteDeployCanaryTask) } @Override @CompileDynamic - > Collection> parallelContexts(Stage stage) { + protected > Collection> parallelContexts(Stage stage) { List baselineAmis = findBaselineAmis(stage) Map defaultStageContext = stage.context List canaryDeployments = defaultStageContext.clusterPairs diff --git a/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/StageDefinitionBuilders.kt b/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/StageDefinitionBuilders.kt index bc05a6bd85..dc4a5d1f54 100644 --- a/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/StageDefinitionBuilders.kt +++ b/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/StageDefinitionBuilders.kt @@ -16,7 +16,6 @@ package com.netflix.spinnaker.orca.q -import com.netflix.spinnaker.orca.pipeline.BranchingStageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.RestrictExecutionDuringTimeWindow import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder.newStage @@ -31,13 +30,7 @@ import com.netflix.spinnaker.orca.pipeline.model.SyntheticStageOwner.STAGE_BEFOR * Build and append the tasks for [stage]. */ fun StageDefinitionBuilder.buildTasks(stage: Stage<*>) { - val taskGraph = - if (this is BranchingStageDefinitionBuilder && (stage.getParentStageId() == null || stage.parent().getType() != stage.getType())) { - buildPostGraph(stage) - } else { - buildTaskGraph(stage) - } - taskGraph + buildTaskGraph(stage) .listIterator() .forEachWithMetadata { processTaskNode(stage, it) } } @@ -90,10 +83,10 @@ fun StageDefinitionBuilder.buildSyntheticStages( } @Suppress("UNCHECKED_CAST") -private fun BranchingStageDefinitionBuilder.parallelContexts(stage: Stage<*>) = +private fun StageDefinitionBuilder.parallelStages(stage: Stage<*>) = when (stage.getExecution()) { - is Pipeline -> parallelContexts(stage as Stage) - is Orchestration -> parallelContexts(stage as Stage) + is Pipeline -> parallelStages(stage as Stage) + is Orchestration -> parallelStages(stage as Stage) else -> throw IllegalStateException() } @@ -148,30 +141,15 @@ private fun SyntheticStages.buildAfterStages(stage: Stage>, cal } private fun StageDefinitionBuilder.buildParallelStages(stage: Stage>, executionWindow: Stage>?, callback: (Stage<*>) -> Unit) { - if (this is BranchingStageDefinitionBuilder && (stage.getParentStageId() == null || stage.parent().getType() != stage.getType())) { - val parallelContexts = parallelContexts(stage) - parallelContexts - .map { context -> - val execution = stage.getExecution() - val stageType = context.getOrDefault("type", stage.getType()).toString() - val stageName = context.getOrDefault("name", stage.getName()).toString() - @Suppress("UNCHECKED_CAST") - when (execution) { - is Pipeline -> newStage(execution, stageType, stageName, context.filterKeys { it != "restrictExecutionDuringTimeWindow" }, stage as Stage, STAGE_BEFORE) - is Orchestration -> newStage(execution, stageType, stageName, context.filterKeys { it != "restrictExecutionDuringTimeWindow" }, stage as Stage, STAGE_BEFORE) - else -> throw IllegalStateException() - } + parallelStages(stage) + .forEachIndexed { i, it -> + it.setRefId("${stage.getRefId()}=${i + 1}") + it.setRequisiteStageRefIds(if (executionWindow == null) emptySet() else setOf(executionWindow.getRefId())) + stage.getExecution().apply { + injectStage(getStages().indexOf(stage), it) + callback.invoke(it) } - .forEachIndexed { i, it -> - it.setRefId("${stage.getRefId()}=${i + 1}") - it.setRequisiteStageRefIds(if (executionWindow == null) emptySet() else setOf(executionWindow.getRefId())) - stage.getExecution().apply { - injectStage(getStages().indexOf(stage), it) - callback.invoke(it) - } - } - stage.setName(parallelStageName(stage, parallelContexts.size > 1)) - } + } } private fun Stage>.buildExecutionWindow(): Stage<*>? { diff --git a/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/Stages.kt b/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/Stages.kt index bcc83fcba5..616ccacf9e 100644 --- a/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/Stages.kt +++ b/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/Stages.kt @@ -16,7 +16,6 @@ package com.netflix.spinnaker.orca.q -import com.netflix.spinnaker.orca.pipeline.BranchingStageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder import com.netflix.spinnaker.orca.pipeline.StageDefinitionBuilder.newStage import com.netflix.spinnaker.orca.pipeline.TaskNode.Builder @@ -104,26 +103,15 @@ val stageWithNestedSynthetics = object : StageDefinitionBuilder { ) } -val stageWithParallelBranches = object : BranchingStageDefinitionBuilder { - override fun > parallelContexts(stage: Stage): Collection> = +val stageWithParallelBranches = object : StageDefinitionBuilder { + override fun > parallelStages(stage: Stage) = listOf( - mapOf("region" to "us-east-1", "name" to "run in us-east-1"), - mapOf("region" to "us-west-2", "name" to "run in us-west-2"), - mapOf("region" to "eu-west-1", "name" to "run in eu-west-1") + newStage(stage.execution, "singleTaskStage", "run in us-east-1", mapOf("region" to "us-east-1"), stage, STAGE_BEFORE), + newStage(stage.execution, "singleTaskStage", "run in us-west-2", mapOf("region" to "us-west-2"), stage, STAGE_BEFORE), + newStage(stage.execution, "singleTaskStage", "run in eu-west-1", mapOf("region" to "eu-west-1"), stage, STAGE_BEFORE) ) - override fun parallelStageName(stage: Stage<*>, hasParallelFlows: Boolean) = - if (hasParallelFlows) "is parallel" else "is not parallel" - - override fun preBranchGraph(stage: Stage<*>, builder: Builder) { - builder.withTask("pre-branch", DummyTask::class.java) - } - override fun > taskGraph(stage: Stage, builder: Builder) { - builder.withTask("in-branch", DummyTask::class.java) - } - - override fun postBranchGraph(stage: Stage<*>, builder: Builder) { builder.withTask("post-branch", DummyTask::class.java) } } diff --git a/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/handler/StartStageHandlerTest.kt b/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/handler/StartStageHandlerTest.kt index 3254db0d8e..180cbfda4d 100644 --- a/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/handler/StartStageHandlerTest.kt +++ b/orca-queue/src/test/kotlin/com/netflix/spinnaker/orca/q/handler/StartStageHandlerTest.kt @@ -17,7 +17,6 @@ package com.netflix.spinnaker.orca.q.handler import com.fasterxml.jackson.databind.ObjectMapper -import com.natpryce.hamkrest.allElements import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import com.natpryce.hamkrest.hasElement @@ -761,13 +760,16 @@ object StartStageHandlerTest : SubjectSpek({ pipeline.stages.size shouldEqual 4 assertThat( pipeline.stages.map { it.type }, - allElements(equalTo(stageWithParallelBranches.type)) + equalTo(listOf(singleTaskStage.type, singleTaskStage.type, singleTaskStage.type, stageWithParallelBranches.type)) ) - // TODO: contexts, etc. } - it("renames the primary branch") { - pipeline.stageByRef("1").name shouldEqual "is parallel" + it("builds stages that will run in parallel") { + assertThat( + pipeline.stages.flatMap { it.requisiteStageRefIds }, + isEmpty + ) + // TODO: contexts, etc. } it("renames each parallel branch") { @@ -808,7 +810,7 @@ object StartStageHandlerTest : SubjectSpek({ it("builds tasks for the branch") { val stage = pipeline.stageById(message.stageId) assertThat(stage.tasks, !isEmpty) - stage.tasks.map(Task::getName) shouldEqual listOf("in-branch") + stage.tasks.map(Task::getName) shouldEqual listOf("dummy") } it("does not build more synthetic stages") {