From 55bec8136ea735461bbab446deaa000e9257c6cd Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 5 Dec 2023 12:26:42 -0600 Subject: [PATCH 01/10] feat: autostart workspaces on ssh --- cli/configssh.go | 28 ++++++++++++++++++++++++---- cli/ssh.go | 26 +++++++++++++++++--------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/cli/configssh.go b/cli/configssh.go index 7e9e8109ea554..09a2b471fcd53 100644 --- a/cli/configssh.go +++ b/cli/configssh.go @@ -13,6 +13,7 @@ import ( "path/filepath" "runtime" "sort" + "strconv" "strings" "github.com/cli/safeexec" @@ -46,9 +47,10 @@ const ( // sshConfigOptions represents options that can be stored and read // from the coder config in ~/.ssh/coder. type sshConfigOptions struct { - waitEnum string - userHostPrefix string - sshOptions []string + waitEnum string + userHostPrefix string + sshOptions []string + disableAutostart bool } // addOptions expects options in the form of "option=value" or "option value". @@ -106,7 +108,7 @@ func (o sshConfigOptions) equal(other sshConfigOptions) bool { if !slices.Equal(opt1, opt2) { return false } - return o.waitEnum == other.waitEnum && o.userHostPrefix == other.userHostPrefix + return o.waitEnum == other.waitEnum && o.userHostPrefix == other.userHostPrefix && o.disableAutostart == other.disableAutostart } func (o sshConfigOptions) asList() (list []string) { @@ -116,6 +118,9 @@ func (o sshConfigOptions) asList() (list []string) { if o.userHostPrefix != "" { list = append(list, fmt.Sprintf("ssh-host-prefix: %s", o.userHostPrefix)) } + if o.disableAutostart { + list = append(list, fmt.Sprintf("disable-autostart: %v", o.disableAutostart)) + } for _, opt := range o.sshOptions { list = append(list, fmt.Sprintf("ssh-option: %s", opt)) } @@ -392,6 +397,9 @@ func (r *RootCmd) configSSH() *clibase.Cmd { if sshConfigOpts.waitEnum != "auto" { flags += " --wait=" + sshConfigOpts.waitEnum } + if sshConfigOpts.disableAutostart { + flags += " --disable-autostart=true" + } defaultOptions = append(defaultOptions, fmt.Sprintf( "ProxyCommand %s --global-config %s ssh --stdio%s %s", escapedCoderBinary, escapedGlobalConfig, flags, workspaceHostname, @@ -566,6 +574,13 @@ func (r *RootCmd) configSSH() *clibase.Cmd { Default: "auto", Value: clibase.EnumOf(&sshConfigOpts.waitEnum, "yes", "no", "auto"), }, + { + Flag: "disable-autostart", + Description: "Disable starting the workspace automatically when connecting via SSH.", + Env: "CODER_CONFIGSSH_DISABLE_AUTOSTART", + Value: clibase.BoolOf(&sshConfigOpts.disableAutostart), + Default: "false", + }, { Flag: "force-unix-filepaths", Env: "CODER_CONFIGSSH_UNIX_FILEPATHS", @@ -602,6 +617,9 @@ func sshConfigWriteSectionHeader(w io.Writer, addNewline bool, o sshConfigOption if o.userHostPrefix != "" { _, _ = fmt.Fprintf(&ow, "# :%s=%s\n", "ssh-host-prefix", o.userHostPrefix) } + if o.disableAutostart { + _, _ = fmt.Fprintf(&ow, "# :%s=%v\n", "disable-autostart", o.disableAutostart) + } for _, opt := range o.sshOptions { _, _ = fmt.Fprintf(&ow, "# :%s=%s\n", "ssh-option", opt) } @@ -634,6 +652,8 @@ func sshConfigParseLastOptions(r io.Reader) (o sshConfigOptions) { o.userHostPrefix = parts[1] case "ssh-option": o.sshOptions = append(o.sshOptions, parts[1]) + case "disable-autostart": + o.disableAutostart, _ = strconv.ParseBool(parts[1]) default: // Unknown option, ignore. } diff --git a/cli/ssh.go b/cli/ssh.go index c409bf877ddfe..d367ed85b3400 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -44,15 +44,16 @@ var ( func (r *RootCmd) ssh() *clibase.Cmd { var ( - stdio bool - forwardAgent bool - forwardGPG bool - identityAgent string - wsPollInterval time.Duration - waitEnum string - noWait bool - logDirPath string - remoteForward string + stdio bool + forwardAgent bool + forwardGPG bool + identityAgent string + wsPollInterval time.Duration + waitEnum string + noWait bool + logDirPath string + remoteForward string + disableAutostart bool ) client := new(codersdk.Client) cmd := &clibase.Cmd{ @@ -459,6 +460,13 @@ func (r *RootCmd) ssh() *clibase.Cmd { FlagShorthand: "R", Value: clibase.StringOf(&remoteForward), }, + { + Flag: "disable-autostart", + Description: "Disable starting the workspace automatically when connecting via SSH.", + Env: "CODER_SSH_DISABLE_AUTOSTART", + Value: clibase.BoolOf(&disableAutostart), + Default: "false", + }, } return cmd } From f422f471f6f7b8ca33e1b376e738d9ff1f5262c5 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 5 Dec 2023 12:51:37 -0600 Subject: [PATCH 02/10] feat: autostart workspaces on ssh & port forward This is opt out by default. VScode ssh does not have this behavior --- cli/ping.go | 1 + cli/portforward.go | 14 +++++++++++--- cli/speedtest.go | 2 +- cli/ssh.go | 38 +++++++++++++++++++++++++++++++++----- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/cli/ping.go b/cli/ping.go index 94ca23e3aa459..2ba07740c3d80 100644 --- a/cli/ping.go +++ b/cli/ping.go @@ -40,6 +40,7 @@ func (r *RootCmd) ping() *clibase.Cmd { workspaceName := inv.Args[0] _, workspaceAgent, err := getWorkspaceAndAgent( ctx, inv, client, + false, // Do not autostart for a ping. codersdk.Me, workspaceName, ) if err != nil { diff --git a/cli/portforward.go b/cli/portforward.go index 73a279200cd5d..64676cc3f0471 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -26,8 +26,9 @@ import ( func (r *RootCmd) portForward() *clibase.Cmd { var ( - tcpForwards []string // : - udpForwards []string // : + tcpForwards []string // : + udpForwards []string // : + disableAutostart bool ) client := new(codersdk.Client) cmd := &clibase.Cmd{ @@ -76,7 +77,7 @@ func (r *RootCmd) portForward() *clibase.Cmd { return xerrors.New("no port-forwards requested") } - workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0]) + workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, codersdk.Me, inv.Args[0]) if err != nil { return err } @@ -180,6 +181,13 @@ func (r *RootCmd) portForward() *clibase.Cmd { Description: "Forward UDP port(s) from the workspace to the local machine. The UDP connection has TCP-like semantics to support stateful UDP protocols.", Value: clibase.StringArrayOf(&udpForwards), }, + { + Flag: "disable-autostart", + Description: "Disable starting the workspace automatically when connecting via port forward.", + Env: "CODER_PORT_FORWARD_DISABLE_AUTOSTART", + Value: clibase.BoolOf(&disableAutostart), + Default: "false", + }, } return cmd diff --git a/cli/speedtest.go b/cli/speedtest.go index bfc259dc67eda..a7734bf68a7af 100644 --- a/cli/speedtest.go +++ b/cli/speedtest.go @@ -35,7 +35,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd { ctx, cancel := context.WithCancel(inv.Context()) defer cancel() - _, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0]) + _, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, false, codersdk.Me, inv.Args[0]) if err != nil { return err } diff --git a/cli/ssh.go b/cli/ssh.go index d367ed85b3400..fa93d6a2d6262 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -144,7 +144,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { } } - workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, codersdk.Me, inv.Args[0]) + workspace, workspaceAgent, err := getWorkspaceAndAgent(ctx, inv, client, !disableAutostart, codersdk.Me, inv.Args[0]) if err != nil { return err } @@ -538,9 +538,9 @@ startWatchLoop: } // getWorkspaceAgent returns the workspace and agent selected using either the -// `[.]` syntax via `in` or picks a random workspace and agent -// if `shuffle` is true. -func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *codersdk.Client, userID string, in string) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive +// `[.]` syntax via `in`. +// If autoStart is true, the workspace will be started if it is not already running. +func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *codersdk.Client, autostart bool, userID string, in string) (codersdk.Workspace, codersdk.WorkspaceAgent, error) { //nolint:revive var ( workspace codersdk.Workspace workspaceParts = strings.Split(in, ".") @@ -553,7 +553,35 @@ func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client * } if workspace.LatestBuild.Transition != codersdk.WorkspaceTransitionStart { - return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.New("workspace must be in start transition to ssh") + if !autostart { + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.New("workspace must be in start transition to ssh") + } + // Autostart the workspace for the user. + // For some failure modes, return a better message. + if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionDelete { + // Any sort of deleting status, we should reject with a nicer error. + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace %q is deleted", workspace.Name) + } + if workspace.LatestBuild.Job.Status == codersdk.ProvisionerJobFailed { + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, + xerrors.Errorf("workspace %q is in failed state, unable to autostart the workspace", workspace.Name) + } + // The workspace needs to be stopped before we can start it. + // It cannot be in any pending or failed state. + if workspace.LatestBuild.Status != codersdk.WorkspaceStatusStopped { + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, + xerrors.Errorf("workspace must be in start transition to ssh, was unable to autostart as the last build job is %q, expected %q", + workspace.LatestBuild.Status, + codersdk.WorkspaceStatusStopped, + ) + } + // startWorkspace based on the last build parameters. + _, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connection %q...\n", workspace.Name) + build, err := startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceStart) + if err != nil { + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace is stopped, failed to start: %w", err) + } + workspace.LatestBuild = build } if workspace.LatestBuild.Job.CompletedAt == nil { err := cliui.WorkspaceBuild(ctx, inv.Stderr, client, workspace.LatestBuild.ID) From 0d129487c70f29e8885102629692d0d45274e54b Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 5 Dec 2023 15:01:09 -0600 Subject: [PATCH 03/10] Add unit test --- cli/ssh_test.go | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index b865d1d20776f..e18866515237e 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -21,6 +21,11 @@ import ( "testing" "time" + "github.com/coder/coder/v2/provisioner/echo" + "github.com/google/uuid" + + "github.com/coder/coder/v2/coderd/rbac" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" @@ -86,6 +91,62 @@ func TestSSH(t *testing.T) { pty.WriteLine("exit") <-cmdDone }) + t.Run("StartStoppedWorkspace", func(t *testing.T) { + t.Parallel() + + //store, ps := dbtestutil.NewDB(t) + //ownerClient := coderdtest.New(t, &coderdtest.Options{Pubsub: ps, Database: store, IncludeProvisionerDaemon: true}) + //ownerClient.SetLogger(slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug)) + //first := coderdtest.CreateFirstUser(t, ownerClient) + //client, user := coderdtest.CreateAnotherUser(t, ownerClient, first.OrganizationID) + // + //// Create a stopped workspace. + //workspaceData := dbfake.WorkspaceBuild(t, store, database.Workspace{ + // OrganizationID: first.OrganizationID, + // OwnerID: user.ID, + //}).Seed(database.WorkspaceBuild{ + // Transition: database.WorkspaceTransitionStop, + //}).WithAgent().Do() + //workspace := workspaceData.Workspace + // + authToken := uuid.NewString() + ownerClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) + owner := coderdtest.CreateFirstUser(t, ownerClient) + client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleTemplateAdmin()) + version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionPlan: echo.PlanComplete, + ProvisionApply: echo.ProvisionApplyWithAgent(authToken), + }) + coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID) + template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID) + // Stop the workspace + workspaceBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop) + coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceBuild.ID) + + // + inv, root := clitest.New(t, "ssh", workspace.Name) + clitest.SetupConfig(t, client, root) + pty := ptytest.New(t).Attach(inv) + + ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) + defer cancel() + + cmdDone := tGo(t, func() { + err := inv.WithContext(ctx).Run() + assert.NoError(t, err) + }) + pty.ExpectMatch("⧗ Running") + + _ = agenttest.New(t, client.URL, authToken) + coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID) + + // Shells on Mac, Windows, and Linux all exit shells with the "exit" command. + pty.WriteLine("exit") + <-cmdDone + }) t.Run("ShowTroubleshootingURLAfterTimeout", func(t *testing.T) { t.Parallel() From 2c6a501ab525f890ce8f68cb8b6d804f43ae0693 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Tue, 5 Dec 2023 22:09:23 +0000 Subject: [PATCH 04/10] Make gen --- cli/testdata/coder_config-ssh_--help.golden | 3 +++ cli/testdata/coder_port-forward_--help.golden | 4 ++++ cli/testdata/coder_ssh_--help.golden | 3 +++ docs/cli/config-ssh.md | 10 ++++++++++ docs/cli/port-forward.md | 10 ++++++++++ docs/cli/ssh.md | 10 ++++++++++ 6 files changed, 40 insertions(+) diff --git a/cli/testdata/coder_config-ssh_--help.golden b/cli/testdata/coder_config-ssh_--help.golden index 66ecba4d354e0..ebbfb7a11676c 100644 --- a/cli/testdata/coder_config-ssh_--help.golden +++ b/cli/testdata/coder_config-ssh_--help.golden @@ -21,6 +21,9 @@ OPTIONS: ProxyCommand. By default, the binary invoking this command ('config ssh') is used. + --disable-autostart bool, $CODER_CONFIGSSH_DISABLE_AUTOSTART (default: false) + Disable starting the workspace automatically when connecting via SSH. + -n, --dry-run bool, $CODER_SSH_DRY_RUN Perform a trial run with no changes made, showing a diff at the end. diff --git a/cli/testdata/coder_port-forward_--help.golden b/cli/testdata/coder_port-forward_--help.golden index d4f8e761846f8..40ddabe358d74 100644 --- a/cli/testdata/coder_port-forward_--help.golden +++ b/cli/testdata/coder_port-forward_--help.golden @@ -34,6 +34,10 @@ USAGE: $ coder port-forward --tcp 1.2.3.4:8080:8080 OPTIONS: + --disable-autostart bool, $CODER_PORT_FORWARD_DISABLE_AUTOSTART (default: false) + Disable starting the workspace automatically when connecting via port + forward. + -p, --tcp string-array, $CODER_PORT_FORWARD_TCP Forward TCP port(s) from the workspace to the local machine. diff --git a/cli/testdata/coder_ssh_--help.golden b/cli/testdata/coder_ssh_--help.golden index 14e3ec2f5d973..b76e56a8abafd 100644 --- a/cli/testdata/coder_ssh_--help.golden +++ b/cli/testdata/coder_ssh_--help.golden @@ -6,6 +6,9 @@ USAGE: Start a shell into a workspace OPTIONS: + --disable-autostart bool, $CODER_SSH_DISABLE_AUTOSTART (default: false) + Disable starting the workspace automatically when connecting via SSH. + -A, --forward-agent bool, $CODER_SSH_FORWARD_AGENT Specifies whether to forward the SSH agent specified in $SSH_AUTH_SOCK. diff --git a/docs/cli/config-ssh.md b/docs/cli/config-ssh.md index b46d6bf55b37f..6fece81c58693 100644 --- a/docs/cli/config-ssh.md +++ b/docs/cli/config-ssh.md @@ -34,6 +34,16 @@ workspaces: Optionally specify the absolute path to the coder binary used in ProxyCommand. By default, the binary invoking this command ('config ssh') is used. +### --disable-autostart + +| | | +| ----------- | ----------------------------------------------- | +| Type | bool | +| Environment | $CODER_CONFIGSSH_DISABLE_AUTOSTART | +| Default | false | + +Disable starting the workspace automatically when connecting via SSH. + ### -n, --dry-run | | | diff --git a/docs/cli/port-forward.md b/docs/cli/port-forward.md index 3419269c220fc..e6c4c9e03b784 100644 --- a/docs/cli/port-forward.md +++ b/docs/cli/port-forward.md @@ -42,6 +42,16 @@ machine: ## Options +### --disable-autostart + +| | | +| ----------- | -------------------------------------------------- | +| Type | bool | +| Environment | $CODER_PORT_FORWARD_DISABLE_AUTOSTART | +| Default | false | + +Disable starting the workspace automatically when connecting via port forward. + ### -p, --tcp | | | diff --git a/docs/cli/ssh.md b/docs/cli/ssh.md index 784ba3674f74c..264b36a89583d 100644 --- a/docs/cli/ssh.md +++ b/docs/cli/ssh.md @@ -12,6 +12,16 @@ coder ssh [flags] ## Options +### --disable-autostart + +| | | +| ----------- | ----------------------------------------- | +| Type | bool | +| Environment | $CODER_SSH_DISABLE_AUTOSTART | +| Default | false | + +Disable starting the workspace automatically when connecting via SSH. + ### -A, --forward-agent | | | From dc363a40db0197826109a9ba0ee84198bf452ee0 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 08:37:56 -0600 Subject: [PATCH 05/10] Remove excess comments --- cli/ssh_test.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index e18866515237e..31fe86fa4f173 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -94,21 +94,6 @@ func TestSSH(t *testing.T) { t.Run("StartStoppedWorkspace", func(t *testing.T) { t.Parallel() - //store, ps := dbtestutil.NewDB(t) - //ownerClient := coderdtest.New(t, &coderdtest.Options{Pubsub: ps, Database: store, IncludeProvisionerDaemon: true}) - //ownerClient.SetLogger(slogtest.Make(t, nil).Named("client").Leveled(slog.LevelDebug)) - //first := coderdtest.CreateFirstUser(t, ownerClient) - //client, user := coderdtest.CreateAnotherUser(t, ownerClient, first.OrganizationID) - // - //// Create a stopped workspace. - //workspaceData := dbfake.WorkspaceBuild(t, store, database.Workspace{ - // OrganizationID: first.OrganizationID, - // OwnerID: user.ID, - //}).Seed(database.WorkspaceBuild{ - // Transition: database.WorkspaceTransitionStop, - //}).WithAgent().Do() - //workspace := workspaceData.Workspace - // authToken := uuid.NewString() ownerClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true}) owner := coderdtest.CreateFirstUser(t, ownerClient) From 1c6669de965576abc38e3fdd606d79360b098f89 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 08:46:07 -0600 Subject: [PATCH 06/10] Fix importS --- cli/ssh_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 31fe86fa4f173..237b102c42bd9 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -21,11 +21,7 @@ import ( "testing" "time" - "github.com/coder/coder/v2/provisioner/echo" "github.com/google/uuid" - - "github.com/coder/coder/v2/coderd/rbac" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" @@ -43,7 +39,9 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbfake" "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/coderd/rbac" "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/provisioner/echo" "github.com/coder/coder/v2/provisionersdk/proto" "github.com/coder/coder/v2/pty" "github.com/coder/coder/v2/pty/ptytest" From d87dd798cd1a1efbe356f46c3dbcce6707dfb855 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 09:06:05 -0600 Subject: [PATCH 07/10] Remove pty.Expect which can be racy --- cli/ssh_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 237b102c42bd9..faf69d0d98faf 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -109,7 +109,7 @@ func TestSSH(t *testing.T) { workspaceBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop) coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceBuild.ID) - // + // SSH to the workspace which should autostart it inv, root := clitest.New(t, "ssh", workspace.Name) clitest.SetupConfig(t, client, root) pty := ptytest.New(t).Attach(inv) @@ -121,8 +121,9 @@ func TestSSH(t *testing.T) { err := inv.WithContext(ctx).Run() assert.NoError(t, err) }) - pty.ExpectMatch("⧗ Running") + // When the agent connects, the workspace was started, and we should + // have access to the shell. _ = agenttest.New(t, client.URL, authToken) coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID) From 6bab0e9d0816a380d79a1e80fa4af6bf780b9268 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 09:09:50 -0600 Subject: [PATCH 08/10] share disable autostart flag between ssh and port forward --- cli/portforward.go | 8 +------- cli/ssh.go | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/cli/portforward.go b/cli/portforward.go index 64676cc3f0471..a42765e3f918d 100644 --- a/cli/portforward.go +++ b/cli/portforward.go @@ -181,13 +181,7 @@ func (r *RootCmd) portForward() *clibase.Cmd { Description: "Forward UDP port(s) from the workspace to the local machine. The UDP connection has TCP-like semantics to support stateful UDP protocols.", Value: clibase.StringArrayOf(&udpForwards), }, - { - Flag: "disable-autostart", - Description: "Disable starting the workspace automatically when connecting via port forward.", - Env: "CODER_PORT_FORWARD_DISABLE_AUTOSTART", - Value: clibase.BoolOf(&disableAutostart), - Default: "false", - }, + sshDisableAutostartOption(clibase.BoolOf(&disableAutostart)), } return cmd diff --git a/cli/ssh.go b/cli/ssh.go index fa93d6a2d6262..d2ee68707f757 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -14,6 +14,7 @@ import ( "sync" "time" + "github.com/coder/retry" "github.com/gen2brain/beeep" "github.com/gofrs/flock" "github.com/google/uuid" @@ -34,7 +35,6 @@ import ( "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/cryptorand" - "github.com/coder/retry" ) var ( @@ -460,13 +460,7 @@ func (r *RootCmd) ssh() *clibase.Cmd { FlagShorthand: "R", Value: clibase.StringOf(&remoteForward), }, - { - Flag: "disable-autostart", - Description: "Disable starting the workspace automatically when connecting via SSH.", - Env: "CODER_SSH_DISABLE_AUTOSTART", - Value: clibase.BoolOf(&disableAutostart), - Default: "false", - }, + sshDisableAutostartOption(clibase.BoolOf(&disableAutostart)), } return cmd } @@ -951,3 +945,13 @@ func (c *rawSSHCopier) Close() error { } return err } + +func sshDisableAutostartOption(src *clibase.Bool) clibase.Option { + return clibase.Option{ + Flag: "disable-autostart", + Description: "Disable starting the workspace automatically when connecting via SSH.", + Env: "CODER_SSH_DISABLE_AUTOSTART", + Value: src, + Default: "false", + } +} From e0e9de5366e211bd9378d07c3e4ca49b96c9ef60 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 09:20:46 -0600 Subject: [PATCH 09/10] make gen --- cli/testdata/coder_port-forward_--help.golden | 5 ++--- docs/cli/port-forward.md | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cli/testdata/coder_port-forward_--help.golden b/cli/testdata/coder_port-forward_--help.golden index 40ddabe358d74..0fb2a673aecb2 100644 --- a/cli/testdata/coder_port-forward_--help.golden +++ b/cli/testdata/coder_port-forward_--help.golden @@ -34,9 +34,8 @@ USAGE: $ coder port-forward --tcp 1.2.3.4:8080:8080 OPTIONS: - --disable-autostart bool, $CODER_PORT_FORWARD_DISABLE_AUTOSTART (default: false) - Disable starting the workspace automatically when connecting via port - forward. + --disable-autostart bool, $CODER_SSH_DISABLE_AUTOSTART (default: false) + Disable starting the workspace automatically when connecting via SSH. -p, --tcp string-array, $CODER_PORT_FORWARD_TCP Forward TCP port(s) from the workspace to the local machine. diff --git a/docs/cli/port-forward.md b/docs/cli/port-forward.md index e6c4c9e03b784..3f51cdd6e37c4 100644 --- a/docs/cli/port-forward.md +++ b/docs/cli/port-forward.md @@ -44,13 +44,13 @@ machine: ### --disable-autostart -| | | -| ----------- | -------------------------------------------------- | -| Type | bool | -| Environment | $CODER_PORT_FORWARD_DISABLE_AUTOSTART | -| Default | false | +| | | +| ----------- | ----------------------------------------- | +| Type | bool | +| Environment | $CODER_SSH_DISABLE_AUTOSTART | +| Default | false | -Disable starting the workspace automatically when connecting via port forward. +Disable starting the workspace automatically when connecting via SSH. ### -p, --tcp From ae7de2645ba92b23db4e8301c54d18e53c490cda Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 7 Dec 2023 09:28:34 -0600 Subject: [PATCH 10/10] Fix error wording --- cli/ssh.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/ssh.go b/cli/ssh.go index d2ee68707f757..b51093469143d 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -570,10 +570,10 @@ func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client * ) } // startWorkspace based on the last build parameters. - _, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connection %q...\n", workspace.Name) + _, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connecting to %q...\n", workspace.Name) build, err := startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceStart) if err != nil { - return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("workspace is stopped, failed to start: %w", err) + return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("unable to start workspace: %w", err) } workspace.LatestBuild = build }