Skip to content

Commit 63cf5a0

Browse files
Fix IBMPowerVSImage deletion (#543)
Signed-off-by: Prajyot-Parab <[email protected]>
1 parent 5f4fc89 commit 63cf5a0

File tree

2 files changed

+133
-3
lines changed

2 files changed

+133
-3
lines changed

controllers/ibmpowervscluster_controller.go

Lines changed: 124 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@ package controllers
1818

1919
import (
2020
"context"
21+
"strings"
22+
"time"
2123

2224
"github.com/go-logr/logr"
2325
"github.com/pkg/errors"
2426

2527
apierrors "k8s.io/apimachinery/pkg/api/errors"
28+
"k8s.io/apimachinery/pkg/api/meta"
2629
"k8s.io/apimachinery/pkg/runtime"
30+
kerrors "k8s.io/apimachinery/pkg/util/errors"
2731
"sigs.k8s.io/cluster-api/util"
2832
ctrl "sigs.k8s.io/controller-runtime"
2933
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -32,6 +36,7 @@ import (
3236

3337
"sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta1"
3438
"sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope"
39+
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
3540
)
3641

3742
// IBMPowerVSClusterReconciler reconciles a IBMPowerVSCluster object
@@ -85,7 +90,7 @@ func (r *IBMPowerVSClusterReconciler) Reconcile(ctx context.Context, req ctrl.Re
8590

8691
// Handle deleted clusters
8792
if !ibmCluster.DeletionTimestamp.IsZero() {
88-
return r.reconcileDelete(clusterScope)
93+
return r.reconcileDelete(ctx, clusterScope)
8994
}
9095

9196
if err != nil {
@@ -105,11 +110,127 @@ func (r *IBMPowerVSClusterReconciler) reconcile(ctx context.Context, clusterScop
105110
return ctrl.Result{}, nil
106111
}
107112

108-
func (r *IBMPowerVSClusterReconciler) reconcileDelete(clusterScope *scope.PowerVSClusterScope) (ctrl.Result, error) {
109-
controllerutil.RemoveFinalizer(clusterScope.IBMPowerVSCluster, v1beta1.IBMPowerVSClusterFinalizer)
113+
func (r *IBMPowerVSClusterReconciler) reconcileDelete(ctx context.Context, clusterScope *scope.PowerVSClusterScope) (ctrl.Result, error) {
114+
log := ctrl.LoggerFrom(ctx)
115+
116+
cluster := clusterScope.IBMPowerVSCluster
117+
descendants, err := r.listDescendants(ctx, cluster)
118+
if err != nil {
119+
log.Error(err, "Failed to list descendants")
120+
return reconcile.Result{}, err
121+
}
122+
123+
children, err := descendants.filterOwnedDescendants(cluster)
124+
if err != nil {
125+
log.Error(err, "Failed to extract direct descendants")
126+
return reconcile.Result{}, err
127+
}
128+
129+
if len(children) > 0 {
130+
log.Info("Cluster still has children - deleting them first", "count", len(children))
131+
132+
var errs []error
133+
134+
for _, child := range children {
135+
if !child.GetDeletionTimestamp().IsZero() {
136+
// Don't handle deleted child
137+
continue
138+
}
139+
gvk := child.GetObjectKind().GroupVersionKind().String()
140+
141+
log.Info("Deleting child object", "gvk", gvk, "name", child.GetName())
142+
if err := r.Client.Delete(ctx, child); err != nil {
143+
err = errors.Wrapf(err, "error deleting cluster %s/%s: failed to delete %s %s", cluster.Namespace, cluster.Name, gvk, child.GetName())
144+
log.Error(err, "Error deleting resource", "gvk", gvk, "name", child.GetName())
145+
errs = append(errs, err)
146+
}
147+
}
148+
149+
if len(errs) > 0 {
150+
return ctrl.Result{}, kerrors.NewAggregate(errs)
151+
}
152+
}
153+
154+
if descendantCount := descendants.length(); descendantCount > 0 {
155+
indirect := descendantCount - len(children)
156+
log.Info("Cluster still has descendants - need to requeue", "descendants", descendants.descendantNames(), "indirect descendants count", indirect)
157+
// Requeue so we can check the next time to see if there are still any descendants left.
158+
return ctrl.Result{RequeueAfter: 5 * time.Second}, nil
159+
}
160+
161+
controllerutil.RemoveFinalizer(cluster, v1beta1.IBMPowerVSClusterFinalizer)
110162
return ctrl.Result{}, nil
111163
}
112164

165+
type clusterDescendants struct {
166+
ibmPowerVSImages v1beta1.IBMPowerVSImageList
167+
}
168+
169+
// length returns the number of descendants.
170+
func (c *clusterDescendants) length() int {
171+
return len(c.ibmPowerVSImages.Items)
172+
}
173+
174+
func (c *clusterDescendants) descendantNames() string {
175+
descendants := make([]string, 0)
176+
ibmPowerVSImageNames := make([]string, len(c.ibmPowerVSImages.Items))
177+
for i, ibmPowerVSImage := range c.ibmPowerVSImages.Items {
178+
ibmPowerVSImageNames[i] = ibmPowerVSImage.Name
179+
}
180+
if len(ibmPowerVSImageNames) > 0 {
181+
descendants = append(descendants, "IBM Powervs Images: "+strings.Join(ibmPowerVSImageNames, ","))
182+
}
183+
184+
return strings.Join(descendants, ";")
185+
}
186+
187+
// listDescendants returns a list of all IBMPowerVSImages for the cluster.
188+
func (r *IBMPowerVSClusterReconciler) listDescendants(ctx context.Context, cluster *v1beta1.IBMPowerVSCluster) (clusterDescendants, error) {
189+
var descendants clusterDescendants
190+
191+
listOptions := []client.ListOption{
192+
client.InNamespace(cluster.Namespace),
193+
client.MatchingLabels(map[string]string{clusterv1.ClusterLabelName: cluster.Name}),
194+
}
195+
196+
if err := r.Client.List(ctx, &descendants.ibmPowerVSImages, listOptions...); err != nil {
197+
return descendants, errors.Wrapf(err, "failed to list IBMPowerVSImages for cluster %s/%s", cluster.Namespace, cluster.Name)
198+
}
199+
200+
return descendants, nil
201+
}
202+
203+
// filterOwnedDescendants returns an array of runtime.Objects containing only those descendants that have the cluster
204+
// as an owner reference.
205+
func (c clusterDescendants) filterOwnedDescendants(cluster *v1beta1.IBMPowerVSCluster) ([]client.Object, error) {
206+
var ownedDescendants []client.Object
207+
eachFunc := func(o runtime.Object) error {
208+
obj := o.(client.Object)
209+
acc, err := meta.Accessor(obj)
210+
if err != nil {
211+
return nil //nolint:nilerr // We don't want to exit the EachListItem loop, just continue
212+
}
213+
214+
if util.IsOwnedByObject(acc, cluster) {
215+
ownedDescendants = append(ownedDescendants, obj)
216+
}
217+
218+
return nil
219+
}
220+
221+
lists := []client.ObjectList{
222+
&c.ibmPowerVSImages,
223+
}
224+
225+
for _, list := range lists {
226+
if err := meta.EachListItem(list, eachFunc); err != nil {
227+
return nil, errors.Wrapf(err, "error finding owned descendants of cluster %s/%s", cluster.Namespace, cluster.Name)
228+
}
229+
}
230+
231+
return ownedDescendants, nil
232+
}
233+
113234
// SetupWithManager creates a new IBMPowerVSCluster controller for a manager.
114235
func (r *IBMPowerVSClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
115236
return ctrl.NewControllerManagedBy(mgr).

controllers/ibmpowervsimage_controller.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ func (r *IBMPowerVSImageReconciler) Reconcile(ctx context.Context, req ctrl.Requ
9797
func (r *IBMPowerVSImageReconciler) reconcile(ctx context.Context, cluster *v1beta1.IBMPowerVSCluster, imageScope *scope.PowerVSImageScope) (ctrl.Result, error) {
9898
controllerutil.AddFinalizer(imageScope.IBMPowerVSImage, v1beta1.IBMPowerVSImageFinalizer)
9999

100+
// Create new labels section for IBMPowerVSImage metadata if nil
101+
if imageScope.IBMPowerVSImage.Labels == nil {
102+
imageScope.IBMPowerVSImage.Labels = make(map[string]string)
103+
}
104+
105+
if _, ok := imageScope.IBMPowerVSImage.Labels[clusterv1.ClusterLabelName]; !ok {
106+
imageScope.IBMPowerVSImage.Labels[clusterv1.ClusterLabelName] = imageScope.IBMPowerVSImage.Spec.ClusterName
107+
}
108+
100109
if r.shouldAdopt(*imageScope.IBMPowerVSImage) {
101110
imageScope.Info("Image Controller has not yet set OwnerRef")
102111
imageScope.IBMPowerVSImage.OwnerReferences = clusterv1util.EnsureOwnerRef(imageScope.IBMPowerVSImage.OwnerReferences, metav1.OwnerReference{

0 commit comments

Comments
 (0)