Skip to content

Commit 5de2bf6

Browse files
Merge pull request #125 from numtide/fix/native-gc-migration-and-coverage
fix(gc): replace manual finalizers with native k8s cascading deletion
2 parents 7227200 + 5a076dc commit 5de2bf6

File tree

8 files changed

+176
-859
lines changed

8 files changed

+176
-859
lines changed

pkg/cluster-handler/controller/multigrescluster/integration_test.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package multigrescluster_test
55

66
import (
77
"path/filepath"
8-
"slices"
98
"testing"
109
"time"
1110

@@ -671,14 +670,6 @@ func TestMultigresCluster_HappyPath(t *testing.T) {
671670
t.Errorf("Resources mismatch:\n%v", err)
672671
}
673672

674-
// Check Finalizer
675-
fetchedCluster := &multigresv1alpha1.MultigresCluster{}
676-
if err := k8sClient.Get(t.Context(), client.ObjectKeyFromObject(tc.cluster), fetchedCluster); err != nil {
677-
t.Fatalf("Failed to get cluster: %v", err)
678-
}
679-
if !slices.Contains(fetchedCluster.Finalizers, "multigres.com/finalizer") {
680-
t.Errorf("Expected finalizer 'multigres.com/finalizer' to be present, got %v", fetchedCluster.Finalizers)
681-
}
682673
})
683674
}
684675
}

pkg/cluster-handler/controller/multigrescluster/multigrescluster_controller.go

Lines changed: 5 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,12 @@ import (
1212
ctrl "sigs.k8s.io/controller-runtime"
1313
"sigs.k8s.io/controller-runtime/pkg/client"
1414
"sigs.k8s.io/controller-runtime/pkg/controller"
15-
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1615
"sigs.k8s.io/controller-runtime/pkg/log"
1716

1817
multigresv1alpha1 "github.com/numtide/multigres-operator/api/v1alpha1"
1918
"github.com/numtide/multigres-operator/pkg/resolver"
2019
)
2120

