Skip to content

Commit 7c9ebb2

Browse files
author
Joshua Reed
committed
Fixup of affinity group ownership and deletion.
1 parent 596ad4f commit 7c9ebb2

File tree

4 files changed

+103
-28
lines changed

4 files changed

+103
-28
lines changed

controllers/cloudstackfailuredomain_controller.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,21 +105,21 @@ func (r *CloudStackFailureDomainReconciliationRunner) Reconcile() (retRes ctrl.R
105105
}
106106

107107
// ReconcileDelete on the ReconciliationRunner attempts to delete the reconciliation subject.
108-
func (r *CloudStackFailureDomainReconciliationRunner) ReconcileDelete() (retRes ctrl.Result, retErr error) {
108+
func (r *CloudStackFailureDomainReconciliationRunner) ReconcileDelete() (ctrl.Result, error) {
109109
r.Log.Info("Deleting CloudStackFailureDomain")
110-
// Address Isolated Networks.
111-
if r.ReconciliationSubject.Spec.Zone.Network.Type == infrav1.NetworkTypeIsolated {
112-
netName := r.ReconciliationSubject.Spec.Zone.Network.Name
113-
if res, err := r.GetObjectByName(r.IsoNetMetaName(netName), r.IsoNet)(); r.ShouldReturn(res, err) {
114-
return res, err
115-
}
116-
if r.IsoNet.Name != "" {
117-
if err := r.K8sClient.Delete(r.RequestCtx, r.IsoNet); err != nil {
118-
return ctrl.Result{}, err
119-
}
120-
return r.RequeueWithMessage("Child IsolatedNetwork still present, requeueing.")
121-
}
122-
}
110+
return r.RunReconciliationStages(
111+
r.DeleteOwnedObjects(
112+
infrav1.GroupVersion.WithKind("CloudStackAffinityGroup"),
113+
infrav1.GroupVersion.WithKind("CloudStackIsolatedNetwork")),
114+
r.CheckOwnedObjectsDeleted(
115+
infrav1.GroupVersion.WithKind("CloudStackAffinityGroup"),
116+
infrav1.GroupVersion.WithKind("CloudStackIsolatedNetwork")),
117+
r.RemoveFinalizer,
118+
)
119+
}
120+
121+
// RemoveFinalizer just removes the finalizer from the failure domain.
122+
func (r *CloudStackFailureDomainReconciliationRunner) RemoveFinalizer() (ctrl.Result, error) {
123123
controllerutil.RemoveFinalizer(r.ReconciliationSubject, infrav1.FailureDomainFinalizer)
124124
return ctrl.Result{}, nil
125125
}

controllers/cloudstackmachine_controller.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,16 @@ func (r *CloudStackMachineReconciliationRunner) ConsiderAffinity() (ctrl.Result,
121121
r.Log.Info("getting affinity group name", err)
122122
}
123123

124+
// Set failure domain name and owners.
124125
r.AffinityGroup.Spec.FailureDomainName = r.ReconciliationSubject.Spec.FailureDomainName
125-
if res, err := r.GetOrCreateAffinityGroup(agName, r.ReconciliationSubject.Spec.Affinity, r.AffinityGroup)(); r.ShouldReturn(res, err) {
126+
if res, err := r.GetOrCreateAffinityGroup(
127+
agName, r.ReconciliationSubject.Spec.Affinity, r.AffinityGroup, r.FailureDomain)(); r.ShouldReturn(res, err) {
126128
return res, err
127129
}
128130
if !r.AffinityGroup.Status.Ready {
129131
return r.RequeueWithMessage("Required affinity group not ready.")
130132
}
133+
131134
return ctrl.Result{}, nil
132135
}
133136

controllers/utils/affinity_group.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,15 @@ import (
2828
"sigs.k8s.io/controller-runtime/pkg/client"
2929
)
3030

31-
// GenerateIsolatedNetwork of the passed name that's owned by the ReconciliationSubject.
32-
func (r *ReconciliationRunner) GetOrCreateAffinityGroup(name string, affinityType string, ag *infrav1.CloudStackAffinityGroup) CloudStackReconcilerMethod {
33-
return func() (ctrl.Result, error) {
31+
// GetOrCreateAffinityGroup of the passed name that's owned by the failure domain of the reconciliation subject and
32+
// the control plane that manages it.
33+
func (r *ReconciliationRunner) GetOrCreateAffinityGroup(
34+
name string,
35+
affinityType string,
36+
ag *infrav1.CloudStackAffinityGroup,
37+
fd *infrav1.CloudStackFailureDomain) CloudStackReconcilerMethod {
3438

39+
return func() (ctrl.Result, error) {
3540
// Start by attempting a fetch.
3641
lowerName := strings.ToLower(name)
3742
namespace := r.ReconciliationSubject.GetNamespace()
@@ -56,7 +61,7 @@ func (r *ReconciliationRunner) GetOrCreateAffinityGroup(name string, affinityTyp
5661
ag.Spec.Name = name
5762
ag.ObjectMeta = r.NewChildObjectMeta(lowerName)
5863

59-
// Replace owner reference with controller of CAPI and CloudStack machines.
64+
// Replace owner reference with controller of CAPI and CloudStack machines and FailureDomain.
6065
for _, ref := range r.ReconciliationSubject.GetOwnerReferences() {
6166
if strings.EqualFold(ref.Kind, "EtcdadmCluster") ||
6267
strings.EqualFold(ref.Kind, "KubeadmControlPlane") ||
@@ -65,6 +70,13 @@ func (r *ReconciliationRunner) GetOrCreateAffinityGroup(name string, affinityTyp
6570
break
6671
}
6772
}
73+
ag.OwnerReferences = append(ag.OwnerReferences,
74+
metav1.OwnerReference{
75+
Name: fd.Name,
76+
Kind: fd.Kind,
77+
APIVersion: fd.APIVersion,
78+
UID: fd.UID,
79+
})
6880

6981
if err := r.K8sClient.Create(r.RequestCtx, ag); err != nil && !ContainsAlreadyExistsSubstring(err) {
7082
return r.ReturnWrappedError(err, "creating affinity group CRD")

controllers/utils/base_reconciler.go

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,11 @@ func (r *ReconciliationRunner) CheckOwnedCRDsForReadiness(gvks ...schema.GroupVe
232232
} else if !found {
233233
return r.RequeueWithMessage(
234234
fmt.Sprintf(
235-
"Owned object of kind %s with name %s not found, requeueing.",
235+
"Owned object of kind %s with name %s not found, requeuing.",
236236
gvk.Kind,
237237
owned.GetName()))
238238
} else {
239-
r.Log.Info(fmt.Sprintf("Owned object %s of kind %s not ready, requeueing", name, gvk.Kind))
239+
r.Log.Info(fmt.Sprintf("Owned object %s of kind %s not ready, requeuing", name, gvk.Kind))
240240
return ctrl.Result{RequeueAfter: RequeueTimeout}, nil
241241
}
242242
}
@@ -246,6 +246,65 @@ func (r *ReconciliationRunner) CheckOwnedCRDsForReadiness(gvks ...schema.GroupVe
246246
}
247247
}
248248

249+
// CheckOwnedObjectsDeleted queries for the presence of owned objects and requeues if any are still present. Primarily
250+
// used to prevent deletions of owners before dependents.
251+
func (r *ReconciliationRunner) DeleteOwnedObjects(gvks ...schema.GroupVersionKind) CloudStackReconcilerMethod {
252+
return func() (ctrl.Result, error) {
253+
// For each GroupVersionKind...
254+
for _, gvk := range gvks {
255+
// Query to find objects of this kind.
256+
potentiallyOnwedObjs := &unstructured.UnstructuredList{}
257+
potentiallyOnwedObjs.SetGroupVersionKind(gvk)
258+
err := r.K8sClient.List(r.RequestCtx, potentiallyOnwedObjs)
259+
if err != nil {
260+
return ctrl.Result{}, errors.Wrapf(err, "requesting owned objects with gvk %s", gvk)
261+
}
262+
263+
// Filter objects not actually owned by reconciliation subject via owner reference UID.
264+
for _, pOwned := range potentiallyOnwedObjs.Items {
265+
refs := pOwned.GetOwnerReferences()
266+
for _, ref := range refs {
267+
if ref.UID == r.ReconciliationSubject.GetUID() {
268+
if err := r.K8sClient.Delete(r.RequestCtx, &pOwned); err != nil {
269+
return ctrl.Result{}, err
270+
}
271+
}
272+
}
273+
}
274+
}
275+
return ctrl.Result{}, nil
276+
}
277+
}
278+
279+
// CheckOwnedObjectsDeleted queries for the presence of owned objects and requeues if any are still present. Primarily
280+
// used to prevent deletions of owners before dependents.
281+
func (r *ReconciliationRunner) CheckOwnedObjectsDeleted(gvks ...schema.GroupVersionKind) CloudStackReconcilerMethod {
282+
return func() (ctrl.Result, error) {
283+
// For each GroupVersionKind...
284+
for _, gvk := range gvks {
285+
// Query to find objects of this kind.
286+
potentiallyOnwedObjs := &unstructured.UnstructuredList{}
287+
potentiallyOnwedObjs.SetGroupVersionKind(gvk)
288+
err := r.K8sClient.List(r.RequestCtx, potentiallyOnwedObjs)
289+
if err != nil {
290+
return ctrl.Result{}, errors.Wrapf(err, "requesting owned objects with gvk %s", gvk)
291+
}
292+
293+
// Filter objects not actually owned by reconciliation subject via owner reference UID.
294+
for _, pOwned := range potentiallyOnwedObjs.Items {
295+
refs := pOwned.GetOwnerReferences()
296+
for _, ref := range refs {
297+
if ref.UID == r.ReconciliationSubject.GetUID() {
298+
return r.RequeueWithMessage(
299+
fmt.Sprintf("Owned object %s of kind %s not yet deleted", pOwned.GetKind(), pOwned.GetName()))
300+
}
301+
}
302+
}
303+
}
304+
return ctrl.Result{}, nil
305+
}
306+
}
307+
249308
// RequeueIfCloudStackClusterNotReady requeues the reconciliation request if the CloudStackCluster is not ready.
250309
func (r *ReconciliationRunner) RequeueIfCloudStackClusterNotReady() (ctrl.Result, error) {
251310
if !r.CSCluster.Status.Ready {
@@ -272,9 +331,9 @@ func (r *ReconciliationRunner) PatchChangesBackToAPI() (res ctrl.Result, retErr
272331

273332
// RequeueWithMessage is a convenience method to log requeue message and then return a result with RequeueAfter set.
274333
func (r *ReconciliationRunner) RequeueWithMessage(msg string, keysAndValues ...interface{}) (ctrl.Result, error) {
275-
// Add requeueing to message if not present. Might turn this into a lint check later.
276-
if !strings.Contains(strings.ToLower(msg), "requeue") {
277-
msg = msg + " Requeueing."
334+
// Add requeuing to message if not present. Might turn this into a lint check later.
335+
if !strings.Contains(strings.ToLower(msg), "requeu") {
336+
msg = msg + " Requeuing."
278337
}
279338
r.Log.Info(msg, keysAndValues...)
280339
return ctrl.Result{RequeueAfter: RequeueTimeout}, nil
@@ -284,7 +343,7 @@ func (r *ReconciliationRunner) RequeueWithMessage(msg string, keysAndValues ...i
284343
func (r *ReconciliationRunner) RequeueWithMessageStage(msg string, keysAndValues ...interface{}) CloudStackReconcilerMethod {
285344
return func() (ctrl.Result, error) {
286345
if !strings.Contains(strings.ToLower(msg), "requeue") {
287-
msg = msg + " Requeueing."
346+
msg = msg + " Requeuing."
288347
}
289348
r.Log.Info(msg, keysAndValues...)
290349
return ctrl.Result{RequeueAfter: RequeueTimeout}, nil
@@ -381,6 +440,7 @@ func (r *ReconciliationRunner) GetReconciliationSubject() (res ctrl.Result, rete
381440
r.Log.V(1).Info("Getting reconciliation subject.")
382441
err := client.IgnoreNotFound(r.K8sClient.Get(r.RequestCtx, r.Request.NamespacedName, r.ReconciliationSubject))
383442
if r.ReconciliationSubject.GetName() == "" { // Resource does not exist. No need to reconcile.
443+
r.Log.V(1).Info("Resource not found. Exiting reconciliation.")
384444
r.SetReturnEarly()
385445
}
386446
if err != nil {
@@ -438,11 +498,11 @@ func (r *ReconciliationRunner) NewChildObjectMeta(name string) metav1.ObjectMeta
438498
// RequeueIfMissingBaseCRs checks that the ReconciliationSubject, CAPI Cluster, and CloudStackCluster objects were
439499
// actually fetched and reques if not. The base reconciliation stages will continue even if not so as to allow deletion.
440500
func (r *ReconciliationRunner) RequeueIfMissingBaseCRs() (ctrl.Result, error) {
441-
r.Log.V(1).Info("Requeueing if missing ReconciliationSubject, CloudStack cluster, or CAPI cluster.")
501+
r.Log.V(1).Info("Requeuing if missing ReconciliationSubject, CloudStack cluster, or CAPI cluster.")
442502
if r.CSCluster.GetName() == "" {
443-
return r.RequeueWithMessage("CloudStackCluster wasn't found. Requeueing.")
503+
return r.RequeueWithMessage("CloudStackCluster wasn't found. Requeuing.")
444504
} else if r.CAPICluster.GetName() == "" {
445-
return r.RequeueWithMessage("CAPI Cluster wasn't found. Requeueing.")
505+
return r.RequeueWithMessage("CAPI Cluster wasn't found. Requeuing.")
446506
}
447507
return ctrl.Result{}, nil
448508
}

0 commit comments

Comments
 (0)