feat: Remove flag for disable apply (#3912)

* Add code for disabling apply flag

* Better comment
This commit is contained in:
Luke Massa
2023-12-11 20:29:21 -05:00
committed by GitHub
parent 242640bbe9
commit fb484ad8eb
9 changed files with 43 additions and 60 deletions

View File

@@ -28,7 +28,6 @@ import (
"github.com/runatlantis/atlantis/server" "github.com/runatlantis/atlantis/server"
"github.com/runatlantis/atlantis/server/core/config/valid" "github.com/runatlantis/atlantis/server/core/config/valid"
"github.com/runatlantis/atlantis/server/events/command"
"github.com/runatlantis/atlantis/server/events/vcs/bitbucketcloud" "github.com/runatlantis/atlantis/server/events/vcs/bitbucketcloud"
"github.com/runatlantis/atlantis/server/logging" "github.com/runatlantis/atlantis/server/logging"
) )
@@ -71,7 +70,6 @@ const (
DataDirFlag = "data-dir" DataDirFlag = "data-dir"
DefaultTFVersionFlag = "default-tf-version" DefaultTFVersionFlag = "default-tf-version"
DisableApplyAllFlag = "disable-apply-all" DisableApplyAllFlag = "disable-apply-all"
DisableApplyFlag = "disable-apply"
DisableAutoplanFlag = "disable-autoplan" DisableAutoplanFlag = "disable-autoplan"
DisableAutoplanLabelFlag = "disable-autoplan-label" DisableAutoplanLabelFlag = "disable-autoplan-label"
DisableMarkdownFoldingFlag = "disable-markdown-folding" DisableMarkdownFoldingFlag = "disable-markdown-folding"
@@ -443,10 +441,6 @@ var boolFlags = map[string]boolFlag{
description: "Disable \"atlantis apply\" command without any flags (i.e. apply all). A specific project/workspace/directory has to be specified for applies.", description: "Disable \"atlantis apply\" command without any flags (i.e. apply all). A specific project/workspace/directory has to be specified for applies.",
defaultValue: false, defaultValue: false,
}, },
DisableApplyFlag: {
description: "Disable all \"atlantis apply\" command regardless of which flags are passed with it.",
defaultValue: false,
},
DisableAutoplanFlag: { DisableAutoplanFlag: {
description: "Disable atlantis auto planning feature", description: "Disable atlantis auto planning feature",
defaultValue: false, defaultValue: false,
@@ -1084,16 +1078,6 @@ func (s *ServerCmd) deprecationWarnings(userConfig *server.UserConfig) error {
deprecatedFlags = append(deprecatedFlags, RequireMergeableFlag) deprecatedFlags = append(deprecatedFlags, RequireMergeableFlag)
commandReqs = append(commandReqs, valid.MergeableCommandReq) commandReqs = append(commandReqs, valid.MergeableCommandReq)
} }
if userConfig.DisableApply {
deprecatedFlags = append(deprecatedFlags, DisableApplyFlag)
var filtered []string
for _, allowCommand := range strings.Split(userConfig.AllowCommands, ",") {
if allowCommand != command.Apply.String() {
filtered = append(filtered, allowCommand)
}
}
userConfig.AllowCommands = strings.Join(filtered, ",")
}
// Build up strings with what the recommended yaml and json config should // Build up strings with what the recommended yaml and json config should
// be instead of using the deprecated flags. // be instead of using the deprecated flags.

View File

@@ -57,7 +57,7 @@ var testFlags = map[string]interface{}{
ADWebhookPasswordFlag: "ad-wh-pass", ADWebhookPasswordFlag: "ad-wh-pass",
ADWebhookUserFlag: "ad-wh-user", ADWebhookUserFlag: "ad-wh-user",
AtlantisURLFlag: "url", AtlantisURLFlag: "url",
AllowCommandsFlag: "version,plan,unlock,import,approve_policies", // apply is disabled by DisableApply AllowCommandsFlag: "version,plan,apply,unlock,import,approve_policies",
AllowForkPRsFlag: true, AllowForkPRsFlag: true,
AllowRepoConfigFlag: true, AllowRepoConfigFlag: true,
AutoDiscoverModeFlag: "auto", AutoDiscoverModeFlag: "auto",
@@ -71,7 +71,6 @@ var testFlags = map[string]interface{}{
DataDirFlag: "/path", DataDirFlag: "/path",
DefaultTFVersionFlag: "v0.11.0", DefaultTFVersionFlag: "v0.11.0",
DisableApplyAllFlag: true, DisableApplyAllFlag: true,
DisableApplyFlag: true,
DisableMarkdownFoldingFlag: true, DisableMarkdownFoldingFlag: true,
DisableRepoLockingFlag: true, DisableRepoLockingFlag: true,
DiscardApprovalOnPlanFlag: true, DiscardApprovalOnPlanFlag: true,
@@ -763,16 +762,6 @@ func TestExecute_AllowAndWhitelist(t *testing.T) {
ErrEquals(t, "--repo-allowlist must be set for security purposes", err) ErrEquals(t, "--repo-allowlist must be set for security purposes", err)
} }
func TestExecute_DisableApplyDeprecation(t *testing.T) {
c := setupWithDefaults(map[string]interface{}{
DisableApplyFlag: true,
AllowCommandsFlag: "plan,apply,unlock",
}, t)
err := c.Execute()
Ok(t, err)
Equals(t, "plan,unlock", passedConfig.AllowCommands)
}
func TestExecute_AutoDetectModulesFromProjects_Env(t *testing.T) { func TestExecute_AutoDetectModulesFromProjects_Env(t *testing.T) {
t.Setenv("ATLANTIS_AUTOPLAN_MODULES_FROM_PROJECTS", "**/init.tf") t.Setenv("ATLANTIS_AUTOPLAN_MODULES_FROM_PROJECTS", "**/init.tf")
c := setupWithDefaults(map[string]interface{}{}, t) c := setupWithDefaults(map[string]interface{}{}, t)

View File

@@ -87,8 +87,6 @@ func TestGitHubWorkflow(t *testing.T) {
ModifiedFiles []string ModifiedFiles []string
// Comments are what our mock user writes to the pull request. // Comments are what our mock user writes to the pull request.
Comments []string Comments []string
// DisableApply flag used by userConfig object when initializing atlantis server.
DisableApply bool
// ApplyLock creates an apply lock that temporarily disables apply command // ApplyLock creates an apply lock that temporarily disables apply command
ApplyLock bool ApplyLock bool
// AllowCommands flag what kind of atlantis commands are available. // AllowCommands flag what kind of atlantis commands are available.
@@ -457,7 +455,6 @@ func TestGitHubWorkflow(t *testing.T) {
Description: "global apply lock disables apply commands", Description: "global apply lock disables apply commands",
RepoDir: "simple-yaml", RepoDir: "simple-yaml",
ModifiedFiles: []string{"main.tf"}, ModifiedFiles: []string{"main.tf"},
DisableApply: false,
ApplyLock: true, ApplyLock: true,
ExpAutoplan: true, ExpAutoplan: true,
Comments: []string{ Comments: []string{
@@ -470,18 +467,22 @@ func TestGitHubWorkflow(t *testing.T) {
}, },
}, },
{ {
Description: "disable apply flag always takes presedence", Description: "omitting apply from allow commands always takes presedence",
RepoDir: "simple-yaml", RepoDir: "simple-yaml",
ModifiedFiles: []string{"main.tf"}, ModifiedFiles: []string{"main.tf"},
DisableApply: true, AllowCommands: []command.Name{command.Plan},
ApplyLock: false, ApplyLock: false,
ExpAutoplan: true, ExpAutoplan: true,
Comments: []string{ Comments: []string{
"atlantis apply", "atlantis apply",
}, },
ExpParseFailedCount: 1,
ExpAllowResponseCommentBack: true,
ExpReplies: [][]string{ ExpReplies: [][]string{
{"exp-output-autoplan.txt"}, {"exp-output-autoplan.txt"},
{"exp-output-apply-locked.txt"}, // Disabling apply is implementing by omitting it from the apply list
// See: https://github.com/runatlantis/atlantis/pull/2877
{"exp-output-allow-command-unknown-apply.txt"},
{"exp-output-merge.txt"}, {"exp-output-merge.txt"},
}, },
}, },
@@ -628,7 +629,6 @@ func TestGitHubWorkflow(t *testing.T) {
// reset userConfig // reset userConfig
userConfig = server.UserConfig{} userConfig = server.UserConfig{}
userConfig.DisableApply = c.DisableApply
opt := setupOption{ opt := setupOption{
repoConfigFile: c.RepoConfigFile, repoConfigFile: c.RepoConfigFile,
@@ -799,7 +799,6 @@ func TestSimpleWorkflow_terraformLockFile(t *testing.T) {
// reset userConfig // reset userConfig
userConfig = server.UserConfig{} userConfig = server.UserConfig{}
userConfig.DisableApply = true
ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, setupOption{}) ctrl, vcsClient, githubGetter, atlantisWorkspace := setupE2E(t, c.RepoDir, setupOption{})
// Set the repo to be cloned through the testing backdoor. // Set the repo to be cloned through the testing backdoor.
@@ -1295,6 +1294,13 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
if opt.allowCommands != nil { if opt.allowCommands != nil {
allowCommands = opt.allowCommands allowCommands = opt.allowCommands
} }
disableApply := true
for _, allowCommand := range allowCommands {
if allowCommand == command.Apply {
disableApply = false
break
}
}
commentParser := &events.CommentParser{ commentParser := &events.CommentParser{
GithubUser: "github-user", GithubUser: "github-user",
GitlabUser: "gitlab-user", GitlabUser: "gitlab-user",
@@ -1308,7 +1314,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
backend := boltdb backend := boltdb
lockingClient := locking.NewClient(boltdb) lockingClient := locking.NewClient(boltdb)
noOpLocker := locking.NewNoOpLocker() noOpLocker := locking.NewNoOpLocker()
applyLocker = locking.NewApplyClient(boltdb, userConfig.DisableApply) applyLocker = locking.NewApplyClient(boltdb, disableApply)
projectLocker := &events.DefaultProjectLocker{ projectLocker := &events.DefaultProjectLocker{
Locker: lockingClient, Locker: lockingClient,
NoOpLocker: noOpLocker, NoOpLocker: noOpLocker,

View File

@@ -0,0 +1,5 @@
```
Error: unknown command "apply".
Run 'atlantis --help' for usage.
Available commands(--allow-commands): plan
```

View File

@@ -31,7 +31,7 @@ type ApplyLocker interface {
// ApplyCommandLock contains information about apply command lock status. // ApplyCommandLock contains information about apply command lock status.
type ApplyCommandLock struct { type ApplyCommandLock struct {
// Locked is true is when apply commands are locked // Locked is true is when apply commands are locked
// Either by using DisableApply flag or creating a global ApplyCommandLock // Either by using omitting apply from AllowCommands or creating a global ApplyCommandLock
// DisableApply lock take precedence when set // DisableApply lock take precedence when set
Locked bool Locked bool
Time time.Time Time time.Time
@@ -39,25 +39,25 @@ type ApplyCommandLock struct {
} }
type ApplyClient struct { type ApplyClient struct {
backend Backend backend Backend
disableApplyFlag bool disableApply bool
} }
func NewApplyClient(backend Backend, disableApplyFlag bool) ApplyLocker { func NewApplyClient(backend Backend, disableApply bool) ApplyLocker {
return &ApplyClient{ return &ApplyClient{
backend: backend, backend: backend,
disableApplyFlag: disableApplyFlag, disableApply: disableApply,
} }
} }
// LockApply acquires global apply lock. // LockApply acquires global apply lock.
// DisableApplyFlag takes presedence to any existing locks, if it is set to true // DisableApply takes presedence to any existing locks, if it is set to true
// this function returns an error // this function returns an error
func (c *ApplyClient) LockApply() (ApplyCommandLock, error) { func (c *ApplyClient) LockApply() (ApplyCommandLock, error) {
response := ApplyCommandLock{} response := ApplyCommandLock{}
if c.disableApplyFlag { if c.disableApply {
return response, errors.New("DisableApplyFlag is set; Apply commands are locked globally until flag is unset") return response, errors.New("apply is omitted from AllowCommands; Apply commands are locked globally until flag is updated")
} }
applyCmdLock, err := c.backend.LockCommand(command.Apply, time.Now()) applyCmdLock, err := c.backend.LockCommand(command.Apply, time.Now())
@@ -73,11 +73,11 @@ func (c *ApplyClient) LockApply() (ApplyCommandLock, error) {
} }
// UnlockApply releases a global apply lock. // UnlockApply releases a global apply lock.
// DisableApplyFlag takes presedence to any existing locks, if it is set to true // DisableApply takes presedence to any existing locks, if it is set to true
// this function returns an error // this function returns an error
func (c *ApplyClient) UnlockApply() error { func (c *ApplyClient) UnlockApply() error {
if c.disableApplyFlag { if c.disableApply {
return errors.New("apply commands are disabled until DisableApply flag is unset") return errors.New("apply commands are disabled until AllowCommands flag is updated")
} }
err := c.backend.UnlockCommand(command.Apply) err := c.backend.UnlockCommand(command.Apply)
@@ -89,11 +89,11 @@ func (c *ApplyClient) UnlockApply() error {
} }
// CheckApplyLock retrieves an apply command lock if present. // CheckApplyLock retrieves an apply command lock if present.
// If DisableApplyFlag is set it will always return a lock. // If DisableApply is set it will always return a lock.
func (c *ApplyClient) CheckApplyLock() (ApplyCommandLock, error) { func (c *ApplyClient) CheckApplyLock() (ApplyCommandLock, error) {
response := ApplyCommandLock{} response := ApplyCommandLock{}
if c.disableApplyFlag { if c.disableApply {
return ApplyCommandLock{ return ApplyCommandLock{
Locked: true, Locked: true,
}, nil }, nil

View File

@@ -202,12 +202,12 @@ func TestApplyLocker(t *testing.T) {
Assert(t, !lock.Locked, "exp false") Assert(t, !lock.Locked, "exp false")
}) })
t.Run("can't lock if userConfig.DisableApply is set", func(t *testing.T) { t.Run("can't lock if apply is omitted from userConfig.AllowCommands", func(t *testing.T) {
backend := mocks.NewMockBackend() backend := mocks.NewMockBackend()
l := locking.NewApplyClient(backend, true) l := locking.NewApplyClient(backend, true)
_, err := l.LockApply() _, err := l.LockApply()
ErrEquals(t, "DisableApplyFlag is set; Apply commands are locked globally until flag is unset", err) ErrEquals(t, "apply is omitted from AllowCommands; Apply commands are locked globally until flag is updated", err)
backend.VerifyWasCalled(Never()).LockCommand(Any[command.Name](), Any[time.Time]()) backend.VerifyWasCalled(Never()).LockCommand(Any[command.Name](), Any[time.Time]())
}) })
@@ -232,12 +232,12 @@ func TestApplyLocker(t *testing.T) {
Equals(t, errExpected, err) Equals(t, errExpected, err)
}) })
t.Run("can't unlock if userConfig.DisableApply is set", func(t *testing.T) { t.Run("can't lock if apply is omitted from userConfig.AllowCommands", func(t *testing.T) {
backend := mocks.NewMockBackend() backend := mocks.NewMockBackend()
l := locking.NewApplyClient(backend, true) l := locking.NewApplyClient(backend, true)
err := l.UnlockApply() err := l.UnlockApply()
ErrEquals(t, "apply commands are disabled until DisableApply flag is unset", err) ErrEquals(t, "apply commands are disabled until AllowCommands flag is updated", err)
backend.VerifyWasCalled(Never()).UnlockCommand(Any[command.Name]()) backend.VerifyWasCalled(Never()).UnlockCommand(Any[command.Name]())
}) })
@@ -264,7 +264,7 @@ func TestApplyLocker(t *testing.T) {
Equals(t, lock.Locked, false) Equals(t, lock.Locked, false)
}) })
t.Run("when DisableApply flag is set always return a lock", func(t *testing.T) { t.Run("when apply is not in AllowCommands always return a lock", func(t *testing.T) {
backend := mocks.NewMockBackend() backend := mocks.NewMockBackend()
l := locking.NewApplyClient(backend, true) l := locking.NewApplyClient(backend, true)

View File

@@ -68,7 +68,7 @@ func (a *ApplyCommandRunner) Run(ctx *command.Context, cmd *CommentCommand) {
pull := ctx.Pull pull := ctx.Pull
locked, err := a.IsLocked() locked, err := a.IsLocked()
// CheckApplyLock falls back to DisableApply flag if fetching the lock // CheckApplyLock falls back to AllowedCommand flag if fetching the lock
// raises an error // raises an error
// We will log failure as warning // We will log failure as warning
if err != nil { if err != nil {

View File

@@ -34,13 +34,13 @@ func TestApplyCommandRunner_IsLocked(t *testing.T) {
ExpComment: "**Error:** Running `atlantis apply` is disabled.", ExpComment: "**Error:** Running `atlantis apply` is disabled.",
}, },
{ {
Description: "When no global apply lock is present and DisableApply flag is false IsDisabled returns false", Description: "When no global apply lock is present IsDisabled returns false",
ApplyLocked: false, ApplyLocked: false,
ApplyLockError: nil, ApplyLockError: nil,
ExpComment: "Ran Apply for 0 projects:", ExpComment: "Ran Apply for 0 projects:",
}, },
{ {
Description: "If ApplyLockChecker returns an error IsDisabled return value of DisableApply flag", Description: "If ApplyLockChecker returns an error IsDisabled returns false",
ApplyLockError: errors.New("error"), ApplyLockError: errors.New("error"),
ApplyLocked: false, ApplyLocked: false,
ExpComment: "Ran Apply for 0 projects:", ExpComment: "Ran Apply for 0 projects:",

View File

@@ -33,7 +33,6 @@ type UserConfig struct {
CheckoutStrategy string `mapstructure:"checkout-strategy"` CheckoutStrategy string `mapstructure:"checkout-strategy"`
DataDir string `mapstructure:"data-dir"` DataDir string `mapstructure:"data-dir"`
DisableApplyAll bool `mapstructure:"disable-apply-all"` DisableApplyAll bool `mapstructure:"disable-apply-all"`
DisableApply bool `mapstructure:"disable-apply"`
DisableAutoplan bool `mapstructure:"disable-autoplan"` DisableAutoplan bool `mapstructure:"disable-autoplan"`
DisableAutoplanLabel string `mapstructure:"disable-autoplan-label"` DisableAutoplanLabel string `mapstructure:"disable-autoplan-label"`
DisableMarkdownFolding bool `mapstructure:"disable-markdown-folding"` DisableMarkdownFolding bool `mapstructure:"disable-markdown-folding"`