Skip to content

Commit a319934

Browse files
feat: adds cluster's ownerref on cilium helm values source object (#1034)
**What problem does this PR solve?**: Adds cluster's ownerref on cilium helm values source object so users won't have to explicitly need to add `clusterctl.cluster.x-k8s.io/move: ""` label **Which issue(s) this PR fixes**: Fixes # NCN-105148 **How Has This Been Tested?**: <!-- Please describe the tests that you ran to verify your changes. Provide output from the tests and any manual steps needed to replicate the tests. --> **Special notes for your reviewer**: <!-- Use this to provide any additional information to the reviewers. This may include: - Best way to review the PR. - Where the author wants the most review attention on. - etc. --> ``` apiVersion: v1 data: values.yaml: |- cni: chainingMode: portmap exclusive: false hubble: enabled: true tls: auto: enabled: true # enable automatic TLS certificate generation method: cronJob # auto generate certificates using cronJob method certValidityDuration: 60 # certificates validity duration in days (default 2 months) schedule: "0 0 5 * *" # schedule on the 1st day regeneration of each month relay: enabled: true image: useDigest: false ipam: mode: kubernetes image: useDigest: false operator: image: useDigest: false certgen: image: useDigest: false socketLB: hostNamespaceOnly: true envoy: image: useDigest: false kind: ConfigMap metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"v1","data":{"values.yaml":"cni:\n chainingMode: portmap\n exclusive: false\nhubble:\n enabled: true\n tls:\n auto:\n enabled: true # enable automatic TLS certificate generation\n method: cronJob # auto generate certificates using cronJob method\n certValidityDuration: 60 # certificates validity duration in days (default 2 months)\n schedule: \"0 0 5 * *\" # schedule on the 1st day regeneration of each month\n relay:\n enabled: true\n image:\n useDigest: false\nipam:\n mode: kubernetes\nimage:\n useDigest: false\noperator:\n image:\n useDigest: false\ncertgen:\n image:\n useDigest: false\nsocketLB:\n hostNamespaceOnly: true\nenvoy:\n image:\n useDigest: false"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"cluster.x-k8s.io/cluster-name":"nkp-mgmt-cluster"},"name":"custom-cilium-cni-helm-values-template","namespace":"default"}} creationTimestamp: "2025-02-05T12:47:38Z" labels: cluster.x-k8s.io/cluster-name: nkp-mgmt-cluster name: custom-cilium-cni-helm-values-template namespace: default ownerReferences: - apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster name: nkp-mgmt-cluster uid: 6915b86b-91ae-4673-bb6e-3a94f96dc22f resourceVersion: "9260" uid: c541aec0-2b5d-4b0d-8cdb-ba63bef0ca06 ``` ``` ➜ ~ kg cluster -oyaml apiVersion: v1 items: - apiVersion: cluster.x-k8s.io/v1beta1 kind: Cluster metadata: annotations: caren.nutanix.com/cluster-uuid: 0194d059-494c-7879-a6bd-fe281ba362d9 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"cluster.x-k8s.io/v1beta1","kind":"Cluster","metadata":{"annotations":{"caren.nutanix.com/cluster-uuid":"0194d059-494c-7879-a6bd-fe281ba362d9"},"labels":{"cluster.x-k8s.io/provider":"nutanix","konvoy.d2iq.io/cluster-name":"nkp-mgmt-cluster","konvoy.d2iq.io/provider":"nutanix"},"name":"nkp-mgmt-cluster","namespace":"default"},"spec":{"clusterNetwork":{"pods":{"cidrBlocks":["192.168.0.0/16"]},"services":{"cidrBlocks":["10.96.0.0/12"]}},"controlPlaneEndpoint":{"host":"","port":0},"topology":{"class":"nutanix-quick-start","controlPlane":{"metadata":{},"replicas":1},"variables":[{"name":"clusterConfig","value":{"addons":{"ccm":{"credentials":{"secretRef":{"name":"nkp-mgmt-cluster-pc-credentials"}},"strategy":"HelmAddon"},"clusterAutoscaler":{"strategy":"HelmAddon"},"cni":{"provider":"Cilium","strategy":"HelmAddon","values":{"sourceRef":{"kind":"ConfigMap","name":"custom-cilium-cni-helm-values-template"}}},"csi":{"defaultStorage":{"provider":"nutanix","storageClassConfig":"volume"},"providers":{"nutanix":{"credentials":{"secretRef":{"name":"nkp-mgmt-cluster-pc-credentials-for-csi"}},"storageClassConfigs":{"volume":{"allowExpansion":true,"parameters":{"csi.storage.k8s.io/fstype":"ext4","description":"CSI StorageClass nutanix-volume for nkp-mgmt-cluster","flashMode":"DISABLED","hypervisorAttached":"ENABLED","storageContainer":"default-container-32638919133770","storageType":"NutanixVolumes"},"reclaimPolicy":"Delete","volumeBindingMode":"WaitForFirstConsumer"}},"strategy":"HelmAddon"}},"snapshotController":{"strategy":"HelmAddon"}},"nfd":{"strategy":"HelmAddon"},"serviceLoadBalancer":{"configuration":{"addressRanges":[{"end":"10.47.10.82","start":"10.47.10.82"}]},"provider":"MetalLB"}},"controlPlane":{"nutanix":{"machineDetails":{"bootType":"uefi","cluster":{"name":"auto_cluster_prod_manoj_surudwad_1a2aac0a51c7","type":"name"},"image":{"name":"nkp-rocky-9.5-release-1.31.4-20250122010854.qcow2","type":"name"},"memorySize":"8Gi","subnets":[{"name":"vlan.155","type":"name"}],"systemDiskSize":"80Gi","vcpuSockets":4,"vcpusPerSocket":1}}},"dns":{"coreDNS":{}},"encryptionAtRest":{"providers":[{"secretbox":{}}]},"imageRegistries":[{"credentials":{"secretRef":{"name":"nkp-mgmt-cluster-image-registry-credentials"}},"url":"https://docker.io"}],"nutanix":{"controlPlaneEndpoint":{"host":"10.47.10.4","port":6443,"virtualIP":{"provider":"KubeVIP"}},"prismCentralEndpoint":{"credentials":{"secretRef":{"name":"nkp-mgmt-cluster-pc-credentials"}},"insecure":true,"url":"https://10.47.10.25:9440"}},"users":[{"name":"konvoy","sshAuthorizedKeys":["ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCky5ata76YkcrZi2EdXcD4ESrHtGUU3+0aqtzG4PueOtjhcLSd5160+fKsyLt/CC7+0MbLW6nQYZH3HHgQuF0gb5gfXHuHH4kLJnmRmP3nM3KhZZ2f0lOtKpSJ5lPxvIlbTHacWkd4s4tlmUQvg4XRasQ1t8dgjQGmyQdCuMHE02jOsmc4JGseJ6u3Ne6dJtTwglwLbN+IlsJECuVubU7yqN0ZHL1G5mAy2IWzCRKmDL9ZyCXSM9QWsbTqnvuVGVUp0cW3YquTuoEuiupWYw6HRQSAvFHGUR+juHmUwIVFkgGjBlOAdMiRhRJHEUnE6YpTKZEfIhw9nlwXQfdXRXsoNcV5pDNr2Pd71DlHocAQRSlEa3sZ75W8O5TPzkzxF8W0WLLiiS2PlnbZFLiY2RyQcUBXKOeYVHZTvUpLNnNISPzF4IB8OwZDpJWWQ4kLZ7PAdL2NeN/uI3a5eDXcyBkqisnBewm6YHvDjfV/dTeJrxo9vPX++rPlAe7C6Edr7as=\n"],"sudo":"ALL=(ALL) NOPASSWD:ALL"}]}}],"version":"v1.31.4","workers":{"machineDeployments":[{"class":"default-worker","metadata":{"annotations":{"cluster.x-k8s.io/cluster-api-autoscaler-node-group-max-size":"3","cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size":"3"}},"name":"md-0","variables":{"overrides":[{"name":"workerConfig","value":{"nutanix":{"machineDetails":{"bootType":"uefi","cluster":{"name":"auto_cluster_prod_manoj_surudwad_1a2aac0a51c7","type":"name"},"image":{"name":"nkp-rocky-9.5-release-1.31.4-20250122010854.qcow2","type":"name"},"memorySize":"8Gi","subnets":[{"name":"vlan.155","type":"name"}],"systemDiskSize":"80Gi","vcpuSockets":8,"vcpusPerSocket":1}}}}]}}]}}}} creationTimestamp: "2025-02-05T12:47:16Z" finalizers: - cluster.cluster.x-k8s.io generation: 5 labels: cluster.x-k8s.io/cluster-name: nkp-mgmt-cluster cluster.x-k8s.io/provider: nutanix konvoy.d2iq.io/cluster-name: nkp-mgmt-cluster konvoy.d2iq.io/provider: nutanix topology.cluster.x-k8s.io/owned: "" name: nkp-mgmt-cluster namespace: default resourceVersion: "9931" uid: 6915b86b-91ae-4673-bb6e-3a94f96dc22f spec: ... ... ... status: conditions: - lastTransitionTime: "2025-02-05T12:48:54Z" status: "True" type: Ready - lastTransitionTime: "2025-02-05T12:48:54Z" status: "True" type: ControlPlaneInitialized - lastTransitionTime: "2025-02-05T12:48:54Z" status: "True" type: ControlPlaneReady - lastTransitionTime: "2025-02-05T12:47:22Z" status: "True" type: InfrastructureReady - lastTransitionTime: "2025-02-05T12:50:34Z" status: "True" type: TopologyReconciled controlPlaneReady: true infrastructureReady: true observedGeneration: 5 phase: Provisioned ``` --------- Co-authored-by: Dimitri Koshkin <[email protected]>
1 parent dc03a29 commit a319934