22-
const (
23-
finalizerName = "multigres.com/finalizer"
24-
)
25-
2621
// MultigresClusterReconciler reconciles a MultigresCluster object.
2722
type MultigresClusterReconciler struct {
2823
client.Client
@@ -54,18 +49,6 @@ func (r *MultigresClusterReconciler) Reconcile(
5449
return ctrl.Result{}, fmt.Errorf("failed to get MultigresCluster: %w", err)
5550
}
5651

57-
if !cluster.DeletionTimestamp.IsZero() {
58-
return r.handleDelete(ctx, cluster)
59-
}
60-
61-
if !controllerutil.ContainsFinalizer(cluster, finalizerName) {
62-
controllerutil.AddFinalizer(cluster, finalizerName)
63-
if err := r.Update(ctx, cluster); err != nil {
64-
return ctrl.Result{}, fmt.Errorf("failed to add finalizer: %w", err)
65-
}
66-
return ctrl.Result{}, nil
67-
}
68-
6952
res := resolver.NewResolver(r.Client, cluster.Namespace, cluster.Spec.TemplateDefaults)
7053

7154
// Apply defaults (in-memory) to ensure we have images/configs/system-catalog even if webhook didn't run.
@@ -74,6 +57,11 @@ func (r *MultigresClusterReconciler) Reconcile(
7457
return ctrl.Result{}, err
7558
}
7659

60+
// If being deleted, let Kubernetes GC handle cleanup
61+
if !cluster.DeletionTimestamp.IsZero() {
62+
return ctrl.Result{}, nil
63+
}
64+
7765
if err := r.reconcileGlobalComponents(ctx, cluster, res); err != nil {
7866
l.Error(err, "Failed to reconcile global components")
7967
return ctrl.Result{}, err
@@ -97,100 +85,6 @@ func (r *MultigresClusterReconciler) Reconcile(
9785
return ctrl.Result{RequeueAfter: 1 * time.Minute}, nil
9886
}
9987

100-
func (r *MultigresClusterReconciler) handleDelete(
101-
ctx context.Context,
102-
cluster *multigresv1alpha1.MultigresCluster,
103-
) (ctrl.Result, error) {
104-
if controllerutil.ContainsFinalizer(cluster, finalizerName) {
105-
if err := r.checkChildrenDeleted(ctx, cluster); err != nil {
106-
// If we are waiting for children, emit an event so the user knows why it's stuck in Terminating
107-
r.Recorder.Event(
108-
cluster,
109-
"Normal",
110-
"Cleanup",
111-
"Waiting for child resources (Cells/TableGroups) to be deleted",
112-
)
113-
return ctrl.Result{}, err
114-
}
115-
controllerutil.RemoveFinalizer(cluster, finalizerName)
116-
if err := r.Update(ctx, cluster); err != nil {
117-
return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err)
118-
}
119-
}
120-
return ctrl.Result{}, nil
121-
}
122-
123-
func (r *MultigresClusterReconciler) checkChildrenDeleted(
124-
ctx context.Context,
125-
cluster *multigresv1alpha1.MultigresCluster,
126-
) error {
127-
childrenStillExist := false
128-
129-
// Helper to delete a list of objects
130-
deleteObjects := func(objects []client.Object) error {
131-
for _, obj := range objects {
132-
if obj.GetDeletionTimestamp().IsZero() {
133-
if err := r.Delete(ctx, obj); err != nil {
134-
if !errors.IsNotFound(err) {
135-
return fmt.Errorf("failed to delete child %s: %w", obj.GetName(), err)
136-
}
137-
}
138-
}
139-
}
140-
if len(objects) > 0 {
141-
childrenStillExist = true
142-
}
143-
return nil
144-
}
145-
146-
// 1. Check Cells
147-
cells := &multigresv1alpha1.CellList{}
148-
if err := r.List(ctx, cells, client.InNamespace(cluster.Namespace), client.MatchingLabels{"multigres.com/cluster": cluster.Name}); err != nil {
149-
return fmt.Errorf("failed to list cells: %w", err)
150-
}
151-
cellObjs := make([]client.Object, len(cells.Items))
152-
for i := range cells.Items {
153-
cellObjs[i] = &cells.Items[i]
154-
}
155-
if err := deleteObjects(cellObjs); err != nil {
156-
return err
157-
}
158-
159-
// 2. Check TableGroups
160-
tgs := &multigresv1alpha1.TableGroupList{}
161-
if err := r.List(ctx, tgs, client.InNamespace(cluster.Namespace), client.MatchingLabels{"multigres.com/cluster": cluster.Name}); err != nil {
162-
return fmt.Errorf("failed to list tablegroups: %w", err)
163-
}
164-
tgObjs := make([]client.Object, len(tgs.Items))
165-
for i := range tgs.Items {
166-
tgObjs[i] = &tgs.Items[i]
167-
}
168-
if err := deleteObjects(tgObjs); err != nil {
169-
return err
170-
}
171-
172-
// 3. Check TopoServers
173-
ts := &multigresv1alpha1.TopoServerList{}
174-
if err := r.List(ctx, ts, client.InNamespace(cluster.Namespace), client.MatchingLabels{"multigres.com/cluster": cluster.Name}); err != nil {
175-
return fmt.Errorf("failed to list toposervers: %w", err)
176-
}
177-
tsObjs := make([]client.Object, len(ts.Items))
178-
for i := range ts.Items {
179-
tsObjs[i] = &ts.Items[i]
180-
}
181-
if err := deleteObjects(tsObjs); err != nil {
182-
return err
183-
}
184-
185-
if childrenStillExist {
186-
return fmt.Errorf(
187-
"waiting for children to be deleted (cells, tablegroups, or toposervers still exist)",
188-
)
189-
}
190-
191-
return nil
192-
}
193-
19488
// SetupWithManager sets up the controller with the Manager.
19589
func (r *MultigresClusterReconciler) SetupWithManager(
19690
mgr ctrl.Manager,

0 commit comments

Comments
 (0)