Skip to content

Commit 59b4deb

Browse files
authored
refactor: use advanced clusteraccess library (#30)
1 parent ce74a8f commit 59b4deb

File tree

4 files changed

+97
-194
lines changed

4 files changed

+97
-194
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ require (
99
github.com/fluxcd/pkg/apis/meta v1.22.0
1010
github.com/fluxcd/source-controller/api v1.7.2
1111
github.com/openmcp-project/controller-utils v0.23.1
12-
github.com/openmcp-project/openmcp-operator/api v0.15.1
13-
github.com/openmcp-project/openmcp-operator/lib v0.15.1
12+
github.com/openmcp-project/openmcp-operator/api v0.15.2
13+
github.com/openmcp-project/openmcp-operator/lib v0.15.3-0.20251017065940-637b58a6e264
1414
github.com/openmcp-project/platform-service-dns/api v0.0.2
1515
github.com/spf13/cobra v1.10.1
1616
k8s.io/api v0.34.1
@@ -106,7 +106,7 @@ require (
106106
k8s.io/component-base v0.34.1 // indirect
107107
k8s.io/klog/v2 v2.130.1 // indirect
108108
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect
109-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect
109+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
110110
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect
111111
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
112112
sigs.k8s.io/randfill v1.0.0 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,10 @@ github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
121121
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
122122
github.com/openmcp-project/controller-utils v0.23.1 h1:suuZ9UWJbSE/LbpZCtzpHg6FOqU7BkR4bq6cEAYykyc=
123123
github.com/openmcp-project/controller-utils v0.23.1/go.mod h1:fU16gy7PHCqMtNaH/nAnHJyGM1SI5ZUiyQcUwrPy2TQ=
124-
github.com/openmcp-project/openmcp-operator/api v0.15.1 h1:xjVQG9zt+QGuSyhGCE0HV0ZCVS9KTX71JALoCUM4BRU=
125-
github.com/openmcp-project/openmcp-operator/api v0.15.1/go.mod h1:eaJf9f3D/SuSF8souPpatSxt67FJkyGFYfKGIEQ2qvA=
126-
github.com/openmcp-project/openmcp-operator/lib v0.15.1 h1:Sqb60JylpodWhhaKAbb4Uk33j/LufimDt9JyfSRnRAM=
127-
github.com/openmcp-project/openmcp-operator/lib v0.15.1/go.mod h1:apLlI2djouLHfv/ZrZM7KoFZwXDTi0GcYQbVVdvRRDw=
124+
github.com/openmcp-project/openmcp-operator/api v0.15.2 h1:Ujf0NLysUSj0Wiel3qnroDcnnHCXMEpbFd0a9rkZoxY=
125+
github.com/openmcp-project/openmcp-operator/api v0.15.2/go.mod h1:0KytEWVi1Gw5SEjyclhNZmUXks+SqbivLW10fDe7vL4=
126+
github.com/openmcp-project/openmcp-operator/lib v0.15.3-0.20251017065940-637b58a6e264 h1:Tb9voXDeiYc4vnkMwEFX7Np9tZQk1XpV6FQ4YB0QcTs=
127+
github.com/openmcp-project/openmcp-operator/lib v0.15.3-0.20251017065940-637b58a6e264/go.mod h1:D/CwGD8NjUHba4PAIzEHFi/FNoj4xcnOb6F2qwcAaIc=
128128
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
129129
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
130130
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -286,8 +286,8 @@ k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
286286
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
287287
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE=
288288
k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
289-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0=
290-
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
289+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
290+
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
291291
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM=
292292
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
293293
sigs.k8s.io/controller-runtime v0.22.3 h1:I7mfqz/a/WdmDCEnXmSPm8/b/yRTy6JsKKENTijTq8Y=

internal/controllers/cluster/controller.go

Lines changed: 61 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,10 @@ import (
1616
"k8s.io/apimachinery/pkg/runtime/schema"
1717
"k8s.io/apimachinery/pkg/types"
1818
"k8s.io/apimachinery/pkg/util/sets"
19-
"k8s.io/client-go/tools/clientcmd"
2019
"k8s.io/client-go/tools/record"
2120
ctrl "sigs.k8s.io/controller-runtime"
2221
"sigs.k8s.io/controller-runtime/pkg/builder"
2322
"sigs.k8s.io/controller-runtime/pkg/client"
24-
"sigs.k8s.io/controller-runtime/pkg/client/fake"
2523
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2624
"sigs.k8s.io/controller-runtime/pkg/handler"
2725
"sigs.k8s.io/controller-runtime/pkg/predicate"
@@ -37,13 +35,14 @@ import (
3735
ctrlutils "github.com/openmcp-project/controller-utils/pkg/controller"
3836
errutils "github.com/openmcp-project/controller-utils/pkg/errors"
3937
"github.com/openmcp-project/controller-utils/pkg/logging"
38+
testutils "github.com/openmcp-project/controller-utils/pkg/testing"
4039

4140
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
4241
clusterconst "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1/constants"
4342
commonapi "github.com/openmcp-project/openmcp-operator/api/common"
4443
openmcpconst "github.com/openmcp-project/openmcp-operator/api/constants"
4544
providerv1alpha1 "github.com/openmcp-project/openmcp-operator/api/provider/v1alpha1"
46-
accesslib "github.com/openmcp-project/openmcp-operator/lib/clusteraccess"
45+
accesslib "github.com/openmcp-project/openmcp-operator/lib/clusteraccess/advanced"
4746

4847
dnsv1alpha1 "github.com/openmcp-project/platform-service-dns/api/dns/v1alpha1"
4948
)
@@ -56,17 +55,19 @@ const (
5655
SourceKindHelmRepository = "HelmRepository"
5756
SourceKindGitRepository = "GitRepository"
5857
SourceKindOCIRepository = "OCIRepository"
58+
59+
clusterId = "cluster"
5960
)
6061

6162
type ClusterReconciler struct {
62-
PlatformCluster *clusters.Cluster
63-
eventRecorder record.EventRecorder
64-
ProviderName string
65-
ProviderNamespace string
66-
Environment string
67-
KnownClusters map[types.NamespacedName]struct{}
68-
KnownClustersLock *sync.RWMutex
69-
FakeClientMappings map[string]client.Client // only used for testing
63+
PlatformCluster *clusters.Cluster
64+
eventRecorder record.EventRecorder
65+
ProviderName string
66+
ProviderNamespace string
67+
Environment string
68+
KnownClusters map[types.NamespacedName]struct{}
69+
KnownClustersLock *sync.RWMutex
70+
ClusterAccessReconciler accesslib.ClusterAccessReconciler
7071
}
7172

7273
func NewClusterReconciler(platformCluster *clusters.Cluster, recorder record.EventRecorder, providerName, providerNamespace, environment string) *ClusterReconciler {
@@ -78,6 +79,22 @@ func NewClusterReconciler(platformCluster *clusters.Cluster, recorder record.Eve
7879
Environment: environment,
7980
KnownClusters: map[types.NamespacedName]struct{}{},
8081
KnownClustersLock: &sync.RWMutex{},
82+
ClusterAccessReconciler: accesslib.NewClusterAccessReconciler(platformCluster.Client(), ControllerName).
83+
WithManagedLabels(func(controllerName string, req reconcile.Request, _ accesslib.ClusterRegistration) (string, string, map[string]string) {
84+
return fmt.Sprintf("%s.%s", providerName, controllerName), req.Name, nil
85+
}).
86+
Register(accesslib.ExistingCluster(clusterId, "", accesslib.IdentityReferenceGenerator).
87+
WithTokenAccess(&clustersv1alpha1.TokenConfig{
88+
RoleRefs: []commonapi.RoleRef{
89+
{
90+
Kind: "ClusterRole",
91+
Name: "cluster-admin",
92+
},
93+
},
94+
}).
95+
WithNamespaceGenerator(accesslib.RequestNamespaceGenerator).
96+
Build(),
97+
),
8198
}
8299
}
83100

@@ -223,80 +240,28 @@ func (r *ClusterReconciler) handleCreateOrUpdate(ctx context.Context, c *cluster
223240
r.addKnownCluster(c)
224241

225242
log.Info("Creating or updating AccessRequest to get access to Cluster")
226-
ar := &clustersv1alpha1.AccessRequest{}
227-
ar.SetName(accesslib.StableRequestNameFromLocalName(ControllerName, c.Name))
228-
ar.SetNamespace(c.Namespace)
229-
if _, err := controllerutil.CreateOrUpdate(ctx, r.PlatformCluster.Client(), ar, func() error {
230-
if err := controllerutil.SetOwnerReference(c, ar, r.PlatformCluster.Scheme()); err != nil {
231-
return fmt.Errorf("error setting owner reference: %w", err)
232-
}
233-
ar.Labels = maputils.Merge(ar.Labels, expectedLabels)
234-
ar.Spec.ClusterRef = &commonapi.ObjectReference{
235-
Name: c.Name,
236-
Namespace: c.Namespace,
237-
}
238-
ar.Spec.Token = &clustersv1alpha1.TokenConfig{
239-
RoleRefs: []commonapi.RoleRef{
240-
{
241-
Kind: "ClusterRole",
242-
Name: "cluster-admin",
243-
},
244-
},
245-
}
246-
return nil
247-
}); err != nil {
248-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error creating or updating AccessRequest '%s/%s': %w", ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
249-
return rr
250-
}
251-
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(ar), ar); err != nil {
252-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting AccessRequest '%s/%s': %w", ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
243+
req := testutils.RequestFromObject(c)
244+
res, err := r.ClusterAccessReconciler.Reconcile(ctx, req)
245+
if err != nil {
246+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error reconciling cluster access: %w", err), clusterconst.ReasonInternalError)
253247
return rr
254248
}
255-
if ar.Status.IsDenied() {
256-
rr.Message = fmt.Sprintf("AccessRequest '%s/%s' was denied, unable to proceed with deploying DNS configuration", ar.Namespace, ar.Name)
249+
if res.RequeueAfter > 0 {
250+
log.Info("Requeuing because cluster access is not yet available", "after", res.RequeueAfter)
251+
rr.Result = res
257252
return rr
258253
}
259-
if !ar.Status.IsGranted() {
260-
rr.Message = fmt.Sprintf("AccessRequest '%s/%s' is not yet granted, waiting for access to be granted", ar.Namespace, ar.Name)
261-
rr.Result.RequeueAfter = defaultRequeueAfterDuration
262-
return rr
254+
ar, err := r.ClusterAccessReconciler.AccessRequest(ctx, req, clusterId)
255+
if err != nil {
256+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting AccessRequest: %w", err), clusterconst.ReasonInternalError)
263257
}
264258
rr.AccessRequest = ar
265-
266-
// get access to Cluster
267-
if ar.Status.SecretRef == nil {
268-
rr.Message = fmt.Sprintf("AccessRequest '%s/%s' does not have a secretRef in its status despite being granted", ar.Namespace, ar.Name)
269-
return rr
270-
}
271-
sec := &corev1.Secret{}
272-
sec.Name = ar.Status.SecretRef.Name
273-
sec.Namespace = ar.Status.SecretRef.Namespace
274-
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(sec), sec); err != nil {
275-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting secret '%s/%s' referenced by AccessRequest '%s/%s': %w", sec.Namespace, sec.Name, ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
259+
access, err := r.ClusterAccessReconciler.Access(ctx, req, clusterId)
260+
if err != nil {
261+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting access to Cluster: %w", err), clusterconst.ReasonInternalError)
276262
return rr
277263
}
278-
kcfgData := sec.Data[clustersv1alpha1.SecretKeyKubeconfig]
279-
if strings.HasPrefix(string(kcfgData), "fake:") && r.FakeClientMappings != nil {
280-
log.Info("Using fake client for testing, this message should never appear outside of tests")
281-
id := strings.TrimPrefix(string(kcfgData), "fake:")
282-
fk := r.FakeClientMappings[id]
283-
if fk == nil {
284-
fk = fake.NewFakeClient()
285-
r.FakeClientMappings[id] = fk
286-
}
287-
rr.Access = clusters.NewTestClusterFromClient(id, fk)
288-
} else {
289-
rest, err := clientcmd.RESTConfigFromKubeConfig(kcfgData)
290-
if err != nil {
291-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error creating REST config for Cluster from kubeconfig in secret '%s/%s': %w", sec.Namespace, sec.Name, err), clusterconst.ReasonInternalError)
292-
return rr
293-
}
294-
rr.Access = clusters.New(ar.Name).WithRESTConfig(rest)
295-
if err := rr.Access.InitializeClient(nil); err != nil {
296-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error initializing client for Cluster from kubeconfig in secret '%s/%s': %w", sec.Namespace, sec.Name, err), clusterconst.ReasonInternalError)
297-
return rr
298-
}
299-
}
264+
rr.Access = access
300265

301266
rr, copied := r.copySecrets(ctx, c.Namespace, expectedLabels, rr, platformClusterCopy)
302267
if rr.ReconcileError != nil || rr.Result.RequeueAfter > 0 {
@@ -354,55 +319,19 @@ func (r *ClusterReconciler) handleDelete(ctx context.Context, c *clustersv1alpha
354319
}
355320

356321
// get access to Cluster
357-
accessRequestGone := false
358-
ar := &clustersv1alpha1.AccessRequest{}
359-
ar.SetName(accesslib.StableRequestNameFromLocalName(ControllerName, c.Name))
360-
ar.SetNamespace(c.Namespace)
361-
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(ar), ar); err != nil {
362-
if !apierrors.IsNotFound(err) {
363-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting AccessRequest '%s/%s': %w", ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
364-
return rr
365-
}
366-
accessRequestGone = true
322+
req := testutils.RequestFromObject(c)
323+
ar, err := r.ClusterAccessReconciler.AccessRequest(ctx, req, clusterId)
324+
if client.IgnoreNotFound(err) != nil {
325+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting AccessRequest: %w", err), clusterconst.ReasonInternalError)
326+
return rr
367327
}
368-
if !accessRequestGone && ar.Status.IsGranted() {
369-
if ar.Status.SecretRef == nil {
370-
rr.Message = fmt.Sprintf("AccessRequest '%s/%s' does not have a secretRef in its status despite being granted", ar.Namespace, ar.Name)
371-
return rr
372-
}
373-
sec := &corev1.Secret{}
374-
sec.Name = ar.Status.SecretRef.Name
375-
sec.Namespace = ar.Status.SecretRef.Namespace
376-
if err := r.PlatformCluster.Client().Get(ctx, client.ObjectKeyFromObject(sec), sec); err != nil {
377-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting secret '%s/%s' referenced by AccessRequest '%s/%s': %w", sec.Namespace, sec.Name, ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
328+
if ar != nil && ar.Status.IsGranted() {
329+
access, err := r.ClusterAccessReconciler.Access(ctx, req, clusterId)
330+
if err != nil {
331+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error getting access to Cluster: %w", err), clusterconst.ReasonInternalError)
378332
return rr
379333
}
380-
kcfgData := sec.Data[clustersv1alpha1.SecretKeyKubeconfig]
381-
if strings.HasPrefix(string(kcfgData), "fake:") && r.FakeClientMappings != nil {
382-
log.Info("Using fake client for testing, this message should never appear outside of tests")
383-
id := strings.TrimPrefix(string(kcfgData), "fake:")
384-
fk := r.FakeClientMappings[id]
385-
if fk == nil {
386-
fk = fake.NewFakeClient()
387-
r.FakeClientMappings[id] = fk
388-
}
389-
rr.Access = clusters.NewTestClusterFromClient(id, fk)
390-
} else {
391-
rest, err := clientcmd.RESTConfigFromKubeConfig(kcfgData)
392-
if err != nil {
393-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error creating REST config for Cluster from kubeconfig in secret '%s/%s': %w", sec.Namespace, sec.Name, err), clusterconst.ReasonInternalError)
394-
return rr
395-
}
396-
rr.Access = clusters.New(ar.Name).WithRESTConfig(rest)
397-
if err := rr.Access.InitializeRESTConfig(); err != nil {
398-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error initializing REST config for Cluster from kubeconfig in secret '%s/%s': %w", sec.Namespace, sec.Name, err), clusterconst.ReasonInternalError)
399-
return rr
400-
}
401-
if err := rr.Access.InitializeClient(nil); err != nil {
402-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error initializing client for Cluster from kubeconfig in secret '%s/%s': %w", sec.Namespace, sec.Name, err), clusterconst.ReasonInternalError)
403-
return rr
404-
}
405-
}
334+
rr.Access = access
406335

407336
rr = r.removeSecrets(ctx, TargetClusterNamespace, expectedLabels, rr, targetClusterCopy, nil)
408337
if rr.ReconcileError != nil || rr.Result.RequeueAfter > 0 {
@@ -422,12 +351,15 @@ func (r *ClusterReconciler) handleDelete(ctx context.Context, c *clustersv1alpha
422351
return rr
423352
}
424353

425-
// delete AccessRequest
426-
if err := r.PlatformCluster.Client().Delete(ctx, ar); err != nil {
427-
if !apierrors.IsNotFound(err) {
428-
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error deleting AccessRequest '%s/%s': %w", ar.Namespace, ar.Name, err), clusterconst.ReasonPlatformClusterInteractionProblem)
429-
return rr
430-
}
354+
res, err := r.ClusterAccessReconciler.ReconcileDelete(ctx, req)
355+
if err != nil {
356+
rr.ReconcileError = errutils.WithReason(fmt.Errorf("error reconciling deletion of cluster access: %w", err), clusterconst.ReasonInternalError)
357+
return rr
358+
}
359+
if res.RequeueAfter > 0 {
360+
log.Info("Requeuing because cluster access has not been fully deleted yet", "after", res.RequeueAfter)
361+
rr.Result = res
362+
return rr
431363
}
432364

433365
// remove finalizer from Cluster

0 commit comments

Comments
 (0)