Skip to content

Start workspaces by shelling out to CLI #518

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

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,21 @@
return matches
}

/**
* Start a workspace.
*
* Throws if the command execution fails.
*/
fun startWorkspace(workspaceOwner: String, workspaceName: String): String {
return exec(
"--global-config",
coderConfigPath.toString(),
"start",
"--yes",
workspaceOwner+"/"+workspaceName,

Check notice on line 465 in src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt

View workflow job for this annotation

GitHub Actions / Build

String concatenation that can be converted to string template

'String' concatenation can be converted to a template
)
}

private fun exec(vararg args: String): String {
val stdout =
ProcessExecutor()
Expand Down Expand Up @@ -521,6 +536,6 @@
@JvmStatic
fun getBackgroundHostName(
hostname: String,
): String = hostname + "--bg"

Check notice on line 539 in src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt

View workflow job for this annotation

GitHub Actions / Build

String concatenation that can be converted to string template

'String' concatenation can be converted to a template
}
}
12 changes: 0 additions & 12 deletions src/main/kotlin/com/coder/gateway/sdk/CoderRestClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,6 @@ open class CoderRestClient(
return templateResponse.body()!!
}

/**
* @throws [APIResponseException].
*/
fun startWorkspace(workspace: Workspace): WorkspaceBuild {
val buildRequest = CreateWorkspaceBuildRequest(null, WorkspaceTransition.START)
val buildResponse = retroRestClient.createWorkspaceBuild(workspace.id, buildRequest).execute()
if (buildResponse.code() != HttpURLConnection.HTTP_CREATED) {
throw APIResponseException("start workspace ${workspace.name}", url, buildResponse)
}
return buildResponse.body()!!
}

/**
* @throws [APIResponseException].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package com.coder.gateway.views
import com.coder.gateway.CoderGatewayBundle
import com.coder.gateway.CoderGatewayConstants
import com.coder.gateway.CoderRemoteConnectionHandle
import com.coder.gateway.cli.CoderCLIManager
import com.coder.gateway.cli.ensureCLI
import com.coder.gateway.icons.CoderIcons
import com.coder.gateway.models.WorkspaceAgentListModel
import com.coder.gateway.models.WorkspaceProjectIDE
Expand Down Expand Up @@ -73,6 +75,8 @@ data class DeploymentInfo(
var items: List<WorkspaceAgentListModel>? = null,
// Null if there have not been any errors yet.
var error: String? = null,
// Null if unable to ensure the CLI is downloaded.
var cli: CoderCLIManager? = null,
)

class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback: (Component) -> Unit) :
Expand Down Expand Up @@ -232,10 +236,10 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
if (enableLinks) {
cell(
ActionLink(workspaceProjectIDE.projectPathDisplay) {
withoutNull(deployment?.client, workspaceWithAgent?.workspace) { client, workspace ->
withoutNull(deployment?.cli, workspaceWithAgent?.workspace) { cli, workspace ->
CoderRemoteConnectionHandle().connect {
if (listOf(WorkspaceStatus.STOPPED, WorkspaceStatus.CANCELED, WorkspaceStatus.FAILED).contains(workspace.latestBuild.status)) {
client.startWorkspace(workspace)
cli.startWorkspace(workspace.ownerName, workspace.name)
}
workspaceProjectIDE
}
Expand Down Expand Up @@ -358,6 +362,19 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
throw Exception("Unable to make request; token was not found in CLI config.")
}

val cli = ensureCLI(
deploymentURL.toURL(),
client.buildInfo().version,
settings,
)

// We only need to log the cli in if we have token-based auth.
// Otherwise, we assume it is set up in the same way the plugin
// is with mTLS.
if (client.token != null) {
cli.login(client.token)
}

// This is purely to populate the current user, which is
// used to match workspaces that were not recorded with owner
// information.
Expand All @@ -378,6 +395,7 @@ class CoderGatewayRecentWorkspaceConnectionsView(private val setContentCallback:
}

deployment.client = client
deployment.cli = cli
deployment.items = items
deployment.error = null
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,13 @@
CoderIcons.RUN,
) {
override fun actionPerformed(p0: AnActionEvent) {
withoutNull(client, tableOfWorkspaces.selectedObject?.workspace) { c, workspace ->
withoutNull(cliManager, tableOfWorkspaces.selectedObject?.workspace) { cliManager, workspace ->
jobs[workspace.id]?.cancel()
jobs[workspace.id] =
cs.launch(ModalityState.current().asContextElement()) {
withContext(Dispatchers.IO) {
try {
c.startWorkspace(workspace)
cliManager.startWorkspace(workspace.ownerName, workspace.name)
loadWorkspaces()
} catch (e: Exception) {
logger.error("Could not start workspace ${workspace.name}", e)
Expand Down Expand Up @@ -659,7 +659,7 @@
cs.launch(ModalityState.current().asContextElement()) {
while (isActive) {
loadWorkspaces()
delay(5000)
delay(1000)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made this poll more frequently because so it picks up the "Starting" state faster while the CLI is starting the workspace. I'm not sure if there's a good way to switch to fast polling while the CLI is running and then switch back - feel free to make suggestions or commit to the branch!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can redirect the output to somewhere (or nothing, since we are not using it) so we can return immediately from the exec and loadWorkspaces() will run right away to pick up the starting state as quickly as possible. Let me experiment for a bit and see. Code looks good to me though!

}
}
}
Expand Down Expand Up @@ -910,7 +910,7 @@
}

private class WorkspaceVersionColumnInfo(columnName: String) : ColumnInfo<WorkspaceAgentListModel, String>(columnName) {
override fun valueOf(workspace: WorkspaceAgentListModel?): String? = if (workspace == null) {

Check warning on line 913 in src/main/kotlin/com/coder/gateway/views/steps/CoderWorkspacesStepView.kt

View workflow job for this annotation

GitHub Actions / Build

Redundant nullable return type

'valueOf' always returns non-null type
"Unknown"
} else if (workspace.workspace.outdated) {
"Outdated"
Expand Down
Loading