Skip to content

Commit b92c86e

Browse files
committed
GREEN: api complete
1 parent 759fb8a commit b92c86e

File tree

5 files changed

+91
-95
lines changed

5 files changed

+91
-95
lines changed

coderd/autobuild/executor/lifecycle_executor_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,14 @@ func TestExecutorAutostopOK(t *testing.T) {
190190
})
191191
// Given: we have a user with a workspace
192192
workspace = mustProvisionWorkspace(t, client)
193-
ttl = *workspace.TTL
194193
)
195194
// Given: workspace is running
196195
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
196+
require.NotZero(t, workspace.LatestBuild.Deadline)
197197

198-
// When: the autobuild executor ticks *after* the TTL:
198+
// When: the autobuild executor ticks *after* the deadline:
199199
go func() {
200-
tickCh <- time.Now().UTC().Add(ttl + time.Minute)
200+
tickCh <- workspace.LatestBuild.Deadline.Add(time.Minute)
201201
close(tickCh)
202202
}()
203203

coderd/database/databasefake/databasefake.go

+1
Original file line numberDiff line numberDiff line change
@@ -1694,6 +1694,7 @@ func (q *fakeQuerier) UpdateWorkspaceBuildByID(_ context.Context, arg database.U
16941694
}
16951695
workspaceBuild.UpdatedAt = arg.UpdatedAt
16961696
workspaceBuild.ProvisionerState = arg.ProvisionerState
1697+
workspaceBuild.Deadline = arg.Deadline
16971698
q.workspaceBuilds[index] = workspaceBuild
16981699
return nil
16991700
}

coderd/provisionerdaemons.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -569,10 +569,10 @@ func (server *provisionerdServer) CompleteJob(ctx context.Context, completed *pr
569569
return xerrors.Errorf("update provisioner job: %w", err)
570570
}
571571
err = db.UpdateWorkspaceBuildByID(ctx, database.UpdateWorkspaceBuildByIDParams{
572-
Deadline: now,
572+
Deadline: workspaceDeadline,
573573
ID: workspaceBuild.ID,
574574
ProvisionerState: jobType.WorkspaceBuild.State,
575-
UpdatedAt: workspaceDeadline,
575+
UpdatedAt: now,
576576
})
577577
if err != nil {
578578
return xerrors.Errorf("update workspace build: %w", err)

coderd/workspaces.go

+42-43
Original file line numberDiff line numberDiff line change
@@ -584,55 +584,54 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
584584
return
585585
}
586586

587-
build, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
588-
if err != nil {
589-
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
590-
Message: fmt.Sprintf("get latest workspace build: %s", err),
591-
})
592-
return
593-
}
587+
var code = http.StatusOK
594588

595-
if build.Transition != database.WorkspaceTransitionStart {
596-
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{
597-
Message: fmt.Sprintf("workspace must be started, current status: %s", build.Transition),
598-
})
599-
return
600-
}
589+
err := api.Database.InTx(func(s database.Store) error {
590+
build, err := s.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
591+
if err != nil {
592+
code = http.StatusInternalServerError
593+
return xerrors.Errorf("get latest workspace build: %w", err)
594+
}
601595

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-
}
596+
if build.Transition != database.WorkspaceTransitionStart {
597+
code = http.StatusConflict
598+
return xerrors.Errorf("workspace must be started, current status: %s", build.Transition)
599+
}
610600

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-
}
601+
newDeadline := req.Deadline.UTC()
602+
if newDeadline.IsZero() {
603+
// This should not be possible because the struct validation field enforces a non-zero value.
604+
code = http.StatusBadRequest
605+
return xerrors.New("new deadline cannot be zero")
606+
}
617607

618-
if newDeadline == build.Deadline {
619-
httpapi.Write(rw, http.StatusNotModified, httpapi.Response{})
620-
return
621-
}
608+
if newDeadline.Before(build.Deadline) {
609+
code = http.StatusBadRequest
610+
return xerrors.Errorf("new deadline %q must be after existing deadline %q", newDeadline.Format(time.RFC3339), build.Deadline.Format(time.RFC3339))
611+
}
622612

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-
}
613+
if newDeadline == build.Deadline {
614+
code = http.StatusNotModified
615+
return nil
616+
}
634617

635-
httpapi.Write(rw, http.StatusOK, httpapi.Response{})
618+
if err := s.UpdateWorkspaceBuildByID(r.Context(), database.UpdateWorkspaceBuildByIDParams{
619+
ID: build.ID,
620+
UpdatedAt: build.UpdatedAt,
621+
ProvisionerState: build.ProvisionerState,
622+
Deadline: newDeadline,
623+
}); err != nil {
624+
return xerrors.Errorf("update workspace build: %w", err)
625+
}
626+
627+
return nil
628+
})
629+
630+
var resp = httpapi.Response{}
631+
if err != nil {
632+
resp.Message = err.Error()
633+
}
634+
httpapi.Write(rw, code, resp)
636635
}
637636

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

coderd/workspaces_test.go

+43-47
Original file line numberDiff line numberDiff line change
@@ -615,58 +615,54 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
615615
})
616616
}
617617

618-
func TestWorkspaceExtendAutostop(t *testing.T) {
618+
func TestWorkspaceExtend(t *testing.T) {
619619
t.Parallel()
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-
})
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+
_ = coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
630+
oldDeadline = time.Now().Add(*workspace.TTL).UTC()
631+
newDeadline = time.Now().Add(*workspace.TTL + extend).UTC()
632+
)
633+
634+
workspace, err := client.Workspace(ctx, workspace.ID)
635+
require.NoError(t, err, "fetch provisioned workspace")
636+
require.InDelta(t, oldDeadline.Unix(), workspace.LatestBuild.Deadline.Unix(), 1)
637+
638+
// Updating the deadline should succeed
639+
req := codersdk.PutExtendWorkspaceRequest{
640+
Deadline: newDeadline,
641+
}
642+
err = client.PutExtendWorkspace(ctx, workspace.ID, req)
643+
require.NoError(t, err, "failed to extend workspace")
646644

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-
)
645+
// Ensure deadline set correctly
646+
updated, err := client.Workspace(ctx, workspace.ID)
647+
require.NoError(t, err, "failed to fetch updated workspace")
648+
require.InDelta(t, newDeadline.Unix(), updated.LatestBuild.Deadline.Unix(), 1)
659649

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")
650+
// Zero time should fail
651+
err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
652+
Deadline: time.Time{},
653+
})
654+
require.ErrorContains(t, err, "deadline: required", "setting an empty deadline on a workspace should fail")
665655

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)
656+
// Updating with an earlier time should also fail
657+
err = client.PutExtendWorkspace(ctx, workspace.ID, codersdk.PutExtendWorkspaceRequest{
658+
Deadline: oldDeadline,
669659
})
660+
require.ErrorContains(t, err, "must be after existing deadline", "setting an earlier deadline should fail")
661+
662+
// Ensure deadline still set correctly
663+
updated, err = client.Workspace(ctx, workspace.ID)
664+
require.NoError(t, err, "failed to fetch updated workspace")
665+
require.InDelta(t, newDeadline.Unix(), updated.LatestBuild.Deadline.Unix(), 1)
670666
}
671667

672668
func TestWorkspaceWatcher(t *testing.T) {

0 commit comments

Comments
 (0)