-
Notifications
You must be signed in to change notification settings - Fork 35
Unify deployment tracking and support multiple deployments #677
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
base: main
Are you sure you want to change the base?
Conversation
ead6f43 to
bc9bce5
Compare
This change centralizes deployment state management and enables seamless multi-deployment support. The extension now properly tracks per-deployment credentials, syncs state across VS Code windows, and handles login/logout flows in a unified way. Key changes: Architecture: - Add DeploymentManager to centralize deployment state (url, label, token, user) and coordinate client updates, auth contexts, and workspace refreshes - Add LoginCoordinator to handle login prompts with cross-window detection, preventing duplicate login dialogs when multiple windows need auth - Move deployment types to src/deployment/ module with proper type guards Storage & Auth: - SecretsManager now stores per-deployment credentials using label-based keys (coder.session.<label>) instead of flat sessionToken storage - Add LRU tracking for deployments with automatic cleanup of old credentials - Add migration from legacy flat storage format - Cross-window sync via secrets.onDidChange events - Debug command (coder.debug.listDeployments) for inspecting stored state Commands & Remote: - Commands now use DeploymentManager instead of directly manipulating client - Remote connection uses LoginCoordinator for auth prompts during workspace connections - Remove forceLogout in favor of unified logout through DeploymentManager - CliManager.configure() now called on every remote connection, with secrets storage as the source of truth for credentials WebSocket improvements: - CoderApi now implements Disposable to clean up WebSocket connections - Add setCredentials() method to update host+token atomically, avoiding unnecessary reconnection cycles - Add suspend() support to ReconnectingWebSocket for clean disconnects - Simplify WebSocket fallback logic with cleaner SSE fallback handling Tests: - Update secretsManager tests for new per-deployment API - Add comprehensive reconnectingWebSocket tests for suspend/reconnect - Extend coderApi tests for credential handling
bc9bce5 to
642fd52
Compare
| const hostChanged = (currentHost || "") !== (host || ""); | ||
| const tokenChanged = currentToken !== token; |
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.
Continuation of #633 (comment)
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.
However, if we authenticate without a token (mTLS) then we might begin with the following: { host: XYZ, token: undefined }, but when we authenticate we change the token to "" and thus trigger a reconnect.
The input is the same since undefined might be treated the same as "" except that we might have started with invalid configuration (401s) and then changed the setting and prompted a login again that worked so we trigger this reconnect with the new info.
But why should this trigger a reconnect? Nothing changes from the Coder server's perspective, it has no distinction between a blank token and a missing token. If a missing token was a 401, so will a blank token, so why reconnect when we know it will fail? Or is that not the case?
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.
Here's my thought process:
Let's assume that we authenticate without a token (mTLS) but we are pointing to the wrong files or there's an issue with the tlsCertFile/tlsKeyFile. Let's assume that we then trigger a login event that actually works, so we set the token to "" and thus trigger a reconnect. It's not necessarily about the empty vs. undefined but more about triggering something when we successfully login.
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.
Ahhhhh ok I think I understand, so embedded in the token is an assumption that, in the mTLS case, undefined means an unsuccessful (or pending?) login and a blank string means a successful login?
This context does introduces its own problem, I think: if one calls setCredentials with defined host and undefined token. This can cause socket.reconnect(). But if I understand correctly, undefined means there is an issue, so socket.reconnect() will fail. If a blank token means success, then I suppose that means we should check explicitly for undefined and not try to reconnect sockets. But maybe we want to formalize this with a boolean or something.
Although, it feels unexpected to me that we would call setCredentials for a failing state. I would expect it to be called when logging in and we know we have valid credentials, or to log out.
Or...should setCredentials be separate from whether sockets are reconnected?
642fd52 to
5fd7241
Compare
5fd7241 to
5e83d93
Compare
5e83d93 to
46afe7f
Compare
46afe7f to
acf26aa
Compare
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 ran out of time to test it but will do so tomorrow!
| allWorkspacesProvider.fetchAndRefresh(); | ||
| } else { | ||
| output.warn("No error, but got unexpected response", user); | ||
| output.info("Deployment set but not authenticated"); |
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.
Ah yeah this is the use case I am curious about. Initially I felt like we would not want to set the deployment until we have valid credentials so we can set it all at once together and avoid having to synchronize, but thinking about it, this could allow you to try logging in, fail or cancel, close the window, come back later, and be able to try logging in again without having to enter the URL a second time?
But, if I try to log into a new deployment, then change my mind and cancel, I could lose being logged into that previous deployment, which might feel bad. We could handle the above case by just having the URL in the history I suppose.
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.
try logging in again without having to enter the URL a second time?
The idea here is that you could login, not open VS Code in a while then open it again and now suddenly you are not authenticated (but still you maintain the last deployment that you were connected to). So you can have stuff like the URL prepopulated when you click login.
I could lose being logged into that previous deployment, which might feel bad
I mean currently you cannot do this WITHOUT clicking logout anyway (unless the current deployment is not authenticated because the token is gone) but then setCurrentDeployment is only set at the very end anyway...
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 idea here is that you could login, not open VS Code in a while then open it again and now suddenly you are not authenticated (but still you maintain the last deployment that you were connected to). So you can have stuff like the URL prepopulated when you click login.
But this does not require that we set the deployment without validation and then set it again with validation later. In this case, of a successful login, we would have a current deployment set.
I mean currently you cannot do this WITHOUT clicking logout anyway
Mostly I was thinking of it from an isolated point of view, that this prevents such a feature, but it is possible to trigger this with the URI handler, for example if I am logged in dev.coder.com and open vscode://coder.coder-remote/open?url=test.coder.com&owner=my-username&workspace=my-ws.
Although, maybe that is actually a bug, I forget if we decided to change the current deployment with the URI handler if something else is logged in.
As an aside, it would be nice if it was possible to directly log in a different deployment without having to log out first (fewer button clicks).
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 feel like we are talking about different things now lol
it would be nice if it was possible to directly log in a different deployment without having to log out first (fewer button clicks).
We currently can do that, that's why we do setDeploymentAndValidate when handling vscode URLs. What was the original ask actually? Setting all the deployment info (with the User) all at once as compared to setting the deployment info partially (everything BUT user)?
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 feel like we are talking about different things now lol
Could be lol
it would be nice if it was possible to directly log in a different deployment without having to log out first (fewer button clicks).
We currently can do that
Here I mean by clicking "login" again (there is no way to do this currently since we have no login button or menu item once you are logged in) or by running the login command (if you do this, it simply does nothing). Not something we need to add, just thinking out loud it would be nice if I could switch deployments without having to click logout first, and that if we did one day add that, setting the current deployment in this way (as a two-step process) would cause issues.
While it is not possible from the UI, it is possible to directly log into a different deployment with the URI handler, though I am not sure that is intended? Should the URI handler change what I am logged into currently? Or should it only be used for the remote connection? I vaguely recall talking about this, but idk what conclusion we reached.
What was the original ask actually? Setting all the deployment info (with the User) all at once as compared to setting the deployment info partially (everything BUT user)?
Yup yup. Currently we set the deployment, then we log in, then we set it again with credentials. This means that if you cancel during the login, you are stuck with this new deployment because we already set it as the current deployment. Or, if your credentials are invalid, you are stuck with that broken connection (until you log into the other again, which admittedly is not that hard since we are not deleting tokens anymore).
For example I am logged into dev.coder.com and if I open a URI from my test deployment but the token is wrong or missing, my current deployment gets nuked so now my sidebar just shows "login". I would expect dev.coder.com to still be logged in. If I restart VS Code, I will actually still be logged into dev.coder.com. We only set it for the current instance, temporarily.
I propose setting it only once we have validated auth, so we set it just the one time, and a failure or cancellation during login will leave the current deployment in place.
|
@code-asher I've add tests in the last commit BTW, if you wish I can make it a separate PR 🙏 |
542c240 to
1abfb6e
Compare
1abfb6e to
b37031e
Compare
b37031e to
6f307c1
Compare
|
Nah same PR for tests I would say! |
code-asher
left a comment
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.
Wow tried it out and the cross-window stuff is quite magical. ✨ Really cool!!
This change centralizes deployment state management and enables seamless multi-deployment support. The extension now properly tracks per-deployment credentials, syncs state across VS Code windows, and handles login/logout flows in a unified way.
Key changes:
Architecture:
DeploymentManagerto centralize deployment state (url,label,token,user) and coordinate client updates, auth contexts, and workspace refreshesLoginCoordinatorto handle login prompts with cross-window detection, preventing duplicate login dialogs when multiple windows need authsrc/deployment/module with proper type guardsStorage & Auth:
SecretsManagernow stores per-deployment credentials using label-based keys (coder.session.<label>) instead of flatsessionTokenstoragesecrets.onDidChangeeventscoder.debug.listDeployments) for inspecting stored stateCommands & Remote:
DeploymentManagerinstead of directly manipulating clientLoginCoordinatorfor auth prompts during workspace connectionsforceLogoutin favor of unified logout throughDeploymentManagerCliManager.configure()now called on every remote connection, with secrets storage as the source of truth for credentialsWebSocket improvements:
CoderApinow implements Disposable to clean up WebSocket connectionssetCredentials()method to update host+token atomically, avoiding unnecessary reconnection cyclessuspend()support toReconnectingWebSocketfor clean disconnectsTests:
secretsManagertests for new per-deployment APIreconnectingWebSockettests for suspend/reconnectcoderApitests for credential handling