Skip to content

Commit

Permalink
Update recents to support workspaces from other users
Browse files Browse the repository at this point in the history
  • Loading branch information
code-asher committed Oct 4, 2024
1 parent 30839c8 commit 322a95c
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 9 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
include agents in the initial workspaces query) and add them individually to
the SSH config. In the future, we would like to use a wildcard host name to
work around this issue.

Additionally, be aware that the recents view is using the same query filter.
This means if you connect to a workspace, then change the filter such that the
workspace is excluded, you could cause the workspace to be deleted from the
recent connections even if the workspace still exists in actuality, as it
would no longer show up in the query which the plugin takes as its cue to
delete the connection.
- Add owner column to connections view table.
- Add agent name to the recent connections view.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import kotlin.io.path.name
* workspace.
*/
class WorkspaceProjectIDE(
// Either `workspace.agent` for old connections or `user/workspace.agent`
// for new connections.
val name: String,
val hostname: String,
val projectPath: String,
Expand Down
3 changes: 1 addition & 2 deletions src/main/kotlin/com/coder/gateway/util/LinkHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ open class LinkHandler(
indicator?.invoke("Configuring Coder CLI...")
cli.configSsh(workspacesAndAgents = client.withAgents(workspaces), currentUser = client.me)

val name = "${workspace.name}.${agent.name}"
val openDialog =
parameters.ideProductCode().isNullOrBlank() ||
parameters.ideBuildNumber().isNullOrBlank() ||
Expand All @@ -122,7 +121,7 @@ open class LinkHandler(
// allowlisted. If not, check with the user whether to proceed.
verifyDownloadLink(parameters)
WorkspaceProjectIDE.fromInputs(
name = name,
name = CoderCLIManager.getWorkspaceParts(workspace, agent),
hostname = CoderCLIManager.getHostName(deploymentURL.toURL(), workspace, client.me, agent),
projectPath = parameters.folder(),
ideProductCode = parameters.ideProductCode(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,16 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
} else {
false
}
val workspaceWithAgent = deployment?.items?.firstOrNull { it.workspace.name == workspaceName }
val me = deployment?.client?.me?.username
val workspaceWithAgent = deployment?.items?.firstOrNull {
it.workspace.ownerName + "/" + it.workspace.name == workspaceName ||
(it.workspace.ownerName == me && it.workspace.name == workspaceName)
}
val status =
if (deploymentError != null) {
Triple(UIUtil.getErrorForeground(), deploymentError, UIUtil.getBalloonErrorIcon())
} else if (workspaceWithAgent != null) {
val inLoadingState = listOf(WorkspaceStatus.STARTING, WorkspaceStatus.CANCELING, WorkspaceStatus.DELETING, WorkspaceStatus.STOPPING).contains(workspaceWithAgent?.workspace?.latestBuild?.status)
val inLoadingState = listOf(WorkspaceStatus.STARTING, WorkspaceStatus.CANCELING, WorkspaceStatus.DELETING, WorkspaceStatus.STOPPING).contains(workspaceWithAgent.workspace.latestBuild.status)

Triple(
workspaceWithAgent.status.statusColor(),
Expand Down Expand Up @@ -244,7 +248,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
foreground = Color.GRAY
}
}
label(workspaceProjectIDE.name.replace(workspaceName + ".", "")).resizableColumn()
label(workspaceProjectIDE.name.replace("$workspaceName.", "")).resizableColumn()
label(workspaceProjectIDE.ideName).applyToComponent {
foreground = JBUI.CurrentTheme.ContextHelp.FOREGROUND
font = ComponentPanelBuilder.getCommentFont(font)
Expand Down Expand Up @@ -276,7 +280,10 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
}

/**
* Get valid connections grouped by deployment and workspace.
* Get valid connections grouped by deployment and workspace name. The
* workspace name will be in the form `owner/workspace.agent`, without the agent
* name, or just `workspace`, if the connection predates when we added owner
* information, in which case it belongs to the current user.
*/
private fun getConnectionsByDeployment(filter: Boolean): Map<String, Map<String, List<WorkspaceProjectIDE>>> = recentConnectionsService.getAllRecentConnections()
// Validate and parse connections.
Expand Down Expand Up @@ -351,10 +358,20 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
throw Exception("Unable to make request; token was not found in CLI config.")
}

// This is purely to populate the current user, which is
// used to match workspaces that were not recorded with owner
// information.
val me = client.authenticate().username

// Delete connections that have no workspace.
// TODO: Deletion without confirmation seems sketchy.
val items = client.workspaces().flatMap { it.toAgentList() }
connectionsByWorkspace.forEach { (name, connections) ->
if (items.firstOrNull { it.workspace.name == name } == null) {
if (items.firstOrNull {
it.workspace.ownerName + "/" + it.workspace.name == name ||
(it.workspace.ownerName == me && it.workspace.name == name)
} == null
) {
logger.info("Removing recent connections for deleted workspace $name (found ${connections.size})")
connections.forEach { recentConnectionsService.removeConnection(it.toRecentWorkspaceConnection()) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,9 +402,8 @@ class CoderWorkspaceProjectIDEStepView(
* Return the selected parameters. Throw if not configured.
*/
override fun data(): WorkspaceProjectIDE = withoutNull(cbIDE.selectedItem, state) { selectedIDE, state ->
val name = "${state.workspace.name}.${state.agent.name}"
selectedIDE.withWorkspaceProject(
name = name,
name = CoderCLIManager.getWorkspaceParts(state.workspace, state.agent),
hostname = CoderCLIManager.getHostName(state.client.url, state.workspace, state.client.me, state.agent),
projectPath = tfProject.text,
deploymentURL = state.client.url,
Expand Down

0 comments on commit 322a95c

Please sign in to comment.