|
| 1 | +/* |
| 2 | +Copyright (c) Microsoft Corporation. |
| 3 | +Licensed under the MIT license. |
| 4 | +*/ |
| 5 | + |
| 6 | +// Package main contains the CRD cleanup job for KubeFleet. |
| 7 | +// This job cleans up all CRDs that were installed by the CRD installer |
| 8 | +// when the Fleet agents are uninstalled via Helm pre-delete hook. |
| 9 | +package main |
| 10 | + |
| 11 | +import ( |
| 12 | + "context" |
| 13 | + "flag" |
| 14 | + "os" |
| 15 | + |
| 16 | + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" |
| 17 | + "k8s.io/apimachinery/pkg/labels" |
| 18 | + "k8s.io/apimachinery/pkg/runtime" |
| 19 | + "k8s.io/klog/v2" |
| 20 | + ctrl "sigs.k8s.io/controller-runtime" |
| 21 | + "sigs.k8s.io/controller-runtime/pkg/client" |
| 22 | + |
| 23 | + "go.goms.io/fleet/cmd/crdinstaller/utils" |
| 24 | +) |
| 25 | + |
| 26 | +var mode = flag.String("mode", "", "Mode to run in: 'hub' or 'member' (required)") |
| 27 | + |
| 28 | +func main() { |
| 29 | + klog.InitFlags(nil) |
| 30 | + flag.Parse() |
| 31 | + |
| 32 | + // Validate required flags. |
| 33 | + if *mode != "hub" && *mode != "member" { |
| 34 | + klog.Fatal("--mode flag must be either 'hub' or 'member'") |
| 35 | + } |
| 36 | + |
| 37 | + klog.Infof("Starting CRD cleanup job in %s mode", *mode) |
| 38 | + |
| 39 | + // Print all flags for debugging. |
| 40 | + flag.VisitAll(func(f *flag.Flag) { |
| 41 | + klog.V(2).InfoS("flag:", "name", f.Name, "value", f.Value) |
| 42 | + }) |
| 43 | + |
| 44 | + // Get Kubernetes config using controller-runtime. |
| 45 | + config := ctrl.GetConfigOrDie() |
| 46 | + |
| 47 | + // Create a scheme that knows about CRD types. |
| 48 | + scheme := runtime.NewScheme() |
| 49 | + if err := apiextensionsv1.AddToScheme(scheme); err != nil { |
| 50 | + klog.Fatalf("Failed to add apiextensions scheme: %v", err) |
| 51 | + } |
| 52 | + |
| 53 | + k8sClient, err := client.New(config, client.Options{ |
| 54 | + Scheme: scheme, |
| 55 | + }) |
| 56 | + if err != nil { |
| 57 | + klog.Fatalf("Failed to create Kubernetes client: %v", err) |
| 58 | + } |
| 59 | + |
| 60 | + // Create context for cleanup operations. |
| 61 | + ctx := context.Background() |
| 62 | + |
| 63 | + // Perform cleanup. |
| 64 | + if err := cleanupCRDs(ctx, k8sClient, *mode); err != nil { |
| 65 | + klog.Errorf("Failed to cleanup CRDs: %v", err) |
| 66 | + os.Exit(1) |
| 67 | + } |
| 68 | + |
| 69 | + klog.Info("CRD cleanup completed successfully") |
| 70 | +} |
| 71 | + |
| 72 | +// cleanupCRDs deletes all CRDs that were installed by the CRD installer for the given mode. |
| 73 | +// It uses the mode label to identify which CRDs to delete. |
| 74 | +func cleanupCRDs(ctx context.Context, k8sClient client.Client, mode string) error { |
| 75 | + // List all CRDs with both the managed label and the matching mode label. |
| 76 | + crdList := &apiextensionsv1.CustomResourceDefinitionList{} |
| 77 | + labelSelector := labels.SelectorFromSet(labels.Set{ |
| 78 | + utils.CRDInstallerLabelKey: "true", |
| 79 | + utils.CRDInstallerModeLabel: mode, |
| 80 | + }) |
| 81 | + |
| 82 | + if err := k8sClient.List(ctx, crdList, &client.ListOptions{ |
| 83 | + LabelSelector: labelSelector, |
| 84 | + }); err != nil { |
| 85 | + return err |
| 86 | + } |
| 87 | + |
| 88 | + klog.Infof("Found %d CRDs to cleanup for mode %s", len(crdList.Items), mode) |
| 89 | + |
| 90 | + // Delete all matching CRDs. |
| 91 | + var deletedCount int |
| 92 | + for i := range crdList.Items { |
| 93 | + crd := &crdList.Items[i] |
| 94 | + |
| 95 | + klog.Infof("Deleting CRD: %s", crd.Name) |
| 96 | + if err := k8sClient.Delete(ctx, crd); err != nil { |
| 97 | + klog.Errorf("Failed to delete CRD %s: %v", crd.Name, err) |
| 98 | + // Continue with other CRDs even if one fails. |
| 99 | + continue |
| 100 | + } |
| 101 | + deletedCount++ |
| 102 | + klog.Infof("Successfully deleted CRD: %s", crd.Name) |
| 103 | + } |
| 104 | + |
| 105 | + klog.Infof("Cleanup complete: deleted %d CRDs", deletedCount) |
| 106 | + return nil |
| 107 | +} |
0 commit comments