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  }