-
Notifications
You must be signed in to change notification settings - Fork 1k
/
project_context.go
206 lines (192 loc) · 8.11 KB
/
project_context.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package command
import (
"fmt"
"strconv"
"strings"
"github.com/hashicorp/go-version"
"github.com/runatlantis/atlantis/server/core/config/valid"
"github.com/runatlantis/atlantis/server/events/models"
"github.com/runatlantis/atlantis/server/logging"
tally "github.com/uber-go/tally/v4"
)
const (
planfileSlashReplace = "::"
)
// ProjectContext defines the context for a plan or apply stage that will
// be executed for a project.
type ProjectContext struct {
CommandName Name
// ApplyCmd is the command that users should run to apply this plan. If
// this is an apply then this will be empty.
ApplyCmd string
// ApprovePoliciesCmd is the command that users should run to approve policies for this plan. If
// this is an apply then this will be empty.
ApprovePoliciesCmd string
// PlanRequirements is the list of requirements that must be satisfied
// before we will run the plan stage.
PlanRequirements []string
// ApplyRequirements is the list of requirements that must be satisfied
// before we will run the apply stage.
ApplyRequirements []string
// ImportRequirements is the list of requirements that must be satisfied
// before we will run the import stage.
ImportRequirements []string
// AutomergeEnabled is true if automerge is enabled for the repo that this
// project is in.
AutomergeEnabled bool
// ParallelApplyEnabled is true if parallel apply is enabled for this project.
ParallelApplyEnabled bool
// ParallelPlanEnabled is true if parallel plan is enabled for this project.
ParallelPlanEnabled bool
// ParallelPolicyCheckEnabled is true if parallel policy_check is enabled for this project.
ParallelPolicyCheckEnabled bool
// AutoplanEnabled is true if autoplanning is enabled for this project.
AutoplanEnabled bool
// BaseRepo is the repository that the pull request will be merged into.
BaseRepo models.Repo
// EscapedCommentArgs are the extra arguments that were added to the atlantis
// command, ex. atlantis plan -- -target=resource. We then escape them
// by adding a \ before each character so that they can be used within
// sh -c safely, i.e. sh -c "terraform plan $(touch bad)".
EscapedCommentArgs []string
// HeadRepo is the repository that is getting merged into the BaseRepo.
// If the pull request branch is from the same repository then HeadRepo will
// be the same as BaseRepo.
HeadRepo models.Repo
// Dependencies are a list of project that this project relies on
// their apply status. These projects must be applied first.
//
// Atlantis uses this information to valid the apply
// orders and to warn the user if they're applying a project that
// depends on other projects.
DependsOn []string
// Log is a logger that's been set up for this context.
Log logging.SimpleLogging
// Scope is the scope for reporting stats setup for this context
Scope tally.Scope
// PullReqStatus holds state about the PR that requires additional computation outside models.PullRequest
PullReqStatus models.PullReqStatus
// CurrentProjectPlanStatus is the status of the current project prior to this command.
ProjectPlanStatus models.ProjectPlanStatus
//PullStatus is the status of the current pull request prior to this command.
PullStatus *models.PullStatus
// ProjectPolicyStatus is the status of policy sets of the current project prior to this command.
ProjectPolicyStatus []models.PolicySetStatus
// Pull is the pull request we're responding to.
Pull models.PullRequest
// ProjectName is the name of the project set in atlantis.yaml. If there was
// no name this will be an empty string.
ProjectName string
// RepoConfigVersion is the version of the repo's atlantis.yaml file. If
// there was no file, this will be 0.
RepoConfigVersion int
// RePlanCmd is the command that users should run to re-plan this project.
// If this is an apply then this will be empty.
RePlanCmd string
// RepoRelDir is the directory of this project relative to the repo root.
RepoRelDir string
// Steps are the sequence of commands we need to run for this project and this
// stage.
Steps []valid.Step
// TerraformVersion is the version of terraform we should use when executing
// commands for this project. This can be set to nil in which case we will
// use the default Atlantis terraform version.
TerraformVersion *version.Version
// Configuration metadata for a given project.
User models.User
// Verbose is true when the user would like verbose output.
Verbose bool
// Workspace is the Terraform workspace this project is in. It will always
// be set.
Workspace string
// PolicySets represent the policies that are run on the plan as part of the
// policy check stage
PolicySets valid.PolicySets
// PolicySetTarget describes which policy sets to target on the approve_policies step.
PolicySetTarget string
// ClearPolicyApproval determines whether policy counts will be incremented or cleared.
ClearPolicyApproval bool
// DeleteSourceBranchOnMerge will attempt to allow a branch to be deleted when merged (AzureDevOps & GitLab Support Only)
DeleteSourceBranchOnMerge bool
// Repo locks mode: disabled, on plan or on apply
RepoLocksMode valid.RepoLocksMode
// RepoConfigFile
RepoConfigFile string
// UUID for atlantis logs
JobID string
// The index of order group. Before planning/applying it will use to sort projects. Default is 0.
ExecutionOrderGroup int
// If plans/applies should be aborted if any prior plan/apply fails
AbortOnExcecutionOrderFail bool
// Allows custom policy check tools outside of Conftest to run in checks
CustomPolicyCheck bool
}
// SetProjectScopeTags adds ProjectContext tags to a new returned scope.
func (p ProjectContext) SetProjectScopeTags(scope tally.Scope) tally.Scope {
v := ""
if p.TerraformVersion != nil {
v = p.TerraformVersion.String()
}
tags := ProjectScopeTags{
BaseRepo: p.BaseRepo.FullName,
PrNumber: strconv.Itoa(p.Pull.Num),
Project: p.ProjectName,
ProjectPath: p.RepoRelDir,
TerraformVersion: v,
Workspace: p.Workspace,
}
return scope.Tagged(tags.Loadtags())
}
// GetShowResultFileName returns the filename (not the path) to store the tf show result
func (p ProjectContext) GetShowResultFileName() string {
if p.ProjectName == "" {
return fmt.Sprintf("%s.json", p.Workspace)
}
projName := strings.Replace(p.ProjectName, "/", planfileSlashReplace, -1)
return fmt.Sprintf("%s-%s.json", projName, p.Workspace)
}
// GetPolicyCheckResultFileName returns the filename (not the path) to store the result from conftest_client.
func (p ProjectContext) GetPolicyCheckResultFileName() string {
if p.ProjectName == "" {
return fmt.Sprintf("%s-policyout.json", p.Workspace)
}
projName := strings.Replace(p.ProjectName, "/", planfileSlashReplace, -1)
return fmt.Sprintf("%s-%s-policyout.json", projName, p.Workspace)
}
// Gets a unique identifier for the current pull request as a single string
func (p ProjectContext) PullInfo() string {
normalizedOwner := strings.ReplaceAll(p.BaseRepo.Owner, "/", "-")
normalizedName := strings.ReplaceAll(p.BaseRepo.Name, "/", "-")
projectRepo := fmt.Sprintf("%s/%s", normalizedOwner, normalizedName)
return buildPullInfo(projectRepo, p.Pull.Num, p.ProjectName, p.RepoRelDir, p.Workspace)
}
func buildPullInfo(repoName string, pullNum int, projectName string, relDir string, workspace string) string {
projectIdentifier := getProjectIdentifier(relDir, projectName)
return fmt.Sprintf("%s/%d/%s/%s", repoName, pullNum, projectIdentifier, workspace)
}
func getProjectIdentifier(relRepoDir string, projectName string) string {
if projectName != "" {
return projectName
}
// Replace directory separator / with -
// Replace . with _ to ensure projects with no project name and root dir set to "." have a valid URL
replacer := strings.NewReplacer("/", "-", ".", "_")
return replacer.Replace(relRepoDir)
}
// PolicyCleared returns whether all policies are passing or not.
func (p ProjectContext) PolicyCleared() bool {
passing := true
for _, psStatus := range p.ProjectPolicyStatus {
if psStatus.Passed {
continue
}
for _, psCfg := range p.PolicySets.PolicySets {
if psStatus.PolicySetName == psCfg.Name {
if psStatus.Approvals != psCfg.ApproveCount {
passing = false
}
}
}
}
return passing
}