mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:58:19 +00:00
feat(4157): adding option to hide UI button to disable apply commands (#4158)
This commit is contained in:
@@ -1295,6 +1295,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
|
||||
allowCommands = opt.allowCommands
|
||||
}
|
||||
disableApply := true
|
||||
disableGlobalApplyLock := false
|
||||
for _, allowCommand := range allowCommands {
|
||||
if allowCommand == command.Apply {
|
||||
disableApply = false
|
||||
@@ -1314,7 +1315,7 @@ func setupE2E(t *testing.T, repoDir string, opt setupOption) (events_controllers
|
||||
backend := boltdb
|
||||
lockingClient := locking.NewClient(boltdb)
|
||||
noOpLocker := locking.NewNoOpLocker()
|
||||
applyLocker = locking.NewApplyClient(boltdb, disableApply)
|
||||
applyLocker = locking.NewApplyClient(boltdb, disableApply, disableGlobalApplyLock)
|
||||
projectLocker := &events.DefaultProjectLocker{
|
||||
Locker: lockingClient,
|
||||
NoOpLocker: noOpLocker,
|
||||
|
||||
@@ -45,9 +45,10 @@ type LockIndexData struct {
|
||||
|
||||
// ApplyLockData holds the fields to display in the index view
|
||||
type ApplyLockData struct {
|
||||
Locked bool
|
||||
Time time.Time
|
||||
TimeFormatted string
|
||||
Locked bool
|
||||
GlobalApplyLockEnabled bool
|
||||
Time time.Time
|
||||
TimeFormatted string
|
||||
}
|
||||
|
||||
// IndexData holds the data for rendering the index page
|
||||
@@ -98,6 +99,7 @@ var IndexTemplate = template.Must(template.New("index.html.tmpl").Parse(`
|
||||
<p class="js-discard-success"><strong>Plan discarded and unlocked!</strong></p>
|
||||
</section>
|
||||
<section>
|
||||
{{ if .ApplyLock.GlobalApplyLockEnabled }}
|
||||
{{ if .ApplyLock.Locked }}
|
||||
<div class="twelve center columns">
|
||||
<h6><strong>Apply commands are disabled globally</strong></h6>
|
||||
@@ -111,6 +113,7 @@ var IndexTemplate = template.Must(template.New("index.html.tmpl").Parse(`
|
||||
<a class="button button-primary" id="applyLockPrompt">Disable Apply Commands</a>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</section>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
@@ -33,20 +33,23 @@ type ApplyCommandLock struct {
|
||||
// Locked is true is when apply commands are locked
|
||||
// Either by using omitting apply from AllowCommands or creating a global ApplyCommandLock
|
||||
// DisableApply lock take precedence when set
|
||||
Locked bool
|
||||
Time time.Time
|
||||
Failure string
|
||||
Locked bool
|
||||
GlobalApplyLockEnabled bool
|
||||
Time time.Time
|
||||
Failure string
|
||||
}
|
||||
|
||||
type ApplyClient struct {
|
||||
backend Backend
|
||||
disableApply bool
|
||||
backend Backend
|
||||
disableApply bool
|
||||
disableGlobalApplyLock bool
|
||||
}
|
||||
|
||||
func NewApplyClient(backend Backend, disableApply bool) ApplyLocker {
|
||||
func NewApplyClient(backend Backend, disableApply bool, disableGlobalApplyLock bool) ApplyLocker {
|
||||
return &ApplyClient{
|
||||
backend: backend,
|
||||
disableApply: disableApply,
|
||||
backend: backend,
|
||||
disableApply: disableApply,
|
||||
disableGlobalApplyLock: disableGlobalApplyLock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +94,9 @@ func (c *ApplyClient) UnlockApply() error {
|
||||
// CheckApplyLock retrieves an apply command lock if present.
|
||||
// If DisableApply is set it will always return a lock.
|
||||
func (c *ApplyClient) CheckApplyLock() (ApplyCommandLock, error) {
|
||||
response := ApplyCommandLock{}
|
||||
response := ApplyCommandLock{
|
||||
GlobalApplyLockEnabled: true,
|
||||
}
|
||||
|
||||
if c.disableApply {
|
||||
return ApplyCommandLock{
|
||||
@@ -108,6 +113,9 @@ func (c *ApplyClient) CheckApplyLock() (ApplyCommandLock, error) {
|
||||
response.Locked = true
|
||||
response.Time = applyCmdLock.LockTime()
|
||||
}
|
||||
if c.disableGlobalApplyLock {
|
||||
response.GlobalApplyLockEnabled = false
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.LockCommand(Any[command.Name](), Any[time.Time]())).ThenReturn(nil, errExpected)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
lock, err := l.LockApply()
|
||||
Equals(t, errExpected, err)
|
||||
Assert(t, !lock.Locked, "exp false")
|
||||
@@ -205,7 +205,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
t.Run("can't lock if apply is omitted from userConfig.AllowCommands", func(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
l := locking.NewApplyClient(backend, true)
|
||||
l := locking.NewApplyClient(backend, true, false)
|
||||
_, err := l.LockApply()
|
||||
ErrEquals(t, "apply is omitted from AllowCommands; Apply commands are locked globally until flag is updated", err)
|
||||
|
||||
@@ -216,7 +216,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.LockCommand(Any[command.Name](), Any[time.Time]())).ThenReturn(applyLock, nil)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
lock, _ := l.LockApply()
|
||||
Assert(t, lock.Locked, "exp lock present")
|
||||
})
|
||||
@@ -227,7 +227,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.UnlockCommand(Any[command.Name]())).ThenReturn(errExpected)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
err := l.UnlockApply()
|
||||
Equals(t, errExpected, err)
|
||||
})
|
||||
@@ -235,7 +235,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
t.Run("can't lock if apply is omitted from userConfig.AllowCommands", func(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
l := locking.NewApplyClient(backend, true)
|
||||
l := locking.NewApplyClient(backend, true, false)
|
||||
err := l.UnlockApply()
|
||||
ErrEquals(t, "apply commands are disabled until AllowCommands flag is updated", err)
|
||||
|
||||
@@ -246,7 +246,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.UnlockCommand(Any[command.Name]())).ThenReturn(nil)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
err := l.UnlockApply()
|
||||
Equals(t, nil, err)
|
||||
})
|
||||
@@ -258,7 +258,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.CheckCommandLock(Any[command.Name]())).ThenReturn(nil, errExpected)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
lock, err := l.CheckApplyLock()
|
||||
Equals(t, errExpected, err)
|
||||
Equals(t, lock.Locked, false)
|
||||
@@ -267,7 +267,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
t.Run("when apply is not in AllowCommands always return a lock", func(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
l := locking.NewApplyClient(backend, true)
|
||||
l := locking.NewApplyClient(backend, true, false)
|
||||
lock, err := l.CheckApplyLock()
|
||||
Ok(t, err)
|
||||
Equals(t, lock.Locked, true)
|
||||
@@ -278,7 +278,7 @@ func TestApplyLocker(t *testing.T) {
|
||||
backend := mocks.NewMockBackend()
|
||||
|
||||
When(backend.CheckCommandLock(Any[command.Name]())).ThenReturn(applyLock, nil)
|
||||
l := locking.NewApplyClient(backend, false)
|
||||
l := locking.NewApplyClient(backend, false, false)
|
||||
lock, err := l.CheckApplyLock()
|
||||
Equals(t, nil, err)
|
||||
Assert(t, lock.Locked, "exp lock present")
|
||||
|
||||
@@ -121,6 +121,7 @@ type Server struct {
|
||||
WebPassword string
|
||||
ProjectCmdOutputHandler jobs.ProjectCommandOutputHandler
|
||||
ScheduledExecutorService *scheduled.ExecutorService
|
||||
DisableGlobalApplyLock bool
|
||||
}
|
||||
|
||||
// Config holds config for server that isn't passed in by the user.
|
||||
@@ -455,8 +456,12 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
} else {
|
||||
lockingClient = locking.NewClient(backend)
|
||||
}
|
||||
disableGlobalApplyLock := false
|
||||
if userConfig.DisableGlobalApplyLock {
|
||||
disableGlobalApplyLock = true
|
||||
}
|
||||
|
||||
applyLockingClient = locking.NewApplyClient(backend, disableApply)
|
||||
applyLockingClient = locking.NewApplyClient(backend, disableApply, disableGlobalApplyLock)
|
||||
workingDirLocker := events.NewDefaultWorkingDirLocker()
|
||||
|
||||
var workingDir events.WorkingDir = &events.FileWorkspace{
|
||||
@@ -919,6 +924,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
ProjectJobsErrorTemplate: templates.ProjectJobsErrorTemplate,
|
||||
SSLKeyFile: userConfig.SSLKeyFile,
|
||||
SSLCertFile: userConfig.SSLCertFile,
|
||||
DisableGlobalApplyLock: userConfig.DisableGlobalApplyLock,
|
||||
Drainer: drainer,
|
||||
ProjectCmdOutputHandler: projectCmdOutputHandler,
|
||||
WebAuthentication: userConfig.WebBasicAuth,
|
||||
@@ -941,8 +947,6 @@ func (s *Server) Start() error {
|
||||
s.Router.HandleFunc("/api/apply", s.APIController.Apply).Methods("POST")
|
||||
s.Router.HandleFunc("/github-app/exchange-code", s.GithubAppController.ExchangeCode).Methods("GET")
|
||||
s.Router.HandleFunc("/github-app/setup", s.GithubAppController.New).Methods("GET")
|
||||
s.Router.HandleFunc("/apply/lock", s.LocksController.LockApply).Methods("POST").Queries()
|
||||
s.Router.HandleFunc("/apply/unlock", s.LocksController.UnlockApply).Methods("DELETE").Queries()
|
||||
s.Router.HandleFunc("/locks", s.LocksController.DeleteLock).Methods("DELETE").Queries("id", "{id:.*}")
|
||||
s.Router.HandleFunc("/lock", s.LocksController.GetLock).Methods("GET").
|
||||
Queries(LockViewRouteIDQueryParam, fmt.Sprintf("{%s}", LockViewRouteIDQueryParam)).Name(LockViewRouteName)
|
||||
@@ -953,6 +957,10 @@ func (s *Server) Start() error {
|
||||
if ok {
|
||||
s.Router.Handle(s.CommandRunner.GlobalCfg.Metrics.Prometheus.Endpoint, r.HTTPHandler())
|
||||
}
|
||||
if !s.DisableGlobalApplyLock {
|
||||
s.Router.HandleFunc("/apply/lock", s.LocksController.LockApply).Methods("POST").Queries()
|
||||
s.Router.HandleFunc("/apply/unlock", s.LocksController.UnlockApply).Methods("DELETE").Queries()
|
||||
}
|
||||
|
||||
n := negroni.New(&negroni.Recovery{
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
@@ -1064,9 +1072,10 @@ func (s *Server) Index(w http.ResponseWriter, _ *http.Request) {
|
||||
}
|
||||
|
||||
applyLockData := templates.ApplyLockData{
|
||||
Time: applyCmdLock.Time,
|
||||
Locked: applyCmdLock.Locked,
|
||||
TimeFormatted: applyCmdLock.Time.Format("02-01-2006 15:04:05"),
|
||||
Time: applyCmdLock.Time,
|
||||
Locked: applyCmdLock.Locked,
|
||||
GlobalApplyLockEnabled: applyCmdLock.GlobalApplyLockEnabled,
|
||||
TimeFormatted: applyCmdLock.Time.Format("02-01-2006 15:04:05"),
|
||||
}
|
||||
//Sort by date - newest to oldest.
|
||||
sort.SliceStable(lockResults, func(i, j int) bool { return lockResults[i].Time.After(lockResults[j].Time) })
|
||||
|
||||
@@ -36,6 +36,7 @@ type UserConfig struct {
|
||||
DisableAutoplanLabel string `mapstructure:"disable-autoplan-label"`
|
||||
DisableMarkdownFolding bool `mapstructure:"disable-markdown-folding"`
|
||||
DisableRepoLocking bool `mapstructure:"disable-repo-locking"`
|
||||
DisableGlobalApplyLock bool `mapstructure:"disable-global-apply-lock"`
|
||||
DisableUnlockLabel string `mapstructure:"disable-unlock-label"`
|
||||
DiscardApprovalOnPlanFlag bool `mapstructure:"discard-approval-on-plan"`
|
||||
EmojiReaction string `mapstructure:"emoji-reaction"`
|
||||
|
||||
Reference in New Issue
Block a user