Skip to content

Commit dc1962a

Browse files
authored
feat: unstructured cluster patch helper (#48)
* refactor: using capi helper for unstructured patch Signed-off-by: Dario Tranchitella <[email protected]> * feat: caching cluster unstructured data Signed-off-by: Dario Tranchitella <[email protected]> * chore(rbac): caching cluster unstructured data Signed-off-by: Dario Tranchitella <[email protected]> --------- Signed-off-by: Dario Tranchitella <[email protected]>
1 parent fa48db6 commit dc1962a

6 files changed

+153
-43
lines changed

config/control-plane-components.yaml

+10-2
Original file line numberDiff line numberDiff line change
@@ -13341,11 +13341,19 @@ rules:
1334113341
- hetznerclusters
1334213342
- ionoscloudclusters
1334313343
- kubevirtclusters
13344-
- kubevirtclusters/status
1334513344
- nutanixclusters
13346-
- nutanixclusters/status
1334713345
- openstackclusters
1334813346
- packetclusters
13347+
verbs:
13348+
- get
13349+
- list
13350+
- patch
13351+
- watch
13352+
- apiGroups:
13353+
- infrastructure.cluster.x-k8s.io
13354+
resources:
13355+
- kubevirtclusters/status
13356+
- nutanixclusters/status
1334913357
- packetclusters/status
1335013358
verbs:
1335113359
- patch

config/rbac/role.yaml

+10-2
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,19 @@ rules:
5656
- hetznerclusters
5757
- ionoscloudclusters
5858
- kubevirtclusters
59-
- kubevirtclusters/status
6059
- nutanixclusters
61-
- nutanixclusters/status
6260
- openstackclusters
6361
- packetclusters
62+
verbs:
63+
- get
64+
- list
65+
- patch
66+
- watch
67+
- apiGroups:
68+
- infrastructure.cluster.x-k8s.io
69+
resources:
70+
- kubevirtclusters/status
71+
- nutanixclusters/status
6472
- packetclusters/status
6573
verbs:
6674
- patch

controllers/kamajicontrolplane_controller_cluster_patch.go

+43-39
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package controllers
55

66
import (
77
"context"
8-
"encoding/json"
98
"fmt"
109
"net"
1110
"strconv"
@@ -16,6 +15,7 @@ import (
1615
"k8s.io/apimachinery/pkg/types"
1716
"k8s.io/client-go/util/retry"
1817
capiv1beta1 "sigs.k8s.io/cluster-api/api/v1beta1"
18+
"sigs.k8s.io/cluster-api/util/patch"
1919
"sigs.k8s.io/controller-runtime/pkg/client"
2020

2121
"github.com/clastix/cluster-api-control-plane-provider-kamaji/api/v1alpha1"
@@ -125,7 +125,7 @@ func (r *KamajiControlPlaneReconciler) checkOrPatchGenericCluster(ctx context.Co
125125
return nil
126126
}
127127

128-
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclusters;hetznerclusters;kubevirtclusters;nutanixclusters;packetclusters;ionoscloudclusters,verbs=patch
128+
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=awsclusters;hetznerclusters;kubevirtclusters;nutanixclusters;packetclusters;ionoscloudclusters,verbs=patch;get;list;watch
129129
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=kubevirtclusters/status;nutanixclusters/status;packetclusters/status,verbs=patch
130130

131131
func (r *KamajiControlPlaneReconciler) patchGenericCluster(ctx context.Context, cluster capiv1beta1.Cluster, endpoint string, port int64, patchStatus bool) error {
@@ -135,37 +135,30 @@ func (r *KamajiControlPlaneReconciler) patchGenericCluster(ctx context.Context,
135135
infraCluster.SetName(cluster.Spec.InfrastructureRef.Name)
136136
infraCluster.SetNamespace(cluster.Spec.InfrastructureRef.Namespace)
137137

138-
specPatch, err := json.Marshal(map[string]interface{}{
139-
"spec": map[string]interface{}{
140-
"controlPlaneEndpoint": map[string]interface{}{
141-
"host": endpoint,
142-
"port": port,
143-
},
144-
},
145-
})
146-
if err != nil {
147-
return errors.Wrap(err, fmt.Sprintf("unable to marshal %s spec patch", infraCluster.GetKind()))
138+
if err := r.client.Get(ctx, types.NamespacedName{Name: infraCluster.GetName(), Namespace: infraCluster.GetNamespace()}, &infraCluster); err != nil {
139+
return errors.Wrap(err, fmt.Sprintf("cannot retrieve the %s resource", infraCluster.GetKind()))
148140
}
149141

150-
if err = r.client.Patch(ctx, &infraCluster, client.RawPatch(types.MergePatchType, specPatch)); err != nil {
151-
return errors.Wrap(err, fmt.Sprintf("cannot perform PATCH update for the %s resource", infraCluster.GetKind()))
142+
patchHelper, err := patch.NewHelper(&infraCluster, r.client)
143+
if err != nil {
144+
return errors.Wrap(err, "unable to create patch helper")
152145
}
153146

154-
if !patchStatus {
155-
return nil
147+
if err = unstructured.SetNestedMap(infraCluster.Object, map[string]interface{}{
148+
"host": endpoint,
149+
"port": port,
150+
}, "spec", "controlPlaneEndpoint"); err != nil {
151+
return errors.Wrap(err, fmt.Sprintf("unable to set unstructured %s spec patch", infraCluster.GetKind()))
156152
}
157153

158-
statusPatch, err := json.Marshal(map[string]interface{}{
159-
"status": map[string]interface{}{
160-
"ready": true,
161-
},
162-
})
163-
if err != nil {
164-
return errors.Wrap(err, fmt.Sprintf("unable to marshal %s status patch", infraCluster.GetKind()))
154+
if patchStatus {
155+
if err = unstructured.SetNestedField(infraCluster.Object, true, "status", "ready"); err != nil {
156+
return errors.Wrap(err, fmt.Sprintf("unable to set unstructured %s status patch", infraCluster.GetKind()))
157+
}
165158
}
166159

167-
if err = r.client.Status().Patch(ctx, &infraCluster, client.RawPatch(types.MergePatchType, statusPatch)); err != nil {
168-
return errors.Wrap(err, fmt.Sprintf("cannot perform PATCH update for the %s status", infraCluster.GetKind()))
160+
if err = patchHelper.Patch(ctx, &infraCluster); err != nil {
161+
return errors.Wrap(err, fmt.Sprintf("cannot perform PATCH update for the %s resource", infraCluster.GetKind()))
169162
}
170163

171164
return nil
@@ -184,14 +177,19 @@ func (r *KamajiControlPlaneReconciler) checkGenericCluster(ctx context.Context,
184177
return errors.Wrap(err, fmt.Sprintf("cannot retrieve the %s resource", gkc.GetKind()))
185178
}
186179

187-
controlPlaneEndpointUn := gkc.Object["spec"].(map[string]interface{})["controlPlaneEndpoint"] //nolint:forcetypeassert
188-
if controlPlaneEndpointUn == nil {
189-
return *NewUnmanagedControlPlaneAddressError(gkc.GetKind())
180+
cpHost, _, err := unstructured.NestedString(gkc.Object, "spec", "controlPlaneEndpoint", "host")
181+
if err != nil {
182+
return errors.Wrap(err, "cannot extract control plane endpoint host")
190183
}
191184

192-
controlPlaneEndpoint := controlPlaneEndpointUn.(map[string]interface{}) //nolint:forcetypeassert
185+
if cpHost == "" {
186+
return *NewUnmanagedControlPlaneAddressError(gkc.GetKind())
187+
}
193188

194-
cpHost, cpPort := controlPlaneEndpoint["host"].(string), controlPlaneEndpoint["port"].(int64) //nolint:forcetypeassert
189+
cpPort, _, err := unstructured.NestedInt64(gkc.Object, "spec", "controlPlaneEndpoint", "port")
190+
if err != nil {
191+
return errors.Wrap(err, "cannot extract control plane endpoint host")
192+
}
195193

196194
if len(cpHost) == 0 && cpPort == 0 {
197195
return *NewUnmanagedControlPlaneAddressError(gkc.GetKind())
@@ -208,7 +206,7 @@ func (r *KamajiControlPlaneReconciler) checkGenericCluster(ctx context.Context,
208206
return nil
209207
}
210208

211-
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters,verbs=patch
209+
//+kubebuilder:rbac:groups=infrastructure.cluster.x-k8s.io,resources=openstackclusters,verbs=patch;get;list;watch
212210

213211
func (r *KamajiControlPlaneReconciler) patchOpenStackCluster(ctx context.Context, cluster capiv1beta1.Cluster, endpoint string, port int64) error {
214212
osc := unstructured.Unstructured{}
@@ -217,17 +215,23 @@ func (r *KamajiControlPlaneReconciler) patchOpenStackCluster(ctx context.Context
217215
osc.SetName(cluster.Spec.InfrastructureRef.Name)
218216
osc.SetNamespace(cluster.Spec.InfrastructureRef.Namespace)
219217

220-
mergePatch, err := json.Marshal(map[string]interface{}{
221-
"spec": map[string]interface{}{
222-
"apiServerFixedIP": endpoint,
223-
"apiServerPort": port,
224-
},
225-
})
218+
if err := r.client.Get(ctx, types.NamespacedName{Name: osc.GetName(), Namespace: osc.GetNamespace()}, &osc); err != nil {
219+
return errors.Wrap(err, fmt.Sprintf("cannot retrieve the %s resource", osc.GetKind()))
220+
}
221+
222+
patchHelper, err := patch.NewHelper(&osc, r.client)
226223
if err != nil {
227-
return errors.Wrap(err, "unable to marshal OpenStackCluster patch")
224+
return errors.Wrap(err, "unable to create patch helper")
225+
}
226+
227+
if err = unstructured.SetNestedMap(osc.Object, map[string]interface{}{
228+
"apiServerFixedIP": endpoint,
229+
"apiServerPort": port,
230+
}, "spec"); err != nil {
231+
return errors.Wrap(err, fmt.Sprintf("unable to set unstructured %s spec patch", osc.GetKind()))
228232
}
229233

230-
if err = r.client.Patch(ctx, &osc, client.RawPatch(types.MergePatchType, mergePatch)); err != nil {
234+
if err = patchHelper.Patch(ctx, &osc); err != nil {
231235
return errors.Wrap(err, "cannot perform PATCH update for the OpenStackCluster resource")
232236
}
233237

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ require (
3333
github.com/go-openapi/jsonreference v0.20.2 // indirect
3434
github.com/go-openapi/swag v0.22.4 // indirect
3535
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
36+
github.com/gobuffalo/flect v1.0.2 // indirect
3637
github.com/gogo/protobuf v1.3.2 // indirect
3738
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
3839
github.com/golang/protobuf v1.5.4 // indirect

0 commit comments

Comments
 (0)