Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
88bf03f
feat: introduced initializer predicate
OlegErshov Dec 23, 2025
498b4da
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Jan 22, 2026
da5a7c2
fixed tests and linter errors
OlegErshov Jan 22, 2026
de20575
addressed linter complience
OlegErshov Jan 22, 2026
84b61e2
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Jan 23, 2026
37e9eed
added the rest of the subroutines
OlegErshov Jan 23, 2026
795f723
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Jan 23, 2026
a55d849
used update instead of patch for idp resource creation
OlegErshov Jan 24, 2026
34c546d
used initializer name from the config
OlegErshov Jan 24, 2026
15a2b7e
chore: naming refactoring
OlegErshov Jan 24, 2026
d9ca19a
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Jan 26, 2026
905bc01
updated initializer name passing
OlegErshov Jan 26, 2026
1edc46a
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Jan 26, 2026
62ebea3
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Feb 6, 2026
31cec8f
chore: refactoring after merge
OlegErshov Feb 6, 2026
b17ef4b
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Feb 9, 2026
6205611
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Feb 26, 2026
fc4acea
chore: code refactoring
OlegErshov Feb 27, 2026
1b3f389
chore: refactor naming
OlegErshov Feb 27, 2026
1e5fa51
Merge branch 'main' into feat/initializer-resources-reconcilation
nexus49 Feb 27, 2026
e007751
chore: remove unneeded cluster setup
OlegErshov Feb 28, 2026
6aa2c89
chore: remove unused getAllClient function
OlegErshov Feb 28, 2026
e1bd31c
Merge branch 'main' into feat/initializer-resources-reconcilation
OlegErshov Feb 28, 2026
dfd408b
fix: address linter issues
OlegErshov Feb 28, 2026
bcdbb7c
chore: refactor predicate
OlegErshov Mar 2, 2026
22c4fb8
chore: refactor controller naming
OlegErshov Mar 2, 2026
36efb7e
chore: address comments
OlegErshov Mar 2, 2026
aa1e453
chore: add missing error wrapping
OlegErshov Mar 2, 2026
9eab21d
chore: fixed typo
OlegErshov Mar 2, 2026
e708c7e
fix: return to manual client creation for terminators
OlegErshov Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
16 changes: 2 additions & 14 deletions cmd/initializer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -100,18 +99,12 @@ 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)
}

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")
Expand All @@ -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.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)
Expand Down
20 changes: 20 additions & 0 deletions cmd/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"

Expand Down Expand Up @@ -169,6 +171,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(ctx, log, fga, mgr).
SetupWithManager(mgr, defaultCfg); err != nil {
log.Error().Err(err).Str("controller", "store").Msg("unable to create controller")
Expand All @@ -188,6 +198,16 @@ var operatorCmd = &cobra.Command{
log.Error().Err(err).Str("controller", "invite").Msg("unable to create controller")
return err
}

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.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
}
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
Expand Down
8 changes: 1 addition & 7 deletions cmd/terminator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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.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)
Expand Down
30 changes: 12 additions & 18 deletions internal/controller/accountlogicalcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,43 +10,37 @@ 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 NewAccountLogicalClusterReconciler(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...)
}
Original file line number Diff line number Diff line change
@@ -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"
)

// AccountLogicalClusterInitializer acts as an initializer for account workspaces.
type AccountLogicalClusterInitializer struct {
log *logger.Logger

mclifecycle *multicluster.LifecycleManager
}

func NewAccountLogicalClusterInitializer(log *logger.Logger, cfg config.Config, fga openfgav1.OpenFGAServiceClient, mgr mcmanager.Manager) *AccountLogicalClusterInitializer {
return &AccountLogicalClusterInitializer{
log: log,
mclifecycle: builder.NewBuilder("security", "AccountLogicalClusterInitializer", []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 *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 *AccountLogicalClusterInitializer) SetupWithManager(mgr mcmanager.Manager, cfg *platformeshconfig.CommonServiceConfig, evp ...predicate.Predicate) error {
return r.mclifecycle.SetupWithManager(mgr, cfg.MaxConcurrentReconciles, "AccountLogicalClusterInitializer", &kcpcorev1alpha1.LogicalCluster{}, cfg.DebugLabelValue, r, r.log, evp...)
}
57 changes: 0 additions & 57 deletions internal/controller/apibinding_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down
15 changes: 8 additions & 7 deletions internal/controller/orglogicalcluster_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -21,8 +22,8 @@ import (
)

type OrgLogicalClusterReconciler struct {
log *logger.Logger

log *logger.Logger
mgr mcmanager.Manager
mclifecycle *multicluster.LifecycleManager
}

Expand All @@ -44,10 +45,9 @@ func NewOrgLogicalClusterReconciler(log *logger.Logger, orgClient client.Client,

return &OrgLogicalClusterReconciler{
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),
}
}
Expand All @@ -57,6 +57,7 @@ func (r *OrgLogicalClusterReconciler) Reconcile(ctx context.Context, req mcrecon
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 *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...)
}
Loading
Loading