From 88bf03f7b0a8b7f2b74e4ce5afc1212400682c4d Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Tue, 23 Dec 2025 15:27:50 +0100 Subject: [PATCH] feat: introduced initializer predicate On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/operator.go | 13 ++++ internal/controller/workspace_controller.go | 83 +++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 internal/controller/workspace_controller.go diff --git a/cmd/operator.go b/cmd/operator.go index 1865f5b..0841263 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -151,6 +151,14 @@ var operatorCmd = &cobra.Command{ fga := openfgav1.NewOpenFGAServiceClient(conn) + k8sCfg := ctrl.GetConfigOrDie() + + runtimeClient, err := client.New(k8sCfg, client.Options{Scheme: scheme}) + if err != nil { + log.Error().Err(err).Msg("Failed to create in cluster client") + return err + } + if err = controller.NewStoreReconciler(log, fga, mgr). SetupWithManager(mgr, defaultCfg); err != nil { log.Error().Err(err).Str("controller", "store").Msg("unable to create controller") @@ -170,6 +178,11 @@ var operatorCmd = &cobra.Command{ log.Error().Err(err).Str("controller", "invite").Msg("unable to create controller") return err } + + if err = controller.NewWorkspaceReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, "root:security"); err != nil { + log.Error().Err(err).Str("controller", "workspace").Msg("unable to create controller") + return err + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/internal/controller/workspace_controller.go b/internal/controller/workspace_controller.go new file mode 100644 index 0000000..b0d9fd0 --- /dev/null +++ b/internal/controller/workspace_controller.go @@ -0,0 +1,83 @@ +package controller + +import ( + "context" + "slices" + "sync" + + kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + platformeshconfig "github.com/platform-mesh/golang-commons/config" + "github.com/platform-mesh/golang-commons/controller/lifecycle/builder" + "github.com/platform-mesh/golang-commons/controller/lifecycle/multicluster" + lifecyclesubroutine "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine" + "github.com/platform-mesh/golang-commons/logger" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + mccontext "sigs.k8s.io/multicluster-runtime/pkg/context" + mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" + mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" + + "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/subroutine" +) + +var ( + shouldReconcileMutex sync.Mutex +) + +type WorkspaceReconciler struct { + log *logger.Logger + mgr mcmanager.Manager + initializer kcpcorev1alpha1.LogicalClusterInitializer + mclifecycle *multicluster.LifecycleManager +} + +func NewWorkspaceReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *WorkspaceReconciler { + return &WorkspaceReconciler{ + log: log, + mgr: mgr, + mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ + subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr), + subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, cfg), + }, log).WithReadOnly().WithStaticThenExponentialRateLimiter().BuildMultiCluster(mgr), + } +} + +func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { + ctxWithCluster := mccontext.WithCluster(ctx, req.ClusterName) + return r.mclifecycle.Reconcile(ctxWithCluster, req, &kcpcorev1alpha1.LogicalCluster{}) +} + +func (r *WorkspaceReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { + r.initializer = kcpcorev1alpha1.LogicalClusterInitializer(initializerName) + allPredicates := append([]predicate.Predicate{HasInitializerPredicate(initializerName)}, evp...) + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, allPredicates...) +} + +func HasInitializerPredicate(initializerName string) predicate.Predicate { + initializer := kcpcorev1alpha1.LogicalClusterInitializer(initializerName) + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + lc := e.Object.(*kcpcorev1alpha1.LogicalCluster) + return shouldReconcile(lc, initializer) + }, + UpdateFunc: func(e event.UpdateEvent) bool { + newLC := e.ObjectNew.(*kcpcorev1alpha1.LogicalCluster) + return shouldReconcile(newLC, initializer) + }, + DeleteFunc: func(e event.DeleteEvent) bool { + lc := e.Object.(*kcpcorev1alpha1.LogicalCluster) + return shouldReconcile(lc, initializer) + }, + GenericFunc: func(e event.GenericEvent) bool { + lc := e.Object.(*kcpcorev1alpha1.LogicalCluster) + return shouldReconcile(lc, initializer) + }, + } +} + +func shouldReconcile(lc *kcpcorev1alpha1.LogicalCluster, initializer kcpcorev1alpha1.LogicalClusterInitializer) bool { + return slices.Contains(lc.Spec.Initializers, initializer) && !slices.Contains(lc.Status.Initializers, initializer) +}