Skip to content

Commit fda43c4

Browse files
committed
feat: cli: add autostart and autostop commands
1 parent 38f0742 commit fda43c4

File tree

5 files changed

+476
-0
lines changed

5 files changed

+476
-0
lines changed

cli/workspaceautostart.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/coder/coder/coderd/autostart/schedule"
10+
"github.com/coder/coder/codersdk"
11+
)
12+
13+
func workspaceAutostart() *cobra.Command {
14+
autostartCmd := &cobra.Command{
15+
Use: "autostart",
16+
}
17+
18+
autostartCmd.AddCommand(workspaceAutostartEnable())
19+
autostartCmd.AddCommand(workspaceAutostartDisable())
20+
21+
return autostartCmd
22+
}
23+
24+
func workspaceAutostartEnable() *cobra.Command {
25+
return &cobra.Command{
26+
Use: "enable <workspace_name> <schedule>",
27+
ValidArgsFunction: validArgsWorkspaceName,
28+
Args: cobra.ExactArgs(2),
29+
RunE: func(cmd *cobra.Command, args []string) error {
30+
client, err := createClient(cmd)
31+
if err != nil {
32+
return err
33+
}
34+
35+
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
36+
if err != nil {
37+
return err
38+
}
39+
40+
validSchedule, err := schedule.Weekly(args[1])
41+
if err != nil {
42+
return err
43+
}
44+
45+
err = client.UpdateWorkspaceAutostart(cmd.Context(), workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
46+
Schedule: validSchedule.String(),
47+
})
48+
if err != nil {
49+
return err
50+
}
51+
52+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will automatically start at %s.\n\n", workspace.Name, validSchedule.Next(time.Now()))
53+
54+
return nil
55+
},
56+
}
57+
}
58+
59+
func workspaceAutostartDisable() *cobra.Command {
60+
return &cobra.Command{
61+
Use: "disable <workspace_name>",
62+
ValidArgsFunction: validArgsWorkspaceName,
63+
Args: cobra.ExactArgs(1),
64+
RunE: func(cmd *cobra.Command, args []string) error {
65+
client, err := createClient(cmd)
66+
if err != nil {
67+
return err
68+
}
69+
70+
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
71+
if err != nil {
72+
return err
73+
}
74+
75+
err = client.UpdateWorkspaceAutostart(cmd.Context(), workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
76+
Schedule: "",
77+
})
78+
if err != nil {
79+
return err
80+
}
81+
82+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will no longer automatically start.\n\n", workspace.Name)
83+
84+
return nil
85+
},
86+
}
87+
}

