6
6
"encoding/json"
7
7
"errors"
8
8
"fmt"
9
+ "github.com/coder/coder/v2/coderd/prebuilds"
9
10
"net/http"
10
11
"slices"
11
12
"strconv"
@@ -635,34 +636,71 @@ func createWorkspace(
635
636
provisionerJob * database.ProvisionerJob
636
637
workspaceBuild * database.WorkspaceBuild
637
638
provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow
639
+
640
+ runningWorkspaceAgentID uuid.UUID
638
641
)
642
+
643
+ prebuilds := (* api .PrebuildsClaimer .Load ()).(prebuilds.Claimer )
644
+
639
645
err = api .Database .InTx (func (db database.Store ) error {
640
- now := dbtime .Now ()
641
- // Workspaces are created without any versions.
642
- minimumWorkspace , err := db .InsertWorkspace (ctx , database.InsertWorkspaceParams {
643
- ID : uuid .New (),
644
- CreatedAt : now ,
645
- UpdatedAt : now ,
646
- OwnerID : owner .ID ,
647
- OrganizationID : template .OrganizationID ,
648
- TemplateID : template .ID ,
649
- Name : req .Name ,
650
- AutostartSchedule : dbAutostartSchedule ,
651
- NextStartAt : nextStartAt ,
652
- Ttl : dbTTL ,
653
- // The workspaces page will sort by last used at, and it's useful to
654
- // have the newly created workspace at the top of the list!
655
- LastUsedAt : dbtime .Now (),
656
- AutomaticUpdates : dbAU ,
657
- })
658
- if err != nil {
659
- return xerrors .Errorf ("insert workspace: %w" , err )
646
+ var (
647
+ workspaceID uuid.UUID
648
+ claimedWorkspace * database.Workspace
649
+ )
650
+
651
+ // If a template preset was chosen, try claim a prebuild.
652
+ if req .TemplateVersionPresetID != uuid .Nil {
653
+ // Try and claim an eligible prebuild, if available.
654
+ claimedWorkspace , err = claimPrebuild (ctx , prebuilds , db , api .Logger , req , owner )
655
+ if err != nil {
656
+ return xerrors .Errorf ("claim prebuild: %w" , err )
657
+ }
658
+ }
659
+
660
+ // No prebuild found; regular flow.
661
+ if claimedWorkspace == nil {
662
+ now := dbtime .Now ()
663
+ // Workspaces are created without any versions.
664
+ minimumWorkspace , err := db .InsertWorkspace (ctx , database.InsertWorkspaceParams {
665
+ ID : uuid .New (),
666
+ CreatedAt : now ,
667
+ UpdatedAt : now ,
668
+ OwnerID : owner .ID ,
669
+ OrganizationID : template .OrganizationID ,
670
+ TemplateID : template .ID ,
671
+ Name : req .Name ,
672
+ AutostartSchedule : dbAutostartSchedule ,
673
+ NextStartAt : nextStartAt ,
674
+ Ttl : dbTTL ,
675
+ // The workspaces page will sort by last used at, and it's useful to
676
+ // have the newly created workspace at the top of the list!
677
+ LastUsedAt : dbtime .Now (),
678
+ AutomaticUpdates : dbAU ,
679
+ })
680
+ if err != nil {
681
+ return xerrors .Errorf ("insert workspace: %w" , err )
682
+ }
683
+ workspaceID = minimumWorkspace .ID
684
+ } else {
685
+ // Prebuild found!
686
+ workspaceID = claimedWorkspace .ID
687
+ initiatorID = prebuilds .Initiator ()
688
+ agents , err := db .GetWorkspaceAgentsInLatestBuildByWorkspaceID (ctx , claimedWorkspace .ID )
689
+ if err != nil {
690
+ // TODO: comment about best-effort, workspace can be restarted if this fails...
691
+ api .Logger .Error (ctx , "failed to retrieve running agents of claimed prebuilt workspace" ,
692
+ slog .F ("workspace_id" , claimedWorkspace .ID ), slog .Error (err ))
693
+ }
694
+ if len (agents ) >= 1 {
695
+ // TODO: handle multiple agents
696
+ runningWorkspaceAgentID = agents [0 ].ID
697
+ }
660
698
}
661
699
662
700
// We have to refetch the workspace for the joined in fields.
663
701
// TODO: We can use WorkspaceTable for the builder to not require
664
702
// this extra fetch.
665
- workspace , err = db .GetWorkspaceByID (ctx , minimumWorkspace . ID )
703
+ workspace , err = db .GetWorkspaceByID (ctx , workspaceID )
666
704
if err != nil {
667
705
return xerrors .Errorf ("get workspace by ID: %w" , err )
668
706
}
@@ -672,10 +710,18 @@ func createWorkspace(
672
710
Initiator (initiatorID ).
673
711
ActiveVersion ().
674
712
RichParameterValues (req .RichParameterValues ).
675
- TemplateVersionPresetID (req .TemplateVersionPresetID )
713
+ TemplateVersionPresetID (req .TemplateVersionPresetID ).
714
+ RunningWorkspaceAgentID (runningWorkspaceAgentID )
676
715
if req .TemplateVersionID != uuid .Nil {
677
716
builder = builder .VersionID (req .TemplateVersionID )
678
717
}
718
+ if req .TemplateVersionPresetID != uuid .Nil {
719
+ builder = builder .TemplateVersionPresetID (req .TemplateVersionPresetID )
720
+ }
721
+
722
+ if claimedWorkspace != nil {
723
+ builder = builder .MarkPrebuildClaimedBy (owner .ID )
724
+ }
679
725
680
726
workspaceBuild , provisionerJob , provisionerDaemons , err = builder .Build (
681
727
ctx ,
@@ -839,6 +885,32 @@ func requestTemplate(ctx context.Context, rw http.ResponseWriter, req codersdk.C
839
885
return template , true
840
886
}
841
887
888
+ func claimPrebuild (ctx context.Context , claimer prebuilds.Claimer , db database.Store , logger slog.Logger , req codersdk.CreateWorkspaceRequest , owner workspaceOwner ) (* database.Workspace , error ) {
889
+ prebuildsCtx := dbauthz .AsPrebuildsOrchestrator (ctx )
890
+
891
+ // TODO: do we need a timeout here?
892
+ claimCtx , cancel := context .WithTimeout (prebuildsCtx , time .Second * 10 )
893
+ defer cancel ()
894
+
895
+ claimedID , err := claimer .Claim (claimCtx , db , owner .ID , req .Name , req .TemplateVersionPresetID )
896
+ if err != nil {
897
+ // TODO: enhance this by clarifying whether this *specific* prebuild failed or whether there are none to claim.
898
+ return nil , xerrors .Errorf ("claim prebuild: %w" , err )
899
+ }
900
+
901
+ // No prebuild available.
902
+ if claimedID == nil {
903
+ return nil , nil
904
+ }
905
+
906
+ lookup , err := db .GetWorkspaceByID (prebuildsCtx , * claimedID )
907
+ if err != nil {
908
+ logger .Error (ctx , "unable to find claimed workspace by ID" , slog .Error (err ), slog .F ("claimed_prebuild_id" , (* claimedID ).String ()))
909
+ return nil , xerrors .Errorf ("find claimed workspace by ID %q: %w" , (* claimedID ).String (), err )
910
+ }
911
+ return & lookup , err
912
+ }
913
+
842
914
func (api * API ) notifyWorkspaceCreated (
843
915
ctx context.Context ,
844
916
receiverID uuid.UUID ,
0 commit comments