mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-07 07:58:44 +00:00
feat: add autodiscover enabled feature (#3895)
* add flag to allow the user disable the autodiscover * add global config and doc * feat: Implement autodiscover.mode * fix: Minor doc fixes * fix: Small fixes to docs/indent/tests * fix: Line length, quoting, function comments * fix: Add a few more tests and remove newlines * fix: Always camel case never snake --------- Co-authored-by: Marcelo Medeiros <m.medeiros@carepay.com> Co-authored-by: nitrocode <7775707+nitrocode@users.noreply.github.com> Co-authored-by: PePe Amengual <jose.amengual@gmail.com>
This commit is contained in:
14
server/core/config/valid/autodiscover.go
Normal file
14
server/core/config/valid/autodiscover.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package valid
|
||||
|
||||
// AutoDiscoverMode enum
|
||||
type AutoDiscoverMode string
|
||||
|
||||
const (
|
||||
AutoDiscoverEnabledMode AutoDiscoverMode = "enabled"
|
||||
AutoDiscoverDisabledMode AutoDiscoverMode = "disabled"
|
||||
AutoDiscoverAutoMode AutoDiscoverMode = "auto"
|
||||
)
|
||||
|
||||
type AutoDiscover struct {
|
||||
Mode AutoDiscoverMode
|
||||
}
|
||||
@@ -28,6 +28,7 @@ const DeleteSourceBranchOnMergeKey = "delete_source_branch_on_merge"
|
||||
const RepoLockingKey = "repo_locking"
|
||||
const PolicyCheckKey = "policy_check"
|
||||
const CustomPolicyCheckKey = "custom_policy_check"
|
||||
const AutoDiscoverKey = "autodiscover"
|
||||
|
||||
// DefaultAtlantisFile is the default name of the config file for each repo.
|
||||
const DefaultAtlantisFile = "atlantis.yaml"
|
||||
@@ -84,6 +85,7 @@ type Repo struct {
|
||||
RepoLocking *bool
|
||||
PolicyCheck *bool
|
||||
CustomPolicyCheck *bool
|
||||
AutoDiscover *AutoDiscover
|
||||
}
|
||||
|
||||
type MergedProjectCfg struct {
|
||||
@@ -245,6 +247,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
|
||||
deleteSourceBranchOnMerge := false
|
||||
repoLockingKey := true
|
||||
customPolicyCheck := false
|
||||
autoDiscover := AutoDiscover{Mode: AutoDiscoverAutoMode}
|
||||
if args.AllowRepoCfg {
|
||||
allowedOverrides = []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, PolicyCheckKey}
|
||||
allowCustomWorkflows = true
|
||||
@@ -269,6 +272,7 @@ func NewGlobalCfgFromArgs(args GlobalCfgArgs) GlobalCfg {
|
||||
RepoLocking: &repoLockingKey,
|
||||
PolicyCheck: &policyCheck,
|
||||
CustomPolicyCheck: &customPolicyCheck,
|
||||
AutoDiscover: &autoDiscover,
|
||||
},
|
||||
},
|
||||
Workflows: map[string]Workflow{
|
||||
@@ -305,7 +309,7 @@ func (r Repo) IDString() string {
|
||||
// final config. It assumes that all configs have been validated.
|
||||
func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, proj Project, rCfg RepoCfg) MergedProjectCfg {
|
||||
log.Debug("MergeProjectCfg started")
|
||||
planReqs, applyReqs, importReqs, workflow, allowedOverrides, allowCustomWorkflows, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck := g.getMatchingCfg(log, repoID)
|
||||
planReqs, applyReqs, importReqs, workflow, allowedOverrides, allowCustomWorkflows, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
|
||||
|
||||
// If repos are allowed to override certain keys then override them.
|
||||
for _, key := range allowedOverrides {
|
||||
@@ -407,7 +411,7 @@ func (g GlobalCfg) MergeProjectCfg(log logging.SimpleLogging, repoID string, pro
|
||||
// repo with id repoID. It is used when there is no repo config.
|
||||
func (g GlobalCfg) DefaultProjCfg(log logging.SimpleLogging, repoID string, repoRelDir string, workspace string) MergedProjectCfg {
|
||||
log.Debug("building config based on server-side config")
|
||||
planReqs, applyReqs, importReqs, workflow, _, _, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck := g.getMatchingCfg(log, repoID)
|
||||
planReqs, applyReqs, importReqs, workflow, _, _, deleteSourceBranchOnMerge, repoLocking, policyCheck, customPolicyCheck, _ := g.getMatchingCfg(log, repoID)
|
||||
return MergedProjectCfg{
|
||||
PlanRequirements: planReqs,
|
||||
ApplyRequirements: applyReqs,
|
||||
@@ -426,6 +430,17 @@ func (g GlobalCfg) DefaultProjCfg(log logging.SimpleLogging, repoID string, repo
|
||||
}
|
||||
}
|
||||
|
||||
// RepoAutoDiscoverCfg returns the AutoDiscover config from the global config
|
||||
// for the repo with id repoID. If no matching repo is found or there is no
|
||||
// AutoDiscover config then this function returns nil.
|
||||
func (g GlobalCfg) RepoAutoDiscoverCfg(repoID string) *AutoDiscover {
|
||||
repo := g.MatchingRepo(repoID)
|
||||
if repo != nil {
|
||||
return repo.AutoDiscover
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateRepoCfg validates that rCfg for repo with id repoID is valid based
|
||||
// on our global config.
|
||||
func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error {
|
||||
@@ -528,7 +543,7 @@ func (g GlobalCfg) ValidateRepoCfg(rCfg RepoCfg, repoID string) error {
|
||||
}
|
||||
|
||||
// getMatchingCfg returns the key settings for repoID.
|
||||
func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (planReqs []string, applyReqs []string, importReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool, deleteSourceBranchOnMerge bool, repoLocking bool, policyCheck bool, customPolicyCheck bool) {
|
||||
func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (planReqs []string, applyReqs []string, importReqs []string, workflow Workflow, allowedOverrides []string, allowCustomWorkflows bool, deleteSourceBranchOnMerge bool, repoLocking bool, policyCheck bool, customPolicyCheck bool, autoDiscover AutoDiscover) {
|
||||
toLog := make(map[string]string)
|
||||
traceF := func(repoIdx int, repoID string, key string, val interface{}) string {
|
||||
from := "default server config"
|
||||
@@ -550,6 +565,9 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
|
||||
return fmt.Sprintf("setting %s: %s from %s", key, valStr, from)
|
||||
}
|
||||
|
||||
// Can't use raw.DefaultAutoDiscoverMode() because of an import cycle. Should refactor to avoid that.
|
||||
autoDiscover = AutoDiscover{Mode: AutoDiscoverAutoMode}
|
||||
|
||||
for _, key := range []string{PlanRequirementsKey, ApplyRequirementsKey, ImportRequirementsKey, WorkflowKey, AllowedOverridesKey, AllowCustomWorkflowsKey, DeleteSourceBranchOnMergeKey, RepoLockingKey, PolicyCheckKey, CustomPolicyCheckKey} {
|
||||
for i, repo := range g.Repos {
|
||||
if repo.IDMatches(repoID) {
|
||||
@@ -604,6 +622,11 @@ func (g GlobalCfg) getMatchingCfg(log logging.SimpleLogging, repoID string) (pla
|
||||
toLog[CustomPolicyCheckKey] = traceF(i, repo.IDString(), CustomPolicyCheckKey, *repo.CustomPolicyCheck)
|
||||
customPolicyCheck = *repo.CustomPolicyCheck
|
||||
}
|
||||
case AutoDiscoverKey:
|
||||
if repo.AutoDiscover != nil {
|
||||
toLog[AutoDiscoverKey] = traceF(i, repo.IDString(), AutoDiscoverKey, repo.AutoDiscover.Mode)
|
||||
autoDiscover = *repo.AutoDiscover
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/mohae/deepcopy"
|
||||
"github.com/runatlantis/atlantis/server/core/config"
|
||||
"github.com/runatlantis/atlantis/server/core/config/raw"
|
||||
"github.com/runatlantis/atlantis/server/core/config/valid"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
@@ -82,6 +83,7 @@ func TestNewGlobalCfg(t *testing.T) {
|
||||
RepoLocking: Bool(true),
|
||||
PolicyCheck: Bool(false),
|
||||
CustomPolicyCheck: Bool(false),
|
||||
AutoDiscover: raw.DefaultAutoDiscover(),
|
||||
},
|
||||
},
|
||||
Workflows: map[string]valid.Workflow{
|
||||
|
||||
@@ -19,6 +19,7 @@ type RepoCfg struct {
|
||||
Workflows map[string]Workflow
|
||||
PolicySets PolicySets
|
||||
Automerge *bool
|
||||
AutoDiscover *AutoDiscover
|
||||
ParallelApply *bool
|
||||
ParallelPlan *bool
|
||||
ParallelPolicyCheck *bool
|
||||
@@ -91,6 +92,24 @@ func isRegexAllowed(name string, allowedRegexpPrefixes []string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// This function returns a final true/false decision for whether AutoDiscover is enabled
|
||||
// for a repo. It takes into account the defaultAutoDiscoverMode when there is no explicit
|
||||
// repo config. The defaultAutoDiscoverMode param should be understood as the default
|
||||
// AutoDiscover mode as may be set via CLI params or server side repo config.
|
||||
func (r RepoCfg) AutoDiscoverEnabled(defaultAutoDiscoverMode AutoDiscoverMode) bool {
|
||||
autoDiscoverMode := defaultAutoDiscoverMode
|
||||
if r.AutoDiscover != nil {
|
||||
autoDiscoverMode = r.AutoDiscover.Mode
|
||||
}
|
||||
|
||||
if autoDiscoverMode == AutoDiscoverAutoMode {
|
||||
// AutoDiscover is enabled by default when no projects are defined
|
||||
return len(r.Projects) == 0
|
||||
}
|
||||
|
||||
return autoDiscoverMode == AutoDiscoverEnabledMode
|
||||
}
|
||||
|
||||
// validateWorkspaceAllowed returns an error if repoCfg defines projects in
|
||||
// repoRelDir but none of them use workspace. We want this to be an error
|
||||
// because if users have gone to the trouble of defining projects in repoRelDir
|
||||
|
||||
@@ -216,3 +216,110 @@ func TestConfig_FindProjectsByDir(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_AutoDiscoverEnabled(t *testing.T) {
|
||||
cases := []struct {
|
||||
description string
|
||||
repoAutoDiscover valid.AutoDiscoverMode
|
||||
defaultAutoDiscover valid.AutoDiscoverMode
|
||||
projects []valid.Project
|
||||
expEnabled bool
|
||||
}{
|
||||
{
|
||||
description: "repo disabled autodiscover default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo disabled autodiscover default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo enabled autodiscover default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo enabled autodiscover default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with no projects default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with no projects default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with a project default enabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
projects: []valid.Project{{}},
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo set auto autodiscover with a project default disabled",
|
||||
repoAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
projects: []valid.Project{{}},
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default enabled",
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default disabled",
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with no projects default auto",
|
||||
defaultAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default enabled",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverEnabledMode,
|
||||
expEnabled: true,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default disabled",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverDisabledMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
{
|
||||
description: "repo unset autodiscover with a project default auto",
|
||||
projects: []valid.Project{{}},
|
||||
defaultAutoDiscover: valid.AutoDiscoverAutoMode,
|
||||
expEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
r := valid.RepoCfg{
|
||||
Projects: c.projects,
|
||||
AutoDiscover: nil,
|
||||
}
|
||||
if c.repoAutoDiscover != "" {
|
||||
r.AutoDiscover = &valid.AutoDiscover{c.repoAutoDiscover}
|
||||
}
|
||||
enabled := r.AutoDiscoverEnabled(c.defaultAutoDiscover)
|
||||
Equals(t, c.expEnabled, enabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user