8000 feat: update workspace deadline when workspace ttl updated by johnstcn · Pull Request #2165 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: update workspace deadline when workspace ttl updated #2165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
REFACTOR: coderd: putWorkspaceTTL: wrap in tx
  • Loading branch information
johnstcn committed Jun 8, 2022
commit 7846dd8640c1b28af6959c4cfe178e8bf292756d
81 changes: 38 additions & 43 deletions coderd/workspaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -533,57 +533,52 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) {
return
}

err = api.Database.UpdateWorkspaceTTL(r.Context(), database.UpdateWorkspaceTTLParams{
ID: workspace.ID,
Ttl: dbTTL,
})
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "Error updating workspace TTL!",
Detail: err.Error(),
})
return
}
err = api.Database.InTx(func(s database.Store) error {
if err := s.UpdateWorkspaceTTL(r.Context(), database.UpdateWorkspaceTTLParams{
ID: workspace.ID,
Ttl: dbTTL,
}); err != nil {
return xerrors.Errorf("update workspace TTL: %w", err)
}

// Also extend the workspace deadline if the workspace is running
latestBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "Error fetching latest workspace build!",
Detail: err.Error(),
})
return
}
// Also extend the workspace deadline if the workspace is running
latestBuild, err := s.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID)
if err != nil {
return xerrors.Errorf("get latest workspace build: %w", err)
}

if latestBuild.Transition != database.WorkspaceTransitionStart {
httpapi.Write(rw, http.StatusOK, nil)
return
}
if latestBuild.Transition != database.WorkspaceTransitionStart {
return nil // nothing to do
}

if latestBuild.UpdatedAt.IsZero() {
// Build in progress; provisionerd should update with the new TTL.
httpapi.Write(rw, http.StatusOK, nil)
return
}
if latestBuild.UpdatedAt.IsZero() {
// Build in progress; provisionerd should update with the new TTL.
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What happens if a user starts a workspace with one TTL, but tries to update the TTL before the build is complete? As a user, I'd expect the new TTL to be in-effect once the build has completed or receive an error that TTL cannot be updated (yet).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See

workspaceDeadline = now.Add(time.Duration(workspace.Ttl.Int64))
-- we fetch the workspace TTL in a transaction and update the deadline upon finishing the provisioner job.

  • If the user updates the TTL first, then the updated TTL will be used by provisionerd.
  • If provisionerd updates the TTL first, then the deadline should be updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh yeah that makes sense, thanks for clarifying!

}

var newDeadline time.Time
if dbTTL.Valid {
newDeadline = latestBuild.UpdatedAt.Add(time.Duration(dbTTL.Int64))
}
var newDeadline time.Time
if dbTTL.Valid {
newDeadline = latestBuild.UpdatedAt.Add(time.Duration(dbTTL.Int64))
}

err = api.Database.UpdateWorkspaceBuildByID(
r.Context(),
database.UpdateWorkspaceBuildByIDParams{
ID: latestBuild.ID,
UpdatedAt: latestBuild.UpdatedAt,
ProvisionerState: latestBuild.ProvisionerState,
Deadline: newDeadline,
},
)
if err := s.UpdateWorkspaceBuildByID(
r.Context(),
database.UpdateWorkspaceBuildByIDParams{
ID: latestBuild.ID,
UpdatedAt: latestBuild.UpdatedAt,
ProvisionerState: latestBuild.ProvisionerState,
Deadline: newDeadline,
},
); err != nil {
return xerrors.Errorf("update workspace deadline: %w", err)
}

return nil
})

if err != nil {
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
Message: "Internal error extending workspace deadline.",
Message: "Error updating workspace time until shutdown!",
Detail: err.Error(),
})
return
Expand Down
8000
29 changes: 16 additions & 13 deletions coderd/workspaces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,34 +611,37 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
name string
ttlMillis *int64
expectedError string
expectedDeadline time.Time
expectedDeadline *time.Time
modifyTemplate func(*codersdk.CreateTemplateRequest)
}{
{
name: "disable ttl",
ttlMillis: nil,
expectedError: "",
name: "disable ttl",
ttlMillis: nil,
expectedError: "",
expectedDeadline: ptr.Ref(time.Time{}),
},
{
name: "update ttl",
ttlMillis: ptr.Ref(12 * time.Hour.Milliseconds()),
expectedError: "",
expectedDeadline: time.Now().Add(12 * time.Hour),
expectedDeadline: ptr.Ref(time.Now().Add(12 * time.Hour)),
},
{
name: "below minimum ttl",
ttlMillis: ptr.Ref((30 * time.Second).Milliseconds()),
expectedError: "ttl must be at least one minute",
},
{
name: "minimum ttl",
ttlMillis: ptr.Ref(time.Minute.Milliseconds()),
expectedError: "",
name: "minimum ttl",
ttlMillis: ptr.Ref(time.Minute.Milliseconds()),
expectedError: "",
expectedDeadline: ptr.Ref(time.Now().Add(time.Minute)),
},
{
name: "maximum ttl",
ttlMillis: ptr.Ref((24 * 7 * time.Hour).Milliseconds()),
expectedError: "",
name: "maximum ttl",
ttlMillis: ptr.Ref((24 * 7 * time.Hour).Milliseconds()),
expectedError: "",
expectedDeadline: ptr.Ref(time.Now().Add(24 * 7 * time.Hour)),
},
{
name: "above maximum ttl",
Expand Down Expand Up @@ -690,8 +693,8 @@ func TestWorkspaceUpdateTTL(t *testing.T) {
require.NoError(t, err, "fetch updated workspace")

require.Equal(t, testCase.ttlMillis, updated.TTLMillis, "expected autostop ttl to equal requested")
if !testCase.expectedDeadline.IsZero() {
require.WithinDuration(t, testCase.expectedDeadline, updated.LatestBuild.Deadline, time.Minute, "expected autostop deadline to be equal expected")
if testCase.expectedDeadline != nil {
require.WithinDuration(t, *testCase.expectedDeadline, updated.LatestBuild.Deadline, time.Minute, "expected autostop deadline to be equal expected")
}
})
}
Expand Down
0