diff --git a/runatlantis.io/docs/repo-level-atlantis-yaml.md b/runatlantis.io/docs/repo-level-atlantis-yaml.md index a1d2612bb..fd93f0275 100644 --- a/runatlantis.io/docs/repo-level-atlantis-yaml.md +++ b/runatlantis.io/docs/repo-level-atlantis-yaml.md @@ -214,6 +214,8 @@ projects: ``` With this config above, Atlantis runs planning/applying for project2 first, then for project1. Several projects can have same `execution_order_group`. Any order in one group isn't guaranteed. +`parallel_plan` and `parallel_apply` respect these order groups, so parallel planning/applying works +in each group one by one. ### Custom Backend Config See [Custom Workflow Use Cases: Custom Backend Config](custom-workflows.html#custom-backend-config) diff --git a/server/events/apply_command_runner.go b/server/events/apply_command_runner.go index 35be6ed87..eae9ec859 100644 --- a/server/events/apply_command_runner.go +++ b/server/events/apply_command_runner.go @@ -144,7 +144,7 @@ func (a *ApplyCommandRunner) Run(ctx *command.Context, cmd *CommentCommand) { var result command.Result if a.isParallelEnabled(projectCmds) { ctx.Log.Info("Running applies in parallel") - result = runProjectCmdsParallel(projectCmds, a.prjCmdRunner.Apply, a.parallelPoolSize) + result = runProjectCmdsParallelGroups(projectCmds, a.prjCmdRunner.Apply, a.parallelPoolSize) } else { result = runProjectCmds(projectCmds, a.prjCmdRunner.Apply) } diff --git a/server/events/plan_command_runner.go b/server/events/plan_command_runner.go index bef34ec5f..0e9d64412 100644 --- a/server/events/plan_command_runner.go +++ b/server/events/plan_command_runner.go @@ -110,7 +110,7 @@ func (p *PlanCommandRunner) runAutoplan(ctx *command.Context) { var result command.Result if p.isParallelEnabled(projectCmds) { ctx.Log.Info("Running plans in parallel") - result = runProjectCmdsParallel(projectCmds, p.prjCmdRunner.Plan, p.parallelPoolSize) + result = runProjectCmdsParallelGroups(projectCmds, p.prjCmdRunner.Plan, p.parallelPoolSize) } else { result = runProjectCmds(projectCmds, p.prjCmdRunner.Plan) } @@ -184,7 +184,7 @@ func (p *PlanCommandRunner) run(ctx *command.Context, cmd *CommentCommand) { var result command.Result if p.isParallelEnabled(projectCmds) { ctx.Log.Info("Running applies in parallel") - result = runProjectCmdsParallel(projectCmds, p.prjCmdRunner.Plan, p.parallelPoolSize) + result = runProjectCmdsParallelGroups(projectCmds, p.prjCmdRunner.Plan, p.parallelPoolSize) } else { result = runProjectCmds(projectCmds, p.prjCmdRunner.Plan) } diff --git a/server/events/project_command_pool_executor.go b/server/events/project_command_pool_executor.go index 75fa38214..1039c7db1 100644 --- a/server/events/project_command_pool_executor.go +++ b/server/events/project_command_pool_executor.go @@ -1,6 +1,7 @@ package events import ( + "sort" "sync" "github.com/remeh/sizedwaitgroup" @@ -50,3 +51,37 @@ func runProjectCmds( } return command.Result{ProjectResults: results} } + +func splitByExecutionOrderGroup(cmds []command.ProjectContext) [][]command.ProjectContext { + groups := make(map[int][]command.ProjectContext) + for _, cmd := range cmds { + groups[cmd.ExecutionOrderGroup] = append(groups[cmd.ExecutionOrderGroup], cmd) + } + + var groupKeys []int + for k := range groups { + groupKeys = append(groupKeys, k) + } + sort.Ints(groupKeys) + + var res [][]command.ProjectContext + for _, group := range groupKeys { + res = append(res, groups[group]) + } + return res +} + +func runProjectCmdsParallelGroups( + cmds []command.ProjectContext, + runnerFunc prjCmdRunnerFunc, + poolSize int, +) command.Result { + var results []command.ProjectResult + groups := splitByExecutionOrderGroup(cmds) + for _, group := range groups { + res := runProjectCmdsParallel(group, runnerFunc, poolSize) + results = append(results, res.ProjectResults...) + } + + return command.Result{ProjectResults: results} +}