internal/controllers/watch/watch.go (76 lines of code) (raw):

package watch import ( "context" "math/rand" "github.com/go-logr/logr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" apiv1 "github.com/Azure/eno/api/v1" "github.com/Azure/eno/internal/manager" ) type WatchController struct { mgr ctrl.Manager client client.Client refControllers map[apiv1.ResourceRef]*KindWatchController } func NewController(mgr ctrl.Manager) error { err := ctrl.NewControllerManagedBy(mgr). Named("watchControllerController"). Watches(&apiv1.Synthesizer{}, manager.SingleEventHandler()). WithLogConstructor(manager.NewLogConstructor(mgr, "watchController")). Complete(&WatchController{ mgr: mgr, client: mgr.GetClient(), refControllers: map[apiv1.ResourceRef]*KindWatchController{}, }) if err != nil { return err } return ctrl.NewControllerManagedBy(mgr). For(&apiv1.Composition{}). WithLogConstructor(manager.NewLogConstructor(mgr, "watchPruningController")). Complete(&pruningController{ client: mgr.GetClient(), }) } func (c *WatchController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := logr.FromContextOrDiscard(ctx) synths := &apiv1.SynthesizerList{} err := c.client.List(ctx, synths) if err != nil { logger.Error(err, "failed to list synthesizers") return ctrl.Result{}, err } // It's important to randomize the order over which we iterate the synths, // otherwise one bad resource reference can block the loop rand.Shuffle(len(synths.Items), func(i, j int) { synths.Items[i], synths.Items[j] = synths.Items[j], synths.Items[i] }) // Start any missing controllers synthsByRef := map[apiv1.ResourceRef]struct{}{} for _, syn := range synths.Items { if syn.DeletionTimestamp != nil { continue } for _, ref := range syn.Spec.Refs { ref := ref synthsByRef[ref.Resource] = struct{}{} current := c.refControllers[ref.Resource] if current != nil { continue // already running } rc, err := NewKindWatchController(ctx, c, &ref.Resource) if err != nil { logger.Error(err, "failed to create kind watch controller", "resource", ref.Resource) return ctrl.Result{}, err } c.refControllers[ref.Resource] = rc return ctrl.Result{Requeue: true}, nil } } // Stop controllers that are no longer needed for ref, rc := range c.refControllers { if _, ok := synthsByRef[ref]; ok { continue } rc.Stop(ctx) delete(c.refControllers, ref) logger.Error(nil, "stopped and removed controller for resource", "resource", ref) return ctrl.Result{Requeue: true}, nil } return ctrl.Result{}, nil }