Skip to content

Commit fd1acde

Browse files
committed
Fix reconnection window getting stuck
- If the deployment has an error (either in creating the client or performing a query), keep trying instead of giving up forever. - Workspace status was masking deployment errors if we had successfully fetched at least once; now the deployment error will show up and put workspaces into an error state. - Dynamically query new deployments instead of using the same set. Closes #398.
1 parent 3dfdd92 commit fd1acde

File tree

1 file changed

+36
-32
lines changed

1 file changed

+36
-32
lines changed

src/main/kotlin/com/coder/gateway/views/CoderGatewayRecentWorkspaceConnectionsView.kt

+36-32
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
9595
* API clients and workspaces grouped by deployment and keyed by their
9696
* config directory.
9797
*/
98-
private var deployments: Map<String, DeploymentInfo> = emptyMap()
98+
private var deployments: MutableMap<String, DeploymentInfo> = mutableMapOf()
9999
private var poller: Job? = null
100100

101101
override fun createRecentsView(lifetime: Lifetime): JComponent {
@@ -167,8 +167,8 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
167167
// Group by the deployment.
168168
.groupBy { it.deploymentURL }
169169
// Group the connections in each deployment by workspace.
170-
.mapValues { (deploymentURL, connections) ->
171-
connections
170+
.mapValues { (deploymentURL, deploymentConnections) ->
171+
deploymentConnections
172172
.groupBy { it.name.split(".", limit = 2).first() }
173173
// Find the matching workspace in the query response.
174174
.mapValues { (workspaceName, connections) ->
@@ -178,9 +178,10 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
178178
}
179179
// Remove connections to workspaces that no longer exist.
180180
.filter {
181-
if (it.value.first == null && deployments[deploymentURL]?.didFetch() == true) {
182-
logger.info("Removing recent connections for deleted workspace ${it.key} (found ${it.value.second.size})")
183-
it.value.second.forEach { conn ->
181+
val (workspaceWithAgent, workspaceConnections) = it.value
182+
if (workspaceWithAgent == null && deployments[deploymentURL]?.didFetch() == true) {
183+
logger.info("Removing recent connections for deleted workspace ${it.key} (found ${workspaceConnections.size})")
184+
workspaceConnections.forEach { conn ->
184185
recentConnectionsService.removeConnection(conn.toRecentWorkspaceConnection())
185186
}
186187
false
@@ -195,10 +196,10 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
195196
val deploymentError = deployment?.error
196197
connectionsByWorkspace.forEach { (workspaceName, value) ->
197198
val (workspaceWithAgent, connections) = value
198-
val status = if (workspaceWithAgent != null) {
199-
Triple(workspaceWithAgent.status.icon, workspaceWithAgent.status.statusColor(), workspaceWithAgent.status.description)
200-
} else if (deploymentError != null) {
199+
val status = if (deploymentError != null) {
201200
Triple(UIUtil.getBalloonErrorIcon(), UIUtil.getErrorForeground(), deploymentError)
201+
} else if (workspaceWithAgent != null) {
202+
Triple(workspaceWithAgent.status.icon, workspaceWithAgent.status.statusColor(), workspaceWithAgent.status.description)
202203
} else {
203204
Triple(AnimatedIcon.Default.INSTANCE, UIUtil.getContextHelpForeground(), "Querying workspace status...")
204205
}
@@ -293,20 +294,6 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
293294
* Start polling for workspaces if not already started.
294295
*/
295296
private fun triggerWorkspacePolling() {
296-
deployments = recentConnectionsService.getAllRecentConnections()
297-
.mapNotNull { it.deploymentURL }.toSet()
298-
.associateWith { deploymentURL ->
299-
deployments[deploymentURL] ?: try {
300-
val cli = CoderCLIManager(deploymentURL.toURL())
301-
val (url, token) = settings.readConfig(cli.coderConfigPath)
302-
val client = CoderRestClientService(url?.toURL() ?: deploymentURL.toURL(), token)
303-
DeploymentInfo(client)
304-
} catch (e: Exception) {
305-
logger.error("Unable to create client for $deploymentURL", e)
306-
DeploymentInfo(error = "Error connecting to $deploymentURL: ${e.message}")
307-
}
308-
}
309-
310297
if (poller?.isActive == true) {
311298
logger.info("Refusing to start already-started poller")
312299
return
@@ -331,16 +318,33 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
331318
*/
332319
private suspend fun fetchWorkspaces() {
333320
withContext(Dispatchers.IO) {
334-
deployments.values
335-
.filter { it.error == null && it.client != null}
336-
.forEach { deployment ->
337-
val url = deployment.client!!.url
338-
try {
339-
deployment.items = deployment.client!!
340-
.workspaces().flatMap { it.toAgentList() }
321+
recentConnectionsService.getAllRecentConnections()
322+
.mapNotNull { it.deploymentURL }
323+
.toSet()
324+
.map { Pair(it, deployments.getOrPut(it) { DeploymentInfo() }) }
325+
.forEach { (deploymentURL, deployment) ->
326+
val client = deployment.client ?: try {
327+
val cli = CoderCLIManager(deploymentURL.toURL())
328+
val (_, token) = settings.readConfig(cli.coderConfigPath)
329+
deployment.client = CoderRestClientService(deploymentURL.toURL(), token)
330+
deployment.error = null
331+
deployment.client
341332
} catch (e: Exception) {
342-
logger.error("Failed to fetch workspaces from $url", e)
343-
deployment.error = e.message ?: "Request failed without further details"
333+
logger.error("Unable to create client for $deploymentURL", e)
334+
deployment.error = "Error connecting to $deploymentURL: ${e.message}"
335+
null
336+
}
337+
if (client != null) {
338+
try {
339+
deployment.items = client.workspaces()
340+
.flatMap { it.toAgentList() }
341+
deployment.error = null
342+
} catch (e: Exception) {
343+
// TODO: If this is an auth error, ask for a new token.
344+
logger.error("Failed to fetch workspaces from ${client.url}", e)
345+
deployment.items = null
346+
deployment.error = e.message ?: "Request failed without further details"
347+
}
344348
}
345349
}
346350
}

0 commit comments

Comments
 (0)