Skip to content

Commit 8436f1c

Browse files
committed
cli: stop before starting on update
1 parent 2cc3a0d commit 8436f1c

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

cli/update.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55

66
"golang.org/x/xerrors"
77

8+
"github.com/coder/coder/v2/cli/cliui"
9+
"github.com/coder/coder/v2/cli/cliutil"
810
"github.com/coder/coder/v2/codersdk"
911
"github.com/coder/serpent"
1012
)
@@ -34,6 +36,29 @@ func (r *RootCmd) update() *serpent.Command {
3436
return nil
3537
}
3638

39+
// #17840: If the workspace is already running, we will stop it before
40+
// updating. Simply performing a new start transition may not work if the
41+
// template specifies ignore_changes.
42+
if workspace.LatestBuild.Transition == codersdk.WorkspaceTransitionStart {
43+
_, _ = fmt.Fprintf(inv.Stdout, "Stopping workspace %s before updating.\n", workspace.Name)
44+
wbr := codersdk.CreateWorkspaceBuildRequest{
45+
Transition: codersdk.WorkspaceTransitionStop,
46+
TemplateVersionID: workspace.LatestBuild.TemplateVersionID,
47+
}
48+
if bflags.provisionerLogDebug {
49+
wbr.LogLevel = codersdk.ProvisionerLogLevelDebug
50+
}
51+
build, err := client.CreateWorkspaceBuild(inv.Context(), workspace.ID, wbr)
52+
if err != nil {
53+
return xerrors.Errorf("stop workspace: %w", err)
54+
}
55+
cliutil.WarnMatchedProvisioners(inv.Stderr, build.MatchedProvisioners, build.Job)
56+
// Wait for the stop to complete.
57+
if err := cliui.WorkspaceBuild(inv.Context(), inv.Stdout, client, build.ID); err != nil {
58+
return xerrors.Errorf("wait for stop: %w", err)
59+
}
60+
}
61+
3762
build, err := startWorkspace(inv, client, workspace, parameterFlags, bflags, WorkspaceUpdate)
3863
if err != nil {
3964
return xerrors.Errorf("start workspace: %w", err)

cli/update_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,64 @@ func TestUpdate(t *testing.T) {
9696
require.Equal(t, codersdk.WorkspaceTransitionStop, prevBuild.Transition, "previous build must be a stop transition")
9797
require.Equal(t, version1.ID.String(), prevBuild.TemplateVersionID.String(), "previous build must have the old template version")
9898
})
99+
100+
t.Run("Stopped", func(t *testing.T) {
101+
t.Parallel()
102+
103+
// Given: a workspace exists on the latest template version.
104+
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
105+
owner := coderdtest.CreateFirstUser(t, client)
106+
member, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
107+
version1 := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil)
108+
109+
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version1.ID)
110+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version1.ID)
111+
112+
ws := coderdtest.CreateWorkspace(t, member, template.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
113+
cwr.Name = "my-workspace"
114+
})
115+
require.False(t, ws.Outdated, "newly created workspace with active template version must not be outdated")
116+
117+
// Given: the template version is updated
118+
version2 := coderdtest.UpdateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
119+
Parse: echo.ParseComplete,
120+
ProvisionApply: echo.ApplyComplete,
121+
ProvisionPlan: echo.PlanComplete,
122+
}, template.ID)
123+
_ = coderdtest.AwaitTemplateVersionJobCompleted(t, client, version2.ID)
124+
125+
ctx := testutil.Context(t, testutil.WaitShort)
126+
err := client.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
127+
ID: version2.ID,
128+
})
129+
require.NoError(t, err, "failed to update active template version")
130+
131+
// Given: the workspace is in a stopped state.
132+
coderdtest.MustTransitionWorkspace(t, member, ws.ID, codersdk.WorkspaceTransitionStart, codersdk.WorkspaceTransitionStop)
133+
134+
// Then: the workspace is marked as 'outdated'
135+
ws, err = member.WorkspaceByOwnerAndName(ctx, codersdk.Me, "my-workspace", codersdk.WorkspaceOptions{})
136+
require.NoError(t, err, "member failed to get workspace they themselves own")
137+
require.True(t, ws.Outdated, "workspace must be outdated after template version update")
138+
139+
// When: the workspace is updated
140+
inv, root := clitest.New(t, "update", ws.Name)
141+
clitest.SetupConfig(t, member, root)
142+
143+
err = inv.Run()
144+
require.NoError(t, err, "update command failed")
145+
146+
// Then: the workspace is no longer 'outdated'
147+
ws, err = member.WorkspaceByOwnerAndName(ctx, codersdk.Me, "my-workspace", codersdk.WorkspaceOptions{})
148+
require.NoError(t, err, "member failed to get workspace they themselves own after update")
149+
require.Equal(t, version2.ID.String(), ws.LatestBuild.TemplateVersionID.String(), "workspace must have latest template version after update")
150+
require.False(t, ws.Outdated, "workspace must not be outdated after update")
151+
152+
// Then: the workspace must have been started with the new template version
153+
require.Equal(t, codersdk.WorkspaceTransitionStart, ws.LatestBuild.Transition, "latest build must be a start transition")
154+
// Then: we expect 3 builds, as we manually stopped the workspace.
155+
require.Equal(t, int32(3), ws.LatestBuild.BuildNumber, "workspace must have 3 builds after update")
156+
})
99157
}
100158

101159
func TestUpdateWithRichParameters(t *testing.T) {

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