Skip to content

Commit f422f47

Browse files
committed
feat: autostart workspaces on ssh & port forward
This is opt out by default. VScode ssh does not have this behavior
1 parent 55bec81 commit f422f47

File tree

4 files changed

+46
-9
lines changed

4 files changed

+46
-9
lines changed

cli/ping.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func (r *RootCmd) ping() *clibase.Cmd {
4040
workspaceName := inv.Args[0]
4141
_, workspaceAgent, err := getWorkspaceAndAgent(
4242
ctx, inv, client,
43+
false, // Do not autostart for a ping.
4344
codersdk.Me, workspaceName,
4445
)
4546
if err != nil {

cli/portforward.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ import (
2626

2727
func (r *RootCmd) portForward() *clibase.Cmd {
2828
var (
29-
tcpForwards []string // <port>:<port>
30-
udpForwards []string // <port>:<port>
29+
tcpForwards []string // <port>:<port>
30+
udpForwards []string // <port>:<port>
31+
disableAutostart bool
3132
)
3233
client := new(codersdk.Client)
3334
cmd := &clibase.Cmd{
@@ -76,7 +77,7 @@ func (r *RootCmd) portForward() *clibase.Cmd {
7677
return xerrors.New("no port-forwards requested")
7778
}
7879

79-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0])
80+
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, codersdk.Me, inv.Args[0])
8081
if err != nil {
8182
return err
8283
}
@@ -180,6 +181,13 @@ func (r *RootCmd) portForward() *clibase.Cmd {
180181
Description: "Forward UDP port(s) from the workspace to the local machine. The UDP connection has TCP-like semantics to support stateful UDP protocols.",
181182
Value: clibase.StringArrayOf(&udpForwards),
182183
},
184+
{
185+
Flag: "disable-autostart",
186+
Description: "Disable starting the workspace automatically when connecting via port forward.",
187+
Env: "CODER_PORT_FORWARD_DISABLE_AUTOSTART",
188+
Value: clibase.BoolOf(&disableAutostart),
189+
Default: "false",
190+
},
183191
}
184192

185193
return cmd

cli/speedtest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd {
3535
ctx, cancel := context.WithCancel(inv.Context())
3636
defer cancel()
3737

38-
_, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0])
38+
_, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, false, codersdk.Me, inv.Args[0])
3939
if err != nil {
4040
return err
4141
}

cli/ssh.go

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
144144
}
145145
}
146146

147-
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0])
147+
workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, codersdk.Me, inv.Args[0])
148148
if err != nil {
149149
return err
150150
}
@@ -538,9 +538,9 @@ startWatchLoop:
538538
}
539539

540540
// getWorkspaceAgent returns the workspace and agent selected using either the
541-
// `<workspace>[.<agent>]` syntax via `in` or picks a random workspace and agent
542-
// if `shuffle` is true.
543-
func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *codersdk.Client, userID string, in string) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive
541+
// `<workspace>[.<agent>]` syntax via `in`.
542+
// If autoStart is true, the workspace will be started if it is not already running.
543+
func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *codersdk.Client, autostart bool, userID string, in string) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive
544544
var (
545545
workspace codersdk.Workspace
546546
workspaceParts = strings.Split(in, ".")
@@ -553,7 +553,35 @@ func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *
553553
}
554554

555555
if workspace.LatestBuild.Transition != codersdk.WorkspaceTransitionStart {
556-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.New("workspace must be in start transition to ssh")
556+
if !autostart {
557+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.New("workspace must be in start transition to ssh")
558+
}
559+
// Autostart the workspace for the user.
560+
// For some failure modes, return a better message.
561+
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionDelete {
562+
// Any sort of deleting status, we should reject with a nicer error.
563+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace %q is deleted", workspace.Name)
564+
}
565+
if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobFailed {
566+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{},
567+
xerrors.Errorf("workspace %q is in failed state, unable to autostart the workspace", workspace.Name)
568+
}
569+
// The workspace needs to be stopped before we can start it.
570+
// It cannot be in any pending or failed state.
571+
if workspace.LatestBuild.Status != codersdk.WorkspaceStatusStopped {
572+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{},
573+
xerrors.Errorf("workspace must be in start transition to ssh, was unable to autostart as the last build job is %q, expected %q",
574+
workspace.LatestBuild.Status,
575+
codersdk.WorkspaceStatusStopped,
576+
)
577+
}
578+
// startWorkspace based on the last build parameters.
579+
_, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connection %q...\n", workspace.Name)
580+
build, err := startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceStart)
581+
if err != nil {
582+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace is stopped, failed to start: %w", err)
583+
}
584+
workspace.LatestBuild = build
557585
}
558586
if workspace.LatestBuild.Job.CompletedAt == nil {
559587
err := cliui.WorkspaceBuild(ctx, inv.Stderr, client, workspace.LatestBuild.ID)

0 commit comments

Comments
 (0)