-
Notifications
You must be signed in to change notification settings - Fork 55
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
cli: put a lock around login #362
Conversation
internal/cmd/cmd.go
Outdated
} | ||
|
||
loginPid := filepath.Join(homeDir, ".infra", "login.pid") | ||
if _, err := os.Stat(loginPid); 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.
Does this mean other kubectl
commands will block, or will they error? If they error clients like Infra App this may cause a strange situation where the first command works but follow up ones just 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.
Subsequent commands should error. I don't have Infra App installed so don't know exactly how it would interact. I feel like erroring out would be the best case scenario. Blocking kubectl
might have additional side effects as well.
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.
$ k get deploy
Error: login already in progress
Unable to connect to the server: getting credentials: exec plugin is configured to use API version client.authentication.k8s.io/v1beta1, plugin returned version client.authentication.k8s.io/__internal
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.
To add: not sure how easy it would be for all these invocations of login()
to wait on each other (especially since they may be separate processes) – but there may be a useful file lock library we can leverage for this (saw a few like https://github.com/zbiljic/go-filelock - but they seem very old)
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 found a more recent library that implements file locking using sync.RWMutex
. It provides a nice interface to build the behaviour we want.
Now, infra login
will create a lock such that other infra login
attempts will block until the lock is freed or it expires (5m). Once it's unlocked, it will not attempt a login. Instead it will continue assuming the other attempt successfully logged in.
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5) | ||
defer cancel() | ||
|
||
_, err = lock.TryLockContext(ctx, time.Second*1) |
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.
1 second interval between retries
Very elegant! Nice work!! |
Just tested this out a bunch with a super short token duration and it feels very good for both One important challenge to think about here: would there ever be a case (e.g. apps like Infra App or a long running scripts using Do we have thoughts on how to handle this? Maybe via a refresh flow that uses their existing token vs requiring a full auth (this could be something we allow for later)? Or maybe we can have a more subtle step before the window when it's time to re-auth (perhaps a prompt on the CLI if interactive? or something else if non interactive? –– maybe a notification even?) |
Really good points around the pop-up. I hate that too personally, it's not good to expect users to enter their credentials into windows that randomly pop up. Gives you a bad reflex to enter your credentials on demand without questioning it. I think adding a refresh flow (in a separate issue) is probably the correct solution. I like the more contextual identity provider re-authentication, but I don't know if it would be possible with every identity provider. |
Another issue to capture in this area: there may also be the case where a computer is left idle and every 5 minutes a popup will come up since the lock will release. Another item to think about around the re-authentication experience since in say after 8 hours of inactivity that may still be 96 popups (if I understand correctly! just a hunch based on how this is implemented) |
I could potentially see problems here where one login screen opens and the user doesn't notice or ignores it, then subsequent logins don't work because we're locked on the one login window. Seems like we may have caused the problem by having infra creds try to automatically login |
infra creds and infra login should probably use github.com/mattn/go-isatty to determine if there's a user at the end of the request, or just a piece of software, and customize accordingly. |
2d55b14
to
8892bbe
Compare
- prevent too many prompts from showing up
- block login while another instance has the lock - assume the other instance is successful and exit with success if instance is able to get the lock and proceed without showing login prompt
@@ -50,7 +51,7 @@ type Config struct { | |||
type ErrUnauthenticated struct{} | |||
|
|||
func (e *ErrUnauthenticated) Error() string { |
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.
Not related to this PR where this is an existing error, but maybe we should move the errors package to the root level and put this there
Fixes #351