-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Orca 679 global atlantis lock new release branch #49
Orca 679 global atlantis lock new release branch #49
Conversation
/ptal @nishkrishnan |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work, have a few comments
transactionErr := b.db.Update(func(tx *bolt.Tx) error { | ||
bucket := tx.Bucket(b.globalLocksBucketName) | ||
|
||
currLockSerialized := bucket.Get([]byte(b.commandLockKey(cmdName))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to make this idempotent? Personally, would rather error out if the lock exists. Think about TFE returning a 409 in a similar situation.
server/events/db/boltdb.go
Outdated
func (b *BoltDB) LockCommand(cmdName models.CommandName, lockTime time.Time) (*models.CommandLock, error) { | ||
lock := models.CommandLock{ | ||
CommandName: cmdName, | ||
Time: lockTime, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd lean to having like a LockMetadata struct which contains the time in addition to a description. Eventually we might want to add user there for example and just makes it easy to add without changing the interface.
//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_apply_command_locker.go ApplyCommandLocker | ||
|
||
type ApplyCommandLocker interface { | ||
IsDisabled(ctx *CommandContext) bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ie. This struct isn't responsible for performing the locking, it's responsible for checking whether a lock exists. Confusing naming imo.
Id suggest renaming this to GlobalApplyLockChecker
with the method isLocked
(disabled also doesn't fit well if the name of the struct doesn't include that)
|
||
func NewApplyCommandLocker( | ||
applyLockChecker locking.ApplyLockChecker, | ||
disableApply bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we rename this to disableApplyFlag or something that hints at this being a global server flag?
// IsDisabled returns true if there is a global apply command lock or | ||
// DisableApply flag is set to true | ||
func (a *DefaultApplyCommandLocker) IsDisabled(ctx *CommandContext) bool { | ||
lock, err := a.ApplyLockChecker.CheckApplyLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like command context is barely used here. The logger embedded in there is useful if we want to debug per-pull issues. However, the global apply lock fetching error doesn't seem necessary to log at that level, you can just keep that logger embedded in the struct.
If you do the above, you can just use a single interface delegation pattern for:
- fetching the global lock
- comparing the result of that to the global server config flag.
@@ -53,7 +88,7 @@ func (a *ApplyCommandRunner) Run(ctx *CommandContext, cmd *CommentCommand) { | |||
baseRepo := ctx.Pull.BaseRepo | |||
pull := ctx.Pull | |||
|
|||
if a.DisableApply { | |||
if a.locker.IsDisabled(ctx) { | |||
ctx.Log.Info("ignoring apply command since apply disabled globally") | |||
if err := a.vcsClient.CreateComment(baseRepo, pull.Num, applyDisabledComment, models.ApplyCommand.String()); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The applyDisabledComment is very generic, would be good to think about how to make that more useful depending on how the apply command lock is configured. Not in scope for this PR though.
server/events/locking/locking.go
Outdated
} | ||
|
||
// CheckApplyLock retrieves an apply command lock if present. | ||
func (c *Client) CheckApplyLock() (ApplyCommandLockResponse, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds to me like this should also follow the same logic of checking the server configuration as well. We should also probably not allow unlocking of atlantis if it's configured to disable applies through server configuration both.
Right now the implementation doesn't do that which is good, however, it'll say it's locked and unlocked it, but applies still won't work if the server configuration has it defined.
server/events/locking/locking.go
Outdated
func (c *Client) LockApply() (ApplyCommandLockResponse, error) { | ||
response := ApplyCommandLockResponse{} | ||
|
||
applyCmdLock, err := c.backend.LockCommand(models.ApplyCommand, time.Now().Local()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Best practice is to use unix timestamps if we are storing data.
"github.com/runatlantis/atlantis/server/events/models/fixtures" | ||
) | ||
|
||
// func setupApplyCmd(t *testing.T) *events.ApplyCommandRunner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented?
server/events/command_runner_test.go
Outdated
When(logger.GetLevel()).ThenReturn(logging.Info) | ||
When(logger.NewLogger("runatlantis/atlantis#1", true, logging.Info)). | ||
ThenReturn(pullLogger) | ||
|
||
scope := stats.NewStore(stats.NewNullSink(), false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this added for fixing the tests you thought were broken? lol do we still need it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is to remove the logging warnings about statsd failing to reach the localhost. Just to reduce the noise :)
server/events/db/boltdb.go
Outdated
pullKeySeparator = "::" | ||
locksBucketName = "runLocks" | ||
pullsBucketName = "pulls" | ||
globalLocksBucketName = "global" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed this earlier but let's name it globalLocks
server/events/db/boltdb.go
Outdated
// If the lock already exists, it will just return pointer to the existing lock | ||
// If lock creation fails it will return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should update this to reflect the new changes.
Address PR comments
💨 |
* Orca 679 global atlantis lock new release branch (#49) * Adding CommandLocker to boltDB and exposing it through locker interface * Apply lock ui and apply command lock controller * Minor comments * Adding more tests and refactorinng * Linting fixes * creating applyLockingClient variable to fix interface error * nullsink for stats * Addressing PR comments * fixing e2e tests * linting fix fml * Update outdated function descriptions Address PR comments * revert stats sink changes * remove unnecessary dependencies
* Adding CommandLocker to boltDB and exposing it through locker interface * Apply lock ui and apply command lock controller * Minor comments * Adding more tests and refactorinng * Linting fixes * creating applyLockingClient variable to fix interface error * nullsink for stats * Addressing PR comments * fixing e2e tests * linting fix fml * Update outdated function descriptions Address PR comments * revert stats sink changes
* Adding CommandLocker to boltDB and exposing it through locker interface * Apply lock ui and apply command lock controller * Minor comments * Adding more tests and refactorinng * Linting fixes * creating applyLockingClient variable to fix interface error * nullsink for stats * Addressing PR comments * fixing e2e tests * linting fix fml * Update outdated function descriptions Address PR comments * revert stats sink changes
Added support to create a command specific lock entries to the db/bolt_db, LockCommand, UnlockCommand, and CheckCommandLock.
Implemented a new ApplyLocker interface for locking.go#Client that manages apply lock creation/deletion and lock retrieval.
LocksController has 2 handlers to create and delete apply locks.
Server#Index renders the UI to create/delete the locks.
ApplyCommandRunner expects an object that implements ApplyCommandLocker which boils down to object having IsDisabled function that returns boolean value.
DefaultApplyCommandLocker implements the interface.
In UI it is just a AJAX request that will create/delete the locks for apply. To create a new lock or delete existing one do to atlantis main page.
Recreated #48 against new release branch.