@@ -18,12 +18,16 @@ package controllers
18
18
19
19
import (
20
20
"context"
21
+ "strings"
22
+ "time"
21
23
22
24
"github.com/go-logr/logr"
23
25
"github.com/pkg/errors"
24
26
25
27
apierrors "k8s.io/apimachinery/pkg/api/errors"
28
+ "k8s.io/apimachinery/pkg/api/meta"
26
29
"k8s.io/apimachinery/pkg/runtime"
30
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
27
31
"sigs.k8s.io/cluster-api/util"
28
32
ctrl "sigs.k8s.io/controller-runtime"
29
33
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -32,6 +36,7 @@ import (
32
36
33
37
"sigs.k8s.io/cluster-api-provider-ibmcloud/api/v1beta1"
34
38
"sigs.k8s.io/cluster-api-provider-ibmcloud/cloud/scope"
39
+ clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
35
40
)
36
41
37
42
// IBMPowerVSClusterReconciler reconciles a IBMPowerVSCluster object
@@ -85,7 +90,7 @@ func (r *IBMPowerVSClusterReconciler) Reconcile(ctx context.Context, req ctrl.Re
85
90
86
91
// Handle deleted clusters
87
92
if ! ibmCluster .DeletionTimestamp .IsZero () {
88
- return r .reconcileDelete (clusterScope )
93
+ return r .reconcileDelete (ctx , clusterScope )
89
94
}
90
95
91
96
if err != nil {
@@ -105,11 +110,127 @@ func (r *IBMPowerVSClusterReconciler) reconcile(ctx context.Context, clusterScop
105
110
return ctrl.Result {}, nil
106
111
}
107
112
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 )
110
162
return ctrl.Result {}, nil
111
163
}
112
164
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
+
113
234
// SetupWithManager creates a new IBMPowerVSCluster controller for a manager.
114
235
func (r * IBMPowerVSClusterReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
115
236
return ctrl .NewControllerManagedBy (mgr ).
0 commit comments