mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-29 11:58:36 +00:00
* Adding policy_check support into yaml config * Added policy check model and runtime structs * Adding BuildPolicyCheckCommand to ProjectCommandBuilder * Return incorrectly deleted code * Remove BuildAutoPolicyPlanCommand from ProjectCommandBuilder * Split runAutoCommand into two functions runAutoPlanCommand - does what originally RunAutoplancommand was doing except now it returns CommandResult and []models.ProjectCommandContext runAutoPolicyCheckCommand - accepts CommandContext, CommandResult, and []models.ProjectCommandContext as arguments and runs PolicyCheckStep runner * Refactor RunCommentCommand * Remove BuildPolicyCheckCommand and rename StepCmdExec back to TerraformExec * Add policy step runner logic and conftest interfaces. * Add show step runner to policy check stage. * Adding models.PolicyCheckCommand to buildCtx This also means buildPlanAllCommands call buildCtx twice once with models.PlanCommand and once with models.PolicyCheckCommand * Adding new project_command_builder that supports policy_check * Refactoring PolicyCheck specific logic into a PolicyCheckProjectCommandBuilder * Moving events.CommandContext to models.CommandContext this will allow me to remove buildCtx method and move ProjectCommandContext creation into models package * Policy Owners might be different types, for that reason we are refactoring Owners into its own struct with specific keys defining different owner types Co-authored-by: Nish Krishnan <nishk@lyft.com> Co-authored-by: Nish Krishnan <nishkrishnan@users.noreply.github.com>
221 lines
6.4 KiB
Go
221 lines
6.4 KiB
Go
package events
|
|
|
|
import (
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
"github.com/hashicorp/terraform-config-inspect/tfconfig"
|
|
"github.com/runatlantis/atlantis/server/events/models"
|
|
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
|
)
|
|
|
|
func NewProjectCommandContextBulder(policyCheckEnabled bool, commentBuilder CommentBuilder) ProjectCommandContextBuilder {
|
|
projectCommandContextBuilder := &DefaultProjectCommandContextBuilder{
|
|
CommentBuilder: commentBuilder,
|
|
}
|
|
|
|
if policyCheckEnabled {
|
|
return &PolicyCheckProjectCommandContextBuilder{
|
|
CommentBuilder: commentBuilder,
|
|
ProjectCommandContextBuilder: projectCommandContextBuilder,
|
|
}
|
|
}
|
|
|
|
return projectCommandContextBuilder
|
|
}
|
|
|
|
type ProjectCommandContextBuilder interface {
|
|
// BuildProjectContext builds project command contexts for atlantis commands
|
|
BuildProjectContext(
|
|
ctx *CommandContext,
|
|
cmdName models.CommandName,
|
|
prjCfg valid.MergedProjectCfg,
|
|
commentFlags []string,
|
|
repoDir string,
|
|
automerge, parallelPlan, parallelApply, verbose bool,
|
|
) []models.ProjectCommandContext
|
|
}
|
|
|
|
type DefaultProjectCommandContextBuilder struct {
|
|
CommentBuilder CommentBuilder
|
|
}
|
|
|
|
func (cb *DefaultProjectCommandContextBuilder) BuildProjectContext(
|
|
ctx *CommandContext,
|
|
cmdName models.CommandName,
|
|
prjCfg valid.MergedProjectCfg,
|
|
commentFlags []string,
|
|
repoDir string,
|
|
automerge, parallelApply, parallelPlan, verbose bool,
|
|
) (projectCmds []models.ProjectCommandContext) {
|
|
ctx.Log.Debug("Building project command context for %s", cmdName)
|
|
|
|
var steps []valid.Step
|
|
switch cmdName {
|
|
case models.PlanCommand:
|
|
steps = prjCfg.Workflow.Plan.Steps
|
|
case models.ApplyCommand:
|
|
steps = prjCfg.Workflow.Apply.Steps
|
|
}
|
|
|
|
// If TerraformVersion not defined in config file look for a
|
|
// terraform.require_version block.
|
|
if prjCfg.TerraformVersion == nil {
|
|
prjCfg.TerraformVersion = getTfVersion(ctx, filepath.Join(repoDir, prjCfg.RepoRelDir))
|
|
}
|
|
|
|
projectCmds = append(projectCmds, newProjectCommandContext(
|
|
ctx,
|
|
cmdName,
|
|
cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name),
|
|
cb.CommentBuilder.BuildPlanComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, commentFlags),
|
|
prjCfg,
|
|
steps,
|
|
prjCfg.PolicySets,
|
|
escapeArgs(commentFlags),
|
|
automerge,
|
|
parallelApply,
|
|
parallelPlan,
|
|
verbose,
|
|
))
|
|
|
|
return
|
|
}
|
|
|
|
type PolicyCheckProjectCommandContextBuilder struct {
|
|
ProjectCommandContextBuilder *DefaultProjectCommandContextBuilder
|
|
CommentBuilder CommentBuilder
|
|
}
|
|
|
|
func (cb *PolicyCheckProjectCommandContextBuilder) BuildProjectContext(
|
|
ctx *CommandContext,
|
|
cmdName models.CommandName,
|
|
prjCfg valid.MergedProjectCfg,
|
|
commentFlags []string,
|
|
repoDir string,
|
|
automerge, parallelApply, parallelPlan, verbose bool,
|
|
) (projectCmds []models.ProjectCommandContext) {
|
|
ctx.Log.Debug("PolicyChecks are enabled")
|
|
projectCmds = cb.ProjectCommandContextBuilder.BuildProjectContext(
|
|
ctx,
|
|
cmdName,
|
|
prjCfg,
|
|
commentFlags,
|
|
repoDir,
|
|
automerge,
|
|
parallelApply,
|
|
parallelPlan,
|
|
verbose,
|
|
)
|
|
|
|
if cmdName == models.PlanCommand {
|
|
ctx.Log.Debug("Building project command context for %s", models.PolicyCheckCommand)
|
|
steps := prjCfg.Workflow.PolicyCheck.Steps
|
|
|
|
projectCmds = append(projectCmds, newProjectCommandContext(
|
|
ctx,
|
|
models.PolicyCheckCommand,
|
|
cb.CommentBuilder.BuildApplyComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name),
|
|
cb.CommentBuilder.BuildPlanComment(prjCfg.RepoRelDir, prjCfg.Workspace, prjCfg.Name, commentFlags),
|
|
prjCfg,
|
|
steps,
|
|
prjCfg.PolicySets,
|
|
escapeArgs(commentFlags),
|
|
automerge,
|
|
parallelApply,
|
|
parallelPlan,
|
|
verbose,
|
|
))
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// newProjectCommandContext is a initializer method that handles constructing the
|
|
// ProjectCommandContext.
|
|
func newProjectCommandContext(ctx *CommandContext,
|
|
cmd models.CommandName,
|
|
applyCmd string,
|
|
planCmd string,
|
|
projCfg valid.MergedProjectCfg,
|
|
steps []valid.Step,
|
|
policySets valid.PolicySets,
|
|
escapedCommentArgs []string,
|
|
automergeEnabled bool,
|
|
parallelApplyEnabled bool,
|
|
parallelPlanEnabled bool,
|
|
verbose bool,
|
|
) models.ProjectCommandContext {
|
|
return models.ProjectCommandContext{
|
|
CommandName: cmd,
|
|
ApplyCmd: applyCmd,
|
|
BaseRepo: ctx.Pull.BaseRepo,
|
|
EscapedCommentArgs: escapedCommentArgs,
|
|
AutomergeEnabled: automergeEnabled,
|
|
ParallelApplyEnabled: parallelApplyEnabled,
|
|
ParallelPlanEnabled: parallelPlanEnabled,
|
|
AutoplanEnabled: projCfg.AutoplanEnabled,
|
|
Steps: steps,
|
|
HeadRepo: ctx.HeadRepo,
|
|
Log: ctx.Log,
|
|
PullMergeable: ctx.PullMergeable,
|
|
Pull: ctx.Pull,
|
|
ProjectName: projCfg.Name,
|
|
ApplyRequirements: projCfg.ApplyRequirements,
|
|
RePlanCmd: planCmd,
|
|
RepoRelDir: projCfg.RepoRelDir,
|
|
RepoConfigVersion: projCfg.RepoCfgVersion,
|
|
TerraformVersion: projCfg.TerraformVersion,
|
|
User: ctx.User,
|
|
Verbose: verbose,
|
|
Workspace: projCfg.Workspace,
|
|
PolicySets: policySets,
|
|
}
|
|
}
|
|
|
|
func escapeArgs(args []string) []string {
|
|
var escaped []string
|
|
for _, arg := range args {
|
|
var escapedArg string
|
|
for i := range arg {
|
|
escapedArg += "\\" + string(arg[i])
|
|
}
|
|
escaped = append(escaped, escapedArg)
|
|
}
|
|
return escaped
|
|
}
|
|
|
|
// Extracts required_version from Terraform configuration.
|
|
// Returns nil if unable to determine version from configuration.
|
|
func getTfVersion(ctx *CommandContext, absProjDir string) *version.Version {
|
|
module, diags := tfconfig.LoadModule(absProjDir)
|
|
if diags.HasErrors() {
|
|
ctx.Log.Err("trying to detect required version: %s", diags.Error())
|
|
return nil
|
|
}
|
|
|
|
if len(module.RequiredCore) != 1 {
|
|
ctx.Log.Info("cannot determine which version to use from terraform configuration, detected %d possibilities.", len(module.RequiredCore))
|
|
return nil
|
|
}
|
|
requiredVersionSetting := module.RequiredCore[0]
|
|
|
|
// We allow `= x.y.z`, `=x.y.z` or `x.y.z` where `x`, `y` and `z` are integers.
|
|
re := regexp.MustCompile(`^=?\s*([^\s]+)\s*$`)
|
|
matched := re.FindStringSubmatch(requiredVersionSetting)
|
|
if len(matched) == 0 {
|
|
ctx.Log.Debug("did not specify exact version in terraform configuration, found %q", requiredVersionSetting)
|
|
return nil
|
|
}
|
|
ctx.Log.Debug("found required_version setting of %q", requiredVersionSetting)
|
|
version, err := version.NewVersion(matched[1])
|
|
if err != nil {
|
|
ctx.Log.Debug(err.Error())
|
|
return nil
|
|
}
|
|
|
|
ctx.Log.Info("detected module requires version: %q", version.String())
|
|
return version
|
|
}
|