@@ -18,21 +18,40 @@ package cmd
18
18
19
19
import (
20
20
"context"
21
+ "fmt"
22
+ "strings"
23
+ "time"
21
24
22
25
"github.com/go-errors/errors"
23
26
"github.com/spf13/cobra"
27
+ corev1 "k8s.io/api/core/v1"
28
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29
+ "k8s.io/apimachinery/pkg/fields"
30
+ "k8s.io/apimachinery/pkg/util/wait"
31
+ "k8s.io/klog/v2/klogr"
32
+
33
+ kerrors "k8s.io/apimachinery/pkg/util/errors"
34
+
35
+ "k8s.io/apimachinery/pkg/api/meta"
36
+ ctrl "sigs.k8s.io/controller-runtime"
37
+
38
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
39
+ apierrors "k8s.io/apimachinery/pkg/api/errors"
40
+ ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
41
+
42
+ operatorv1 "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
24
43
)
25
44
26
45
type deleteOptions struct {
27
46
kubeconfig string
28
47
kubeconfigContext string
29
- coreProvider string
48
+ coreProvider bool
30
49
bootstrapProviders []string
31
50
controlPlaneProviders []string
32
51
infrastructureProviders []string
33
52
ipamProviders []string
53
+ addonProviders []string
34
54
// runtimeExtensionProviders []string
35
- addonProviders []string
36
55
includeNamespace bool
37
56
includeCRDs bool
38
57
deleteAll bool
@@ -63,7 +82,7 @@ var deleteCmd = &cobra.Command{
63
82
# Important! As a consequence of this operation, all the corresponding resources managed by
64
83
# the AWS infrastructure provider and Cluster API Providers are orphaned and there might be
65
84
# ongoing costs incurred as a result of this.
66
- capioperator delete --core cluster-api --infrastructure aws
85
+ capioperator delete --core --infrastructure aws
67
86
68
87
# Delete the AWS infrastructure provider and related CRDs. Please note that this forces deletion of
69
88
# all the related objects (e.g. AWSClusters, AWSMachines etc.).
@@ -98,20 +117,20 @@ func init() {
98
117
deleteCmd .Flags ().BoolVar (& deleteOpts .includeCRDs , "include-crd" , false ,
99
118
"Forces the deletion of the provider's CRDs (and of all the related objects)" )
100
119
101
- deleteCmd .Flags ().StringVar (& deleteOpts .coreProvider , "core" , "" ,
102
- "Core provider version (e.g. cluster-api:v1.1.5) to delete from the management cluster" )
120
+ deleteCmd .Flags ().BoolVar (& deleteOpts .coreProvider , "core" , false ,
121
+ "Core provider to delete from the management cluster. If not set, core provider is not removed. Cluster cannot have more then 1 core provider in total. " )
103
122
deleteCmd .Flags ().StringSliceVarP (& deleteOpts .infrastructureProviders , "infrastructure" , "i" , nil ,
104
- "Infrastructure providers and versions (e.g. aws:v0.5.0 ) to delete from the management cluster" )
123
+ "Infrastructure provider and namespace (e.g. aws:<namespace> ) to delete from the management cluster" )
105
124
deleteCmd .Flags ().StringSliceVarP (& deleteOpts .bootstrapProviders , "bootstrap" , "b" , nil ,
106
- "Bootstrap providers and versions (e.g. kubeadm:v1.1.5 ) to delete from the management cluster" )
125
+ "Bootstrap provider and namespace (e.g. kubeadm:<namespace> ) to delete from the management cluster" )
107
126
deleteCmd .Flags ().StringSliceVarP (& deleteOpts .controlPlaneProviders , "control-plane" , "c" , nil ,
108
- "ControlPlane providers and versions (e.g. kubeadm:v1.1.5 ) to delete from the management cluster" )
127
+ "ControlPlane provider and namespace (e.g. kubeadm:<namespace> ) to delete from the management cluster" )
109
128
deleteCmd .Flags ().StringSliceVar (& deleteOpts .ipamProviders , "ipam" , nil ,
110
- "IPAM providers and versions (e.g. infoblox:v0.0.1 ) to delete from the management cluster" )
129
+ "IPAM provider and namespace (e.g. infoblox:<namespace> ) to delete from the management cluster" )
111
130
// deleteCmd.Flags().StringSliceVar(&deleteOpts.runtimeExtensionProviders, "runtime-extension", nil,
112
131
// "Runtime extension providers and versions (e.g. test:v0.0.1) to delete from the management cluster")
113
132
deleteCmd .Flags ().StringSliceVar (& deleteOpts .addonProviders , "addon" , nil ,
114
- "Add-on providers and versions (e.g. helm:v0.1.0 ) to delete from the management cluster" )
133
+ "Add-on providers and versions (e.g. helm:<namespace> ) to delete from the management cluster" )
115
134
116
135
deleteCmd .Flags ().BoolVar (& deleteOpts .deleteAll , "all" , false ,
117
136
"Force deletion of all the providers" )
@@ -122,11 +141,13 @@ func init() {
122
141
func runDelete () error {
123
142
ctx := context .Background ()
124
143
125
- hasProviderNames := (deleteOpts .coreProvider != "" ) ||
144
+ ctrl .SetLogger (klogr .New ())
145
+
146
+ hasProviderNames := deleteOpts .coreProvider ||
126
147
(len (deleteOpts .bootstrapProviders ) > 0 ) ||
127
148
(len (deleteOpts .controlPlaneProviders ) > 0 ) ||
128
149
(len (deleteOpts .infrastructureProviders ) > 0 ) ||
129
- // (len(deleteOpts.ipamProviders) > 0) ||
150
+ (len (deleteOpts .ipamProviders ) > 0 ) ||
130
151
// (len(deleteOpts.runtimeExtensionProviders) > 0) ||
131
152
(len (deleteOpts .addonProviders ) > 0 )
132
153
@@ -138,9 +159,194 @@ func runDelete() error {
138
159
return errors .New ("At least one of --core, --bootstrap, --control-plane, --infrastructure, --ipam, --extension, --addon should be specified or the --all flag should be set" )
139
160
}
140
161
141
- return deleteProvider (ctx , deleteOpts )
162
+ if deleteOpts .kubeconfig == "" {
163
+ deleteOpts .kubeconfig = GetKubeconfigLocation ()
164
+ }
165
+
166
+ cl , err := CreateKubeClient (deleteOpts .kubeconfig , deleteOpts .kubeconfigContext )
167
+ if err != nil {
168
+ return fmt .Errorf ("unable to create client from kubeconfig flag %s with context %s: %w" , deleteOpts .kubeconfig , deleteOpts .kubeconfigContext , err )
169
+ }
170
+
171
+ group := & DeleteGroup {
172
+ selectors : []fields.Set {},
173
+ providers : []genericProviderList {},
174
+ }
175
+ errors := append ([]error {},
176
+ group .delete (& operatorv1.BootstrapProviderList {}, deleteOpts .bootstrapProviders ... ),
177
+ group .delete (& operatorv1.ControlPlaneProviderList {}, deleteOpts .controlPlaneProviders ... ),
178
+ group .delete (& operatorv1.InfrastructureProviderList {}, deleteOpts .infrastructureProviders ... ),
179
+ group .delete (& operatorv1.IPAMProviderList {}, deleteOpts .ipamProviders ... ),
180
+ group .delete (& operatorv1.AddonProviderList {}, deleteOpts .addonProviders ... ))
181
+
182
+ if deleteOpts .coreProvider {
183
+ errors = append (errors , group .delete (& operatorv1.CoreProviderList {}, []string {"" }... ))
184
+ }
185
+
186
+ if err := kerrors .NewAggregate (errors ); err != nil {
187
+ return err
188
+ }
189
+
190
+ if deleteOpts .deleteAll {
191
+ group .deleteAll ()
192
+ }
193
+
194
+ return group .execute (ctx , cl )
142
195
}
143
196
144
- func deleteProvider (ctx context.Context , opts * deleteOptions ) error {
145
- return errors .New ("Not implemented" )
197
+ type DeleteGroup struct {
198
+ selectors []fields.Set
199
+ providers []genericProviderList
200
+ }
201
+
202
+ func (d * DeleteGroup ) delete (providerType genericProviderList , names ... string ) error {
203
+ for _ , provider := range names {
204
+ selector , err := selectorFromProvider (provider )
205
+ if err != nil {
206
+ return fmt .Errorf ("invalid provider format: %w" , err )
207
+ }
208
+
209
+ d .providers = append (d .providers , providerType )
210
+ d .selectors = append (d .selectors , selector )
211
+ }
212
+
213
+ return nil
214
+ }
215
+
216
+ func (d * DeleteGroup ) deleteAll () {
217
+ for _ , list := range operatorv1 .ProviderLists {
218
+ providerList , ok := list .(genericProviderList )
219
+ if ! ok {
220
+ log .V (5 ).Info ("Expected to get GenericProviderList" )
221
+ continue
222
+ }
223
+
224
+ d .providers = append (d .providers , providerList )
225
+ d .selectors = append (d .selectors , fields.Set {})
226
+ }
227
+ }
228
+
229
+ func (d * DeleteGroup ) execute (ctx context.Context , cl ctrlclient.Client ) error {
230
+ opts := wait.Backoff {
231
+ Duration : 500 * time .Millisecond ,
232
+ Factor : 1.5 ,
233
+ Steps : 10 ,
234
+ Jitter : 0.4 ,
235
+ }
236
+
237
+ log .Info ("Waiting for CAPI Operator manifests to be removed..." )
238
+
239
+ if err := wait .ExponentialBackoff (opts , func () (bool , error ) {
240
+ ready := true
241
+ for i := range d .providers {
242
+ if done , err := deleteProviders (ctx , cl , d .providers [i ], ctrlclient.MatchingFieldsSelector {
243
+ Selector : fields .SelectorFromSet (d .selectors [i ]),
244
+ }); err != nil {
245
+ return false , err
246
+ } else {
247
+ ready = ready && done
248
+ }
249
+ }
250
+
251
+ return ready , nil
252
+ }); err != nil {
253
+ return fmt .Errorf ("cannot remove provider: %w" , err )
254
+ }
255
+
256
+ return nil
257
+ }
258
+
259
+ func selectorFromProvider (provider string ) (fields.Set , error ) {
260
+ var name , namespace string
261
+
262
+ parts := strings .Split (provider , ":" )
263
+ switch len (parts ) {
264
+ case 0 | 3 :
265
+ case 1 :
266
+ name = parts [0 ]
267
+ case 2 :
268
+ name , namespace = parts [0 ], parts [1 ]
269
+ default :
270
+ return nil , fmt .Errorf ("invalid provider format: %s" , provider )
271
+ }
272
+
273
+ selector := fields.Set {}
274
+
275
+ if name != "" {
276
+ selector ["metadata.name" ] = name
277
+ }
278
+
279
+ if namespace != "" {
280
+ selector ["metadata.namespace" ] = namespace
281
+ }
282
+
283
+ return selector , nil
284
+ }
285
+
286
+ func deleteProviders (ctx context.Context , client ctrlclient.Client , providerList genericProviderList , selector ctrlclient.MatchingFieldsSelector ) (bool , error ) {
287
+ //nolint:forcetypeassert
288
+ providerList = providerList .DeepCopyObject ().(genericProviderList )
289
+ ready := true
290
+
291
+ gvks , _ , err := scheme .ObjectKinds (providerList )
292
+ if err != nil {
293
+ log .Error (err , "Kind is not registered in provider list" )
294
+ return false , err
295
+ }
296
+
297
+ gvk := gvks [0 ]
298
+
299
+ if err := client .List (ctx , providerList , selector ); meta .IsNoMatchError (err ) || apierrors .IsNotFound (err ) {
300
+ return true , nil
301
+ } else if err != nil {
302
+ log .Error (err , fmt .Sprintf ("Unable to list providers to delete, %#v" , err ))
303
+ return false , err
304
+ }
305
+
306
+ for _ , provider := range providerList .GetItems () {
307
+ log .Info (fmt .Sprintf ("Deleting %s %s/%s" , provider .GetType (), provider .GetName (), provider .GetNamespace ()))
308
+
309
+ provider , ok := provider .(genericProvider )
310
+ if ! ok {
311
+ log .Info (fmt .Sprintf ("Expected to get GenericProvider for %s" , gvk ))
312
+ continue
313
+ }
314
+
315
+ if err := client .DeleteAllOf (ctx , provider , ctrlclient .InNamespace (provider .GetNamespace ())); err != nil {
316
+ return false , fmt .Errorf ("unable to issue delete for %s: %w" , gvk , err )
317
+ }
318
+
319
+ if deleteOpts .includeNamespace {
320
+ if strings .HasPrefix (provider .GetNamespace (), "kube-" ) || provider .GetNamespace () == "default" {
321
+ log .Info (fmt .Sprintf ("Skipping system namespace %s" , provider .GetNamespace ()))
322
+ continue
323
+ }
324
+
325
+ ns := & corev1.Namespace {ObjectMeta : metav1.ObjectMeta {Name : provider .GetNamespace ()}}
326
+ if err := client .Delete (ctx , ns ); ctrlclient .IgnoreNotFound (err ) != nil {
327
+ return false , fmt .Errorf ("unable to issue delete for Namespace %s: %w" , provider .GetNamespace (), err )
328
+ }
329
+ }
330
+ }
331
+
332
+ if len (providerList .GetItems ()) > 0 {
333
+ log .Info (fmt .Sprintf ("%d items remaning..." , len (providerList .GetItems ())))
334
+ return false , nil
335
+ }
336
+
337
+ if deleteOpts .includeCRDs && len (providerList .GetItems ()) == 0 {
338
+ log .Info ("Removing CRDs" )
339
+
340
+ group := gvk .GroupKind ()
341
+ group .Kind = strings .Replace (strings .ToLower (group .Kind ), "list" , "s" , 1 )
342
+ crd := & apiextensionsv1.CustomResourceDefinition {ObjectMeta : metav1.ObjectMeta {Name : group .String ()}}
343
+
344
+ if err := client .Delete (ctx , crd ); ctrlclient .IgnoreNotFound (err ) != nil {
345
+ return false , fmt .Errorf ("unable to issue delete for %s: %w" , group , err )
346
+ }
347
+ }
348
+
349
+ log .Info ("All requested providers are deleted" )
350
+
351
+ return ready , nil
146
352
}
0 commit comments