From 86227539c2fd145649c79f460224bd96e210f7cb Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Wed, 15 Jan 2025 23:08:45 +0200 Subject: [PATCH] componentsregistry: use structure to store handlers (#1502) To make it possible to mock registry in DSC reconcileComponents for testing, implement registry as a structure. Make package level wrappers on Add and ForEach methods which operate on the default statically created registry. This allows keep working with the default registry in a real system without extra arguments like before. It's in init()s and in main(). But code under test can accept testing version when needed. Signed-off-by: Yauheni Kaliuta --- .../datasciencecluster_controller.go | 9 +++-- pkg/componentsregistry/componentsregistry.go | 34 ++++++++++++++----- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/controllers/datasciencecluster/datasciencecluster_controller.go b/controllers/datasciencecluster/datasciencecluster_controller.go index 331deddc526..d306ccb9a05 100644 --- a/controllers/datasciencecluster/datasciencecluster_controller.go +++ b/controllers/datasciencecluster/datasciencecluster_controller.go @@ -101,7 +101,7 @@ func (r *DataScienceClusterReconciler) Reconcile(ctx context.Context, req ctrl.R } // deploy components - if err := r.reconcileComponents(ctx, instance); err != nil { + if err := r.reconcileComponents(ctx, instance, cr.DefaultRegistry()); err != nil { log.Info(err.Error()) status.SetCondition(&instance.Status.Conditions, "Degraded", status.ReconcileFailed, err.Error(), corev1.ConditionTrue) } @@ -150,13 +150,16 @@ func (r *DataScienceClusterReconciler) validate(ctx context.Context, _ *dscv1.Da return nil } -func (r *DataScienceClusterReconciler) reconcileComponents(ctx context.Context, instance *dscv1.DataScienceCluster) error { +func (r *DataScienceClusterReconciler) reconcileComponents( + ctx context.Context, + instance *dscv1.DataScienceCluster, + reg *cr.Registry) error { log := logf.FromContext(ctx).WithName("DataScienceCluster") notReadyComponents := make([]string, 0) // all DSC defined components - componentErrors := cr.ForEach(func(component cr.ComponentHandler) error { + componentErrors := reg.ForEach(func(component cr.ComponentHandler) error { ci, err := r.reconcileComponent(ctx, instance, component) if err != nil { return err diff --git a/pkg/componentsregistry/componentsregistry.go b/pkg/componentsregistry/componentsregistry.go index cd9c07b6402..3ad76706067 100644 --- a/pkg/componentsregistry/componentsregistry.go +++ b/pkg/componentsregistry/componentsregistry.go @@ -32,25 +32,43 @@ type ComponentHandler interface { UpdateDSCStatus(dsc *dscv1.DataScienceCluster, obj client.Object) error } -var registry = []ComponentHandler{} +// Registry is a struct that maintains a list of registered ComponentHandlers. +type Registry struct { + handlers []ComponentHandler +} + +var r = &Registry{} -// Add registers a new component handler +// Add registers a new ComponentHandler to the registry. // not thread safe, supposed to be called during init. -// TODO: check if init() can be called in parallel. -func Add(ch ComponentHandler) { - registry = append(registry, ch) +func (r *Registry) Add(ch ComponentHandler) { + r.handlers = append(r.handlers, ch) } -// ForEach iterates over all registered component handlers +// ForEach iterates over all registered ComponentHandlers and applies the given function. +// If any handler returns an error, that error is collected and returned at the end. // With go1.23 probably https://go.dev/blog/range-functions can be used. -func ForEach(f func(ch ComponentHandler) error) error { +func (r *Registry) ForEach(f func(ch ComponentHandler) error) error { var errs *multierror.Error - for _, ch := range registry { + for _, ch := range r.handlers { errs = multierror.Append(errs, f(ch)) } + return errs.ErrorOrNil() } +func Add(ch ComponentHandler) { + r.Add(ch) +} + +func ForEach(f func(ch ComponentHandler) error) error { + return r.ForEach(f) +} + +func DefaultRegistry() *Registry { + return r +} + func IsManaged(ch ComponentHandler, dsc *dscv1.DataScienceCluster) bool { return ch.GetManagementState(dsc) == operatorv1.Managed }