Skip to content

Commit f6895b7

Browse files
committed
feat: implement update workspace autostart
1 parent 6fa386d commit f6895b7

File tree

4 files changed

+93
-0
lines changed

4 files changed

+93
-0
lines changed

coderd/coderd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ func New(options *Options) (http.Handler, func()) {
184184
r.Post("/", api.postWorkspaceBuilds)
185185
r.Get("/{workspacebuildname}", api.workspaceBuildByName)
186186
})
187+
r.Route("/autostart", func(r chi.Router) {
188+
r.Put("/", api.putWorkspaceAutostart)
189+
})
187190
})
188191
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {
189192
r.Use(

coderd/workspaces.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,32 @@ func (api *api) workspaceBuildByName(rw http.ResponseWriter, r *http.Request) {
290290
render.JSON(rw, r, convertWorkspaceBuild(workspaceBuild, convertProvisionerJob(job)))
291291
}
292292

293+
func (api *api) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
294+
var spec codersdk.UpdateWorkspaceAutostartRequest
295+
if !httpapi.Read(rw, r, &spec) {
296+
return
297+
}
298+
299+
workspace := httpmw.WorkspaceParam(r)
300+
err := api.Database.UpdateWorkspaceAutostart(r.Context(), database.UpdateWorkspaceAutostartParams{
301+
ID: workspace.ID,
302+
AutostartSchedule: sql.NullString{
303+
String: spec.Schedule,
304+
Valid: true,
305+
},
306+
})
307+
if err != nil {
308+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
309+
Message: fmt.Sprintf("update workspace autostart schedule: %s", err),
310+
})
311+
return
312+
}
313+
314+
return
315+
}
316+
317+
// TODO(cian): api.updateWorkspaceAutostop
318+
293319
func convertWorkspace(workspace database.Workspace, workspaceBuild codersdk.WorkspaceBuild, template database.Template) codersdk.Workspace {
294320
return codersdk.Workspace{
295321
ID: workspace.ID,

coderd/workspaces_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"context"
55
"net/http"
66
"testing"
7+
"time"
78

89
"github.com/google/uuid"
910
"github.com/stretchr/testify/require"
1011

12+
"github.com/coder/coder/coderd/autostart/schedule"
1113
"github.com/coder/coder/coderd/coderdtest"
1214
"github.com/coder/coder/coderd/database"
1315
"github.com/coder/coder/codersdk"
@@ -182,3 +184,44 @@ func TestWorkspaceBuildByName(t *testing.T) {
182184
require.NoError(t, err)
183185
})
184186
}
187+
188+
func TestWorkspaceUpdateAutostart(t *testing.T) {
189+
// fri -> monday
190+
// TODO(cian): mon -> tue
191+
// TODO(cian): CST -> CDT
192+
// TODO(cian): CDT -> CST
193+
t.Parallel()
194+
var (
195+
ctx = context.Background()
196+
client = coderdtest.New(t, nil)
197+
_ = coderdtest.NewProvisionerDaemon(t, client)
198+
user = coderdtest.CreateFirstUser(t, client)
199+
version = coderdtest.CreateProjectVersion(t, client, user.OrganizationID, nil)
200+
_ = coderdtest.AwaitProjectVersionJob(t, client, version.ID)
201+
project = coderdtest.CreateProject(t, client, user.OrganizationID, version.ID)
202+
workspace = coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID)
203+
)
204+
205+
require.Empty(t, workspace.AutostartSchedule, "expected newly-minted workspace to have no autostart schedule")
206+
207+
schedSpec := "CRON_TZ=Europe/Dublin 30 9 1-5"
208+
err := client.UpdateWorkspaceAutostart(ctx, workspace.ID, codersdk.UpdateWorkspaceAutostartRequest{
209+
Schedule: schedSpec,
210+
})
211+
require.NoError(t, err, "expected no error")
212+
213+
updated, err := client.Workspace(ctx, workspace.ID)
214+
require.NoError(t, err, "fetch updated workspace")
215+
216+
require.Equal(t, schedSpec, updated.AutostartSchedule, "expected autostart schedule to equal")
217+
218+
sched, err := schedule.Weekly(updated.AutostartSchedule)
219+
require.NoError(t, err, "parse returned schedule")
220+
221+
dublinLoc, err := time.LoadLocation("Europe/Dublin")
222+
require.NoError(t, err, "failed to load timezone location")
223+
224+
timeAt := time.Date(2022, 5, 6, 9, 31, 0, 0, dublinLoc)
225+
expectedNext := time.Date(2022, 5, 9, 9, 30, 0, 0, dublinLoc)
226+
require.Equal(t, expectedNext, sched.Next(timeAt), "unexpected next scheduled autostart time")
227+
}

codersdk/workspaces.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/google/uuid"
11+
"golang.org/x/xerrors"
1112

1213
"github.com/coder/coder/coderd/database"
1314
)
@@ -88,3 +89,23 @@ func (c *Client) WorkspaceBuildByName(ctx context.Context, workspace uuid.UUID,
8889
var workspaceBuild WorkspaceBuild
8990
return workspaceBuild, json.NewDecoder(res.Body).Decode(&workspaceBuild)
9091
}
92+
93+
type UpdateWorkspaceAutostartRequest struct {
94+
Schedule string
95+
}
96+
97+
func (c *Client) UpdateWorkspaceAutostart(ctx context.Context, id uuid.UUID, req UpdateWorkspaceAutostartRequest) error {
98+
path := fmt.Sprintf("/api/v2/workspaces/%s/autostart", id.String())
99+
res, err := c.request(ctx, http.MethodPut, path, req)
100+
if err != nil {
101+
return xerrors.Errorf("update workspace autostart: %w", err)
102+
}
103+
defer res.Body.Close()
104+
if res.StatusCode != http.StatusOK {
105+
return readBodyAsError(res)
106+
}
107+
// TODO(cian): should we return the updated schedule?
108+
return nil
109+
}
110+
111+
// TODO(cian): client.UpdateWorkspaceAutostop

0 commit comments

Comments
 (0)