cli/workspaceautostart_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package cli_test
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"testing"
7+
8+
"github.com/coder/coder/cli/clitest"
9+
"github.com/coder/coder/coderd/coderdtest"
10+
"github.com/coder/coder/codersdk"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestWorkspaceAutostart(t *testing.T) {
15+
t.Parallel()
16+
17+
t.Run("EnableDisableOK", func(t *testing.T) {
18+
t.Parallel()
19+
20+
var (
21+
ctx = context.Background()
22+
client = coderdtest.New(t, nil)
23+
_ = coderdtest.NewProvisionerDaemon(t, client)
24+
user = coderdtest.CreateFirstUser(t, client)
25+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
26+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
27+
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
28+
workspace = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
29+
sched = "CRON_TZ=Europe/Dublin 30 9 1-5"
30+
stdoutBuf = &bytes.Buffer{}
31+
)
32+
33+
cmd, root := clitest.New(t, "workspaces", "autostart", "enable", workspace.Name, sched)
34+
clitest.SetupConfig(t, client, root)
35+
cmd.SetOut(stdoutBuf)
36+
37+
err := cmd.Execute()
38+
require.NoError(t, err, "unexpected error")
39+
require.Contains(t, stdoutBuf.String(), "will automatically start at", "unexpected output")
40+
41+
// Ensure autostart schedule updated
42+
updated, err := client.Workspace(ctx, workspace.ID)
43+
require.NoError(t, err, "fetch updated workspace")
44+
require.Equal(t, sched, updated.AutostartSchedule, "expected autostart schedule to be set")
45+
46+
// Disable schedule
47+
cmd, root = clitest.New(t, "workspaces", "autostart", "disable", workspace.Name)
48+
clitest.SetupConfig(t, client, root)
49+
cmd.SetOut(stdoutBuf)
50+
51+
err = cmd.Execute()
52+
require.NoError(t, err, "unexpected error")
53+
require.Contains(t, stdoutBuf.String(), "will no longer automatically start", "unexpected output")
54+
55+
// Ensure autostart schedule updated
56+
updated, err = client.Workspace(ctx, workspace.ID)
57+
require.NoError(t, err, "fetch updated workspace")
58+
require.Empty(t, updated.AutostartSchedule, "expected autostart schedule to not be set")
59+
})
60+
61+
t.Run("Enable_NotFound", func(t *testing.T) {
62+
t.Parallel()
63+
64+
var (
65+
client = coderdtest.New(t, nil)
66+
_ = coderdtest.NewProvisionerDaemon(t, client)
67+
user = coderdtest.CreateFirstUser(t, client)
68+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
69+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
70+
sched = "CRON_TZ=Europe/Dublin 30 9 1-5"
71+
)
72+
73+
cmd, root := clitest.New(t, "workspaces", "autostart", "enable", "doesnotexist", sched)
74+
clitest.SetupConfig(t, client, root)
75+
76+
err := cmd.Execute()
77+
require.ErrorContains(t, err, "status code 404: no workspace found by name", "unexpected error")
78+
})
79+
80+
t.Run("Disable_NotFound", func(t *testing.T) {
81+
t.Parallel()
82+
83+
var (
84+
client = coderdtest.New(t, nil)
85+
_ = coderdtest.NewProvisionerDaemon(t, client)
86+
user = coderdtest.CreateFirstUser(t, client)
87+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
88+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
89+
)
90+
91+
cmd, root := clitest.New(t, "workspaces", "autostart", "disable", "doesnotexist")
92+
clitest.SetupConfig(t, client, root)
93+
94+
err := cmd.Execute()
95+
require.ErrorContains(t, err, "status code 404: no workspace found by name", "unexpected error")
96+
})
97+
98+
t.Run("Enable_InvalidSchedule", func(t *testing.T) {
99+
t.Parallel()
100+
101+
var (
102+
ctx = context.Background()
103+
client = coderdtest.New(t, nil)
104+
_ = coderdtest.NewProvisionerDaemon(t, client)
105+
user = coderdtest.CreateFirstUser(t, client)
106+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
107+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
108+
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
109+
workspace = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
110+
sched = "sdfasdfasdf asdf asdf"
111+
)
112+
113+
cmd, root := clitest.New(t, "workspaces", "autostart", "enable", workspace.Name, sched)
114+
clitest.SetupConfig(t, client, root)
115+
116+
err := cmd.Execute()
117+
require.ErrorContains(t, err, "failed to parse int from sdfasdfasdf: strconv.Atoi:", "unexpected error")
118+
119+
// Ensure nothing happened
120+
updated, err := client.Workspace(ctx, workspace.ID)
121+
require.NoError(t, err, "fetch updated workspace")
122+
require.Empty(t, updated.AutostartSchedule, "expected autostart schedule to be empty")
123+
})
124+
125+
t.Run("Enable_NoSchedule", func(t *testing.T) {
126+
t.Parallel()
127+
128+
var (
129+
ctx = context.Background()
130+
client = coderdtest.New(t, nil)
131+
_ = coderdtest.NewProvisionerDaemon(t, client)
132+
user = coderdtest.CreateFirstUser(t, client)
133+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
134+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
135+
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
136+
workspace = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
137+
)
138+
139+
cmd, root := clitest.New(t, "workspaces", "autostart", "enable", workspace.Name)
140+
clitest.SetupConfig(t, client, root)
141+
142+
err := cmd.Execute()
143+
require.ErrorContains(t, err, "accepts 2 arg(s), received 1", "unexpected error")
144+
145+
// Ensure nothing happened
146+
updated, err := client.Workspace(ctx, workspace.ID)
147+
require.NoError(t, err, "fetch updated workspace")
148+
require.Empty(t, updated.AutostartSchedule, "expected autostart schedule to be empty")
149+
})
150+
}

cli/workspaceautostop.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package cli
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/spf13/cobra"
8+
9+
"github.com/coder/coder/coderd/autostart/schedule"
10+
"github.com/coder/coder/codersdk"
11+
)
12+
13+
func workspaceAutostop() *cobra.Command {
14+
autostopCmd := &cobra.Command{
15+
Use: "autostop",
16+
}
17+
18+
autostopCmd.AddCommand(workspaceAutostopEnable())
19+
autostopCmd.AddCommand(workspaceAutostopDisable())
20+
21+
return autostopCmd
22+
}
23+
24+
func workspaceAutostopEnable() *cobra.Command {
25+
return &cobra.Command{
26+
Use: "enable <workspace_name> <schedule>",
27+
ValidArgsFunction: validArgsWorkspaceName,
28+
Args: cobra.ExactArgs(2),
29+
RunE: func(cmd *cobra.Command, args []string) error {
30+
client, err := createClient(cmd)
31+
if err != nil {
32+
return err
33+
}
34+
35+
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
36+
if err != nil {
37+
return err
38+
}
39+
40+
validSchedule, err := schedule.Weekly(args[1])
41+
if err != nil {
42+
return err
43+
}
44+
45+
err = client.UpdateWorkspaceAutostop(cmd.Context(), workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
46+
Schedule: validSchedule.String(),
47+
})
48+
if err != nil {
49+
return err
50+
}
51+
52+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will automatically stop at %s.\n\n", workspace.Name, validSchedule.Next(time.Now()))
53+
54+
return nil
55+
},
56+
}
57+
}
58+
59+
func workspaceAutostopDisable() *cobra.Command {
60+
return &cobra.Command{
61+
Use: "disable <workspace_name>",
62+
ValidArgsFunction: validArgsWorkspaceName,
63+
Args: cobra.ExactArgs(1),
64+
RunE: func(cmd *cobra.Command, args []string) error {
65+
client, err := createClient(cmd)
66+
if err != nil {
67+
return err
68+
}
69+
70+
workspace, err := client.WorkspaceByName(cmd.Context(), codersdk.Me, args[0])
71+
if err != nil {
72+
return err
73+
}
74+
75+
err = client.UpdateWorkspaceAutostop(cmd.Context(), workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
76+
Schedule: "",
77+
})
78+
if err != nil {
79+
return err
80+
}
81+
82+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace will no longer automatically stop.\n\n", workspace.Name)
83+
84+
return nil
85+
},
86+
}
87+
}

0 commit comments

Comments
 (0)