Skip to content

Commit 9ab8769

Browse files
committed
Check agent status
Previously we only checked the workspace, now we also check the agent. Should help ensure we only connect when the connection will succeed.
1 parent 9b787c7 commit 9ab8769

File tree

5 files changed

+107
-52
lines changed

5 files changed

+107
-52
lines changed

src/main/kotlin/com/coder/gateway/models/WorkspaceAgentModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.coder.gateway.models
22

33
import com.coder.gateway.sdk.Arch
44
import com.coder.gateway.sdk.OS
5+
import com.coder.gateway.sdk.v2.models.WorkspaceStatus
56
import com.coder.gateway.sdk.v2.models.WorkspaceTransition
67
import java.util.UUID
78
import javax.swing.Icon
@@ -15,11 +16,12 @@ data class WorkspaceAgentModel(
1516
val templateIconPath: String,
1617
var templateIcon: Icon?,
1718
val status: WorkspaceVersionStatus,
18-
val agentStatus: WorkspaceAgentStatus,
19+
val workspaceStatus: WorkspaceStatus,
20+
val agentStatus: WorkspaceAndAgentStatus,
1921
val lastBuildTransition: WorkspaceTransition,
2022
val agentOS: OS?,
2123
val agentArch: Arch?,
22-
val homeDirectory: String?
24+
val homeDirectory: String?,
2325
) {
2426
override fun equals(other: Any?): Boolean {
2527
if (this === other) return true

src/main/kotlin/com/coder/gateway/models/WorkspaceAgentStatus.kt

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package com.coder.gateway.models
2+
3+
import com.coder.gateway.sdk.v2.models.Workspace
4+
import com.coder.gateway.sdk.v2.models.WorkspaceAgent
5+
import com.coder.gateway.sdk.v2.models.WorkspaceAgentLifecycleState
6+
import com.coder.gateway.sdk.v2.models.WorkspaceAgentStatus
7+
import com.coder.gateway.sdk.v2.models.WorkspaceStatus
8+
import com.intellij.ui.JBColor
9+
10+
/**
11+
* WorkspaceAndAgentStatus represents the combined status of a single agent and
12+
* its workspace (or just the workspace if there are no agents).
13+
*/
14+
enum class WorkspaceAndAgentStatus(val label: String) {
15+
// Workspace states.
16+
QUEUED("◍ Queued"), STARTING("⦿ Starting"), FAILED("ⓧ Failed"),
17+
DELETING("⦸ Deleting"), DELETED("⦸ Deleted"),
18+
STOPPING("◍ Stopping"), STOPPED("◍ Stopped"),
19+
CANCELING("◍ Canceling action"), CANCELED("◍ Canceled action"),
20+
RUNNING("⦿ Running"),
21+
22+
// Agent states.
23+
OFF("⦸ Off"), CONNECTING("⦿ Connecting"), DISCONNECTED("⦸ Disconnected"), TIMEOUT("ⓧ Timeout"),
24+
AGENT_STARTING("⦿ Starting"), AGENT_STARTING_READY("⦿ Starting"),
25+
CREATED("⦿ Created"), START_ERROR("◍ Started with error"), START_TIMEOUT("◍ Started with timeout"),
26+
SHUTTING_DOWN("◍ Shutting down"), SHUTDOWN_ERROR("⦸ Shutdown with error"), SHUTDOWN_TIMEOUT("⦸ Shutdown with timeout"),
27+
READY("⦿ Ready");
28+
29+
fun statusColor(): JBColor = when (this) {
30+
READY, AGENT_STARTING_READY -> JBColor.GREEN
31+
START_ERROR, START_TIMEOUT -> JBColor.YELLOW
32+
FAILED, DISCONNECTED, TIMEOUT, SHUTTING_DOWN, SHUTDOWN_ERROR, SHUTDOWN_TIMEOUT -> JBColor.RED
33+
else -> if (JBColor.isBright()) JBColor.LIGHT_GRAY else JBColor.DARK_GRAY
34+
}
35+
36+
// We want to check that the workspace is `running`, the agent is
37+
// `connected`, and the agent lifecycle state is `ready` to ensure the best
38+
// possible scenario for attempting a connection.
39+
//
40+
// We can also choose to allow `start_timeout` and `start_error` for the
41+
// agent state; this means the startup script did not successfully complete
42+
// but the agent will accept SSH connections.
43+
//
44+
// Lastly we can also allow connections when the agent lifecycle state is
45+
// `starting` if `login_before_ready` is true on the workspace response.
46+
//
47+
// Note that latest_build.status is derived from latest_build.job.status and
48+
// latest_build.job.transition so there is no need to check those.
49+
companion object {
50+
fun from(workspace: Workspace, agent: WorkspaceAgent? = null) = when (workspace.latestBuild.status) {
51+
WorkspaceStatus.PENDING -> QUEUED
52+
WorkspaceStatus.STARTING -> STARTING
53+
WorkspaceStatus.RUNNING -> when (agent?.status) {
54+
WorkspaceAgentStatus.CONNECTED -> when (agent.lifecycleState) {
55+
WorkspaceAgentLifecycleState.CREATED -> CREATED
56+
WorkspaceAgentLifecycleState.STARTING -> if (agent.loginBeforeReady == true) AGENT_STARTING_READY else AGENT_STARTING
57+
WorkspaceAgentLifecycleState.START_TIMEOUT -> START_TIMEOUT
58+
WorkspaceAgentLifecycleState.START_ERROR -> START_ERROR
59+
WorkspaceAgentLifecycleState.READY -> READY
60+
WorkspaceAgentLifecycleState.SHUTTING_DOWN -> SHUTTING_DOWN
61+
WorkspaceAgentLifecycleState.SHUTDOWN_TIMEOUT -> SHUTDOWN_TIMEOUT
62+
WorkspaceAgentLifecycleState.SHUTDOWN_ERROR -> SHUTDOWN_ERROR
63+
WorkspaceAgentLifecycleState.OFF -> OFF
64+
}
65+
66+
WorkspaceAgentStatus.DISCONNECTED -> DISCONNECTED
67+
WorkspaceAgentStatus.TIMEOUT -> TIMEOUT
68+
WorkspaceAgentStatus.CONNECTING -> CONNECTING
69+
else -> RUNNING
70+
}
71+
72+
WorkspaceStatus.STOPPING -> STOPPING
73+
WorkspaceStatus.STOPPED -> STOPPED
74+
WorkspaceStatus.FAILED -> FAILED
75+
WorkspaceStatus.CANCELING -> CANCELING
76+
WorkspaceStatus.CANCELED -> CANCELED
77+
WorkspaceStatus.DELETING -> DELETING
78+
WorkspaceStatus.DELETED -> DELETED
79+
}
80+
81+
fun from(str: String) = WorkspaceAndAgentStatus.values().first { it.label.contains(str, true) }
82+
}
83+
}

src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ import com.coder.gateway.icons.CoderIcons
55
import com.coder.gateway.models.CoderWorkspacesWizardModel
66
import com.coder.gateway.models.TokenSource
77
import com.coder.gateway.models.WorkspaceAgentModel
8-
import com.coder.gateway.models.WorkspaceAgentStatus
9-
import com.coder.gateway.models.WorkspaceAgentStatus.FAILED
10-
import com.coder.gateway.models.WorkspaceAgentStatus.RUNNING
11-
import com.coder.gateway.models.WorkspaceAgentStatus.STOPPED
8+
import com.coder.gateway.models.WorkspaceAndAgentStatus
129
import com.coder.gateway.models.WorkspaceVersionStatus
1310
import com.coder.gateway.sdk.Arch
1411
import com.coder.gateway.sdk.CoderCLIManager
@@ -24,6 +21,7 @@ import com.coder.gateway.sdk.ex.TemplateResponseException
2421
import com.coder.gateway.sdk.ex.WorkspaceResponseException
2522
import com.coder.gateway.sdk.toURL
2623
import com.coder.gateway.sdk.v2.models.Workspace
24+
import com.coder.gateway.sdk.v2.models.WorkspaceStatus
2725
import com.coder.gateway.sdk.withPath
2826
import com.coder.gateway.services.CoderSettingsState
2927
import com.intellij.ide.ActivityTracker
@@ -134,8 +132,12 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
134132
setEmptyState("Disconnected")
135133
setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
136134
selectionModel.addListSelectionListener {
137-
setNextButtonEnabled(selectedObject != null && selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS == OS.LINUX)
138-
if (selectedObject?.agentStatus == RUNNING && selectedObject?.agentOS != OS.LINUX) {
135+
val ready = listOf(
136+
WorkspaceAndAgentStatus.READY, WorkspaceAndAgentStatus.START_ERROR,
137+
WorkspaceAndAgentStatus.START_TIMEOUT, WorkspaceAndAgentStatus.AGENT_STARTING_READY
138+
).contains(selectedObject?.agentStatus)
139+
setNextButtonEnabled(ready && selectedObject?.agentOS == OS.LINUX)
140+
if (ready && selectedObject?.agentOS != OS.LINUX) {
139141
notificationBanner.apply {
140142
component.isVisible = true
141143
showInfo(CoderGatewayBundle.message("gateway.connector.view.coder.workspaces.unsupported.os.info"))
@@ -384,8 +386,8 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
384386
private fun updateWorkspaceActions() {
385387
goToDashboardAction.isEnabled = coderClient.isReady
386388
createWorkspaceAction.isEnabled = coderClient.isReady
387-
when (tableOfWorkspaces.selectedObject?.agentStatus) {
388-
RUNNING -> {
389+
when (tableOfWorkspaces.selectedObject?.workspaceStatus) {
390+
WorkspaceStatus.RUNNING -> {
389391
startWorkspaceAction.isEnabled = false
390392
stopWorkspaceAction.isEnabled = true
391393
when (tableOfWorkspaces.selectedObject?.status) {
@@ -395,7 +397,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
395397

396398
}
397399

398-
STOPPED, FAILED -> {
400+
WorkspaceStatus.STOPPED, WorkspaceStatus.FAILED -> {
399401
startWorkspaceAction.isEnabled = true
400402
stopWorkspaceAction.isEnabled = false
401403
when (tableOfWorkspaces.selectedObject?.status) {
@@ -698,7 +700,8 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
698700
this.templateIcon,
699701
null,
700702
WorkspaceVersionStatus.from(this),
701-
WorkspaceAgentStatus.from(this),
703+
this.latestBuild.status,
704+
WorkspaceAndAgentStatus.from(this, agent),
702705
this.latestBuild.transition,
703706
OS.from(agent.operatingSystem),
704707
Arch.from(agent.architecture),
@@ -723,7 +726,8 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
723726
this.templateIcon,
724727
null,
725728
WorkspaceVersionStatus.from(this),
726-
WorkspaceAgentStatus.from(this),
729+
this.latestBuild.status,
730+
WorkspaceAndAgentStatus.from(this),
727731
this.latestBuild.transition,
728732
null,
729733
null,
@@ -918,7 +922,7 @@ class CoderWorkspacesStepView(val setNextButtonEnabled: (Boolean) -> Unit) : Cod
918922
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column)
919923
if (value is String) {
920924
text = value
921-
foreground = WorkspaceAgentStatus.from(value).statusColor()
925+
foreground = WorkspaceAndAgentStatus.from(value).statusColor()
922926
}
923927
font = this@CoderWorkspacesStepView.tableOfWorkspaces.tableHeader.font
924928
border = JBUI.Borders.empty(0, 8)

src/test/groovy/CoderCLIManagerTest.groovy

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package com.coder.gateway.sdk
22

33
import com.coder.gateway.models.WorkspaceAgentModel
4-
import com.coder.gateway.models.WorkspaceAgentStatus
4+
import com.coder.gateway.models.WorkspaceAndAgentStatus
55
import com.coder.gateway.models.WorkspaceVersionStatus
6+
import com.coder.gateway.sdk.v2.models.WorkspaceStatus
67
import com.coder.gateway.sdk.v2.models.WorkspaceTransition
78
import com.sun.net.httpserver.HttpExchange
89
import com.sun.net.httpserver.HttpHandler
@@ -373,7 +374,8 @@ class CoderCLIManagerTest extends Specification {
373374
"template-icon-path",
374375
null,
375376
WorkspaceVersionStatus.UPDATED,
376-
WorkspaceAgentStatus.RUNNING,
377+
WorkspaceStatus.RUNNING,
378+
WorkspaceAndAgentStatus.READY,
377379
WorkspaceTransition.START,
378380
null,
379381
null,

0 commit comments

Comments
 (0)