Skip to content

Commit c4857b4

Browse files
committed
✨ Add cluster owner references to external MS/MD objects
Signed-off-by: Vince Prignano <[email protected]>
1 parent f38f44a commit c4857b4

10 files changed

+178
-8
lines changed

controllers/cluster_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func (r *ClusterReconciler) reconcileDelete(ctx context.Context, cluster *cluste
254254
if cluster.Spec.InfrastructureRef != nil {
255255
obj, err := external.Get(ctx, r.Client, cluster.Spec.InfrastructureRef, cluster.Namespace)
256256
switch {
257-
case apierrors.IsNotFound(err):
257+
case apierrors.IsNotFound(errors.Cause(err)):
258258
// All good - the infra resource has been deleted
259259
case err != nil:
260260
return ctrl.Result{}, errors.Wrapf(err, "failed to get %s %q for Cluster %s/%s",

controllers/cluster_controller_phases.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func (r *ClusterReconciler) reconcileExternal(ctx context.Context, cluster *clus
6666

6767
obj, err := external.Get(ctx, r.Client, ref, cluster.Namespace)
6868
if err != nil {
69-
if apierrors.IsNotFound(err) {
69+
if apierrors.IsNotFound(errors.Cause(err)) {
7070
return nil, errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: 30 * time.Second},
7171
"could not find %v %q for Cluster %q in namespace %q, requeuing",
7272
ref.GroupVersionKind(), ref.Name, cluster.Name, cluster.Namespace)

controllers/external/testing.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,56 @@ import (
2222
)
2323

2424
var (
25+
TestGenericBootstrapCRD = &apiextensionsv1beta1.CustomResourceDefinition{
26+
ObjectMeta: metav1.ObjectMeta{
27+
Name: "genericmachines.bootstrap.cluster.x-k8s.io",
28+
},
29+
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
30+
Group: "bootstrap.cluster.x-k8s.io",
31+
Scope: apiextensionsv1beta1.NamespaceScoped,
32+
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
33+
Kind: "BootstrapMachine",
34+
Plural: "genericmachines",
35+
},
36+
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
37+
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
38+
},
39+
Validation: &apiextensionsv1beta1.CustomResourceValidation{},
40+
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
41+
{
42+
Name: "v1alpha3",
43+
Served: true,
44+
Storage: true,
45+
},
46+
},
47+
},
48+
}
49+
50+
TestGenericBootstrapTemplateCRD = &apiextensionsv1beta1.CustomResourceDefinition{
51+
ObjectMeta: metav1.ObjectMeta{
52+
Name: "genericmachinetemplates.bootstrap.cluster.x-k8s.io",
53+
},
54+
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
55+
Group: "bootstrap.cluster.x-k8s.io",
56+
Scope: apiextensionsv1beta1.NamespaceScoped,
57+
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
58+
Kind: "BootstrapMachineTemplate",
59+
Plural: "genericmachinetemplates",
60+
},
61+
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
62+
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
63+
},
64+
Validation: &apiextensionsv1beta1.CustomResourceValidation{},
65+
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
66+
{
67+
Name: "v1alpha3",
68+
Served: true,
69+
Storage: true,
70+
},
71+
},
72+
},
73+
}
74+
2575
TestGenericInfrastructureCRD = &apiextensionsv1beta1.CustomResourceDefinition{
2676
ObjectMeta: metav1.ObjectMeta{
2777
Name: "genericmachines.infrastructure.cluster.x-k8s.io",

controllers/external/util.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ import (
2727
"sigs.k8s.io/controller-runtime/pkg/client"
2828
)
2929

30+
const (
31+
// TemplateSuffix is the object kind suffix used by infrastructure references associated
32+
// with MachineSet or MachineDeployments.
33+
TemplateSuffix = "Template"
34+
)
35+
3036
// Get uses the client and reference to get an external, unstructured object.
3137
func Get(ctx context.Context, c client.Client, ref *corev1.ObjectReference, namespace string) (*unstructured.Unstructured, error) {
3238
obj := new(unstructured.Unstructured)
@@ -35,7 +41,7 @@ func Get(ctx context.Context, c client.Client, ref *corev1.ObjectReference, name
3541
obj.SetName(ref.Name)
3642
key := client.ObjectKey{Name: obj.GetName(), Namespace: namespace}
3743
if err := c.Get(ctx, key, obj); err != nil {
38-
return nil, err
44+
return nil, errors.Wrapf(err, "failed to retrieve %s external object %q/%q", obj.GetKind(), key.Namespace, key.Name)
3945
}
4046
return obj, nil
4147
}
@@ -71,7 +77,7 @@ func CloneTemplate(ctx context.Context, c client.Client, ref *corev1.ObjectRefer
7177

7278
// Set the object Kind and strip the word "Template" if it's a suffix.
7379
if to.GetKind() == "" {
74-
to.SetKind(strings.TrimSuffix(ref.Kind, "Template"))
80+
to.SetKind(strings.TrimSuffix(ref.Kind, TemplateSuffix))
7581
}
7682

7783
// Create the external clone.

controllers/machine_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ func (r *MachineReconciler) reconcileDeleteExternal(ctx context.Context, m *clus
396396
}
397397

398398
obj, err := external.Get(ctx, r.Client, ref, m.Namespace)
399-
if err != nil && !apierrors.IsNotFound(err) {
399+
if err != nil && !apierrors.IsNotFound(errors.Cause(err)) {
400400
return false, errors.Wrapf(err, "failed to get %s %q for Machine %q in namespace %q",
401401
ref.GroupVersionKind(), ref.Name, m.Name, m.Namespace)
402402
}

controllers/machine_controller_phases.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (r *MachineReconciler) reconcileExternal(ctx context.Context, m *clusterv1.
7979

8080
obj, err := external.Get(ctx, r.Client, ref, m.Namespace)
8181
if err != nil {
82-
if apierrors.IsNotFound(err) {
82+
if apierrors.IsNotFound(errors.Cause(err)) {
8383
return nil, errors.Wrapf(&capierrors.RequeueAfterError{RequeueAfter: externalReadyWait},
8484
"could not find %v %q for Machine %q in namespace %q, requeuing",
8585
ref.GroupVersionKind(), ref.Name, m.Name, m.Namespace)

controllers/machinedeployment_controller_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import (
2929
"k8s.io/client-go/tools/record"
3030
"k8s.io/utils/pointer"
3131
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
32+
"sigs.k8s.io/cluster-api/controllers/external"
33+
"sigs.k8s.io/cluster-api/util"
3234
"sigs.k8s.io/cluster-api/util/kubeconfig"
3335
"sigs.k8s.io/controller-runtime/pkg/client"
3436
"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -162,6 +164,22 @@ var _ = Describe("MachineDeployment Reconciler", func() {
162164
return len(machineSets.Items)
163165
}, timeout).Should(BeEquivalentTo(1))
164166

167+
By("Verifying the linked infrastructure template has a cluster owner reference")
168+
Eventually(func() bool {
169+
obj, err := external.Get(ctx, k8sClient, &deployment.Spec.Template.Spec.InfrastructureRef, deployment.Namespace)
170+
if err != nil {
171+
return false
172+
}
173+
174+
return util.HasOwnerRef(obj.GetOwnerReferences(), metav1.OwnerReference{
175+
APIVersion: clusterv1.GroupVersion.String(),
176+
Kind: "Cluster",
177+
Name: testCluster.Name,
178+
UID: testCluster.UID,
179+
})
180+
181+
}, timeout).Should(BeTrue())
182+
165183
// Verify that expected number of machines are created
166184
By("Verify expected number of machines are created")
167185
machines := &clusterv1.MachineList{}

controllers/machineset_controller.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3636
"sigs.k8s.io/cluster-api/controllers/external"
3737
"sigs.k8s.io/cluster-api/util"
38+
"sigs.k8s.io/cluster-api/util/patch"
3839
ctrl "sigs.k8s.io/controller-runtime"
3940
"sigs.k8s.io/controller-runtime/pkg/client"
4041
"sigs.k8s.io/controller-runtime/pkg/controller"
@@ -153,6 +154,17 @@ func (r *MachineSetReconciler) reconcile(ctx context.Context, machineSet *cluste
153154
}
154155
}
155156

157+
// Make sure to reconcile the external infrastructure reference.
158+
if err := r.reconcileExternalReference(ctx, cluster, machineSet.Spec.Template.Spec.InfrastructureRef); err != nil {
159+
return ctrl.Result{}, err
160+
}
161+
// Make sure to reconcile the external bootstrap reference, if any.
162+
if machineSet.Spec.Template.Spec.Bootstrap.ConfigRef != nil {
163+
if err := r.reconcileExternalReference(ctx, cluster, *machineSet.Spec.Template.Spec.Bootstrap.ConfigRef); err != nil {
164+
return ctrl.Result{}, err
165+
}
166+
}
167+
156168
// Make sure selector and template to be in the same cluster.
157169
machineSet.Spec.Selector.MatchLabels[clusterv1.ClusterLabelName] = machineSet.Spec.ClusterName
158170
machineSet.Spec.Template.Labels[clusterv1.ClusterLabelName] = machineSet.Spec.ClusterName
@@ -253,6 +265,34 @@ func (r *MachineSetReconciler) reconcile(ctx context.Context, machineSet *cluste
253265
return ctrl.Result{}, nil
254266
}
255267

268+
func (r *MachineSetReconciler) reconcileExternalReference(ctx context.Context, cluster *clusterv1.Cluster, ref corev1.ObjectReference) error {
269+
if !strings.HasSuffix(ref.Kind, external.TemplateSuffix) {
270+
return nil
271+
}
272+
273+
obj, err := external.Get(ctx, r.Client, &ref, cluster.Namespace)
274+
if err != nil {
275+
return err
276+
}
277+
278+
patchHelper, err := patch.NewHelper(obj, r.Client)
279+
if err != nil {
280+
return err
281+
}
282+
283+
obj.SetOwnerReferences(util.EnsureOwnerRef(obj.GetOwnerReferences(), metav1.OwnerReference{
284+
APIVersion: clusterv1.GroupVersion.String(),
285+
Kind: "Cluster",
286+
Name: cluster.Name,
287+
UID: cluster.UID,
288+
}))
289+
290+
if err := patchHelper.Patch(ctx, obj); err != nil {
291+
return err
292+
}
293+
return nil
294+
}
295+
256296
// syncReplicas scales Machine resources up or down.
257297
func (r *MachineSetReconciler) syncReplicas(ctx context.Context, ms *clusterv1.MachineSet, machines []*clusterv1.Machine) error {
258298
logger := r.Log.WithValues("machineset", ms.Name, "namespace", ms.Namespace)

controllers/machineset_controller_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ import (
3131
"k8s.io/client-go/kubernetes/scheme"
3232
"k8s.io/client-go/tools/record"
3333
"k8s.io/klog/klogr"
34-
"k8s.io/utils/pointer"
3534
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
35+
"sigs.k8s.io/cluster-api/controllers/external"
36+
"sigs.k8s.io/cluster-api/util"
3637
"sigs.k8s.io/cluster-api/util/kubeconfig"
3738
"sigs.k8s.io/controller-runtime/pkg/client"
3839
"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -89,7 +90,11 @@ var _ = Describe("MachineSet Reconciler", func() {
8990
ClusterName: testCluster.Name,
9091
Version: &version,
9192
Bootstrap: clusterv1.Bootstrap{
92-
Data: pointer.StringPtr("x"),
93+
ConfigRef: &corev1.ObjectReference{
94+
APIVersion: "bootstrap.cluster.x-k8s.io/v1alpha3",
95+
Kind: "BootstrapMachineTemplate",
96+
Name: "ms-template",
97+
},
9398
},
9499
InfrastructureRef: corev1.ObjectReference{
95100
APIVersion: "infrastructure.cluster.x-k8s.io/v1alpha3",
@@ -101,6 +106,25 @@ var _ = Describe("MachineSet Reconciler", func() {
101106
},
102107
}
103108

109+
// Create bootstrap template resource.
110+
bootstrapResource := map[string]interface{}{
111+
"kind": "BootstrapMachine",
112+
"apiVersion": "bootstrap.cluster.x-k8s.io/v1alpha3",
113+
"metadata": map[string]interface{}{},
114+
}
115+
bootstrapTmpl := &unstructured.Unstructured{
116+
Object: map[string]interface{}{
117+
"spec": map[string]interface{}{
118+
"template": bootstrapResource,
119+
},
120+
},
121+
}
122+
bootstrapTmpl.SetKind("BootstrapMachineTemplate")
123+
bootstrapTmpl.SetAPIVersion("bootstrap.cluster.x-k8s.io/v1alpha3")
124+
bootstrapTmpl.SetName("ms-template")
125+
bootstrapTmpl.SetNamespace(namespace.Name)
126+
Expect(k8sClient.Create(ctx, bootstrapTmpl)).To(BeNil())
127+
104128
// Create infrastructure template resource.
105129
infraResource := map[string]interface{}{
106130
"kind": "InfrastructureMachine",
@@ -131,6 +155,36 @@ var _ = Describe("MachineSet Reconciler", func() {
131155
Expect(err).NotTo(HaveOccurred())
132156
}()
133157

158+
By("Verifying the linked bootstrap template has a cluster owner reference")
159+
Eventually(func() bool {
160+
obj, err := external.Get(ctx, k8sClient, instance.Spec.Template.Spec.Bootstrap.ConfigRef, instance.Namespace)
161+
if err != nil {
162+
return false
163+
}
164+
165+
return util.HasOwnerRef(obj.GetOwnerReferences(), metav1.OwnerReference{
166+
APIVersion: clusterv1.GroupVersion.String(),
167+
Kind: "Cluster",
168+
Name: testCluster.Name,
169+
UID: testCluster.UID,
170+
})
171+
}, timeout).Should(BeTrue())
172+
173+
By("Verifying the linked infrastructure template has a cluster owner reference")
174+
Eventually(func() bool {
175+
obj, err := external.Get(ctx, k8sClient, &instance.Spec.Template.Spec.InfrastructureRef, instance.Namespace)
176+
if err != nil {
177+
return false
178+
}
179+
180+
return util.HasOwnerRef(obj.GetOwnerReferences(), metav1.OwnerReference{
181+
APIVersion: clusterv1.GroupVersion.String(),
182+
Kind: "Cluster",
183+
Name: testCluster.Name,
184+
UID: testCluster.UID,
185+
})
186+
}, timeout).Should(BeTrue())
187+
134188
machines := &clusterv1.MachineList{}
135189

136190
// Verify that we have 2 replicas.

controllers/suite_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ var _ = BeforeSuite(func(done Done) {
7575
By("bootstrapping test environment")
7676
testEnv = &envtest.Environment{
7777
CRDs: []*apiextensionsv1beta1.CustomResourceDefinition{
78+
external.TestGenericBootstrapCRD,
79+
external.TestGenericBootstrapTemplateCRD,
7880
external.TestGenericInfrastructureCRD,
7981
external.TestGenericInfrastructureTemplateCRD,
8082
},

0 commit comments

Comments
 (0)