Skip to content

Commit f6cb705

Browse files
committed
plumb deadline field through database
1 parent 8987e9e commit f6cb705

File tree

9 files changed

+123
-37
lines changed

9 files changed

+123
-37
lines changed

coderd/coderd.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ func New(options *Options) *API {
315315
r.Put("/", api.putWorkspaceTTL)
316316
})
317317
r.Get("/watch", api.watchWorkspace)
318-
r.Put("/deadline", api.putWorkspaceDeadline)
318+
r.Put("/extend", api.putExtendWorkspace)
319319
})
320320
})
321321
r.Route("/workspacebuilds/{workspacebuild}", func(r chi.Router) {

coderd/database/queries.sql.go

+9-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/workspacebuilds.sql

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ UPDATE
112112
workspace_builds
113113
SET
114114
updated_at = $2,
115-
provisioner_state = $3
115+
provisioner_state = $3,
116+
deadline = $4
116117
WHERE
117118
id = $1;

coderd/provisionerdaemons.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ func (server *provisionerdServer) FailJob(ctx context.Context, failJob *proto.Fa
473473
ID: input.WorkspaceBuildID,
474474
UpdatedAt: database.Now(),
475475
ProvisionerState: jobType.WorkspaceBuild.State,
476+
// We are explicitly not updating deadline here.
476477
})
477478
if err != nil {
478479
return nil, xerrors.Errorf("update workspace build state: %w", err)
@@ -544,6 +545,18 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
544545
}
545546

546547
err = server.Database.InTx(func(db database.Store) error {
548+
now := database.Now()
549+
var workspaceDeadline time.Time
550+
workspace, err := db.GetWorkspaceByID(ctx, workspaceBuild.WorkspaceID)
551+
if err == nil {
552+
if workspace.Ttl.Valid {
553+
workspaceDeadline = now.Add(time.Duration(workspace.Ttl.Int64))
554+
}
555+
} else {
556+
// Huh? Did the workspace get deleted?
557+
// In any case, since this is just for the TTL, try and continue anyway.
558+
server.Logger.Error(ctx, "fetch workspace for build", slog.F("workspace_build_id", workspaceBuild.ID), slog.F("workspace_id", workspaceBuild.WorkspaceID))
559+
}
547560
err = db.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
548561
ID: jobID,
549562
UpdatedAt: database.Now(),
@@ -556,9 +569,10 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
556569
return xerrors.Errorf("update provisioner job: %w", err)
557570
}
558571
err = db.UpdateWorkspaceBuildByID(ctx, database.UpdateWorkspaceBuildByIDParams{
572+
Deadline: now,
559573
ID: workspaceBuild.ID,
560-
UpdatedAt: database.Now(),
561574
ProvisionerState: jobType.WorkspaceBuild.State,
575+
UpdatedAt: workspaceDeadline,
562576
})
563577
if err != nil {
564578
return xerrors.Errorf("update workspace build: %w", err)

coderd/workspacebuilds.go

+1
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ func convertWorkspaceBuild(workspaceBuild database.WorkspaceBuild, job codersdk.
445445
Transition: codersdk.WorkspaceTransition(workspaceBuild.Transition),
446446
InitiatorID: workspaceBuild.InitiatorID,
447447
Job: job,
448+
Deadline: workspaceBuild.Deadline,
448449
}
449450
}
450451

coderd/workspaces.go

+38-2
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
477477
InitiatorID: apiKey.UserID,
478478
Transition: database.WorkspaceTransitionStart,
479479
JobID: provisionerJob.ID,
480-
BuildNumber: 1, // First build!
480+
BuildNumber: 1, // First build!
481+
Deadline: time.Time{}, // provisionerd will set this upon success
481482
})
482483
if err != nil {
483484
return xerrors.Errorf("insert workspace build: %w", err)
@@ -570,7 +571,7 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
570571
}
571572
}
572573

