@@ -7,6 +7,7 @@ import com.coder.toolbox.sdk.CoderRestClient
77import com.coder.toolbox.sdk.ex.APIResponseException
88import com.coder.toolbox.sdk.v2.models.Workspace
99import com.coder.toolbox.sdk.v2.models.WorkspaceAgent
10+ import com.coder.toolbox.util.waitForFalseWithTimeout
1011import com.coder.toolbox.util.withPath
1112import com.coder.toolbox.views.Action
1213import com.coder.toolbox.views.EnvironmentView
@@ -43,6 +44,10 @@ class CoderRemoteEnvironment(
4344 private var wsRawStatus = WorkspaceAndAgentStatus .from(workspace, agent)
4445
4546 override var name: String = " ${workspace.name} .${agent.name} "
47+
48+ private var isConnected: MutableStateFlow <Boolean > = MutableStateFlow (false )
49+ override val connectionRequest: MutableStateFlow <Boolean > = MutableStateFlow (false )
50+
4651 override val state: MutableStateFlow <RemoteEnvironmentState > =
4752 MutableStateFlow (wsRawStatus.toRemoteEnvironmentState(context))
4853 override val description: MutableStateFlow <EnvironmentDescription > =
@@ -106,6 +111,8 @@ class CoderRemoteEnvironment(
106111 } else {
107112 actions.add(Action (context.i18n.ptrl(" Stop" )) {
108113 context.cs.launch {
114+ tryStopSshConnection()
115+
109116 val build = client.stopWorkspace(workspace)
110117 update(workspace.copy(latestBuild = build), agent)
111118 }
@@ -115,18 +122,30 @@ class CoderRemoteEnvironment(
115122 return actions
116123 }
117124
125+ private suspend fun tryStopSshConnection () {
126+ if (isConnected.value) {
127+ connectionRequest.update {
128+ false
129+ }
130+
131+ if (isConnected.waitForFalseWithTimeout(10 .seconds) == null ) {
132+ context.logger.warn(" The SSH connection to workspace $name could not be dropped in time, going to stop the workspace while the SSH connection is live" )
133+ }
134+ }
135+ }
136+
118137 override fun getBeforeConnectionHooks (): List <BeforeConnectionHook > = listOf (this )
119138
120139 override fun getAfterDisconnectHooks (): List <AfterDisconnectHook > = listOf (this )
121140
122141 override fun beforeConnection () {
123142 context.logger.info(" Connecting to $id ..." )
124- this . isConnected = true
143+ isConnected.update { true }
125144 }
126145
127146 override fun afterDisconnect () {
128147 this .connectionRequest.update { false }
129- this . isConnected = false
148+ isConnected.update { false }
130149 context.logger.info(" Disconnected from $id " )
131150 }
132151
@@ -161,17 +180,14 @@ class CoderRemoteEnvironment(
161180 agent
162181 )
163182
164- private var isConnected = false
165- override val connectionRequest: MutableStateFlow <Boolean > = MutableStateFlow (false )
166-
167183 /* *
168184 * Does nothing. In theory, we could do something like start the workspace
169185 * when you click into the workspace, but you would still need to press
170186 * "connect" anyway before the content is populated so there does not seem
171187 * to be much value.
172188 */
173189 override fun setVisible (visibilityState : EnvironmentVisibilityState ) {
174- if (wsRawStatus.ready() && visibilityState.contentsVisible == true && isConnected == false ) {
190+ if (wsRawStatus.ready() && visibilityState.contentsVisible == true && isConnected.value == false ) {
175191 context.cs.launch {
176192 connectionRequest.update {
177193 true
0 commit comments