mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: set prebuilds lifecycle parameters on creation and claim (#19252)
## Description This PR ensures that prebuilt workspaces are properly excluded from the lifecycle executor and treated as a separate class of workspaces, fully managed by the prebuild reconciliation loop. It introduces two lifecycle guarantees: * When a prebuilt workspace is created (i.e., when the workspace build completes), all lifecycle-related fields are unset, ensuring the workspace does not participate in TTL, autostop, autostart, dormancy, or auto-deletion logic. * When a prebuilt workspace is claimed, it transitions into a regular user workspace. At this point, all lifecycle fields are correctly populated according to template-level configurations, allowing the workspace to be managed by the lifecycle executor as expected. ## Changes * Prebuilt workspaces now have all lifecycle-relevant fields unset during creation * When a prebuild is claimed: * Lifecycle fields are set based on template and workspace level configurations. This ensures a clean transition into the standard workspace lifecycle flow. * Updated lifecycle-related SQL update queries to explicitly exclude prebuilt workspaces. ## Relates Related issue: https://github.com/coder/coder/issues/18898 To reduce the scope of this PR and make the review process more manageable, the original implementation has been split into the following focused PRs: * https://github.com/coder/coder/pull/19259 * https://github.com/coder/coder/pull/19263 * https://github.com/coder/coder/pull/19264 * https://github.com/coder/coder/pull/19265 These PRs should be considered in conjunction with this one to understand the complete set of lifecycle separation changes for prebuilt workspaces.
This commit is contained in:
@@ -1183,11 +1183,18 @@ func (s *server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*proto.
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update workspace build state: %w", err)
|
||||
}
|
||||
|
||||
deadline := build.Deadline
|
||||
maxDeadline := build.MaxDeadline
|
||||
if workspace.IsPrebuild() {
|
||||
deadline = time.Time{}
|
||||
maxDeadline = time.Time{}
|
||||
}
|
||||
err = db.UpdateWorkspaceBuildDeadlineByID(ctx, database.UpdateWorkspaceBuildDeadlineByIDParams{
|
||||
ID: input.WorkspaceBuildID,
|
||||
UpdatedAt: s.timeNow(),
|
||||
Deadline: build.Deadline,
|
||||
MaxDeadline: build.MaxDeadline,
|
||||
Deadline: deadline,
|
||||
MaxDeadline: maxDeadline,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update workspace build deadline: %w", err)
|
||||
@@ -1860,38 +1867,47 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
|
||||
return getWorkspaceError
|
||||
}
|
||||
|
||||
templateScheduleStore := *s.TemplateScheduleStore.Load()
|
||||
// Prebuilt workspaces must not have Deadline or MaxDeadline set,
|
||||
// as they are managed by the prebuild reconciliation loop, not the lifecycle executor
|
||||
deadline := time.Time{}
|
||||
maxDeadline := time.Time{}
|
||||
|
||||
autoStop, err := schedule.CalculateAutostop(ctx, schedule.CalculateAutostopParams{
|
||||
Database: db,
|
||||
TemplateScheduleStore: templateScheduleStore,
|
||||
UserQuietHoursScheduleStore: *s.UserQuietHoursScheduleStore.Load(),
|
||||
// `now` is used below to set the build completion time.
|
||||
WorkspaceBuildCompletedAt: now,
|
||||
Workspace: workspace.WorkspaceTable(),
|
||||
// Allowed to be the empty string.
|
||||
WorkspaceAutostart: workspace.AutostartSchedule.String,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("calculate auto stop: %w", err)
|
||||
}
|
||||
if !workspace.IsPrebuild() {
|
||||
templateScheduleStore := *s.TemplateScheduleStore.Load()
|
||||
|
||||
if workspace.AutostartSchedule.Valid {
|
||||
templateScheduleOptions, err := templateScheduleStore.Get(ctx, db, workspace.TemplateID)
|
||||
autoStop, err := schedule.CalculateAutostop(ctx, schedule.CalculateAutostopParams{
|
||||
Database: db,
|
||||
TemplateScheduleStore: templateScheduleStore,
|
||||
UserQuietHoursScheduleStore: *s.UserQuietHoursScheduleStore.Load(),
|
||||
// `now` is used below to set the build completion time.
|
||||
WorkspaceBuildCompletedAt: now,
|
||||
Workspace: workspace.WorkspaceTable(),
|
||||
// Allowed to be the empty string.
|
||||
WorkspaceAutostart: workspace.AutostartSchedule.String,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get template schedule options: %w", err)
|
||||
return xerrors.Errorf("calculate auto stop: %w", err)
|
||||
}
|
||||
|
||||
nextStartAt, err := schedule.NextAllowedAutostart(now, workspace.AutostartSchedule.String, templateScheduleOptions)
|
||||
if err == nil {
|
||||
err = db.UpdateWorkspaceNextStartAt(ctx, database.UpdateWorkspaceNextStartAtParams{
|
||||
ID: workspace.ID,
|
||||
NextStartAt: sql.NullTime{Valid: true, Time: nextStartAt.UTC()},
|
||||
})
|
||||
if workspace.AutostartSchedule.Valid {
|
||||
templateScheduleOptions, err := templateScheduleStore.Get(ctx, db, workspace.TemplateID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update workspace next start at: %w", err)
|
||||
return xerrors.Errorf("get template schedule options: %w", err)
|
||||
}
|
||||
|
||||
nextStartAt, err := schedule.NextAllowedAutostart(now, workspace.AutostartSchedule.String, templateScheduleOptions)
|
||||
if err == nil {
|
||||
err = db.UpdateWorkspaceNextStartAt(ctx, database.UpdateWorkspaceNextStartAtParams{
|
||||
ID: workspace.ID,
|
||||
NextStartAt: sql.NullTime{Valid: true, Time: nextStartAt.UTC()},
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("update workspace next start at: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
deadline = autoStop.Deadline
|
||||
maxDeadline = autoStop.MaxDeadline
|
||||
}
|
||||
|
||||
err = db.UpdateProvisionerJobWithCompleteByID(ctx, database.UpdateProvisionerJobWithCompleteByIDParams{
|
||||
@@ -1917,8 +1933,8 @@ func (s *server) completeWorkspaceBuildJob(ctx context.Context, job database.Pro
|
||||
}
|
||||
err = db.UpdateWorkspaceBuildDeadlineByID(ctx, database.UpdateWorkspaceBuildDeadlineByIDParams{
|
||||
ID: workspaceBuild.ID,
|
||||
Deadline: autoStop.Deadline,
|
||||
MaxDeadline: autoStop.MaxDeadline,
|
||||
Deadline: deadline,
|
||||
MaxDeadline: maxDeadline,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user