573-
func (api *API) putWorkspaceDeadline(rw http.ResponseWriter, r *http.Request) {
574+
func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
574575
workspace := httpmw.WorkspaceParam(r)
575576

576577
if !api.Authorize(rw, r, rbac.ActionUpdate, rbac.ResourceWorkspace.
@@ -597,6 +598,41 @@ func (api *API) putWorkspaceDeadline(rw http.ResponseWriter, r *http.Request) {
597598
})
598599
return
599600
}
601+
602+
newDeadline := req.Deadline.UTC()
603+
if newDeadline.IsZero() {
604+
// This should not be possible because the validation requires a non-zero value.
605+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
606+
Message: fmt.Sprintf("new deadline %q cannot be zero", newDeadline),
607+
})
608+
return
609+
}
610+
611+
if newDeadline.Before(build.Deadline) {
612+
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
613+
Message: fmt.Sprintf("new deadline %q must be after existing deadline %q", newDeadline, build.Deadline),
614+
})
615+
return
616+
}
617+
618+
if newDeadline == build.Deadline {
619+
httpapi.Write(rw, http.StatusNotModified, httpapi.Response{})
620+
return
621+
}
622+
623+
if err := api.Database.UpdateWorkspaceBuildByID(r.Context(), database.UpdateWorkspaceBuildByIDParams{
624+
ID: build.ID,
625+
UpdatedAt: build.UpdatedAt,
626+
ProvisionerState: build.ProvisionerState,
627+
Deadline: newDeadline,
628+
}); err != nil {
629+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
630+
Message: err.Error(),
631+
})
632+
return
633+
}
634+
635+
httpapi.Write(rw, http.StatusOK, httpapi.Response{})
600636
}
601637

