github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/builder.go (about) 1 package controllers 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "sigs.k8s.io/controller-runtime/pkg/builder" 9 10 ctrl "sigs.k8s.io/controller-runtime" 11 "sigs.k8s.io/controller-runtime/pkg/reconcile" 12 13 "github.com/tilt-dev/tilt/internal/analytics" 14 "github.com/tilt-dev/tilt/internal/store" 15 "github.com/tilt-dev/tilt/pkg/logger" 16 ) 17 18 type Controller interface { 19 reconcile.Reconciler 20 CreateBuilder(mgr ctrl.Manager) (*builder.Builder, error) 21 } 22 23 // Little helper class to propagate the logger 24 // from the setup phase. 25 // 26 // Discussion: 27 // https://github.com/kubernetes-sigs/controller-runtime/issues/1752 28 type ctrlWrapper struct { 29 ctx context.Context 30 reconcile.Reconciler 31 } 32 33 // Propagate the logger and analytics from setup 34 func (w ctrlWrapper) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { 35 ctx = logger.WithLogger(ctx, logger.Get(w.ctx)) 36 ctx = analytics.WithAnalytics(ctx, analytics.Get(w.ctx)) 37 return w.Reconciler.Reconcile(ctx, req) 38 } 39 40 type ControllerBuilder struct { 41 tscm *TiltServerControllerManager 42 controllers []Controller 43 } 44 45 func NewControllerBuilder(tscm *TiltServerControllerManager, controllers []Controller) *ControllerBuilder { 46 return &ControllerBuilder{ 47 tscm: tscm, 48 controllers: controllers, 49 } 50 } 51 52 var _ store.Subscriber = &ControllerBuilder{} 53 var _ store.SetUpper = &ControllerBuilder{} 54 var _ store.TearDowner = &ControllerBuilder{} 55 56 func (c *ControllerBuilder) OnChange(_ context.Context, _ store.RStore, _ store.ChangeSummary) error { 57 return nil 58 } 59 60 func (c *ControllerBuilder) SetUp(ctx context.Context, st store.RStore) error { 61 mgr := c.tscm.GetManager() 62 client := c.tscm.GetClient() 63 64 if mgr == nil || client == nil { 65 return errors.New("controller manager not initialized") 66 } 67 68 // create all the builders and THEN start them all - if each builder is created + started, 69 // initialization will fail because indexes cannot be added to an Informer after start, and 70 // the builders register informers 71 builders := make([]*builder.Builder, 0, len(c.controllers)) 72 for _, controller := range c.controllers { 73 b, err := controller.CreateBuilder(mgr) 74 if err != nil { 75 return fmt.Errorf("error creating builder: %v", err) 76 } 77 builders = append(builders, b) 78 } 79 80 for i, b := range builders { 81 wrapper := ctrlWrapper{ctx: ctx, Reconciler: c.controllers[i]} 82 if err := b.Complete(wrapper); err != nil { 83 return fmt.Errorf("error starting controller: %v", err) 84 } 85 } 86 87 // start the controller manager now that all the controllers are initialized 88 go func() { 89 if err := mgr.Start(ctx); err != nil && !errors.Is(err, context.Canceled) { 90 err = fmt.Errorf("controller manager stopped unexpectedly: %v", err) 91 st.Dispatch(store.NewErrorAction(err)) 92 } 93 }() 94 95 return nil 96 } 97 98 func (c *ControllerBuilder) TearDown(ctx context.Context) { 99 for _, controller := range c.controllers { 100 td, ok := controller.(store.TearDowner) 101 if ok { 102 td.TearDown(ctx) 103 } 104 } 105 }