Skip to content

Commit 78abfae

Browse files
Ensure ownerReference apiVersions are updated
1 parent c6cac04 commit 78abfae

File tree

25 files changed

+365
-196
lines changed

25 files changed

+365
-196
lines changed

bootstrap/kubeadm/internal/controllers/kubeadmconfig_controller.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,15 +1089,15 @@ func (r *KubeadmConfigReconciler) ensureBootstrapSecretOwnersRef(ctx context.Con
10891089
return errors.Wrapf(err, "failed to add KubeadmConfig %s as ownerReference to bootstrap Secret %s", scope.ConfigOwner.GetName(), secret.GetName())
10901090
}
10911091
if c := metav1.GetControllerOf(secret); c != nil && c.Kind != "KubeadmConfig" {
1092-
secret.OwnerReferences = util.RemoveOwnerRef(secret.OwnerReferences, *c)
1092+
secret.SetOwnerReferences(util.RemoveOwnerRef(secret.GetOwnerReferences(), *c))
10931093
}
1094-
secret.OwnerReferences = util.EnsureOwnerRef(secret.OwnerReferences, metav1.OwnerReference{
1094+
secret.SetOwnerReferences(util.EnsureOwnerRef(secret.GetOwnerReferences(), metav1.OwnerReference{
10951095
APIVersion: bootstrapv1.GroupVersion.String(),
10961096
Kind: "KubeadmConfig",
10971097
UID: scope.Config.UID,
10981098
Name: scope.Config.Name,
10991099
Controller: pointer.Bool(true),
1100-
})
1100+
}))
11011101
err = patchHelper.Patch(ctx, secret)
11021102
if err != nil {
11031103
return errors.Wrapf(err, "could not add KubeadmConfig %s as ownerReference to bootstrap Secret %s", scope.ConfigOwner.GetName(), secret.GetName())

cmd/clusterctl/client/cluster/ownergraph.go

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,15 @@ limitations under the License.
1717
package cluster
1818

1919
import (
20+
"strings"
21+
2022
"github.com/pkg/errors"
2123
corev1 "k8s.io/api/core/v1"
2224
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26+
"sigs.k8s.io/controller-runtime/pkg/client"
27+
28+
clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3"
2329
)
2430

2531
// OwnerGraph contains a graph with all the objects considered by clusterctl move as nodes and the OwnerReference relationship
@@ -32,22 +38,6 @@ type OwnerGraphNode struct {
3238
Owners []metav1.OwnerReference
3339
}
3440

35-
func nodeToOwnerRef(n *node, attributes ownerReferenceAttributes) metav1.OwnerReference {
36-
ref := metav1.OwnerReference{
37-
Name: n.identity.Name,
38-
APIVersion: n.identity.APIVersion,
39-
Kind: n.identity.Kind,
40-
UID: n.identity.UID,
41-
}
42-
if attributes.BlockOwnerDeletion != nil {
43-
ref.BlockOwnerDeletion = attributes.BlockOwnerDeletion
44-
}
45-
if attributes.Controller != nil {
46-
ref.Controller = attributes.Controller
47-
}
48-
return ref
49-
}
50-
5141
// GetOwnerGraph returns a graph with all the objects considered by clusterctl move as nodes and the OwnerReference relationship between those objects as edges.
5242
// NOTE: this data structure is exposed to allow implementation of E2E tests verifying that CAPI can properly rebuild its
5343
// own owner references; there is no guarantee about the stability of this API. Using this test with providers may require
@@ -64,20 +54,79 @@ func GetOwnerGraph(namespace, kubeconfigPath string) (OwnerGraph, error) {
6454
return OwnerGraph{}, errors.Wrap(err, "failed to retrieve discovery types")
6555
}
6656

67-
// Discovery the object graph for the selected types:
68-
// - Nodes are defined the Kubernetes objects (Clusters, Machines etc.) identified during the discovery process.
69-
// - Edges are derived by the OwnerReferences between nodes.
70-
if err := graph.Discovery(namespace); err != nil {
71-
return OwnerGraph{}, errors.Wrap(err, "failed to discover the object graph")
57+
// graph.Discovery can not be used here as it will use the latest APIVersion for ownerReferences - not those
58+
// present in the object 'metadata.ownerReferences`.
59+
owners, err := discoverOwnerGraph(namespace, graph)
60+
if err != nil {
61+
return OwnerGraph{}, errors.Wrap(err, "failed to discovery ownerGraph types")
7262
}
73-
owners := OwnerGraph{}
74-
// Using getMoveNodes here ensures only objects that are part of the Cluster are added to the OwnerGraph.
75-
for _, v := range graph.getMoveNodes() {
76-
n := OwnerGraphNode{Object: v.identity, Owners: []metav1.OwnerReference{}}
77-
for owner, attributes := range v.owners {
78-
n.Owners = append(n.Owners, nodeToOwnerRef(owner, attributes))
63+
return owners, nil
64+
}
65+
66+
func discoverOwnerGraph(namespace string, o *objectGraph) (OwnerGraph, error) {
67+
selectors := []client.ListOption{}
68+
if namespace != "" {
69+
selectors = append(selectors, client.InNamespace(namespace))
70+
}
71+
ownerGraph := OwnerGraph{}
72+
73+
discoveryBackoff := newReadBackoff()
74+
for _, discoveryType := range o.types {
75+
typeMeta := discoveryType.typeMeta
76+
objList := new(unstructured.UnstructuredList)
77+
78+
if err := retryWithExponentialBackoff(discoveryBackoff, func() error {
79+
return getObjList(o.proxy, typeMeta, selectors, objList)
80+
}); err != nil {
81+
return nil, err
82+
}
83+
84+
// if we are discovering Secrets, also secrets from the providers namespace should be included.
85+
if discoveryType.typeMeta.GetObjectKind().GroupVersionKind().GroupKind() == corev1.SchemeGroupVersion.WithKind("SecretList").GroupKind() {
86+
providers, err := o.providerInventory.List()
87+
if err != nil {
88+
return nil, err
89+
}
90+
for _, p := range providers.Items {
91+
if p.Type == string(clusterctlv1.InfrastructureProviderType) {
92+
providerNamespaceSelector := []client.ListOption{client.InNamespace(p.Namespace)}
93+
providerNamespaceSecretList := new(unstructured.UnstructuredList)
94+
if err := retryWithExponentialBackoff(discoveryBackoff, func() error {
95+
return getObjList(o.proxy, typeMeta, providerNamespaceSelector, providerNamespaceSecretList)
96+
}); err != nil {
97+
return nil, err
98+
}
99+
objList.Items = append(objList.Items, providerNamespaceSecretList.Items...)
100+
}
101+
}
102+
}
103+
for _, obj := range objList.Items {
104+
// Exclude the kube-root-ca.crt ConfigMap from the owner graph.
105+
if obj.GetKind() == "ConfigMap" && obj.GetName() == "kube-root-ca.crt" {
106+
continue
107+
}
108+
// Exclude the default service account from the owner graph.
109+
// This Secret is not longer generated by default in Kubernetes 1.24+.
110+
// This is not a CAPI related Secret, so it can be ignored.
111+
if obj.GetKind() == "Secret" && strings.Contains(obj.GetName(), "default-token") {
112+
continue
113+
}
114+
ownerGraph = addNodeToOwnerGraph(ownerGraph, obj)
79115
}
80-
owners[string(v.identity.UID)] = n
81116
}
82-
return owners, nil
117+
return ownerGraph, nil
118+
}
119+
120+
func addNodeToOwnerGraph(graph OwnerGraph, obj unstructured.Unstructured) OwnerGraph {
121+
// write code to add a node to the ownerGraph
122+
graph[string(obj.GetUID())] = OwnerGraphNode{
123+
Owners: obj.GetOwnerReferences(),
124+
Object: corev1.ObjectReference{
125+
APIVersion: obj.GetAPIVersion(),
126+
Kind: obj.GetKind(),
127+
Name: obj.GetName(),
128+
Namespace: obj.GetNamespace(),
129+
},
130+
}
131+
return graph
83132
}

cmd/clusterctl/internal/test/fake_objects.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
4040
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
4141
"sigs.k8s.io/cluster-api/internal/test/builder"
42+
"sigs.k8s.io/cluster-api/util"
4243
)
4344

4445
type FakeCluster struct {
@@ -563,14 +564,14 @@ func (f *FakeMachineDeployment) Objs(cluster *clusterv1.Cluster) []client.Object
563564
machineDeploymentInfrastructure = NewFakeInfrastructureTemplate(f.name)
564565
}
565566
machineDeploymentInfrastructure.Namespace = cluster.Namespace
566-
machineDeploymentInfrastructure.OwnerReferences = append(machineDeploymentInfrastructure.OwnerReferences, // Added by the machine set controller -- RECONCILED
567+
machineDeploymentInfrastructure.SetOwnerReferences(util.EnsureOwnerRef(machineDeploymentInfrastructure.GetOwnerReferences(), // Added by the machine set controller -- RECONCILED
567568
metav1.OwnerReference{
568569
APIVersion: clusterv1.GroupVersion.String(),
569570
Kind: "Cluster",
570571
Name: cluster.Name,
571572
UID: cluster.UID,
572573
},
573-
)
574+
))
574575
setUID(machineDeploymentInfrastructure)
575576

576577
machineDeploymentBootstrap := &fakebootstrap.GenericBootstrapConfigTemplate{

controlplane/kubeadm/internal/controllers/controller.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ import (
5757
"sigs.k8s.io/cluster-api/util/version"
5858
)
5959

60-
const kcpManagerName = "capi-kubeadmcontrolplane"
60+
const (
61+
kcpManagerName = "capi-kubeadmcontrolplane"
62+
kubeadmControlPlaneKind = "KubeadmControlPlane"
63+
)
6164

6265
// +kubebuilder:rbac:groups=core,resources=events,verbs=get;list;watch;create;patch
6366
// +kubebuilder:rbac:groups=core,resources=secrets,verbs=get;list;watch;create;update;patch
@@ -290,7 +293,7 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster *
290293
config.ClusterConfiguration = &bootstrapv1.ClusterConfiguration{}
291294
}
292295
certificates := secret.NewCertificatesForInitialControlPlane(config.ClusterConfiguration)
293-
controllerRef := metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane"))
296+
controllerRef := metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind))
294297
if err := certificates.LookupOrGenerate(ctx, r.Client, util.ObjectKey(cluster), *controllerRef); err != nil {
295298
log.Error(err, "unable to lookup or create cluster certificates")
296299
conditions.MarkFalse(kcp, controlplanev1.CertificatesAvailableCondition, controlplanev1.CertificatesGenerationFailedReason, clusterv1.ConditionSeverityWarning, err.Error())
@@ -528,7 +531,7 @@ func (r *KubeadmControlPlaneReconciler) ClusterToKubeadmControlPlane(o client.Ob
528531
}
529532

530533
controlPlaneRef := c.Spec.ControlPlaneRef
531-
if controlPlaneRef != nil && controlPlaneRef.Kind == "KubeadmControlPlane" {
534+
if controlPlaneRef != nil && controlPlaneRef.Kind == kubeadmControlPlaneKind {
532535
return []ctrl.Request{{NamespacedName: client.ObjectKey{Namespace: controlPlaneRef.Namespace, Name: controlPlaneRef.Name}}}
533536
}
534537

@@ -888,21 +891,29 @@ func ensureCertificatesOwnerRef(ctx context.Context, ctrlclient client.Client, c
888891
if err := ctrlclient.Get(ctx, secretKey, s); err != nil {
889892
return errors.Wrapf(err, "failed to get Secret %s", secretKey)
890893
}
891-
// If the Type doesn't match the type used for secrets created by core components, KCP included
892-
if s.Type != clusterv1.ClusterSecretType {
893-
continue
894-
}
894+
895895
patchHelper, err := patch.NewHelper(s, ctrlclient)
896896
if err != nil {
897897
return errors.Wrapf(err, "failed to create patchHelper for Secret %s", secretKey)
898898
}
899899

900-
// Remove the current controller if one exists.
901-
if controller := metav1.GetControllerOf(s); controller != nil {
902-
s.SetOwnerReferences(util.RemoveOwnerRef(s.OwnerReferences, *controller))
900+
controller := metav1.GetControllerOf(s)
901+
// If the current controller is KCP, ensure the owner reference is up to date.
902+
// Note: This ensures secrets created prior to v1alpha4 are updated to have the correct owner reference apiVersion.
903+
if controller != nil && controller.Kind == kubeadmControlPlaneKind {
904+
s.SetOwnerReferences(util.EnsureOwnerRef(s.GetOwnerReferences(), owner))
903905
}
904906

905-
s.OwnerReferences = util.EnsureOwnerRef(s.OwnerReferences, owner)
907+
// If the Type doesn't match the type used for secrets created by core components continue without altering the owner reference further.
908+
// Note: This ensures that control plane related secrets created by KubeadmConfig are eventually owned by KCP.
909+
// TODO: Remove this logic once standalone control plane machines are no longer allowed.
910+
if s.Type == clusterv1.ClusterSecretType {
911+
// Remove the current controller if one exists.
912+
if controller != nil {
913+
s.SetOwnerReferences(util.RemoveOwnerRef(s.GetOwnerReferences(), *controller))
914+
}
915+
s.SetOwnerReferences(util.EnsureOwnerRef(s.GetOwnerReferences(), owner))
916+
}
906917
if err := patchHelper.Patch(ctx, s); err != nil {
907918
return errors.Wrapf(err, "failed to patch Secret %s with ownerReference %s", secretKey, owner.String())
908919
}

controlplane/kubeadm/internal/controllers/controller_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -878,8 +878,8 @@ func TestKubeadmControlPlaneReconciler_ensureOwnerReferences(t *testing.T) {
878878
// Set the Secret Type to any type which signals this Secret was is user-provided.
879879
s.Type = corev1.SecretTypeOpaque
880880
// Set the a controller owner reference of an unknown type on the secret.
881-
s.SetOwnerReferences([]metav1.OwnerReference{
882-
{
881+
s.SetOwnerReferences(util.EnsureOwnerRef(s.GetOwnerReferences(),
882+
metav1.OwnerReference{
883883
APIVersion: bootstrapv1.GroupVersion.String(),
884884
// This owner reference to a different controller should be preserved.
885885
Kind: "OtherController",
@@ -888,7 +888,7 @@ func TestKubeadmControlPlaneReconciler_ensureOwnerReferences(t *testing.T) {
888888
Controller: pointer.Bool(true),
889889
BlockOwnerDeletion: pointer.Bool(true),
890890
},
891-
})
891+
))
892892

893893
objs = append(objs, s)
894894
}

controlplane/kubeadm/internal/controllers/helpers.go

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"k8s.io/apimachinery/pkg/types"
3030
kerrors "k8s.io/apimachinery/pkg/util/errors"
3131
"k8s.io/apiserver/pkg/storage/names"
32-
"k8s.io/klog/v2"
3332
ctrl "sigs.k8s.io/controller-runtime"
3433
"sigs.k8s.io/controller-runtime/pkg/client"
3534

@@ -56,7 +55,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,
5655
return ctrl.Result{}, nil
5756
}
5857

59-
controllerOwnerRef := *metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane"))
58+
controllerOwnerRef := *metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind))
6059
clusterName := util.ObjectKey(cluster)
6160
configSecret, err := secret.GetFromNamespacedName(ctx, r.Client, clusterName, secret.Kubeconfig)
6261
switch {
@@ -102,46 +101,43 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,
102101
}
103102

104103
// Ensure the KubeadmConfigSecret has an owner reference to the control plane if it is not a user-provided secret.
105-
func (r *KubeadmControlPlaneReconciler) adoptKubeconfigSecret(ctx context.Context, cluster *clusterv1.Cluster, configSecret *corev1.Secret, kcp *controlplanev1.KubeadmControlPlane) error {
106-
log := ctrl.LoggerFrom(ctx)
107-
controller := metav1.GetControllerOf(configSecret)
108-
109-
// If the Type doesn't match the CAPI-created secret type this is a no-op.
110-
if configSecret.Type != clusterv1.ClusterSecretType {
111-
return nil
112-
}
113-
// If the secret is already controlled by KCP this is a no-op.
114-
if controller != nil && controller.Kind == "KubeadmControlPlane" {
115-
return nil
116-
}
117-
log.Info("Adopting KubeConfig secret", "Secret", klog.KObj(configSecret))
118-
patch, err := patch.NewHelper(configSecret, r.Client)
104+
func (r *KubeadmControlPlaneReconciler) adoptKubeconfigSecret(ctx context.Context, cluster *clusterv1.Cluster, configSecret *corev1.Secret, kcp *controlplanev1.KubeadmControlPlane) (reterr error) {
105+
patchHelper, err := patch.NewHelper(configSecret, r.Client)
119106
if err != nil {
120107
return errors.Wrap(err, "failed to create patch helper for the kubeconfig secret")
121108
}
109+
defer func() {
110+
if err := patchHelper.Patch(ctx, configSecret); err != nil {
111+
reterr = errors.Wrap(err, "failed to patch the kubeconfig secret")
112+
}
113+
}()
114+
controller := metav1.GetControllerOf(configSecret)
122115

123-
// If the kubeconfig secret was created by v1alpha2 controllers, and thus it has the Cluster as the owner instead of KCP.
124-
// In this case remove the ownerReference to the Cluster.
125-
if util.IsOwnedByObject(configSecret, cluster) {
126-
configSecret.SetOwnerReferences(util.RemoveOwnerRef(configSecret.OwnerReferences, metav1.OwnerReference{
127-
APIVersion: clusterv1.GroupVersion.String(),
128-
Kind: "Cluster",
129-
Name: cluster.Name,
130-
UID: cluster.UID,
131-
}))
132-
}
133-
134-
// Remove the current controller if one exists.
135-
if controller != nil {
136-
configSecret.SetOwnerReferences(util.RemoveOwnerRef(configSecret.OwnerReferences, *controller))
116+
// If the current controller is KCP, ensure the owner reference is up to date and return early.
117+
// Note: This ensures secrets created prior to v1alpha4 are updated to have the correct owner reference apiVersion.
118+
if controller != nil && controller.Kind == kubeadmControlPlaneKind {
119+
configSecret.SetOwnerReferences(util.EnsureOwnerRef(configSecret.GetOwnerReferences(), *metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind))))
120+
return nil
137121
}
138122

139-
// Add the KubeadmControlPlane as the controller for this secret.
140-
configSecret.OwnerReferences = util.EnsureOwnerRef(configSecret.OwnerReferences,
141-
*metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane")))
142-
143-
if err := patch.Patch(ctx, configSecret); err != nil {
144-
return errors.Wrap(err, "failed to patch the kubeconfig secret")
123+
// If secret type is a CAPI-created secret ensure the owner reference is to KCP.
124+
if configSecret.Type == clusterv1.ClusterSecretType {
125+
// Remove the current controller if one exists and ensure KCP is the controller of the secret.
126+
if controller != nil {
127+
configSecret.SetOwnerReferences(util.RemoveOwnerRef(configSecret.GetOwnerReferences(), *controller))
128+
}
129+
configSecret.SetOwnerReferences(util.EnsureOwnerRef(configSecret.GetOwnerReferences(), *metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind))))
130+
131+
// If the kubeconfig secret was created by v1alpha2 controllers, and thus it has the Cluster as the owner instead of KCP.
132+
// In this case remove the ownerReference to the Cluster.
133+
if util.IsOwnedByObject(configSecret, cluster) {
134+
configSecret.SetOwnerReferences(util.RemoveOwnerRef(configSecret.GetOwnerReferences(), metav1.OwnerReference{
135+
APIVersion: clusterv1.GroupVersion.String(),
136+
Kind: "Cluster",
137+
Name: cluster.Name,
138+
UID: cluster.UID,
139+
}))
140+
}
145141
}
146142
return nil
147143
}
@@ -184,7 +180,7 @@ func (r *KubeadmControlPlaneReconciler) cloneConfigsAndGenerateMachine(ctx conte
184180
// OwnerReference here without the Controller field set
185181
infraCloneOwner := &metav1.OwnerReference{
186182
APIVersion: controlplanev1.GroupVersion.String(),
187-
Kind: "KubeadmControlPlane",
183+
Kind: kubeadmControlPlaneKind,
188184
Name: kcp.Name,
189185
UID: kcp.UID,
190186
}
@@ -260,7 +256,7 @@ func (r *KubeadmControlPlaneReconciler) generateKubeadmConfig(ctx context.Contex
260256
// Create an owner reference without a controller reference because the owning controller is the machine controller
261257
owner := metav1.OwnerReference{
262258
APIVersion: controlplanev1.GroupVersion.String(),
263-
Kind: "KubeadmControlPlane",
259+
Kind: kubeadmControlPlaneKind,
264260
Name: kcp.Name,
265261
UID: kcp.UID,
266262
}
@@ -408,7 +404,7 @@ func (r *KubeadmControlPlaneReconciler) computeDesiredMachine(kcp *controlplanev
408404
Namespace: kcp.Namespace,
409405
// Note: by setting the ownerRef on creation we signal to the Machine controller that this is not a stand-alone Machine.
410406
OwnerReferences: []metav1.OwnerReference{
411-
*metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind("KubeadmControlPlane")),
407+
*metav1.NewControllerRef(kcp, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind)),
412408
},
413409
Labels: map[string]string{},
414410
Annotations: map[string]string{},

exp/addons/api/v1beta1/clusterresourcesetbinding_types.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (c *ClusterResourceSetBinding) DeleteBinding(clusterResourceSet *ClusterRes
106106
break
107107
}
108108
}
109-
c.OwnerReferences = util.RemoveOwnerRef(c.OwnerReferences, metav1.OwnerReference{
109+
c.OwnerReferences = util.RemoveOwnerRef(c.GetOwnerReferences(), metav1.OwnerReference{
110110
APIVersion: clusterResourceSet.APIVersion,
111111
Kind: clusterResourceSet.Kind,
112112
Name: clusterResourceSet.Name,

0 commit comments

Comments
 (0)