602638
func (api *API) watchWorkspace(rw http.ResponseWriter, r *http.Request) {

coderd/workspaces_test.go

+49-20
Original file line numberDiff line numberDiff line change
@@ -617,27 +617,56 @@ func TestWorkspaceUpdateAutostop(t *testing.T) {
617617

618618
func TestWorkspaceExtendAutostop(t *testing.T) {
619619
t.Parallel()
620-
var (
621-
ctx = context.Background()
622-
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
623-
user = coderdtest.CreateFirstUser(t, client)
624-
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
625-
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
626-
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
627-
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
628-
extend = 90 * time.Minute
629-
)
630-
631-
initTTL := time.Now()
632-
req := codersdk.PutExtendWorkspaceRequest{
633-
Deadline: initTTL.Add(extend),
634-
}
635-
err := client.PutExtendWorkspace(ctx, workspace.ID, req)
636-
require.NoError(t, err, "failed to update workspace ttl")
620+
t.Run("OK", func(t *testing.T) {
621+
t.Parallel()
622+
var (
623+
ctx = context.Background()
624+
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
625+
user = coderdtest.CreateFirstUser(t, client)
626+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
627+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
628+
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
629+
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
630+
extend = 90 * time.Minute
631+
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
632+
)
633+
634+
workspace, err := client.Workspace(ctx, workspace.ID)
635+
require.NoError(t, err, "fetch provisioned workspace")
636+
req := codersdk.PutExtendWorkspaceRequest{
637+
Deadline: workspace.LatestBuild.UpdatedAt.Add(extend),
638+
}
639+
err = client.PutExtendWorkspace(ctx, workspace.ID, req)
640+
require.NoError(t, err, "failed to extend workspace")
641+
642+
updated, err := client.Workspace(ctx, workspace.ID)
643+
require.NoError(t, err, "failed to fetch updated workspace")
644+
require.Equal(t, workspace.LatestBuild.Deadline.Add(extend), updated.LatestBuild.Deadline)
645+
})
637646

638-
updated, err := client.Workspace(ctx, workspace.ID)
639-
require.NoError(t, err, "failed to fetch updated workspace")
640-
require.Equal(t, workspace.LatestBuild.Deadline.Add(extend), updated.LatestBuild.Deadline)
647+
t.Run("DeadlineZero", func(t *testing.T) {
648+
t.Parallel()
649+
var (
650+
ctx = context.Background()
651+
client = coderdtest.New(t, &coderdtest.Options{IncludeProvisionerD: true})
652+
user = coderdtest.CreateFirstUser(t, client)
653+
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
654+
_ = coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
655+
project = coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
656+
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, project.ID)
657+
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
658+
)
659+
660+
req := codersdk.PutExtendWorkspaceRequest{
661+
Deadline: time.Time{},
662+
}
663+
err := client.PutExtendWorkspace(ctx, workspace.ID, req)
664+
require.ErrorContains(t, err, "deadline: required", "failed to update workspace ttl")
665+
666+
updated, err := client.Workspace(ctx, workspace.ID)
667+
require.NoError(t, err, "failed to fetch updated workspace")
668+
require.Equal(t, workspace.LatestBuild.Deadline, updated.LatestBuild.Deadline)
669+
})
641670
}
642671

643672
func TestWorkspaceWatcher(t *testing.T) {

codersdk/workspaces.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ type Workspace struct {
2828
Name string `json:"name"`
2929
AutostartSchedule string `json:"autostart_schedule"`
3030
TTL *time.Duration `json:"ttl"`
31-
Deadline time.Time `json:"deadline"`
3231
}
3332

3433
// CreateWorkspaceBuildRequest provides options to update the latest workspace build.
@@ -181,7 +180,7 @@ func (c *Client) UpdateWorkspaceTTL(ctx context.Context, id uuid.UUID, req Updat
181180
// PutExtendWorkspaceRequest is a request to extend the deadline of
182181
// the active workspace build.
183182
type PutExtendWorkspaceRequest struct {
184-
Deadline time.Time `json:"deadline" validate:"required, datetime=RFC3339"`
183+
Deadline time.Time `json:"deadline" validate:"required"`
185184
}
186185

187186
// PutExtendWorkspace updates the deadline for resources of the latest workspace build.
@@ -192,7 +191,7 @@ func (c *Client) PutExtendWorkspace(ctx context.Context, id uuid.UUID, req PutEx
192191
return xerrors.Errorf("extend workspace ttl: %w", err)
193192
}
194193
defer res.Body.Close()
195-
if res.StatusCode != http.StatusOK {
194+
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNotModified {
196195
return readBodyAsError(res)
197196
}
198197
return nil

site/src/api/typesGenerated.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export interface CreateUserRequest {
8282
readonly organization_id: string
8383
}
8484

85-
// From codersdk/workspaces.go:35:6
85+
// From codersdk/workspaces.go:34:6
8686
export interface CreateWorkspaceBuildRequest {
8787
readonly template_version_id?: string
8888
readonly transition: WorkspaceTransition
@@ -216,7 +216,7 @@ export interface ProvisionerJobLog {
216216
readonly output: string
217217
}
218218

219-
// From codersdk/workspaces.go:183:6
219+
// From codersdk/workspaces.go:182:6
220220
export interface PutExtendWorkspaceRequest {
221221
readonly deadline: string
222222
}
@@ -292,12 +292,12 @@ export interface UpdateUserProfileRequest {
292292
readonly username: string
293293
}
294294

295-
// From codersdk/workspaces.go:142:6
295+
// From codersdk/workspaces.go:141:6
296296
export interface UpdateWorkspaceAutostartRequest {
297297
readonly schedule: string
298298
}
299299

300-
// From codersdk/workspaces.go:162:6
300+
// From codersdk/workspaces.go:161:6
301301
export interface UpdateWorkspaceTTLRequest {
302302
// This is likely an enum in an external package ("time.Duration")
303303
readonly ttl?: number
@@ -368,7 +368,6 @@ export interface Workspace {
368368
readonly autostart_schedule: string
369369
// This is likely an enum in an external package ("time.Duration")
370370
readonly ttl?: number
371-
readonly deadline: string
372371
}
373372

374373
// From codersdk/workspaceresources.go:31:6
@@ -432,12 +431,12 @@ export interface WorkspaceBuild {
432431
readonly deadline: string
433432
}
434433

435-
// From codersdk/workspaces.go:65:6
434+
// From codersdk/workspaces.go:64:6
436435
export interface WorkspaceBuildsRequest extends Pagination {
437436
readonly WorkspaceID: string
438437
}
439438

440-
// From codersdk/workspaces.go:201:6
439+
// From codersdk/workspaces.go:200:6
441440
export interface WorkspaceFilter {
442441
readonly OrganizationID: string
443442
readonly Owner: string

0 commit comments

Comments
 (0)