Skip to content

[DO NOT MERGE] Toolbox refactor #478

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

Draft
wants to merge 11 commits into
base: main
Choose a base branch
from
Prev Previous commit
[toolbox] Add workspace actions (#486)
You can now start, stop, and update workspaces.  Since you can start
workspaces yourself now, I marked non-ready states as unreachable, which
prevents JetBrains from overriding with their own text ("disconnected"
and "connected").  So now you will be able to see "stopped", "starting",
and so on.  For the ready states you will still see "disconnected" or
"connected" unfortunately.  Ideally this would be a completely separate
state displayed next to the workspace state.
  • Loading branch information
code-asher authored Sep 24, 2024
commit 547fdead9fca06ace85206dc75d4be610f4e443e
54 changes: 52 additions & 2 deletions src/main/kotlin/com/coder/gateway/CoderRemoteEnvironment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import com.coder.gateway.models.WorkspaceAndAgentStatus
import com.coder.gateway.sdk.CoderRestClient
import com.coder.gateway.sdk.v2.models.Workspace
import com.coder.gateway.sdk.v2.models.WorkspaceAgent
import com.coder.gateway.util.withPath
import com.coder.gateway.views.Action
import com.coder.gateway.views.EnvironmentView
import com.jetbrains.toolbox.gateway.AbstractRemoteProviderEnvironment
import com.jetbrains.toolbox.gateway.EnvironmentVisibilityState
import com.jetbrains.toolbox.gateway.environments.EnvironmentContentsView
import com.jetbrains.toolbox.gateway.states.EnvironmentStateConsumer
import com.jetbrains.toolbox.gateway.ui.ObservablePropertiesFactory
import com.jetbrains.toolbox.gateway.ui.ToolboxUi
import java.util.concurrent.CompletableFuture

/**
Expand All @@ -19,18 +22,60 @@ import java.util.concurrent.CompletableFuture
*/
class CoderRemoteEnvironment(
private val client: CoderRestClient,
private val workspace: Workspace,
private val agent: WorkspaceAgent,
private var workspace: Workspace,
private var agent: WorkspaceAgent,
private val ui: ToolboxUi,
observablePropertiesFactory: ObservablePropertiesFactory,
) : AbstractRemoteProviderEnvironment(observablePropertiesFactory) {
override fun getId(): String = "${workspace.name}.${agent.name}"
override fun getName(): String = "${workspace.name}.${agent.name}"
private var status = WorkspaceAndAgentStatus.from(workspace, agent)

init {
actionsList.add(
Action("Open web terminal") {
ui.openUrl(client.url.withPath("/${workspace.ownerName}/$name/terminal").toString())
},
)
actionsList.add(
Action("Open in dashboard") {
ui.openUrl(client.url.withPath("/@${workspace.ownerName}/${workspace.name}").toString())
},
)
actionsList.add(
Action("View template") {
ui.openUrl(client.url.withPath("/templates/${workspace.templateName}").toString())
},
)
actionsList.add(
Action("Start", enabled = { status.canStart() }) {
val build = client.startWorkspace(workspace)
workspace = workspace.copy(latestBuild = build)
update(workspace, agent)
},
)
actionsList.add(
Action("Stop", enabled = { status.ready() || status.pending() }) {
val build = client.stopWorkspace(workspace)
workspace = workspace.copy(latestBuild = build)
update(workspace, agent)
},
)
actionsList.add(
Action("Update", enabled = { workspace.outdated }) {
val build = client.updateWorkspace(workspace)
workspace = workspace.copy(latestBuild = build)
update(workspace, agent)
},
)
}

/**
* Update the workspace/agent status to the listeners, if it has changed.
*/
fun update(workspace: Workspace, agent: WorkspaceAgent) {
this.workspace = workspace
this.agent = agent
val newStatus = WorkspaceAndAgentStatus.from(workspace, agent)
if (newStatus != status) {
status = newStatus
Expand Down Expand Up @@ -58,6 +103,11 @@ class CoderRemoteEnvironment(
* Immediately send the state to the listener and store for updates.
*/
override fun addStateListener(consumer: EnvironmentStateConsumer): Boolean {
// TODO@JB: It would be ideal if we could have the workspace state and
// the connected state listed separately, since right now the
// connected state can mask the workspace state.
// TODO@JB: You can still press connect if the environment is
// unreachable. Is that expected?
consumer.consume(status.toRemoteEnvironmentState())
return super.addStateListener(consumer)
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/coder/gateway/CoderRemoteProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class CoderRemoteProvider(
it.name
}?.map { agent ->
// If we have an environment already, update that.
val env = CoderRemoteEnvironment(client, ws, agent, observablePropertiesFactory)
val env = CoderRemoteEnvironment(client, ws, agent, ui, observablePropertiesFactory)
lastEnvironments?.firstOrNull { it == env }?.let {
it.update(ws, agent)
it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,8 @@ enum class WorkspaceAndAgentStatus(val label: String, val description: String) {
* Return the environment state for Toolbox, which tells it the label, color
* and whether the environment is reachable.
*
* We mark all ready and pending states as reachable since if the workspace
* is pending the cli will wait for it anyway.
*
* Additionally, terminal states like stopped are also marked as reachable,
* since the cli will start them.
* Note that a reachable environment will always display "connected" or
* "disconnected" regardless of the label we give that status.
*/
fun toRemoteEnvironmentState(): CustomRemoteEnvironmentState {
// Use comments; no named arguments for non-Kotlin functions.
Expand All @@ -67,7 +64,7 @@ enum class WorkspaceAndAgentStatus(val label: String, val description: String) {
Color(104, 112, 128, 255), // lightThemeColor
Color(224, 224, 240, 26), // darkThemeBackgroundColor
Color(224, 224, 245, 250), // lightThemeBackgroundColor
ready() || pending() || canStart(), // reachable
ready(), // reachable
// TODO@JB: How does this work? Would like a spinner for pending states.
null, // iconId
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ data class Workspace(
@Json(name = "latest_build") val latestBuild: WorkspaceBuild,
@Json(name = "outdated") val outdated: Boolean,
@Json(name = "name") val name: String,
@Json(name = "owner_name") val ownerName: String,
)
4 changes: 3 additions & 1 deletion src/main/kotlin/com/coder/gateway/views/CoderPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,13 @@ abstract class CoderPage(
*/
class Action(
private val label: String,
private val closesPage: Boolean,
private val closesPage: Boolean = false,
private val enabled: () -> Boolean = { true },
private val cb: () -> Unit,
) : RunnableActionDescription {
override fun getLabel(): String = label
override fun getShouldClosePage(): Boolean = closesPage
override fun isEnabled(): Boolean = enabled()
override fun run() {
cb()
}
Expand Down
1 change: 1 addition & 0 deletions src/test/kotlin/com/coder/gateway/sdk/DataGen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class DataGen {
),
outdated = false,
name = name,
ownerName = "owner",
)
}

Expand Down