mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:48:20 +00:00
Signed-off-by: Andrew Borg <andrew.b.borg@gmail.com> Co-authored-by: PePe Amengual <2208324+jamengual@users.noreply.github.com> Co-authored-by: Simon Heather <32168619+X-Guardian@users.noreply.github.com>
215 lines
8.5 KiB
Go
215 lines
8.5 KiB
Go
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
|
|
// TerraformDistribution is the distribution 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 distribution.
|
|
TerraformDistribution *string
|
|
// 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
|
|
AbortOnExecutionOrderFail bool
|
|
// Allows custom policy check tools outside of Conftest to run in checks
|
|
CustomPolicyCheck bool
|
|
SilencePRComments []string
|
|
|
|
// TeamAllowlistChecker is used to check authorization on a project-level
|
|
TeamAllowlistChecker TeamAllowlistChecker
|
|
}
|
|
|
|
// 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
|
|
}
|