mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: implement claiming of prebuilt workspaces (#17458)
Signed-off-by: Danny Kopping <dannykopping@gmail.com> Co-authored-by: Danny Kopping <dannykopping@gmail.com> Co-authored-by: Danny Kopping <danny@coder.com> Co-authored-by: Edward Angert <EdwardAngert@users.noreply.github.com> Co-authored-by: EdwardAngert <17991901+EdwardAngert@users.noreply.github.com> Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com> Co-authored-by: Ethan <39577870+ethanndickson@users.noreply.github.com> Co-authored-by: M Atif Ali <atif@coder.com> Co-authored-by: Aericio <16523741+Aericio@users.noreply.github.com> Co-authored-by: M Atif Ali <me@matifali.dev> Co-authored-by: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
25dacd39e7
commit
118f12ac3a
@ -18,6 +18,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/agent/proto"
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
@ -28,6 +29,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/prebuilds"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
"github.com/coder/coder/v2/coderd/schedule"
|
||||
@ -636,33 +638,57 @@ func createWorkspace(
|
||||
workspaceBuild *database.WorkspaceBuild
|
||||
provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow
|
||||
)
|
||||
|
||||
err = api.Database.InTx(func(db database.Store) error {
|
||||
now := dbtime.Now()
|
||||
// Workspaces are created without any versions.
|
||||
minimumWorkspace, err := db.InsertWorkspace(ctx, database.InsertWorkspaceParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
OwnerID: owner.ID,
|
||||
OrganizationID: template.OrganizationID,
|
||||
TemplateID: template.ID,
|
||||
Name: req.Name,
|
||||
AutostartSchedule: dbAutostartSchedule,
|
||||
NextStartAt: nextStartAt,
|
||||
Ttl: dbTTL,
|
||||
// The workspaces page will sort by last used at, and it's useful to
|
||||
// have the newly created workspace at the top of the list!
|
||||
LastUsedAt: dbtime.Now(),
|
||||
AutomaticUpdates: dbAU,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert workspace: %w", err)
|
||||
var (
|
||||
workspaceID uuid.UUID
|
||||
claimedWorkspace *database.Workspace
|
||||
prebuildsClaimer = *api.PrebuildsClaimer.Load()
|
||||
)
|
||||
|
||||
// If a template preset was chosen, try claim a prebuilt workspace.
|
||||
if req.TemplateVersionPresetID != uuid.Nil {
|
||||
// Try and claim an eligible prebuild, if available.
|
||||
claimedWorkspace, err = claimPrebuild(ctx, prebuildsClaimer, db, api.Logger, req, owner)
|
||||
if err != nil && !errors.Is(err, prebuilds.ErrNoClaimablePrebuiltWorkspaces) {
|
||||
return xerrors.Errorf("claim prebuild: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// No prebuild found; regular flow.
|
||||
if claimedWorkspace == nil {
|
||||
now := dbtime.Now()
|
||||
// Workspaces are created without any versions.
|
||||
minimumWorkspace, err := db.InsertWorkspace(ctx, database.InsertWorkspaceParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
OwnerID: owner.ID,
|
||||
OrganizationID: template.OrganizationID,
|
||||
TemplateID: template.ID,
|
||||
Name: req.Name,
|
||||
AutostartSchedule: dbAutostartSchedule,
|
||||
NextStartAt: nextStartAt,
|
||||
Ttl: dbTTL,
|
||||
// The workspaces page will sort by last used at, and it's useful to
|
||||
// have the newly created workspace at the top of the list!
|
||||
LastUsedAt: dbtime.Now(),
|
||||
AutomaticUpdates: dbAU,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert workspace: %w", err)
|
||||
}
|
||||
workspaceID = minimumWorkspace.ID
|
||||
} else {
|
||||
// Prebuild found!
|
||||
workspaceID = claimedWorkspace.ID
|
||||
initiatorID = prebuildsClaimer.Initiator()
|
||||
}
|
||||
|
||||
// We have to refetch the workspace for the joined in fields.
|
||||
// TODO: We can use WorkspaceTable for the builder to not require
|
||||
// this extra fetch.
|
||||
workspace, err = db.GetWorkspaceByID(ctx, minimumWorkspace.ID)
|
||||
workspace, err = db.GetWorkspaceByID(ctx, workspaceID)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get workspace by ID: %w", err)
|
||||
}
|
||||
@ -676,6 +702,13 @@ func createWorkspace(
|
||||
if req.TemplateVersionID != uuid.Nil {
|
||||
builder = builder.VersionID(req.TemplateVersionID)
|
||||
}
|
||||
if req.TemplateVersionPresetID != uuid.Nil {
|
||||
builder = builder.TemplateVersionPresetID(req.TemplateVersionPresetID)
|
||||
}
|
||||
if claimedWorkspace != nil {
|
||||
builder = builder.MarkPrebuildClaimedBy(owner.ID)
|
||||
}
|
||||
|
||||
if req.EnableDynamicParameters && api.Experiments.Enabled(codersdk.ExperimentDynamicParameters) {
|
||||
builder = builder.UsingDynamicParameters()
|
||||
}
|
||||
@ -842,6 +875,21 @@ func requestTemplate(ctx context.Context, rw http.ResponseWriter, req codersdk.C
|
||||
return template, true
|
||||
}
|
||||
|
||||
func claimPrebuild(ctx context.Context, claimer prebuilds.Claimer, db database.Store, logger slog.Logger, req codersdk.CreateWorkspaceRequest, owner workspaceOwner) (*database.Workspace, error) {
|
||||
claimedID, err := claimer.Claim(ctx, owner.ID, req.Name, req.TemplateVersionPresetID)
|
||||
if err != nil {
|
||||
// TODO: enhance this by clarifying whether this *specific* prebuild failed or whether there are none to claim.
|
||||
return nil, xerrors.Errorf("claim prebuild: %w", err)
|
||||
}
|
||||
|
||||
lookup, err := db.GetWorkspaceByID(ctx, *claimedID)
|
||||
if err != nil {
|
||||
logger.Error(ctx, "unable to find claimed workspace by ID", slog.Error(err), slog.F("claimed_prebuild_id", claimedID.String()))
|
||||
return nil, xerrors.Errorf("find claimed workspace by ID %q: %w", claimedID.String(), err)
|
||||
}
|
||||
return &lookup, nil
|
||||
}
|
||||
|
||||
func (api *API) notifyWorkspaceCreated(
|
||||
ctx context.Context,
|
||||
receiverID uuid.UUID,
|
||||
|
Reference in New Issue
Block a user