Skip to content

Commit c3cbf1d

Browse files
ammariokylecarbs
authored andcommitted
Make coder bump idempotent (#2225)
Resolves #2223 In addition to solving what's outlined in the issue, I remove the client-side minute check because it had no clear purpose when the API already returns an error.
1 parent 928315f commit c3cbf1d

File tree

2 files changed

+23
-104
lines changed

2 files changed

+23
-104
lines changed

cli/bump.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,55 +12,55 @@ import (
1212
)
1313

1414
const (
15-
bumpDescriptionLong = `To extend the autostop deadline for a workspace.
16-
If no unit is specified in the duration, we assume minutes.`
17-
defaultBumpDuration = 90 * time.Minute
15+
bumpDescriptionLong = `To extend the autostop deadline for a workspace.`
1816
)
1917

2018
func bump() *cobra.Command {
2119
bumpCmd := &cobra.Command{
2220
Args: cobra.RangeArgs(1, 2),
2321
Annotations: workspaceCommand,
24-
Use: "bump <workspace-name> [duration]",
22+
Use: "bump <workspace-name> <duration>",
2523
Short: "Extend the autostop deadline for a workspace.",
2624
Long: bumpDescriptionLong,
2725
Example: "coder bump my-workspace 90m",
2826
RunE: func(cmd *cobra.Command, args []string) error {
29-
bumpDuration := defaultBumpDuration
30-
if len(args) > 1 {
31-
d, err := tryParseDuration(args[1])
32-
if err != nil {
33-
return err
34-
}
35-
bumpDuration = d
36-
}
37-
38-
if bumpDuration < time.Minute {
39-
return xerrors.New("minimum bump duration is 1 minute")
27+
bumpDuration, err := tryParseDuration(args[1])
28+
if err != nil {
29+
return err
4030
}
4131

4232
client, err := createClient(cmd)
4333
if err != nil {
4434
return xerrors.Errorf("create client: %w", err)
4535
}
36+
4637
workspace, err := namedWorkspace(cmd, client, args[0])
4738
if err != nil {
4839
return xerrors.Errorf("get workspace: %w", err)
4940
}
5041

51-
if workspace.LatestBuild.Deadline.IsZero() {
52-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "no deadline set\n")
42+
newDeadline := time.Now().Add(bumpDuration)
43+
44+
if newDeadline.Before(workspace.LatestBuild.Deadline) {
45+
_, _ = fmt.Fprintf(
46+
cmd.OutOrStdout(),
47+
"The proposed deadline is %s before the current deadline.\n",
48+
workspace.LatestBuild.Deadline.Sub(newDeadline).Round(time.Minute),
49+
)
5350
return nil
5451
}
5552

56-
newDeadline := workspace.LatestBuild.Deadline.Add(bumpDuration)
5753
if err := client.PutExtendWorkspace(cmd.Context(), workspace.ID, codersdk.PutExtendWorkspaceRequest{
5854
Deadline: newDeadline,
5955
}); err != nil {
6056
return err
6157
}
6258

63-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Workspace %q will now stop at %s\n", workspace.Name, newDeadline.Format(time.RFC3339))
59+
_, _ = fmt.Fprintf(
60+
cmd.OutOrStdout(),
61+
"Workspace %q will now stop at %s\n", workspace.Name,
62+
newDeadline.Format(time.RFC822),
63+
)
6464

6565
return nil
6666
},

cli/bump_test.go

Lines changed: 4 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,6 @@ import (
1717
func TestBump(t *testing.T) {
1818
t.Parallel()
1919

20-
t.Run("BumpOKDefault", func(t *testing.T) {
21-
t.Parallel()
22-
23-
// Given: we have a workspace
24-
var (
25-
err error
26-
ctx = context.Background()
27-
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
28-
user = coderdtest.CreateFirstUser(t, client)
29-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
30-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
31-
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
32-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
33-
cmdArgs = []string{"bump", workspace.Name}
34-
stdoutBuf = &bytes.Buffer{}
35-
)
36-
37-
// Given: we wait for the workspace to be built
38-
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
39-
workspace, err = client.Workspace(ctx, workspace.ID)
40-
require.NoError(t, err)
41-
expectedDeadline := workspace.LatestBuild.Deadline.Add(90 * time.Minute)
42-
43-
// Assert test invariant: workspace build has a deadline set equal to now plus ttl
44-
initDeadline := time.Now().Add(time.Duration(*workspace.TTLMillis) * time.Millisecond)
45-
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline, time.Minute)
46-
47-
cmd, root := clitest.New(t, cmdArgs...)
48-
clitest.SetupConfig(t, client, root)
49-
cmd.SetOut(stdoutBuf)
50-
51-
// When: we execute `coder bump <workspace>`
52-
err = cmd.ExecuteContext(ctx)
53-
require.NoError(t, err, "unexpected error")
54-
55-
// Then: the deadline of the latest build is updated
56-
updated, err := client.Workspace(ctx, workspace.ID)
57-
require.NoError(t, err)
58-
require.WithinDuration(t, expectedDeadline, updated.LatestBuild.Deadline, time.Minute)
59-
})
60-
6120
t.Run("BumpSpecificDuration", func(t *testing.T) {
6221
t.Parallel()
6322

@@ -71,15 +30,15 @@ func TestBump(t *testing.T) {
7130
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
7231
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
7332
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
74-
cmdArgs = []string{"bump", workspace.Name, "30"}
33+
cmdArgs = []string{"bump", workspace.Name, "10h"}
7534
stdoutBuf = &bytes.Buffer{}
7635
)
7736

7837
// Given: we wait for the workspace to be built
7938
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
8039
workspace, err = client.Workspace(ctx, workspace.ID)
8140
require.NoError(t, err)
82-
expectedDeadline := workspace.LatestBuild.Deadline.Add(30 * time.Minute)
41+
expectedDeadline := time.Now().Add(10 * time.Hour)
8342

8443
// Assert test invariant: workspace build has a deadline set equal to now plus ttl
8544
initDeadline := time.Now().Add(time.Duration(*workspace.TTLMillis) * time.Millisecond)
@@ -150,7 +109,7 @@ func TestBump(t *testing.T) {
150109
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID, func(cwr *codersdk.CreateWorkspaceRequest) {
151110
cwr.TTLMillis = nil
152111
})
153-
cmdArgs = []string{"bump", workspace.Name}
112+
cmdArgs = []string{"bump", workspace.Name, "1h"}
154113
stdoutBuf = &bytes.Buffer{}
155114
)
156115
// Unset the workspace TTL
@@ -180,51 +139,11 @@ func TestBump(t *testing.T) {
180139

181140
// When: we execute `coder bump workspace``
182141
err = cmd.ExecuteContext(ctx)
183-
require.NoError(t, err)
142+
require.Error(t, err)
184143

185144
// Then: nothing happens and the deadline remains unset
186145
updated, err := client.Workspace(ctx, workspace.ID)
187146
require.NoError(t, err)
188147
require.Zero(t, updated.LatestBuild.Deadline)
189148
})
190-
191-
t.Run("BumpMinimumDuration", func(t *testing.T) {
192-
t.Parallel()
193-
194-
// Given: we have a workspace with no deadline set
195-
var (
196-
err error
197-
ctx = context.Background()
198-
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
199-
user = coderdtest.CreateFirstUser(t, client)
200-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
201-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
202-
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
203-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
204-
cmdArgs = []string{"bump", workspace.Name, "59s"}
205-
stdoutBuf = &bytes.Buffer{}
206-
)
207-
208-
// Given: we wait for the workspace to build
209-
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
210-
workspace, err = client.Workspace(ctx, workspace.ID)
211-
require.NoError(t, err)
212-
213-
// Assert test invariant: workspace build has a deadline set equal to now plus ttl
214-
initDeadline := time.Now().Add(time.Duration(*workspace.TTLMillis) * time.Millisecond)
215-
require.WithinDuration(t, initDeadline, workspace.LatestBuild.Deadline, time.Minute)
216-
217-
cmd, root := clitest.New(t, cmdArgs...)
218-
clitest.SetupConfig(t, client, root)
219-
cmd.SetOut(stdoutBuf)
220-
221-
// When: we execute `coder bump workspace 59s`
222-
err = cmd.ExecuteContext(ctx)
223-
require.ErrorContains(t, err, "minimum bump duration is 1 minute")
224-
225-
// Then: an error is reported and the deadline remains as before
226-
updated, err := client.Workspace(ctx, workspace.ID)
227-
require.NoError(t, err)
228-
require.WithinDuration(t, workspace.LatestBuild.Deadline, updated.LatestBuild.Deadline, time.Minute)
229-
})
230149
}

0 commit comments

Comments
 (0)