Skip to content

Commit 77a4792

Browse files
authored
fix(cli): ssh: auto-update workspace (#11773)
1 parent 369821e commit 77a4792

File tree

5 files changed

+97
-8
lines changed

5 files changed

+97
-8
lines changed

cli/restart.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ func (r *RootCmd) restart() *clibase.Cmd {
6464
// It's possible for a workspace build to fail due to the template requiring starting
6565
// workspaces with the active version.
6666
if cerr, ok := codersdk.AsError(err); ok && cerr.StatusCode() == http.StatusForbidden {
67-
_, _ = fmt.Fprintln(inv.Stdout, "Failed to restart with the template version from your last build. Policy may require you to restart with the current active template version.")
67+
_, _ = fmt.Fprintln(inv.Stdout, "Unable to restart the workspace with the template version from the last build. Policy may require you to restart with the current active template version.")
6868
build, err = startWorkspace(inv, client, workspace, parameterFlags, WorkspaceUpdate)
6969
if err != nil {
7070
return xerrors.Errorf("start workspace with active template version: %w", err)

cli/ssh.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"errors"
77
"fmt"
88
"io"
9+
"net/http"
910
"net/url"
1011
"os"
1112
"os/exec"
@@ -575,13 +576,27 @@ func getWorkspaceAndAgent(ctx context.Context, inv *clibase.Invocation, client *
575576
codersdk.WorkspaceStatusStopped,
576577
)
577578
}
578-
// startWorkspace based on the last build parameters.
579+
580+
// Start workspace based on the last build parameters.
581+
// It's possible for a workspace build to fail due to the template requiring starting
582+
// workspaces with the active version.
579583
_, _ = fmt.Fprintf(inv.Stderr, "Workspace was stopped, starting workspace to allow connecting to %q...\n", workspace.Name)
580-
build, err := startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceStart)
584+
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceStart)
585+
if cerr, ok := codersdk.AsError(err); ok && cerr.StatusCode() == http.StatusForbidden {
586+
_, _ = fmt.Fprintln(inv.Stdout, "Unable to start the workspace with template version from last build. The auto-update policy may require you to restart with the current active template version.")
587+
_, err = startWorkspace(inv, client, workspace, workspaceParameterFlags{}, WorkspaceUpdate)
588+
if err != nil {
589+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with active template version: %w", err)
590+
}
591+
} else if err != nil {
592+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("start workspace with current template version: %w", err)
593+
}
594+
595+
// Refresh workspace state so that `outdated`, `build`,`template_*` fields are up-to-date.
596+
workspace, err = namedWorkspace(ctx, client, workspaceParts[0])
581597
if err != nil {
582-
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, xerrors.Errorf("unable to start workspace: %w", err)
598+
return codersdk.Workspace{}, codersdk.WorkspaceAgent{}, err
583599
}
584-
workspace.LatestBuild = build
585600
}
586601
if workspace.LatestBuild.Job.CompletedAt == nil {
587602
err := cliui.WorkspaceBuild(ctx, inv.Stderr, client, workspace.LatestBuild.ID)

cli/ssh_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,71 @@ func TestSSH(t *testing.T) {
133133
pty.WriteLine("exit")
134134
<-cmdDone
135135
})
136+
t.Run("RequireActiveVersion", func(t *testing.T) {
137+
t.Parallel()
138+
139+
authToken := uuid.NewString()
140+
ownerClient := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
141+
owner := coderdtest.CreateFirstUser(t, ownerClient)
142+
client, _ := coderdtest.CreateAnotherUser(t, ownerClient, owner.OrganizationID, rbac.RoleMember())
143+
144+
echoResponses := &echo.Responses{
145+
Parse: echo.ParseComplete,
146+
ProvisionPlan: echo.PlanComplete,
147+
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
148+
}
149+
150+
version := coderdtest.CreateTemplateVersion(t, ownerClient, owner.OrganizationID, echoResponses)
151+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version.ID)
152+
template := coderdtest.CreateTemplate(t, ownerClient, owner.OrganizationID, version.ID)
153+
154+
workspace := coderdtest.CreateWorkspace(t, client, owner.OrganizationID, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
155+
cwr.AutomaticUpdates = codersdk.AutomaticUpdatesAlways
156+
})
157+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
158+
159+
// Stop the workspace
160+
workspaceBuild := coderdtest.CreateWorkspaceBuild(t, client, workspace, database.WorkspaceTransitionStop)
161+
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspaceBuild.ID)
162+
163+
// Update template version
164+
version = coderdtest.UpdateTemplateVersion(t, ownerClient, owner.OrganizationID, echoResponses, template.ID)
165+
coderdtest.AwaitTemplateVersionJobCompleted(t, ownerClient, version.ID)
166+
err := ownerClient.UpdateActiveTemplateVersion(context.Background(), template.ID, codersdk.UpdateActiveTemplateVersion{
167+
ID: version.ID,
168+
})
169+
require.NoError(t, err)
170+
171+
// SSH to the workspace which should auto-update and autostart it
172+
inv, root := clitest.New(t, "ssh", workspace.Name)
173+
clitest.SetupConfig(t, client, root)
174+
pty := ptytest.New(t).Attach(inv)
175+
176+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
177+
defer cancel()
178+
179+
cmdDone := tGo(t, func() {
180+
err := inv.WithContext(ctx).Run()
181+
assert.NoError(t, err)
182+
})
183+
184+
// When the agent connects, the workspace was started, and we should
185+
// have access to the shell.
186+
_ = agenttest.New(t, client.URL, authToken)
187+
coderdtest.AwaitWorkspaceAgents(t, client, workspace.ID)
188+
189+
// Shells on Mac, Windows, and Linux all exit shells with the "exit" command.
190+
pty.WriteLine("exit")
191+
<-cmdDone
192+
193+
// Double-check if workspace's template version is up-to-date
194+
workspace, err = client.Workspace(context.Background(), workspace.ID)
195+
require.NoError(t, err)
196+
assert.Equal(t, version.ID, workspace.TemplateActiveVersionID)
197+
assert.Equal(t, workspace.TemplateActiveVersionID, workspace.LatestBuild.TemplateVersionID)
198+
assert.False(t, workspace.Outdated)
199+
})
200+
136201
t.Run("ShowTroubleshootingURLAfterTimeout", func(t *testing.T) {
137202
t.Parallel()
138203

cli/start.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (r *RootCmd) start() *clibase.Cmd {
4949
// It's possible for a workspace build to fail due to the template requiring starting
5050
// workspaces with the active version.
5151
if cerr, ok := codersdk.AsError(err); ok && cerr.StatusCode() == http.StatusForbidden {
52-
_, _ = fmt.Fprintln(inv.Stdout, "Failed to restart with the template version from your last build. Policy may require you to restart with the current active template version.")
52+
_, _ = fmt.Fprintln(inv.Stdout, "Unable to start the workspace with the template version from the last build. Policy may require you to restart with the current active template version.")
5353
build, err = startWorkspace(inv, client, workspace, parameterFlags, WorkspaceUpdate)
5454
if err != nil {
5555
return xerrors.Errorf("start workspace with active template version: %w", err)
@@ -79,6 +79,7 @@ func (r *RootCmd) start() *clibase.Cmd {
7979

8080
func buildWorkspaceStartRequest(inv *clibase.Invocation, client *codersdk.Client, workspace codersdk.Workspace, parameterFlags workspaceParameterFlags, action WorkspaceCLIAction) (codersdk.CreateWorkspaceBuildRequest, error) {
8181
version := workspace.LatestBuild.TemplateVersionID
82+
8283
if workspace.AutomaticUpdates == codersdk.AutomaticUpdatesAlways || action == WorkspaceUpdate {
8384
version = workspace.TemplateActiveVersionID
8485
if version != workspace.LatestBuild.TemplateVersionID {

enterprise/cli/start_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,16 @@ func TestStart(t *testing.T) {
159159

160160
ws = coderdtest.MustWorkspace(t, c.Client, ws.ID)
161161
require.Equal(t, c.ExpectedVersion, ws.LatestBuild.TemplateVersionID)
162-
if initialTemplateVersion != ws.LatestBuild.TemplateVersionID {
163-
require.Contains(t, buf.String(), "Failed to restart with the template version from your last build. Policy may require you to restart with the current active template version.")
162+
if initialTemplateVersion == ws.LatestBuild.TemplateVersionID {
163+
return
164+
}
165+
166+
if cmd == "start" {
167+
require.Contains(t, buf.String(), "Unable to start the workspace with the template version from the last build")
168+
}
169+
170+
if cmd == "restart" {
171+
require.Contains(t, buf.String(), "Unable to restart the workspace with the template version from the last build")
164172
}
165173
})
166174
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy