From 88bf03f7b0a8b7f2b74e4ce5afc1212400682c4d Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Tue, 23 Dec 2025 15:27:50 +0100 Subject: [PATCH 01/20] 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 1865f5b3..0841263d 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 00000000..b0d9fd08 --- /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) +} From da5a7c2796c0f9cd1d264e8bff7e718e5a6a53a2 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Thu, 22 Jan 2026 09:53:49 +0100 Subject: [PATCH 02/20] fixed tests and linter errors On-behalf-of: SAP aleh.yarshou@sap.com --- internal/controller/workspace_controller.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/controller/workspace_controller.go b/internal/controller/workspace_controller.go index b0d9fd08..95505232 100644 --- a/internal/controller/workspace_controller.go +++ b/internal/controller/workspace_controller.go @@ -3,9 +3,8 @@ package controller import ( "context" "slices" - "sync" - kcpcorev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1" + kcpcorev1alpha1 "github.com/kcp-dev/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" @@ -23,10 +22,6 @@ import ( "github.com/platform-mesh/security-operator/internal/subroutine" ) -var ( - shouldReconcileMutex sync.Mutex -) - type WorkspaceReconciler struct { log *logger.Logger mgr mcmanager.Manager @@ -40,7 +35,7 @@ func NewWorkspaceReconciler(log *logger.Logger, orgClient client.Client, cfg con mgr: mgr, mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr), - subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, cfg), + subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg), }, log).WithReadOnly().WithStaticThenExponentialRateLimiter().BuildMultiCluster(mgr), } } From de205751b01cb3fbfb52b957104127fc182c86e4 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Thu, 22 Jan 2026 10:31:48 +0100 Subject: [PATCH 03/20] addressed linter complience On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/operator.go | 1 - internal/controller/workspace_controller.go | 6 +++--- internal/subroutine/workspace_authorization_test.go | 8 ++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cmd/operator.go b/cmd/operator.go index 3339c4ff..e8eaf83e 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -44,7 +44,6 @@ var ( type NewLogicalClusterClientFunc func(clusterKey logicalcluster.Name) (client.Client, error) - func logicalClusterClientFromKey(config *rest.Config, log *logger.Logger) NewLogicalClusterClientFunc { return func(clusterKey logicalcluster.Name) (client.Client, error) { cfg := rest.CopyConfig(config) diff --git a/internal/controller/workspace_controller.go b/internal/controller/workspace_controller.go index 95505232..cb8f34ba 100644 --- a/internal/controller/workspace_controller.go +++ b/internal/controller/workspace_controller.go @@ -4,12 +4,13 @@ import ( "context" "slices" - kcpcorev1alpha1 "github.com/kcp-dev/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" + "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/subroutine" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/event" @@ -18,8 +19,7 @@ import ( 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" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) type WorkspaceReconciler struct { diff --git a/internal/subroutine/workspace_authorization_test.go b/internal/subroutine/workspace_authorization_test.go index 0c475f8b..63c36712 100644 --- a/internal/subroutine/workspace_authorization_test.go +++ b/internal/subroutine/workspace_authorization_test.go @@ -197,8 +197,8 @@ func TestWorkspaceAuthSubroutine_Process(t *testing.T) { Annotations: map[string]string{}, }, }, - cfg: config.Config{BaseDomain: "test.domain", GroupClaim: "groups", UserClaim: "email"}, - setupMocks: func(m *mocks.MockClient, mgrClient *mocks.MockClient) {}, + cfg: config.Config{BaseDomain: "test.domain", GroupClaim: "groups", UserClaim: "email"}, + setupMocks: func(m *mocks.MockClient, mgrClient *mocks.MockClient) {}, expectError: true, expectedResult: ctrl.Result{}, }, @@ -211,8 +211,8 @@ func TestWorkspaceAuthSubroutine_Process(t *testing.T) { }, }, }, - cfg: config.Config{BaseDomain: "test.domain", GroupClaim: "groups", UserClaim: "email"}, - setupMocks: func(m *mocks.MockClient, mgrClient *mocks.MockClient) {}, + cfg: config.Config{BaseDomain: "test.domain", GroupClaim: "groups", UserClaim: "email"}, + setupMocks: func(m *mocks.MockClient, mgrClient *mocks.MockClient) {}, expectError: true, expectedResult: ctrl.Result{}, }, From 37e9eed9990d645a03c69d6339340cda31cb3867 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Fri, 23 Jan 2026 16:34:28 +0100 Subject: [PATCH 04/20] added the rest of the subroutines On-behalf-of: SAP aleh.yarshou@sap.com --- internal/controller/workspace_controller.go | 4 +- internal/subroutine/idp.go | 62 +++++++-------------- 2 files changed, 23 insertions(+), 43 deletions(-) diff --git a/internal/controller/workspace_controller.go b/internal/controller/workspace_controller.go index cb8f34ba..ec3675df 100644 --- a/internal/controller/workspace_controller.go +++ b/internal/controller/workspace_controller.go @@ -35,8 +35,10 @@ func NewWorkspaceReconciler(log *logger.Logger, orgClient client.Client, cfg con mgr: mgr, mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr), + subroutine.NewIDPSubroutine(orgClient, mgr, cfg), + subroutine.NewInviteSubroutine(orgClient, mgr), subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg), - }, log).WithReadOnly().WithStaticThenExponentialRateLimiter().BuildMultiCluster(mgr), + }, log).WithReadOnly().BuildMultiCluster(mgr), } } diff --git a/internal/subroutine/idp.go b/internal/subroutine/idp.go index ebbfed6f..7e42597e 100644 --- a/internal/subroutine/idp.go +++ b/internal/subroutine/idp.go @@ -3,7 +3,6 @@ package subroutine import ( "context" "fmt" - "slices" "strings" accountv1alpha1 "github.com/platform-mesh/account-operator/api/v1alpha1" @@ -90,32 +89,28 @@ func (i *IDPSubroutine) Process(ctx context.Context, instance runtimeobject.Runt return ctrl.Result{}, nil } - clients := []v1alpha1.IdentityProviderClientConfig{ - { - ClientName: workspaceName, - ClientType: v1alpha1.IdentityProviderClientTypeConfidential, - RedirectURIs: append(i.additionalRedirectURLs, fmt.Sprintf("https://%s.%s/*", workspaceName, i.baseDomain)), - PostLogoutRedirectURIs: []string{fmt.Sprintf("https://%s.%s/logout*", workspaceName, i.baseDomain)}, - SecretRef: corev1.SecretReference{ - Name: fmt.Sprintf("portal-client-secret-%s-%s", workspaceName, workspaceName), - Namespace: secretNamespace, - }, - }, - { - ClientName: kubectlClientName, - ClientType: v1alpha1.IdentityProviderClientTypePublic, - RedirectURIs: i.kubectlClientRedirectURLs, - SecretRef: corev1.SecretReference{ - Name: fmt.Sprintf("portal-client-secret-%s-%s", workspaceName, kubectlClientName), - Namespace: secretNamespace, - }, - }, - } - idp := &v1alpha1.IdentityProviderConfiguration{ObjectMeta: metav1.ObjectMeta{Name: workspaceName}} _, err = controllerutil.CreateOrPatch(ctx, cl.GetClient(), idp, func() error { - for _, desired := range clients { - idp.Spec.Clients = ensureClient(idp.Spec.Clients, desired) + idp.Spec.Clients = []v1alpha1.IdentityProviderClientConfig{ + { + ClientName: workspaceName, + ClientType: v1alpha1.IdentityProviderClientTypeConfidential, + RedirectURIs: append(i.additionalRedirectURLs, fmt.Sprintf("https://%s.%s/*", workspaceName, i.baseDomain)), + PostLogoutRedirectURIs: []string{fmt.Sprintf("https://%s.%s/logout*", workspaceName, i.baseDomain)}, + SecretRef: corev1.SecretReference{ + Name: fmt.Sprintf("portal-client-secret-%s-%s", workspaceName, workspaceName), + Namespace: secretNamespace, + }, + }, + { + ClientName: kubectlClientName, + ClientType: v1alpha1.IdentityProviderClientTypePublic, + RedirectURIs: i.kubectlClientRedirectURLs, + SecretRef: corev1.SecretReference{ + Name: fmt.Sprintf("portal-client-secret-%s-%s", workspaceName, kubectlClientName), + Namespace: secretNamespace, + }, + }, } return nil }) @@ -140,23 +135,6 @@ func (i *IDPSubroutine) Process(ctx context.Context, instance runtimeobject.Runt return ctrl.Result{}, nil } -// ensureClient updates only fields managed by this subroutine, preserving ClientID and RegistrationClientURI -// that are set by reconciling an idp resource -func ensureClient(existing []v1alpha1.IdentityProviderClientConfig, desired v1alpha1.IdentityProviderClientConfig) []v1alpha1.IdentityProviderClientConfig { - idx := slices.IndexFunc(existing, func(c v1alpha1.IdentityProviderClientConfig) bool { - return c.ClientName == desired.ClientName - }) - - if idx != -1 { - existing[idx].ClientType = desired.ClientType - existing[idx].RedirectURIs = desired.RedirectURIs - existing[idx].PostLogoutRedirectURIs = desired.PostLogoutRedirectURIs - existing[idx].SecretRef = desired.SecretRef - return existing - } - - return append(existing, desired) -} func getWorkspaceName(lc *kcpcorev1alpha1.LogicalCluster) string { if path, ok := lc.Annotations["kcp.io/path"]; ok { From a55d8494b9ab9a9114b0f430110061a3f2c18419 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 24 Jan 2026 14:46:31 +0100 Subject: [PATCH 05/20] used update instead of patch for idp resource creation On-behalf-of: SAP aleh.yarshou@sap.com --- internal/subroutine/idp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/subroutine/idp.go b/internal/subroutine/idp.go index 7e42597e..5b7f1056 100644 --- a/internal/subroutine/idp.go +++ b/internal/subroutine/idp.go @@ -90,7 +90,7 @@ func (i *IDPSubroutine) Process(ctx context.Context, instance runtimeobject.Runt } idp := &v1alpha1.IdentityProviderConfiguration{ObjectMeta: metav1.ObjectMeta{Name: workspaceName}} - _, err = controllerutil.CreateOrPatch(ctx, cl.GetClient(), idp, func() error { + _, err = controllerutil.CreateOrUpdate(ctx, cl.GetClient(), idp, func() error { idp.Spec.Clients = []v1alpha1.IdentityProviderClientConfig{ { ClientName: workspaceName, From 34c546dfe710a4231efe7008e639a7a0330ebac7 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 24 Jan 2026 14:59:01 +0100 Subject: [PATCH 06/20] used initializer name from the config On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/operator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/operator.go b/cmd/operator.go index e8eaf83e..1d72dffd 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -183,7 +183,7 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewWorkspaceReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, "root:security"); err != nil { + if err = controller.NewWorkspaceReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName); err != nil { log.Error().Err(err).Str("controller", "workspace").Msg("unable to create controller") return err } From 15a2b7e65a387a93d6b097412df1a09bc57bdf71 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 24 Jan 2026 15:06:56 +0100 Subject: [PATCH 07/20] chore: naming refactoring On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/initializer.go | 2 +- cmd/operator.go | 2 +- internal/controller/initializer_controller.go | 10 +++++----- ...e_controller.go => logical_cluster_controller.go} | 12 +++++------- 4 files changed, 12 insertions(+), 14 deletions(-) rename internal/controller/{workspace_controller.go => logical_cluster_controller.go} (82%) diff --git a/cmd/initializer.go b/cmd/initializer.go index 38f224ba..8455e83d 100644 --- a/cmd/initializer.go +++ b/cmd/initializer.go @@ -93,7 +93,7 @@ var initializerCmd = &cobra.Command{ initializerCfg.IDP.AdditionalRedirectURLs = []string{} } - if err := controller.NewLogicalClusterReconciler(log, orgClient, initializerCfg, runtimeClient, mgr). + if err := controller.NewLogicalClusterInitializer(log, orgClient, initializerCfg, runtimeClient, mgr). SetupWithManager(mgr, defaultCfg); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LogicalCluster") os.Exit(1) diff --git a/cmd/operator.go b/cmd/operator.go index 1d72dffd..d89cae2e 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -183,7 +183,7 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewWorkspaceReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName); err != nil { + if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName); err != nil { log.Error().Err(err).Str("controller", "workspace").Msg("unable to create controller") return err } diff --git a/internal/controller/initializer_controller.go b/internal/controller/initializer_controller.go index 8a77a2f9..a1ebb973 100644 --- a/internal/controller/initializer_controller.go +++ b/internal/controller/initializer_controller.go @@ -20,14 +20,14 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -type LogicalClusterReconciler struct { +type LogicalClusterInitializer struct { log *logger.Logger mclifecycle *multicluster.LifecycleManager } -func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { - return &LogicalClusterReconciler{ +func NewLogicalClusterInitializer(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterInitializer { + return &LogicalClusterInitializer{ log: log, mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr), @@ -42,11 +42,11 @@ func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cf } } -func (r *LogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *LogicalClusterInitializer) 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 *LogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { +func (r *LogicalClusterInitializer) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) } diff --git a/internal/controller/workspace_controller.go b/internal/controller/logical_cluster_controller.go similarity index 82% rename from internal/controller/workspace_controller.go rename to internal/controller/logical_cluster_controller.go index ec3675df..735dfbd6 100644 --- a/internal/controller/workspace_controller.go +++ b/internal/controller/logical_cluster_controller.go @@ -22,15 +22,14 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -type WorkspaceReconciler struct { +type LogicalClusterReconciler 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{ +func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { + return &LogicalClusterReconciler{ log: log, mgr: mgr, mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ @@ -42,13 +41,12 @@ func NewWorkspaceReconciler(log *logger.Logger, orgClient client.Client, cfg con } } -func (r *WorkspaceReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *LogicalClusterReconciler) 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) +func (r *LogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { allPredicates := append([]predicate.Predicate{HasInitializerPredicate(initializerName)}, evp...) return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, allPredicates...) } From 905bc01189715a16ddb3c2c34e3f9ad2962a4ada Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 26 Jan 2026 09:35:25 +0100 Subject: [PATCH 08/20] updated initializer name passing On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/operator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/operator.go b/cmd/operator.go index 2762ddb6..930a5d36 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -183,7 +183,7 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName); err != nil { + if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName()); err != nil { log.Error().Err(err).Str("controller", "workspace").Msg("unable to create controller") return err } From 31cec8fe98685ad696bffada99f97a9d1c2be9ac Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Fri, 6 Feb 2026 15:57:09 +0100 Subject: [PATCH 09/20] chore: refactoring after merge On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/operator.go | 4 ++-- internal/subroutine/idp.go | 18 ------------------ 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/cmd/operator.go b/cmd/operator.go index f3995646..b786552f 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -184,8 +184,8 @@ var operatorCmd = &cobra.Command{ if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName()); err != nil { log.Error().Err(err).Str("controller", "logicalcluster").Msg("unable to create controller") - return err - } + return err + } if err = controller.NewAccountInfoReconciler(log, mgr).SetupWithManager(mgr, defaultCfg); err != nil { log.Error().Err(err).Str("controller", "accountinfo").Msg("unable to create controller") return err diff --git a/internal/subroutine/idp.go b/internal/subroutine/idp.go index cd3611dc..f0e00cb3 100644 --- a/internal/subroutine/idp.go +++ b/internal/subroutine/idp.go @@ -190,24 +190,6 @@ func (i *IDPSubroutine) patchAccountInfo(ctx context.Context, cl client.Client, return nil } -// ensureClient updates only fields managed by this subroutine, preserving ClientID and RegistrationClientURI -// that are set by reconciling an idp resource -func ensureClient(existing []v1alpha1.IdentityProviderClientConfig, desired v1alpha1.IdentityProviderClientConfig) []v1alpha1.IdentityProviderClientConfig { - idx := slices.IndexFunc(existing, func(c v1alpha1.IdentityProviderClientConfig) bool { - return c.ClientName == desired.ClientName - }) - - if idx != -1 { - existing[idx].ClientType = desired.ClientType - existing[idx].RedirectURIs = desired.RedirectURIs - existing[idx].PostLogoutRedirectURIs = desired.PostLogoutRedirectURIs - existing[idx].SecretRef = desired.SecretRef - return existing - } - - return append(existing, desired) -} - func getWorkspaceName(lc *kcpcorev1alpha1.LogicalCluster) string { if path, ok := lc.Annotations["kcp.io/path"]; ok { pathElements := strings.Split(path, ":") From fc4acea899c3709730872534c43b94568965d1ef Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Fri, 27 Feb 2026 15:51:15 +0100 Subject: [PATCH 10/20] chore: code refactoring On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/initializer.go | 14 +--- cmd/operator.go | 11 ++- cmd/terminator.go | 8 +- .../accountlogicalcluster_controller.go | 33 ++++---- ...ntlogicalcluster_initializer_controller.go | 51 ++++++++++++ .../controller/logical_cluster_controller.go | 78 ------------------- .../orglogicalcluster_controller.go | 23 +++--- ...rglogicalcluster_initializer_controller.go | 62 +++++++++++++++ internal/predicates/initializer.go | 35 +++++++++ internal/subroutine/account_tuples.go | 36 +++------ internal/subroutine/workspace_initializer.go | 18 +---- 11 files changed, 200 insertions(+), 169 deletions(-) create mode 100644 internal/controller/accountlogicalcluster_initializer_controller.go delete mode 100644 internal/controller/logical_cluster_controller.go create mode 100644 internal/controller/orglogicalcluster_initializer_controller.go create mode 100644 internal/predicates/initializer.go diff --git a/cmd/initializer.go b/cmd/initializer.go index e3005736..a2145075 100644 --- a/cmd/initializer.go +++ b/cmd/initializer.go @@ -24,7 +24,6 @@ import ( "k8s.io/client-go/rest" "github.com/kcp-dev/logicalcluster/v3" - mcclient "github.com/kcp-dev/multicluster-provider/client" "github.com/kcp-dev/multicluster-provider/initializingworkspaces" ) @@ -106,12 +105,6 @@ var initializerCmd = &cobra.Command{ os.Exit(1) } - kcpCfg, err := getKubeconfigFromPath(initializerCfg.KCP.Kubeconfig) - if err != nil { - log.Error().Err(err).Msg("unable to get KCP kubeconfig") - return err - } - conn, err := grpc.NewClient(initializerCfg.FGA.Target, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { log.Error().Err(err).Msg("unable to create grpc client") @@ -120,12 +113,7 @@ var initializerCmd = &cobra.Command{ defer func() { _ = conn.Close() }() fga := openfgav1.NewOpenFGAServiceClient(conn) - mcc, err := mcclient.New(kcpCfg, client.Options{Scheme: scheme}) - if err != nil { - log.Error().Err(err).Msg("Failed to create multicluster client") - os.Exit(1) - } - if err := controller.NewAccountLogicalClusterReconciler(log, initializerCfg, fga, mcc, mgr). + if err := controller.NewAccountLogicalClusterReconciler(log, initializerCfg, fga, mgr). SetupWithManager(mgr, defaultCfg, predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AccountLogicalCluster") os.Exit(1) diff --git a/cmd/operator.go b/cmd/operator.go index 107f3bfc..a1679c06 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -14,6 +14,7 @@ import ( corev1alpha1 "github.com/platform-mesh/security-operator/api/v1alpha1" iclient "github.com/platform-mesh/security-operator/internal/client" "github.com/platform-mesh/security-operator/internal/controller" + "github.com/platform-mesh/security-operator/internal/predicates" internalwebhook "github.com/platform-mesh/security-operator/internal/webhook" "github.com/spf13/cobra" "google.golang.org/grpc" @@ -22,6 +23,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/webhook" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" @@ -177,7 +179,7 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewStoreReconciler(log, fga, mgr). + if err = controller.NewStoreReconciler(ctx, log, fga, mgr). SetupWithManager(mgr, defaultCfg); err != nil { log.Error().Err(err).Str("controller", "store").Msg("unable to create controller") return err @@ -197,10 +199,15 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName()); err != nil { + if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicates.LogicalClusterIsAccountTypeOrg()); err != nil { log.Error().Err(err).Str("controller", "logicalcluster").Msg("unable to create controller") return err } + + if err = controller.NewAccountTypeLogicalClusterReconciler(log, operatorCfg, fga, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { + log.Error().Err(err).Str("controller", "accounttypelogicalcluster").Msg("unable to create controller") + return err + } if err = controller.NewAccountInfoReconciler(log, mgr).SetupWithManager(mgr, defaultCfg); err != nil { log.Error().Err(err).Str("controller", "accountinfo").Msg("unable to create controller") return err diff --git a/cmd/terminator.go b/cmd/terminator.go index 972fe58b..f7e137ca 100644 --- a/cmd/terminator.go +++ b/cmd/terminator.go @@ -26,7 +26,6 @@ import ( "k8s.io/client-go/rest" "github.com/kcp-dev/logicalcluster/v3" - mcclient "github.com/kcp-dev/multicluster-provider/client" kcptenancyv1alphav1 "github.com/kcp-dev/sdk/apis/tenancy/v1alpha1" ) @@ -66,11 +65,6 @@ var terminatorCmd = &cobra.Command{ mgrOpts.LeaderElectionConfig = inClusterCfg } - mcc, err := mcclient.New(kcpCfg, client.Options{Scheme: scheme}) - if err != nil { - log.Error().Err(err).Msg("Failed to create multicluster client") - os.Exit(1) - } rootClient, err := iclient.NewForLogicalCluster(kcpCfg, scheme, logicalcluster.Name("root")) if err != nil { log.Error().Err(err).Msgf("Failed to get root client") @@ -112,7 +106,7 @@ var terminatorCmd = &cobra.Command{ defer func() { _ = conn.Close() }() fga := openfgav1.NewOpenFGAServiceClient(conn) - if err := controller.NewAccountLogicalClusterReconciler(log, terminatorCfg, fga, mcc, mgr). + if err := controller.NewAccountLogicalClusterReconciler(log, terminatorCfg, fga, mgr). SetupWithManager(mgr, defaultCfg, predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { log.Error().Err(err).Msg("Unable to create AccountLogicalClusterTerminator") os.Exit(1) diff --git a/internal/controller/accountlogicalcluster_controller.go b/internal/controller/accountlogicalcluster_controller.go index f9641695..7f3a0502 100644 --- a/internal/controller/accountlogicalcluster_controller.go +++ b/internal/controller/accountlogicalcluster_controller.go @@ -3,6 +3,7 @@ package controller import ( "context" + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" openfgav1 "github.com/openfga/api/proto/openfga/v1" platformeshconfig "github.com/platform-mesh/golang-commons/config" "github.com/platform-mesh/golang-commons/controller/lifecycle/builder" @@ -10,43 +11,35 @@ import ( lifecyclesubroutine "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine" "github.com/platform-mesh/golang-commons/logger" "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/predicates" "github.com/platform-mesh/security-operator/internal/subroutine" ctrl "sigs.k8s.io/controller-runtime" "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" - - mcclient "github.com/kcp-dev/multicluster-provider/client" - kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -// AccountLogicalClusterReconciler acts as an initializer for account workspaces. -type AccountLogicalClusterReconciler struct { - log *logger.Logger - +type AccountTypeLogicalClusterReconciler struct { + log *logger.Logger mclifecycle *multicluster.LifecycleManager } -func NewAccountLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mcc mcclient.ClusterClient, mgr mcmanager.Manager) *AccountLogicalClusterReconciler { - return &AccountLogicalClusterReconciler{ +func NewAccountTypeLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountTypeLogicalClusterReconciler { + return &AccountTypeLogicalClusterReconciler{ log: log, - mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ - subroutine.NewAccountTuplesSubroutine(mcc, mgr, fga, cfg.FGA.CreatorRelation, cfg.FGA.ParentRelation, cfg.FGA.ObjectType), - }, log). - WithReadOnly(). - WithStaticThenExponentialRateLimiter(). - WithInitializer(cfg.InitializerName()). - WithTerminator(cfg.TerminatorName()). - BuildMultiCluster(mgr), + mclifecycle: builder.NewBuilder("logicalcluster", "AccountTypeLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ + subroutine.NewAccountTuplesSubroutine(mgr, fga, cfg.FGA.CreatorRelation, cfg.FGA.ParentRelation, cfg.FGA.ObjectType), + }, log).WithReadOnly().BuildMultiCluster(mgr), } } -func (r *AccountLogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *AccountTypeLogicalClusterReconciler) 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 *AccountLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { - return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) +func (r *AccountTypeLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { + allPredicates := append([]predicate.Predicate{predicates.HasInitializerPredicate(initializerName)}, evp...) + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountTypeLogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, allPredicates...) } diff --git a/internal/controller/accountlogicalcluster_initializer_controller.go b/internal/controller/accountlogicalcluster_initializer_controller.go new file mode 100644 index 00000000..6dde2bfc --- /dev/null +++ b/internal/controller/accountlogicalcluster_initializer_controller.go @@ -0,0 +1,51 @@ +package controller + +import ( + "context" + + openfgav1 "github.com/openfga/api/proto/openfga/v1" + 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" + "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/subroutine" + ctrl "sigs.k8s.io/controller-runtime" + "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" + + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" +) + +// AccountLogicalClusterReconciler acts as an initializer for account workspaces. +type AccountLogicalClusterReconciler struct { + log *logger.Logger + + mclifecycle *multicluster.LifecycleManager +} + +func NewAccountLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountLogicalClusterReconciler { + return &AccountLogicalClusterReconciler{ + log: log, + mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ + subroutine.NewAccountTuplesSubroutine(mgr, fga, cfg.FGA.CreatorRelation, cfg.FGA.ParentRelation, cfg.FGA.ObjectType), + }, log). + WithReadOnly(). + WithStaticThenExponentialRateLimiter(). + WithInitializer(cfg.InitializerName()). + WithTerminator(cfg.TerminatorName()). + BuildMultiCluster(mgr), + } +} + +func (r *AccountLogicalClusterReconciler) 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 *AccountLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) +} diff --git a/internal/controller/logical_cluster_controller.go b/internal/controller/logical_cluster_controller.go deleted file mode 100644 index 735dfbd6..00000000 --- a/internal/controller/logical_cluster_controller.go +++ /dev/null @@ -1,78 +0,0 @@ -package controller - -import ( - "context" - "slices" - - 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" - "github.com/platform-mesh/security-operator/internal/config" - "github.com/platform-mesh/security-operator/internal/subroutine" - 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" - - kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" -) - -type LogicalClusterReconciler struct { - log *logger.Logger - mgr mcmanager.Manager - mclifecycle *multicluster.LifecycleManager -} - -func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { - return &LogicalClusterReconciler{ - log: log, - mgr: mgr, - mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ - subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr), - subroutine.NewIDPSubroutine(orgClient, mgr, cfg), - subroutine.NewInviteSubroutine(orgClient, mgr), - subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg), - }, log).WithReadOnly().BuildMultiCluster(mgr), - } -} - -func (r *LogicalClusterReconciler) 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 *LogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { - 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) -} diff --git a/internal/controller/orglogicalcluster_controller.go b/internal/controller/orglogicalcluster_controller.go index 6840f8c6..b003af4b 100644 --- a/internal/controller/orglogicalcluster_controller.go +++ b/internal/controller/orglogicalcluster_controller.go @@ -9,6 +9,7 @@ import ( lifecyclesubroutine "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine" "github.com/platform-mesh/golang-commons/logger" "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/predicates" "github.com/platform-mesh/security-operator/internal/subroutine" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -20,13 +21,13 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -type OrgLogicalClusterReconciler struct { - log *logger.Logger - +type LogicalClusterReconciler struct { + log *logger.Logger + mgr mcmanager.Manager mclifecycle *multicluster.LifecycleManager } -func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *OrgLogicalClusterReconciler { +func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { var subroutines []lifecyclesubroutine.Subroutine if cfg.Initializer.WorkspaceInitializerEnabled { @@ -42,21 +43,21 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, subroutines = append(subroutines, subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg)) } - return &OrgLogicalClusterReconciler{ + return &LogicalClusterReconciler{ log: log, - mclifecycle: builder.NewBuilder("logicalcluster", "OrgLogicalClusterReconciler", subroutines, log). + mgr: mgr, + mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", subroutines, log). WithReadOnly(). - WithStaticThenExponentialRateLimiter(). - WithInitializer(cfg.InitializerName()). BuildMultiCluster(mgr), } } -func (r *OrgLogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *LogicalClusterReconciler) 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 *OrgLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { - return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) +func (r *LogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { + allPredicates := append([]predicate.Predicate{predicates.HasInitializerPredicate(initializerName)}, evp...) + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, allPredicates...) } diff --git a/internal/controller/orglogicalcluster_initializer_controller.go b/internal/controller/orglogicalcluster_initializer_controller.go new file mode 100644 index 00000000..6840f8c6 --- /dev/null +++ b/internal/controller/orglogicalcluster_initializer_controller.go @@ -0,0 +1,62 @@ +package controller + +import ( + "context" + + 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" + "github.com/platform-mesh/security-operator/internal/config" + "github.com/platform-mesh/security-operator/internal/subroutine" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "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" + + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" +) + +type OrgLogicalClusterReconciler struct { + log *logger.Logger + + mclifecycle *multicluster.LifecycleManager +} + +func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *OrgLogicalClusterReconciler { + var subroutines []lifecyclesubroutine.Subroutine + + if cfg.Initializer.WorkspaceInitializerEnabled { + subroutines = append(subroutines, subroutine.NewWorkspaceInitializer(orgClient, cfg, mgr, cfg.FGA.CreatorRelation, cfg.FGA.ObjectType)) + } + if cfg.Initializer.IDPEnabled { + subroutines = append(subroutines, subroutine.NewIDPSubroutine(orgClient, mgr, cfg)) + } + if cfg.Initializer.InviteEnabled { + subroutines = append(subroutines, subroutine.NewInviteSubroutine(orgClient, mgr)) + } + if cfg.Initializer.WorkspaceAuthEnabled { + subroutines = append(subroutines, subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg)) + } + + return &OrgLogicalClusterReconciler{ + log: log, + mclifecycle: builder.NewBuilder("logicalcluster", "OrgLogicalClusterReconciler", subroutines, log). + WithReadOnly(). + WithStaticThenExponentialRateLimiter(). + WithInitializer(cfg.InitializerName()). + BuildMultiCluster(mgr), + } +} + +func (r *OrgLogicalClusterReconciler) 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 *OrgLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) +} diff --git a/internal/predicates/initializer.go b/internal/predicates/initializer.go new file mode 100644 index 00000000..222e4e59 --- /dev/null +++ b/internal/predicates/initializer.go @@ -0,0 +1,35 @@ +package predicates + +import ( + "slices" + + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +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) +} diff --git a/internal/subroutine/account_tuples.go b/internal/subroutine/account_tuples.go index dcf3bf5d..86665db9 100644 --- a/internal/subroutine/account_tuples.go +++ b/internal/subroutine/account_tuples.go @@ -23,7 +23,6 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" "github.com/kcp-dev/logicalcluster/v3" - mcclient "github.com/kcp-dev/multicluster-provider/client" kcpcore "github.com/kcp-dev/sdk/apis/core" kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) @@ -34,7 +33,6 @@ const accountTuplesTerminatorFinalizer = "core.platform-mesh.io/account-tuples-t // "org"-type when initializing, and deletes them when terminating. type AccountTuplesSubroutine struct { mgr mcmanager.Manager - mcc mcclient.ClusterClient fga openfgav1.OpenFGAServiceClient objectType string @@ -51,23 +49,18 @@ func (s *AccountTuplesSubroutine) Process(ctx context.Context, instance runtimeo // Initialize implements lifecycle.Initializer. func (s *AccountTuplesSubroutine) Initialize(ctx context.Context, instance runtimeobject.RuntimeObject) (ctrl.Result, errors.OperatorError) { lc := instance.(*kcpcorev1alpha1.LogicalCluster) - acc, ai, opErr := AccountAndInfoForLogicalCluster(ctx, s.mgr, lc) + acc, ai, opErr := accountAndInfoForLogicalCluster(ctx, s.mgr, lc) if opErr != nil { return ctrl.Result{}, opErr } if updated := controllerutil.AddFinalizer(&ai, accountTuplesTerminatorFinalizer); updated { - lcID, ok := mccontext.ClusterFrom(ctx) - if !ok { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("cluster name not found in context"), true, true) - } - - lcClient, err := iclient.NewForLogicalCluster(s.mgr.GetLocalManager().GetConfig(), s.mgr.GetLocalManager().GetScheme(), logicalcluster.Name(lcID)) + cl, err := s.mgr.ClusterFromContext(ctx) if err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting client: %w", err), true, true) + return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context %w", err), true, true) } - if err := lcClient.Update(ctx, &ai); err != nil { + if err := cl.GetClient().Update(ctx, &ai); err != nil { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("updating AccountInfo to set finalizer: %w", err), true, true) } } @@ -87,7 +80,7 @@ func (s *AccountTuplesSubroutine) Initialize(ctx context.Context, instance runti // Terminate implements lifecycle.Terminator. func (s *AccountTuplesSubroutine) Terminate(ctx context.Context, instance runtimeobject.RuntimeObject) (ctrl.Result, errors.OperatorError) { lc := instance.(*kcpcorev1alpha1.LogicalCluster) - _, ai, opErr := AccountAndInfoForLogicalCluster(ctx, s.mgr, lc) + _, ai, opErr := accountAndInfoForLogicalCluster(ctx, s.mgr, lc) if opErr != nil { return ctrl.Result{}, opErr } @@ -152,10 +145,9 @@ func (s *AccountTuplesSubroutine) Finalizers(_ runtimeobject.RuntimeObject) []st // GetName implements lifecycle.Subroutine. func (s *AccountTuplesSubroutine) GetName() string { return "AccountTuplesSubroutine" } -func NewAccountTuplesSubroutine(mcc mcclient.ClusterClient, mgr mcmanager.Manager, fga openfgav1.OpenFGAServiceClient, creatorRelation, parentRelation, objectType string) *AccountTuplesSubroutine { +func NewAccountTuplesSubroutine(mgr mcmanager.Manager, fga openfgav1.OpenFGAServiceClient, creatorRelation, parentRelation, objectType string) *AccountTuplesSubroutine { return &AccountTuplesSubroutine{ mgr: mgr, - mcc: mcc, fga: fga, creatorRelation: creatorRelation, parentRelation: parentRelation, @@ -172,23 +164,19 @@ var ( // AccountAndInfoForLogicalCluster fetches the AccountInfo from the // LogicalCluster and the corresponding Account from the parent account's // workspace. -func AccountAndInfoForLogicalCluster(ctx context.Context, mgr mcmanager.Manager, lc *kcpcorev1alpha1.LogicalCluster) (accountsv1alpha1.Account, accountsv1alpha1.AccountInfo, errors.OperatorError) { +func accountAndInfoForLogicalCluster(ctx context.Context, mgr mcmanager.Manager, lc *kcpcorev1alpha1.LogicalCluster) (accountsv1alpha1.Account, accountsv1alpha1.AccountInfo, errors.OperatorError) { if lc.Annotations[kcpcore.LogicalClusterPathAnnotationKey] == "" { return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("annotation on LogicalCluster is not set"), true, true) } - lcID, ok := mccontext.ClusterFrom(ctx) - if !ok { - return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("cluster name not found in context"), true, true) + cl, err := mgr.ClusterFromContext(ctx) + if err != nil { + return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context"), true, true) } - + // The AccountInfo in the logical cluster belongs to the Account the // Workspace was created for - lcClient, err := iclient.NewForLogicalCluster(mgr.GetLocalManager().GetConfig(), mgr.GetLocalManager().GetScheme(), logicalcluster.Name(lcID)) - if err != nil { - return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("getting client: %w", err), true, true) - } var ai accountsv1alpha1.AccountInfo - if err := lcClient.Get(ctx, client.ObjectKey{ + if err := cl.GetClient().Get(ctx, client.ObjectKey{ Name: "account", }, &ai); err != nil && !kerrors.IsNotFound(err) { return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("getting AccountInfo for LogicalCluster: %w", err), true, true) diff --git a/internal/subroutine/workspace_initializer.go b/internal/subroutine/workspace_initializer.go index dba9b64c..8e0cfa57 100644 --- a/internal/subroutine/workspace_initializer.go +++ b/internal/subroutine/workspace_initializer.go @@ -12,19 +12,16 @@ import ( lifecyclesubroutine "github.com/platform-mesh/golang-commons/controller/lifecycle/subroutine" "github.com/platform-mesh/golang-commons/errors" "github.com/platform-mesh/security-operator/api/v1alpha1" - iclient "github.com/platform-mesh/security-operator/internal/client" "github.com/platform-mesh/security-operator/internal/config" "github.com/platform-mesh/security-operator/pkg/fga" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - mccontext "sigs.k8s.io/multicluster-runtime/pkg/context" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/kcp-dev/logicalcluster/v3" kcpcore "github.com/kcp-dev/sdk/apis/core" kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) @@ -87,15 +84,13 @@ func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeo if p == "" { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("annotation on LogicalCluster is not set"), true, true) } - lcID, _ := mccontext.ClusterFrom(ctx) - - lcClient, err := iclient.NewForLogicalCluster(w.mgr.GetLocalManager().GetConfig(), w.mgr.GetLocalManager().GetScheme(), logicalcluster.Name(lcID)) + cl, err := w.mgr.ClusterFromContext(ctx) if err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting client: %w", err), true, true) + return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context"), true, true) } var ai accountsv1alpha1.AccountInfo - if err := lcClient.Get(ctx, client.ObjectKey{ + if err := cl.GetClient().Get(ctx, client.ObjectKey{ Name: "account", }, &ai); err != nil && !kerrors.IsNotFound(err) { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting AccountInfo for LogicalCluster: %w", err), true, true) @@ -103,13 +98,8 @@ func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeo return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("AccountInfo not found yet, requeueing"), true, false) } - orgsClient, err := iclient.NewForLogicalCluster(w.mgr.GetLocalManager().GetConfig(), w.mgr.GetLocalManager().GetScheme(), logicalcluster.Name("root:orgs")) - if err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting parent organisation client: %w", err), true, true) - } - var acc accountsv1alpha1.Account - if err := orgsClient.Get(ctx, client.ObjectKey{ + if err := w.orgsClient.Get(ctx, client.ObjectKey{ Name: ai.Spec.Account.Name, }, &acc); err != nil { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting Account in platform-mesh-system: %w", err), true, true) From 1b3f38934709aa4d5dd2f562ba213230c1722321 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Fri, 27 Feb 2026 16:24:59 +0100 Subject: [PATCH 11/20] chore: refactor naming On-behalf-of: SAP aleh.yarshou@sap.com --- cmd/initializer.go | 4 ++-- cmd/operator.go | 4 ++-- cmd/terminator.go | 2 +- .../controller/accountlogicalcluster_controller.go | 2 +- .../accountlogicalcluster_initializer_controller.go | 10 +++++----- internal/controller/orglogicalcluster_controller.go | 2 +- .../orglogicalcluster_initializer_controller.go | 10 +++++----- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/cmd/initializer.go b/cmd/initializer.go index a2145075..cf937dda 100644 --- a/cmd/initializer.go +++ b/cmd/initializer.go @@ -99,7 +99,7 @@ var initializerCmd = &cobra.Command{ initializerCfg.IDP.AdditionalRedirectURLs = []string{} } - if err := controller.NewOrgLogicalClusterReconciler(log, orgClient, initializerCfg, runtimeClient, mgr). + if err := controller.NewOrgLogicalClusterInitializer(log, orgClient, initializerCfg, runtimeClient, mgr). SetupWithManager(mgr, defaultCfg, predicates.LogicalClusterIsAccountTypeOrg()); err != nil { setupLog.Error(err, "unable to create controller", "controller", "LogicalCluster") os.Exit(1) @@ -113,7 +113,7 @@ var initializerCmd = &cobra.Command{ defer func() { _ = conn.Close() }() fga := openfgav1.NewOpenFGAServiceClient(conn) - if err := controller.NewAccountLogicalClusterReconciler(log, initializerCfg, fga, mgr). + if err := controller.NewAccountLogicalClusterInitializer(log, initializerCfg, fga, mgr). SetupWithManager(mgr, defaultCfg, predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { setupLog.Error(err, "unable to create controller", "controller", "AccountLogicalCluster") os.Exit(1) diff --git a/cmd/operator.go b/cmd/operator.go index a1679c06..97addfe9 100644 --- a/cmd/operator.go +++ b/cmd/operator.go @@ -199,12 +199,12 @@ var operatorCmd = &cobra.Command{ return err } - if err = controller.NewLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicates.LogicalClusterIsAccountTypeOrg()); err != nil { + if err = controller.NewOrgLogicalClusterReconciler(log, orgClient, operatorCfg, runtimeClient, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicates.LogicalClusterIsAccountTypeOrg()); err != nil { log.Error().Err(err).Str("controller", "logicalcluster").Msg("unable to create controller") return err } - if err = controller.NewAccountTypeLogicalClusterReconciler(log, operatorCfg, fga, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { + if err = controller.NewAccountLogicalClusterReconciler(log, operatorCfg, fga, mgr).SetupWithManager(mgr, defaultCfg, operatorCfg.InitializerName(), predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { log.Error().Err(err).Str("controller", "accounttypelogicalcluster").Msg("unable to create controller") return err } diff --git a/cmd/terminator.go b/cmd/terminator.go index f7e137ca..325af8b5 100644 --- a/cmd/terminator.go +++ b/cmd/terminator.go @@ -106,7 +106,7 @@ var terminatorCmd = &cobra.Command{ defer func() { _ = conn.Close() }() fga := openfgav1.NewOpenFGAServiceClient(conn) - if err := controller.NewAccountLogicalClusterReconciler(log, terminatorCfg, fga, mgr). + if err := controller.NewAccountLogicalClusterInitializer(log, terminatorCfg, fga, mgr). SetupWithManager(mgr, defaultCfg, predicate.Not(predicates.LogicalClusterIsAccountTypeOrg())); err != nil { log.Error().Err(err).Msg("Unable to create AccountLogicalClusterTerminator") os.Exit(1) diff --git a/internal/controller/accountlogicalcluster_controller.go b/internal/controller/accountlogicalcluster_controller.go index 7f3a0502..455130d0 100644 --- a/internal/controller/accountlogicalcluster_controller.go +++ b/internal/controller/accountlogicalcluster_controller.go @@ -25,7 +25,7 @@ type AccountTypeLogicalClusterReconciler struct { mclifecycle *multicluster.LifecycleManager } -func NewAccountTypeLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountTypeLogicalClusterReconciler { +func NewAccountLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountTypeLogicalClusterReconciler { return &AccountTypeLogicalClusterReconciler{ log: log, mclifecycle: builder.NewBuilder("logicalcluster", "AccountTypeLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ diff --git a/internal/controller/accountlogicalcluster_initializer_controller.go b/internal/controller/accountlogicalcluster_initializer_controller.go index 6dde2bfc..97abd64c 100644 --- a/internal/controller/accountlogicalcluster_initializer_controller.go +++ b/internal/controller/accountlogicalcluster_initializer_controller.go @@ -21,14 +21,14 @@ import ( ) // AccountLogicalClusterReconciler acts as an initializer for account workspaces. -type AccountLogicalClusterReconciler struct { +type AccountLogicalClusterInitializer struct { log *logger.Logger mclifecycle *multicluster.LifecycleManager } -func NewAccountLogicalClusterReconciler(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountLogicalClusterReconciler { - return &AccountLogicalClusterReconciler{ +func NewAccountLogicalClusterInitializer(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountLogicalClusterInitializer { + return &AccountLogicalClusterInitializer{ log: log, mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ subroutine.NewAccountTuplesSubroutine(mgr, fga, cfg.FGA.CreatorRelation, cfg.FGA.ParentRelation, cfg.FGA.ObjectType), @@ -41,11 +41,11 @@ func NewAccountLogicalClusterReconciler(log *logger.Logger, cfg config.Config, f } } -func (r *AccountLogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *AccountLogicalClusterInitializer) 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 *AccountLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { +func (r *AccountLogicalClusterInitializer) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) } diff --git a/internal/controller/orglogicalcluster_controller.go b/internal/controller/orglogicalcluster_controller.go index b003af4b..aec91a8f 100644 --- a/internal/controller/orglogicalcluster_controller.go +++ b/internal/controller/orglogicalcluster_controller.go @@ -27,7 +27,7 @@ type LogicalClusterReconciler struct { mclifecycle *multicluster.LifecycleManager } -func NewLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { +func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { var subroutines []lifecyclesubroutine.Subroutine if cfg.Initializer.WorkspaceInitializerEnabled { diff --git a/internal/controller/orglogicalcluster_initializer_controller.go b/internal/controller/orglogicalcluster_initializer_controller.go index 6840f8c6..fdd0b098 100644 --- a/internal/controller/orglogicalcluster_initializer_controller.go +++ b/internal/controller/orglogicalcluster_initializer_controller.go @@ -20,13 +20,13 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -type OrgLogicalClusterReconciler struct { +type OrgLogicalClusterInitializer struct { log *logger.Logger mclifecycle *multicluster.LifecycleManager } -func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *OrgLogicalClusterReconciler { +func NewOrgLogicalClusterInitializer(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *OrgLogicalClusterInitializer { var subroutines []lifecyclesubroutine.Subroutine if cfg.Initializer.WorkspaceInitializerEnabled { @@ -42,7 +42,7 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, subroutines = append(subroutines, subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg)) } - return &OrgLogicalClusterReconciler{ + return &OrgLogicalClusterInitializer{ log: log, mclifecycle: builder.NewBuilder("logicalcluster", "OrgLogicalClusterReconciler", subroutines, log). WithReadOnly(). @@ -52,11 +52,11 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, } } -func (r *OrgLogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *OrgLogicalClusterInitializer) 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 *OrgLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { +func (r *OrgLogicalClusterInitializer) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) } From e007751383f7b398c01d642ed8142854dc75585e Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 28 Feb 2026 13:51:11 +0100 Subject: [PATCH 12/20] chore: remove unneeded cluster setup On-behalf-of: SAP aleh.yarshou@sap.com --- internal/subroutine/workspace_initializer.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/internal/subroutine/workspace_initializer.go b/internal/subroutine/workspace_initializer.go index 8e0cfa57..60222345 100644 --- a/internal/subroutine/workspace_initializer.go +++ b/internal/subroutine/workspace_initializer.go @@ -22,7 +22,6 @@ import ( kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kcpcore "github.com/kcp-dev/sdk/apis/core" kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) @@ -80,17 +79,13 @@ func (w *workspaceInitializer) Process(ctx context.Context, instance runtimeobje // Initialize implements lifecycle.Initializer. func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeobject.RuntimeObject) (ctrl.Result, errors.OperatorError) { lc := instance.(*kcpcorev1alpha1.LogicalCluster) - p := lc.Annotations[kcpcore.LogicalClusterPathAnnotationKey] - if p == "" { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("annotation on LogicalCluster is not set"), true, true) - } - cl, err := w.mgr.ClusterFromContext(ctx) + cluster, err := w.mgr.ClusterFromContext(ctx) if err != nil { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context"), true, true) } var ai accountsv1alpha1.AccountInfo - if err := cl.GetClient().Get(ctx, client.ObjectKey{ + if err := cluster.GetClient().Get(ctx, client.ObjectKey{ Name: "account", }, &ai); err != nil && !kerrors.IsNotFound(err) { return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting AccountInfo for LogicalCluster: %w", err), true, true) @@ -102,7 +97,7 @@ func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeo if err := w.orgsClient.Get(ctx, client.ObjectKey{ Name: ai.Spec.Account.Name, }, &acc); err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting Account in platform-mesh-system: %w", err), true, true) + return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting Account in orgs workspace: %w", err), true, true) } store := v1alpha1.Store{ @@ -152,11 +147,6 @@ func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeo return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("store id is empty"), true, false) } - cluster, err := w.mgr.ClusterFromContext(ctx) - if err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("unable to get cluster from context: %w", err), true, false) - } - accountInfo := accountsv1alpha1.AccountInfo{ ObjectMeta: metav1.ObjectMeta{Name: "account"}, } From 6aa2c896047bffe144116dd2726bb598ca6dda24 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 28 Feb 2026 13:54:20 +0100 Subject: [PATCH 13/20] chore: remove unused getAllClient function On-behalf-of: SAP aleh.yarshou@sap.com --- go.mod | 4 +- go.sum | 4 ++ internal/controller/apibinding_controller.go | 57 -------------------- 3 files changed, 6 insertions(+), 59 deletions(-) diff --git a/go.mod b/go.mod index 68baead6..71623d7f 100644 --- a/go.mod +++ b/go.mod @@ -20,8 +20,8 @@ require ( github.com/kcp-dev/sdk v0.30.0 github.com/openfga/api/proto v0.0.0-20260217232149-f917ddb000ce github.com/openfga/language/pkg/go v0.2.0-beta.2.0.20251027165255-0f8f255e5f6c - github.com/platform-mesh/account-operator v0.11.10 - github.com/platform-mesh/golang-commons v0.12.4 + github.com/platform-mesh/account-operator v0.11.14 + github.com/platform-mesh/golang-commons v0.12.6 github.com/rs/zerolog v1.34.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 diff --git a/go.sum b/go.sum index 92b12e2b..30646982 100644 --- a/go.sum +++ b/go.sum @@ -189,8 +189,12 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/platform-mesh/account-operator v0.11.10 h1:Pw3iifw68xoEoPV9xrXN6lK6PHocgC32DeiFQ6olxz8= github.com/platform-mesh/account-operator v0.11.10/go.mod h1:D8mI/lFEH0R7v1tveJJJQv61D8siUzziPKnUi3WKZmw= +github.com/platform-mesh/account-operator v0.11.14 h1:Ij9LTlPmoXNJlGCioxK5mw8OyhKj3uF51+NX/kkZ30M= +github.com/platform-mesh/account-operator v0.11.14/go.mod h1:DPHpMov5kKDVxfx0XHNyBhs8Ogdj+Mtjj6r6YoVFCBk= github.com/platform-mesh/golang-commons v0.12.4 h1:rjUZKb5Co0XryYVAFbNAgplVNSpZj2eR8H3LfRtKRpw= github.com/platform-mesh/golang-commons v0.12.4/go.mod h1:R5zM+28HUtFf1gNirbMax1UWKY1L9woiRRgbLVno+qk= +github.com/platform-mesh/golang-commons v0.12.6 h1:f5F0bNnfh91yGRPvMeZMEhUIrhV0DO6x8VIoNRdGXHs= +github.com/platform-mesh/golang-commons v0.12.6/go.mod h1:eewPnH3+OQ8x+ON0t+toSRjcuPIcgTfcm68jMRH6/0o= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/internal/controller/apibinding_controller.go b/internal/controller/apibinding_controller.go index 7bfdb353..a0659f8b 100644 --- a/internal/controller/apibinding_controller.go +++ b/internal/controller/apibinding_controller.go @@ -2,7 +2,6 @@ package controller import ( "context" - "net/url" platformeshconfig "github.com/platform-mesh/golang-commons/config" "github.com/platform-mesh/golang-commons/controller/lifecycle/builder" @@ -13,70 +12,14 @@ import ( "github.com/platform-mesh/security-operator/internal/subroutine" "github.com/rs/zerolog/log" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "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" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - - "github.com/kcp-dev/logicalcluster/v3" - kcpapisv1alpha1 "github.com/kcp-dev/sdk/apis/apis/v1alpha1" kcpapisv1alpha2 "github.com/kcp-dev/sdk/apis/apis/v1alpha2" ) -func GetAllClient(config *rest.Config, schema *runtime.Scheme) (client.Client, error) { - allCfg := rest.CopyConfig(config) - - platformMeshClient, err := client.New(allCfg, client.Options{ - Scheme: schema, - }) - if err != nil { - log.Error().Err(err).Msg("unable to create client from config") - return nil, err - } - - var apiExportEndpointSlice kcpapisv1alpha1.APIExportEndpointSlice - err = platformMeshClient.Get(context.Background(), types.NamespacedName{Name: "core.platform-mesh.io"}, &apiExportEndpointSlice) - if err != nil { - log.Error().Err(err).Msg("unable to get APIExportEndpointSlice") - return nil, err - } - - virtualWorkspaceUrl, err := url.Parse(apiExportEndpointSlice.Status.APIExportEndpoints[0].URL) - if err != nil { - log.Error().Err(err).Msg("unable to parse virtual workspace URL") - return nil, err - } - - parsed, err := url.Parse(allCfg.Host) - if err != nil { - log.Error().Err(err).Msg("unable to parse host from config") - return nil, err - } - - parsed.Path, err = url.JoinPath(virtualWorkspaceUrl.Path, "clusters", logicalcluster.Wildcard.String()) - if err != nil { - log.Error().Err(err).Msg("unable to join path") - return nil, err - } - - allCfg.Host = parsed.String() - - log.Info().Str("host", allCfg.Host).Msg("using host") - - allClient, err := client.New(allCfg, client.Options{ - Scheme: schema, - }) - if err != nil { - return nil, err - } - return allClient, nil -} - func NewAPIBindingReconciler(ctx context.Context, logger *logger.Logger, mcMgr mcmanager.Manager) *APIBindingReconciler { allclient, err := iclient.NewForAllPlatformMeshResources(ctx, mcMgr.GetLocalManager().GetConfig(), mcMgr.GetLocalManager().GetScheme()) if err != nil { From dfd408b6df58d9c1e00438cffa50cbb2165d307c Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Sat, 28 Feb 2026 14:17:34 +0100 Subject: [PATCH 14/20] fix: address linter issues On-behalf-of: SAP aleh.yarshou@sap.com --- Taskfile.yaml | 2 +- internal/controller/accountlogicalcluster_controller.go | 3 ++- internal/predicates/initializer.go | 3 ++- internal/subroutine/account_tuples.go | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index 54447cd4..4d0299b3 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -8,7 +8,7 @@ vars: CRD_DIRECTORY: config/crd/bases KCP_APIGEN_VERSION: v0.29.0 KCP_VERSION: 0.29.0 - GOLANGCI_LINT_VERSION: v2.8.0 + GOLANGCI_LINT_VERSION: v2.10.1 GOARCH: sh: go env GOARCH GOOS: diff --git a/internal/controller/accountlogicalcluster_controller.go b/internal/controller/accountlogicalcluster_controller.go index 455130d0..de7dd085 100644 --- a/internal/controller/accountlogicalcluster_controller.go +++ b/internal/controller/accountlogicalcluster_controller.go @@ -3,7 +3,6 @@ package controller import ( "context" - kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" openfgav1 "github.com/openfga/api/proto/openfga/v1" platformeshconfig "github.com/platform-mesh/golang-commons/config" "github.com/platform-mesh/golang-commons/controller/lifecycle/builder" @@ -18,6 +17,8 @@ import ( mccontext "sigs.k8s.io/multicluster-runtime/pkg/context" mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager" mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile" + + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) type AccountTypeLogicalClusterReconciler struct { diff --git a/internal/predicates/initializer.go b/internal/predicates/initializer.go index 222e4e59..21b813de 100644 --- a/internal/predicates/initializer.go +++ b/internal/predicates/initializer.go @@ -3,9 +3,10 @@ package predicates import ( "slices" - kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/event" "sigs.k8s.io/controller-runtime/pkg/predicate" + + kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) func HasInitializerPredicate(initializerName string) predicate.Predicate { diff --git a/internal/subroutine/account_tuples.go b/internal/subroutine/account_tuples.go index 86665db9..d3f86115 100644 --- a/internal/subroutine/account_tuples.go +++ b/internal/subroutine/account_tuples.go @@ -170,9 +170,9 @@ func accountAndInfoForLogicalCluster(ctx context.Context, mgr mcmanager.Manager, } cl, err := mgr.ClusterFromContext(ctx) if err != nil { - return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context"), true, true) + return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context: %w", err), true, true) } - + // The AccountInfo in the logical cluster belongs to the Account the // Workspace was created for var ai accountsv1alpha1.AccountInfo From bcdbb7ce1c646fbb6224e6a47ec0b96a6911d17c Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 10:20:25 +0100 Subject: [PATCH 15/20] chore: refactor predicate On-behalf-of: SAP aleh.yarshou@sap.com --- internal/predicates/initializer.go | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/internal/predicates/initializer.go b/internal/predicates/initializer.go index 21b813de..4a037c54 100644 --- a/internal/predicates/initializer.go +++ b/internal/predicates/initializer.go @@ -1,9 +1,10 @@ package predicates import ( + "fmt" "slices" - "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/predicate" kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" @@ -11,24 +12,13 @@ import ( 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) - }, - } + return predicate.NewPredicateFuncs(func(object client.Object) bool { + lc, ok := object.(*kcpcorev1alpha1.LogicalCluster) + if !ok { + panic(fmt.Errorf("received non-LogicalCluster resource in LogicalClusterIsAccountTypeOrg predicate")) + } + return shouldReconcile(lc, initializer) + }) } func shouldReconcile(lc *kcpcorev1alpha1.LogicalCluster, initializer kcpcorev1alpha1.LogicalClusterInitializer) bool { From 22c4fb8bfa6b1d144318a6ba2eabe7d97d100826 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 10:28:48 +0100 Subject: [PATCH 16/20] chore: refactor controller naming On-behalf-of: SAP aleh.yarshou@sap.com --- internal/controller/orglogicalcluster_controller.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/controller/orglogicalcluster_controller.go b/internal/controller/orglogicalcluster_controller.go index aec91a8f..2daca674 100644 --- a/internal/controller/orglogicalcluster_controller.go +++ b/internal/controller/orglogicalcluster_controller.go @@ -21,13 +21,13 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -type LogicalClusterReconciler struct { +type OrgLogicalClusterReconciler struct { log *logger.Logger mgr mcmanager.Manager mclifecycle *multicluster.LifecycleManager } -func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *LogicalClusterReconciler { +func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, cfg config.Config, inClusterClient client.Client, mgr mcmanager.Manager) *OrgLogicalClusterReconciler { var subroutines []lifecyclesubroutine.Subroutine if cfg.Initializer.WorkspaceInitializerEnabled { @@ -43,7 +43,7 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, subroutines = append(subroutines, subroutine.NewWorkspaceAuthConfigurationSubroutine(orgClient, inClusterClient, mgr, cfg)) } - return &LogicalClusterReconciler{ + return &OrgLogicalClusterReconciler{ log: log, mgr: mgr, mclifecycle: builder.NewBuilder("logicalcluster", "LogicalClusterReconciler", subroutines, log). @@ -52,12 +52,12 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client, } } -func (r *LogicalClusterReconciler) Reconcile(ctx context.Context, req mcreconcile.Request) (ctrl.Result, error) { +func (r *OrgLogicalClusterReconciler) 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 *LogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { +func (r *OrgLogicalClusterReconciler) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, initializerName string, evp ...predicate.Predicate) error { allPredicates := append([]predicate.Predicate{predicates.HasInitializerPredicate(initializerName)}, evp...) return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "LogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, allPredicates...) } From 36efb7e77b0854e215d2d423978ea1f7211cf54b Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 10:55:30 +0100 Subject: [PATCH 17/20] chore: address comments On-behalf-of: SAP aleh.yarshou@sap.com --- .../accountlogicalcluster_initializer_controller.go | 6 +++--- internal/predicates/initializer.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/controller/accountlogicalcluster_initializer_controller.go b/internal/controller/accountlogicalcluster_initializer_controller.go index 97abd64c..6d3e8655 100644 --- a/internal/controller/accountlogicalcluster_initializer_controller.go +++ b/internal/controller/accountlogicalcluster_initializer_controller.go @@ -20,7 +20,7 @@ import ( kcpcorev1alpha1 "github.com/kcp-dev/sdk/apis/core/v1alpha1" ) -// AccountLogicalClusterReconciler acts as an initializer for account workspaces. +// AccountLogicalClusterInitializer acts as an initializer for account workspaces. type AccountLogicalClusterInitializer struct { log *logger.Logger @@ -30,7 +30,7 @@ type AccountLogicalClusterInitializer struct { func NewAccountLogicalClusterInitializer(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountLogicalClusterInitializer { return &AccountLogicalClusterInitializer{ log: log, - mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterReconciler", []lifecyclesubroutine.Subroutine{ + mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterInitializer", []lifecyclesubroutine.Subroutine{ subroutine.NewAccountTuplesSubroutine(mgr, fga, cfg.FGA.CreatorRelation, cfg.FGA.ParentRelation, cfg.FGA.ObjectType), }, log). WithReadOnly(). @@ -47,5 +47,5 @@ func (r *AccountLogicalClusterInitializer) Reconcile(ctx context.Context, req mc } func (r *AccountLogicalClusterInitializer) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error { - return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalCluster", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) + return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalClusterInitializer", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...) } diff --git a/internal/predicates/initializer.go b/internal/predicates/initializer.go index 4a037c54..11b3192b 100644 --- a/internal/predicates/initializer.go +++ b/internal/predicates/initializer.go @@ -15,7 +15,7 @@ func HasInitializerPredicate(initializerName string) predicate.Predicate { return predicate.NewPredicateFuncs(func(object client.Object) bool { lc, ok := object.(*kcpcorev1alpha1.LogicalCluster) if !ok { - panic(fmt.Errorf("received non-LogicalCluster resource in LogicalClusterIsAccountTypeOrg predicate")) + panic(fmt.Errorf("received non-LogicalCluster resource in HasInitializer predicate")) } return shouldReconcile(lc, initializer) }) From aa1e453a488485d083d0fe0820262ffb9df047d2 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 10:57:48 +0100 Subject: [PATCH 18/20] chore: add missing error wrapping On-behalf-of: SAP aleh.yarshou@sap.com --- internal/subroutine/workspace_initializer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/subroutine/workspace_initializer.go b/internal/subroutine/workspace_initializer.go index 60222345..10cead48 100644 --- a/internal/subroutine/workspace_initializer.go +++ b/internal/subroutine/workspace_initializer.go @@ -81,7 +81,7 @@ func (w *workspaceInitializer) Initialize(ctx context.Context, instance runtimeo lc := instance.(*kcpcorev1alpha1.LogicalCluster) cluster, err := w.mgr.ClusterFromContext(ctx) if err != nil { - return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context"), true, true) + return ctrl.Result{}, errors.NewOperatorError(fmt.Errorf("getting cluster from context: %w", err), true, true) } var ai accountsv1alpha1.AccountInfo From 9eab21d244468689f2c3c71ed506fec449a69c88 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 11:15:38 +0100 Subject: [PATCH 19/20] chore: fixed typo On-behalf-of: SAP aleh.yarshou@sap.com --- internal/controller/orglogicalcluster_initializer_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/orglogicalcluster_initializer_controller.go b/internal/controller/orglogicalcluster_initializer_controller.go index fdd0b098..5b77d3c8 100644 --- a/internal/controller/orglogicalcluster_initializer_controller.go +++ b/internal/controller/orglogicalcluster_initializer_controller.go @@ -44,7 +44,7 @@ func NewOrgLogicalClusterInitializer(log *logger.Logger, orgClient client.Client return &OrgLogicalClusterInitializer{ log: log, - mclifecycle: builder.NewBuilder("logicalcluster", "OrgLogicalClusterReconciler", subroutines, log). + mclifecycle: builder.NewBuilder("logicalcluster", "OrgLogicalClusterInitializer", subroutines, log). WithReadOnly(). WithStaticThenExponentialRateLimiter(). WithInitializer(cfg.InitializerName()). From e708c7e85acad6c9fc82d45323202f6dd7341738 Mon Sep 17 00:00:00 2001 From: OlegErshov Date: Mon, 2 Mar 2026 14:21:01 +0100 Subject: [PATCH 20/20] fix: return to manual client creation for terminators On-behalf-of: SAP aleh.yarshou@sap.com --- internal/subroutine/account_tuples.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/subroutine/account_tuples.go b/internal/subroutine/account_tuples.go index d3f86115..b3feac53 100644 --- a/internal/subroutine/account_tuples.go +++ b/internal/subroutine/account_tuples.go @@ -168,15 +168,22 @@ func accountAndInfoForLogicalCluster(ctx context.Context, mgr mcmanager.Manager, if lc.Annotations[kcpcore.LogicalClusterPathAnnotationKey] == "" { return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("annotation on LogicalCluster is not set"), true, true) } - cl, err := mgr.ClusterFromContext(ctx) + lcID, ok := mccontext.ClusterFrom(ctx) + if !ok { + return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("cluster name not found in context"), true, true) + } + + // The AccountInfo in the logical cluster belongs to the Account the + // Workspace was created for + lcClient, err := iclient.NewForLogicalCluster(mgr.GetLocalManager().GetConfig(), mgr.GetLocalManager().GetScheme(), logicalcluster.Name(lcID)) if err != nil { - return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("failed to get cluster from context: %w", err), true, true) + return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("getting client: %w", err), true, true) } // The AccountInfo in the logical cluster belongs to the Account the // Workspace was created for var ai accountsv1alpha1.AccountInfo - if err := cl.GetClient().Get(ctx, client.ObjectKey{ + if err := lcClient.Get(ctx, client.ObjectKey{ Name: "account", }, &ai); err != nil && !kerrors.IsNotFound(err) { return accountsv1alpha1.Account{}, accountsv1alpha1.AccountInfo{}, errors.NewOperatorError(fmt.Errorf("getting AccountInfo for LogicalCluster: %w", err), true, true)