File tree

7 files changed

+111
-21
lines changed

7 files changed

+111
-21
lines changed

docs/content/addons/cni.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,6 @@ data:
7575
mode: kubernetes
7676
kind: ConfigMap
7777
metadata:
78-
labels:
79-
clusterctl.cluster.x-k8s.io/move: ""
8078
name: <CLUSTER_NAME>-cilium-cni-helm-values-template
8179
namespace: <CLUSTER_NAMESPACE>
8280
```

pkg/handlers/generic/lifecycle/ccm/nutanix/handler.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/go-logr/logr"
1515
"github.com/spf13/pflag"
16+
corev1 "k8s.io/api/core/v1"
1617
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1718
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1819
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -84,10 +85,13 @@ func (p *provider) Apply(
8485
// However, that would leave the credentials visible in the HelmChartProxy.
8586
// Instead, we'll create the Secret on the remote cluster and reference it in the Helm values.
8687
if clusterConfig.Addons.CCM.Credentials != nil {
87-
err := handlersutils.EnsureOwnerReferenceForSecret(
88+
err := handlersutils.EnsureClusterOwnerReferenceForObject(
8889
ctx,
8990
p.client,
90-
clusterConfig.Addons.CCM.Credentials.SecretRef.Name,
91+
corev1.TypedLocalObjectReference{
92+
Kind: "Secret",
93+
Name: clusterConfig.Addons.CCM.Credentials.SecretRef.Name,
94+
},
9195
cluster,
9296
)
9397
if err != nil {

pkg/handlers/generic/lifecycle/cni/cilium/handler.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
"github.com/spf13/pflag"
11+
corev1 "k8s.io/api/core/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1213
"k8s.io/utils/ptr"
1314
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -22,6 +23,7 @@ import (
2223
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/addons"
2324
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config"
2425
"github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/options"
26+
handlersutils "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/utils"
2527
)
2628

2729
type CNIConfig struct {
@@ -183,6 +185,33 @@ func (c *CiliumCNI) apply(
183185
helmValuesSourceRefName = cniVar.Values.SourceRef.Name
184186
// Use cluster's namespace since Values.SourceRef is always a LocalObjectReference
185187
targetNamespace = cluster.Namespace
188+
189+
err := handlersutils.EnsureClusterOwnerReferenceForObject(
190+
ctx,
191+
c.client,
192+
corev1.TypedLocalObjectReference{
193+
Kind: cniVar.Values.SourceRef.Kind,
194+
Name: cniVar.Values.SourceRef.Name,
195+
},
196+
cluster,
197+
)
198+
if err != nil {
199+
log.Error(
200+
err,
201+
"error updating Cluster's owner reference on Cilium helm values source object",
202+
"name",
203+
cniVar.Values.SourceRef.Name,
204+
"kind",
205+
cniVar.Values.SourceRef.Kind,
206+
)
207+
resp.SetStatus(runtimehooksv1.ResponseStatusFailure)
208+
resp.SetMessage(
209+
fmt.Sprintf(
210+
"failed to set Cluster's owner reference on Cilium helm values source object: %v",
211+
err,
212+
),
213+
)
214+
}
186215
}
187216

188217
strategy = addons.NewHelmAddonApplier(

pkg/handlers/generic/lifecycle/csi/nutanix/handler.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/go-logr/logr"
1111
"github.com/spf13/pflag"
12+
corev1 "k8s.io/api/core/v1"
1213
"k8s.io/utils/ptr"
1314
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
1415
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -108,10 +109,13 @@ func (n *NutanixCSI) Apply(
108109
}
109110

110111
if provider.Credentials != nil {
111-
err := handlersutils.EnsureOwnerReferenceForSecret(
112+
err := handlersutils.EnsureClusterOwnerReferenceForObject(
112113
ctx,
113114
n.client,
114-
provider.Credentials.SecretRef.Name,
115+
corev1.TypedLocalObjectReference{
116+
Kind: "Secret",
117+
Name: provider.Credentials.SecretRef.Name,
118+
},
115119
cluster,
116120
)
117121
if err != nil {

pkg/handlers/generic/mutation/imageregistries/credentials/inject.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"errors"
99
"fmt"
1010

11+
corev1 "k8s.io/api/core/v1"
1112
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1213
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1314
"k8s.io/apimachinery/pkg/runtime"
@@ -287,10 +288,13 @@ func ensureOwnerReferenceOnCredentialsSecrets(
287288
if secretName := handlersutils.SecretNameForImageRegistryCredentials(credential); secretName != "" {
288289
// Ensure the Secret is owned by the Cluster so it is correctly moved and deleted with the Cluster.
289290
// This code assumes that Secret exists and that was validated before calling this function.
290-
err := handlersutils.EnsureOwnerReferenceForSecret(
291+
err := handlersutils.EnsureClusterOwnerReferenceForObject(
291292
ctx,
292293
c,
293-
secretName,
294+
corev1.TypedLocalObjectReference{
295+
Kind: "Secret",
296+
Name: secretName,
297+
},
294298
cluster,
295299
)
296300
if err != nil {

pkg/handlers/utils/secrets.go

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99

1010
corev1 "k8s.io/api/core/v1"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1213
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
14+
"sigs.k8s.io/cluster-api/controllers/external"
1315
"sigs.k8s.io/cluster-api/controllers/remote"
1416
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
1517
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
@@ -64,30 +66,62 @@ func CopySecretToRemoteCluster(
6466
return nil
6567
}
6668

67-
// EnsureOwnerReferenceForSecret will ensure that the secretName Secret has an OwnerReference of the cluster.
68-
func EnsureOwnerReferenceForSecret(
69+
// EnsureClusterOwnerReferenceForObject ensures that OwnerReference of the cluster is added on provided object.
70+
func EnsureClusterOwnerReferenceForObject(
6971
ctx context.Context,
7072
cl ctrlclient.Client,
71-
secretName string,
73+
objectRef corev1.TypedLocalObjectReference,
7274
cluster *clusterv1.Cluster,
7375
) error {
74-
secret, err := getSecretForCluster(ctx, cl, secretName, cluster)
76+
targetObj, err := GetResourceFromTypedLocalObjectReference(
77+
ctx,
78+
cl,
79+
objectRef,
80+
cluster.Namespace,
81+
)
7582
if err != nil {
7683
return err
7784
}
7885

79-
err = controllerutil.SetOwnerReference(cluster, secret, cl.Scheme())
86+
err = controllerutil.SetOwnerReference(cluster, targetObj, cl.Scheme())
8087
if err != nil {
81-
return fmt.Errorf("failed to set owner reference on Secret: %w", err)
88+
return fmt.Errorf("failed to set cluster's owner reference on object: %w", err)
8289
}
8390

84-
err = cl.Update(ctx, secret)
91+
err = cl.Update(ctx, targetObj)
8592
if err != nil {
86-
return fmt.Errorf("failed to update Secret with owner references: %w", err)
93+
return fmt.Errorf("failed to update object with cluster's owner reference: %w", err)
8794
}
8895
return nil
8996
}
9097

98+
// GetResourceFromTypedLocalObjectReference gets the resource from the provided TypedLocalObjectReference.
99+
func GetResourceFromTypedLocalObjectReference(
100+
ctx context.Context,
101+
cl ctrlclient.Client,
102+
typedLocalObjectRef corev1.TypedLocalObjectReference,
103+
ns string,
104+
) (*unstructured.Unstructured, error) {
105+
apiVersion := corev1.SchemeGroupVersion.String()
106+
if typedLocalObjectRef.APIGroup != nil {
107+
apiVersion = *typedLocalObjectRef.APIGroup
108+
}
109+
110+
objectRef := &corev1.ObjectReference{
111+
APIVersion: apiVersion,
112+
Kind: typedLocalObjectRef.Kind,
113+
Name: typedLocalObjectRef.Name,
114+
Namespace: ns,
115+
}
116+
117+
targetObj, err := external.Get(ctx, cl, objectRef)
118+
if err != nil {
119+
return nil, fmt.Errorf("failed to get resource from object reference: %w", err)
120+
}
121+
122+
return targetObj, nil
123+
}
124+
91125
func getSecretForCluster(
92126
ctx context.Context,
93127
c ctrlclient.Client,

pkg/handlers/utils/secrets_test.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ package utils
55

66
import (
77
"context"
8+
"fmt"
89
"testing"
910

11+
"github.com/pkg/errors"
1012
"github.com/stretchr/testify/assert"
1113
"github.com/stretchr/testify/require"
1214
corev1 "k8s.io/api/core/v1"
13-
"k8s.io/apimachinery/pkg/api/errors"
15+
apiErrors "k8s.io/apimachinery/pkg/api/errors"
1416
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1517
"k8s.io/apimachinery/pkg/runtime"
1618
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -97,23 +99,38 @@ func Test_EnsureOwnerReferenceForSecret(t *testing.T) {
9799
client: buildFakeClient(t, testSecret, testCluster),
98100
secretName: "missing-secret",
99101
cluster: testCluster,
100-
wantErr: errors.NewNotFound(corev1.Resource("secrets"), "missing-secret"),
102+
wantErr: fmt.Errorf(
103+
"failed to get resource from object reference: %w",
104+
errors.Wrapf(
105+
apiErrors.NewNotFound(corev1.Resource("secrets"), "missing-secret"),
106+
"failed to retrieve %s %s",
107+
"Secret",
108+
"missing-secret",
109+
),
110+
),
101111
},
102112
}
103113
for _, tt := range tests {
104114
t.Run(tt.name, func(t *testing.T) {
105115
t.Parallel()
106116

107-
err := EnsureOwnerReferenceForSecret(
117+
err := EnsureClusterOwnerReferenceForObject(
108118
context.Background(),
109119
tt.client,
110-
tt.secretName,
120+
corev1.TypedLocalObjectReference{
121+
Kind: "Secret",
122+
Name: tt.secretName,
123+
},
111124
tt.cluster,
112125
)
113-
require.Equal(t, tt.wantErr, err)
126+
114127
if tt.wantErr != nil {
128+
assert.Equal(t, tt.wantErr.Error(), err.Error())
115129
return
130+
} else {
131+
require.NoError(t, err)
116132
}
133+
117134
// verify that the owner reference was added
118135
secret := &corev1.Secret{}
119136
err = tt.client.Get(

0 commit comments

Comments
 (0)