@@ -3,6 +3,7 @@ package coderd
3
3
import (
4
4
"context"
5
5
"crypto/ed25519"
6
+ "errors"
6
7
"fmt"
7
8
"math"
8
9
"net/http"
@@ -583,23 +584,6 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
583
584
}
584
585
go api .runEntitlementsLoop (ctx )
585
586
586
- if api .AGPL .Experiments .Enabled (codersdk .ExperimentWorkspacePrebuilds ) {
587
- // TODO: future enhancement, start this up without restarting coderd when entitlement is updated.
588
- if ! api .Entitlements .Enabled (codersdk .FeatureWorkspacePrebuilds ) {
589
- options .Logger .Warn (ctx , "prebuilds experiment enabled but not entitled to use" )
590
- } else {
591
- api .prebuildsController = prebuilds .NewController (options .Database , options .Pubsub , options .DeploymentValues .Prebuilds , options .Logger .Named ("prebuilds.controller" ))
592
- go api .prebuildsController .Loop (ctx )
593
-
594
- prebuildMetricsCollector := prebuilds .NewMetricsCollector (options .Database , options .Logger )
595
- // should this be api.prebuild...
596
- err = api .PrometheusRegistry .Register (prebuildMetricsCollector )
597
- if err != nil {
598
- return nil , xerrors .Errorf ("unable to register prebuilds metrics collector: %w" , err )
599
- }
600
- }
601
- }
602
-
603
587
return api , nil
604
588
}
605
589
@@ -654,7 +638,8 @@ type API struct {
654
638
licenseMetricsCollector * license.MetricsCollector
655
639
tailnetService * tailnet.ClientService
656
640
657
- prebuildsController * prebuilds.Controller
641
+ prebuildsReconciler agplprebuilds.Reconciler
642
+ prebuildsMetricsCollector * prebuilds.MetricsCollector
658
643
}
659
644
660
645
// writeEntitlementWarningsHeader writes the entitlement warnings to the response header
@@ -686,8 +671,10 @@ func (api *API) Close() error {
686
671
api .Options .CheckInactiveUsersCancelFunc ()
687
672
}
688
673
689
- if api .prebuildsController != nil {
690
- api .prebuildsController .Close (xerrors .New ("api closed" )) // TODO: determine root cause (requires changes up the stack, though).
674
+ if api .prebuildsReconciler != nil {
675
+ ctx , giveUp := context .WithTimeoutCause (context .Background (), time .Second * 30 , errors .New ("gave up waiting for reconciler to stop" ))
676
+ defer giveUp ()
677
+ api .prebuildsReconciler .Stop (ctx , xerrors .New ("api closed" )) // TODO: determine root cause (requires changes up the stack, though).
691
678
}
692
679
693
680
return api .AGPL .Close ()
@@ -893,11 +880,22 @@ func (api *API) updateEntitlements(ctx context.Context) error {
893
880
}
894
881
895
882
if initial , changed , enabled := featureChanged (codersdk .FeatureWorkspacePrebuilds ); shouldUpdate (initial , changed , enabled ) {
896
- c := agplprebuilds .DefaultClaimer
897
- if enabled {
898
- c = prebuilds.EnterpriseClaimer {}
883
+ reconciler , claimer , metrics := api .setupPrebuilds (enabled )
884
+ if api .prebuildsReconciler != nil {
885
+ stopCtx , giveUp := context .WithTimeoutCause (context .Background (), time .Second * 30 , errors .New ("gave up waiting for reconciler to stop" ))
886
+ defer giveUp ()
887
+ api .prebuildsReconciler .Stop (stopCtx , errors .New ("entitlements change" ))
888
+ }
889
+
890
+ // Only register metrics once.
891
+ if api .prebuildsMetricsCollector != nil {
892
+ api .prebuildsMetricsCollector = metrics
899
893
}
900
- api .AGPL .PrebuildsClaimer .Store (& c )
894
+
895
+ api .prebuildsReconciler = reconciler
896
+ go reconciler .RunLoop (context .Background ())
897
+
898
+ api .AGPL .PrebuildsClaimer .Store (& claimer )
901
899
}
902
900
903
901
// External token encryption is soft-enforced
@@ -1168,3 +1166,25 @@ func (api *API) runEntitlementsLoop(ctx context.Context) {
1168
1166
func (api * API ) Authorize (r * http.Request , action policy.Action , object rbac.Objecter ) bool {
1169
1167
return api .AGPL .HTTPAuth .Authorize (r , action , object )
1170
1168
}
1169
+
1170
+ func (api * API ) setupPrebuilds (entitled bool ) (agplprebuilds.Reconciler , agplprebuilds.Claimer , * prebuilds.MetricsCollector ) {
1171
+ enabled := api .AGPL .Experiments .Enabled (codersdk .ExperimentWorkspacePrebuilds )
1172
+ if ! enabled || ! entitled {
1173
+ api .Logger .Debug (context .Background (), "prebuilds not enabled" ,
1174
+ slog .F ("experiment_enabled" , enabled ), slog .F ("entitled" , entitled ))
1175
+
1176
+ return agplprebuilds .NewNoopReconciler (), agplprebuilds .DefaultClaimer , nil
1177
+ }
1178
+
1179
+ logger := api .Logger .Named ("prebuilds.metrics" )
1180
+ collector := prebuilds .NewMetricsCollector (api .Database , logger )
1181
+ err := api .PrometheusRegistry .Register (collector )
1182
+ if err != nil {
1183
+ logger .Error (context .Background (), "failed to register prebuilds metrics collector" , slog .F ("error" , err ))
1184
+ collector = nil
1185
+ }
1186
+
1187
+ return prebuilds .NewStoreReconciler (api .Database , api .Pubsub , api .DeploymentValues .Prebuilds , api .Logger .Named ("prebuilds" )),
1188
+ prebuilds.EnterpriseClaimer {},
1189
+ collector
1190
+ }
0 commit comments