forked from runatlantis/atlantis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
project_pre_execute.go
127 lines (116 loc) · 4.9 KB
/
project_pre_execute.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package events
import (
"fmt"
"path/filepath"
"strings"
"github.com/hashicorp/go-version"
"github.com/pkg/errors"
"github.com/runatlantis/atlantis/server/events/locking"
"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/events/run"
"github.com/runatlantis/atlantis/server/events/terraform"
)
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_project_pre_executor.go ProjectPreExecutor
// ProjectPreExecutor executes before the plan and apply executors. It handles
// the setup tasks that are common to both plan and apply.
type ProjectPreExecutor interface {
// Execute executes the pre plan/apply tasks.
Execute(ctx *CommandContext, repoDir string, project models.Project) PreExecuteResult
}
// DefaultProjectPreExecutor implements ProjectPreExecutor.
type DefaultProjectPreExecutor struct {
Locker locking.Locker
ConfigReader ProjectConfigReader
Terraform terraform.Client
Run run.Runner
}
// PreExecuteResult is the result of running the pre execute.
type PreExecuteResult struct {
ProjectResult ProjectResult
ProjectConfig ProjectConfig
TerraformVersion *version.Version
LockResponse locking.TryLockResponse
}
// Execute executes the pre plan/apply tasks.
func (p *DefaultProjectPreExecutor) Execute(ctx *CommandContext, repoDir string, project models.Project) PreExecuteResult {
workspace := ctx.Command.Workspace
lockAttempt, err := p.Locker.TryLock(project, workspace, ctx.Pull, ctx.User)
if err != nil {
return PreExecuteResult{ProjectResult: ProjectResult{Error: errors.Wrap(err, "acquiring lock")}}
}
if !lockAttempt.LockAcquired && lockAttempt.CurrLock.Pull.Num != ctx.Pull.Num {
return PreExecuteResult{ProjectResult: ProjectResult{Failure: fmt.Sprintf(
"This project is currently locked by #%d. The locking plan must be applied or discarded before future plans can execute.",
lockAttempt.CurrLock.Pull.Num)}}
}
ctx.Log.Info("acquired lock with id %q", lockAttempt.LockKey)
config, tfVersion, err := p.executeWithLock(ctx, repoDir, project)
if err != nil {
p.Locker.Unlock(lockAttempt.LockKey) // nolint: errcheck
return PreExecuteResult{ProjectResult: ProjectResult{Error: err}}
}
return PreExecuteResult{ProjectConfig: config, TerraformVersion: tfVersion, LockResponse: lockAttempt}
}
// executeWithLock executes the pre plan/apply tasks after the lock has been
// acquired. This helper func makes revoking the lock on error easier.
// Returns the project config, terraform version, or an error.
func (p *DefaultProjectPreExecutor) executeWithLock(ctx *CommandContext, repoDir string, project models.Project) (ProjectConfig, *version.Version, error) {
workspace := ctx.Command.Workspace
// Check if config file is found, if not we continue the run.
var config ProjectConfig
absolutePath := filepath.Join(repoDir, project.Path)
if p.ConfigReader.Exists(absolutePath) {
var err error
config, err = p.ConfigReader.Read(absolutePath)
if err != nil {
return config, nil, err
}
ctx.Log.Info("parsed atlantis config file in %q", absolutePath)
}
// Check if terraform version is >= 0.9.0.
terraformVersion := p.Terraform.Version()
if config.TerraformVersion != nil {
terraformVersion = config.TerraformVersion
}
constraints, _ := version.NewConstraint(">= 0.9.0")
if constraints.Check(terraformVersion) {
ctx.Log.Info("determined that we are running terraform with version >= 0.9.0. Running version %s", terraformVersion)
if len(config.PreInit) > 0 {
_, err := p.Run.Execute(ctx.Log, config.PreInit, absolutePath, workspace, terraformVersion, "pre_init")
if err != nil {
return config, nil, errors.Wrapf(err, "running %s commands", "pre_init")
}
}
_, err := p.Terraform.Init(ctx.Log, absolutePath, workspace, config.GetExtraArguments("init"), terraformVersion)
if err != nil {
return config, nil, err
}
} else {
ctx.Log.Info("determined that we are running terraform with version < 0.9.0. Running version %s", terraformVersion)
if len(config.PreGet) > 0 {
_, err := p.Run.Execute(ctx.Log, config.PreGet, absolutePath, workspace, terraformVersion, "pre_get")
if err != nil {
return config, nil, errors.Wrapf(err, "running %s commands", "pre_get")
}
}
terraformGetCmd := append([]string{"get", "-no-color"}, config.GetExtraArguments("get")...)
_, err := p.Terraform.RunCommandWithVersion(ctx.Log, absolutePath, terraformGetCmd, terraformVersion, workspace)
if err != nil {
return config, nil, err
}
}
stage := fmt.Sprintf("pre_%s", strings.ToLower(ctx.Command.Name.String()))
var commands []string
if ctx.Command.Name == Plan {
commands = config.PrePlan
} else {
commands = config.PreApply
}
if len(commands) > 0 {
_, err := p.Run.Execute(ctx.Log, commands, absolutePath, workspace, terraformVersion, stage)
if err != nil {
return config, nil, errors.Wrapf(err, "running %s commands", stage)
}
}
return config, terraformVersion, nil
}