Skip to content

Commit db67596

Browse files
feat: improve controller logic (#51)
* fix: update `PROJECT` file * chore: remove kubebuilder markers * feat: check if `Cluster` has profile `kind` configured * fix: update sample files * feat(controller): add logic to accessrequest controller * fix(accessrequest): error handling * chore(accessrequest): update sample file * fix(accessrequest): improve error message * feat(accessrequest): enhance create/update/delete logic * chore: add TODO * fix(feedback): introduce shared function to check if cluster-provider-kind is responsible to reconcile `Cluster` resource * feat: release v0.0.7
1 parent bbf82b7 commit db67596

File tree

6 files changed

+90
-27
lines changed

6 files changed

+90
-27
lines changed

PROJECT

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ repo: github.com/openmcp-project/cluster-provider-kind
1010
resources:
1111
- api:
1212
crdVersion: v1
13-
namespaced: true
14-
controller: true
13+
namespaced: false
14+
controller: false
1515
domain: kind.clusters.openmcp.cloud
16-
kind: Cluster
16+
kind: ProviderConfig
1717
path: github.com/openmcp-project/cluster-provider-kind/api/v1alpha1
1818
version: v1alpha1
1919
version: "3"

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.0.6-dev
1+
v0.0.7

config/samples/v1alpha1_accessrequest.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ metadata:
55
spec:
66
clusterRef:
77
name: one
8-
rules: []
8+
namespace: default
9+
permissions: []
Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
apiVersion: kind.clusters.openmcp.cloud/v1alpha1
1+
apiVersion: clusters.openmcp.cloud/v1alpha1
22
kind: Cluster
33
metadata:
44
name: one
5-
spec: {}
5+
spec:
6+
profile: kind
7+
tenancy: Exclusive
68
---
7-
apiVersion: kind.clusters.openmcp.cloud/v1alpha1
9+
apiVersion: clusters.openmcp.cloud/v1alpha1
810
kind: Cluster
911
metadata:
1012
name: two
11-
spec: {}
13+
spec:
14+
profile: kind
15+
tenancy: Exclusive
1216
---
13-
apiVersion: kind.clusters.openmcp.cloud/v1alpha1
17+
apiVersion: clusters.openmcp.cloud/v1alpha1
1418
kind: Cluster
1519
metadata:
1620
name: three
1721
namespace: kube-system
18-
spec: {}
22+
spec:
23+
profile: kind
24+
tenancy: Exclusive

internal/controller/accessrequest_controller.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package controller
1919
import (
2020
"context"
2121
"errors"
22+
"fmt"
2223

2324
corev1 "k8s.io/api/core/v1"
2425
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -31,6 +32,7 @@ import (
3132
logf "sigs.k8s.io/controller-runtime/pkg/log"
3233

3334
clustersv1alpha1 "github.com/openmcp-project/openmcp-operator/api/clusters/v1alpha1"
35+
commonapi "github.com/openmcp-project/openmcp-operator/api/common"
3436

3537
"github.com/openmcp-project/cluster-provider-kind/pkg/kind"
3638
)
@@ -46,14 +48,12 @@ type AccessRequestReconciler struct {
4648
Provider kind.Provider
4749
}
4850

49-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=accessrequests,verbs=get;list;watch;create;update;patch;delete
50-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=accessrequests/status,verbs=get;update;patch
51-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=accessrequests/finalizers,verbs=update
52-
5351
// Reconcile is part of the main kubernetes reconciliation loop which aims to
5452
// move the current state of the cluster closer to the desired state.
5553
func (r *AccessRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
56-
_ = logf.FromContext(ctx)
54+
log := logf.FromContext(ctx)
55+
log.Info("Reconcile")
56+
defer log.Info("Done")
5757

5858
ar := &clustersv1alpha1.AccessRequest{}
5959
if err := r.Get(ctx, req.NamespacedName, ar); err != nil {
@@ -63,22 +63,47 @@ func (r *AccessRequestReconciler) Reconcile(ctx context.Context, req ctrl.Reques
6363
return ctrl.Result{}, err
6464
}
6565

66+
ar.Status.Phase = clustersv1alpha1.AccessRequestPending
67+
68+
defer r.Status().Update(ctx, ar) //nolint:errcheck
69+
6670
clusterRef := types.NamespacedName{Name: ar.Spec.ClusterRef.Name, Namespace: ar.Namespace}
6771
cluster := &clustersv1alpha1.Cluster{}
6872
if err := r.Get(ctx, clusterRef, cluster); err != nil {
73+
// TODO: report event or status condition?
6974
return ctrl.Result{}, errors.Join(err, errFailedToGetReferencedCluster)
7075
}
7176

77+
if !isClusterProviderResponsible(cluster) {
78+
return ctrl.Result{}, fmt.Errorf("profile '%s' is not supported by kind controller", cluster.Spec.Profile)
79+
}
80+
81+
// handle deletion
82+
if !ar.DeletionTimestamp.IsZero() {
83+
return r.handleDelete(ctx, ar)
84+
}
85+
86+
return r.handleCreateOrUpdate(ctx, ar, cluster)
87+
}
88+
89+
func (r *AccessRequestReconciler) handleCreateOrUpdate(ctx context.Context, ar *clustersv1alpha1.AccessRequest, cluster *clustersv1alpha1.Cluster) (ctrl.Result, error) {
90+
if controllerutil.AddFinalizer(ar, Finalizer) {
91+
if err := r.Update(ctx, ar); err != nil {
92+
return ctrl.Result{}, err
93+
}
94+
}
95+
7296
name := kindName(cluster)
7397
kubeconfigStr, err := r.Provider.KubeConfig(name)
7498
if err != nil {
7599
return ctrl.Result{}, err
76100
}
77101

102+
s := getSecretNamespacedName(ar)
78103
secret := &corev1.Secret{
79104
ObjectMeta: metav1.ObjectMeta{
80-
Name: ar.Name + ".kubeconfig",
81-
Namespace: ar.Namespace,
105+
Name: s.Name,
106+
Namespace: s.Namespace,
82107
},
83108
}
84109
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, secret, func() error {
@@ -89,11 +114,35 @@ func (r *AccessRequestReconciler) Reconcile(ctx context.Context, req ctrl.Reques
89114
}
90115
secret.Data["kubeconfig"] = []byte(kubeconfigStr)
91116
return controllerutil.SetOwnerReference(ar, secret, r.Scheme)
92-
93-
// TODO: write kubeconfig to secret and reference secret in status of AccessRequest resource
94-
// ignore clusterrequest ref
95117
})
96-
return ctrl.Result{}, err
118+
119+
if err != nil {
120+
return ctrl.Result{}, fmt.Errorf("failed to create or update secret for access request %q/%q: %w", ar.Namespace, ar.Name, err)
121+
}
122+
123+
ar.Status.Phase = clustersv1alpha1.AccessRequestGranted
124+
ar.Status.SecretRef = &commonapi.ObjectReference{
125+
Name: secret.Name,
126+
Namespace: secret.Namespace,
127+
}
128+
129+
return ctrl.Result{}, nil
130+
}
131+
132+
func (r *AccessRequestReconciler) handleDelete(ctx context.Context, ar *clustersv1alpha1.AccessRequest) (ctrl.Result, error) {
133+
// remove finalizer - Secret will automatically get deleted because of OwnerReference
134+
controllerutil.RemoveFinalizer(ar, Finalizer)
135+
if err := r.Update(ctx, ar); err != nil {
136+
return ctrl.Result{}, err
137+
}
138+
return ctrl.Result{}, nil
139+
}
140+
141+
func getSecretNamespacedName(ar *clustersv1alpha1.AccessRequest) types.NamespacedName {
142+
return types.NamespacedName{
143+
Name: ar.Name + ".kubeconfig",
144+
Namespace: ar.Namespace,
145+
}
97146
}
98147

99148
// SetupWithManager sets up the controller with the Manager.

internal/controller/cluster_controller.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ var (
4343
Finalizer = clustersv1alpha1.GroupVersion.Group + "/finalizer"
4444
)
4545

46+
const (
47+
profileKind = "kind"
48+
)
49+
4650
// ClusterReconciler reconciles a Cluster object
4751
type ClusterReconciler struct {
4852
client.Client
@@ -51,10 +55,6 @@ type ClusterReconciler struct {
5155
Provider kind.Provider
5256
}
5357

54-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=clusters,verbs=get;list;watch;create;update;patch;delete
55-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=clusters/status,verbs=get;update;patch
56-
// +kubebuilder:rbac:groups=kind.clusters.openmcp.cloud,resources=clusters/finalizers,verbs=update
57-
5858
// Reconcile is part of the main kubernetes reconciliation loop which aims to
5959
// move the current state of the cluster closer to the desired state.
6060
func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -71,9 +71,12 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct
7171
}
7272

7373
// Always try to update the status
74-
// FIXME: error handling at the end?
7574
defer r.Status().Update(ctx, cluster) //nolint:errcheck
7675

76+
if !isClusterProviderResponsible(cluster) {
77+
return ctrl.Result{}, fmt.Errorf("profile '%s' is not supported by kind controller", cluster.Spec.Profile)
78+
}
79+
7780
ctx = smartrequeue.NewContext(ctx, r.RequeueStore.For(cluster))
7881

7982
if !cluster.DeletionTimestamp.IsZero() {
@@ -231,3 +234,7 @@ func namespaceOrDefault(namespace string) string {
231234
}
232235
return namespace
233236
}
237+
238+
func isClusterProviderResponsible(cluster *clustersv1alpha1.Cluster) bool {
239+
return cluster.Spec.Profile == profileKind
240+
}

0 commit comments

Comments
 (0)