Skip to content

Commit a49f1a6

Browse files
committed
fix: need wait backup delete before delete cluster/pvc
1 parent 0da6300 commit a49f1a6

File tree

1 file changed

+122
-3
lines changed

1 file changed

+122
-3
lines changed

controllers/account/controllers/namespace_controller.go

Lines changed: 122 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package controllers
33
import (
44
"context"
55
"encoding/json"
6+
errors2 "errors"
67
"fmt"
78
"os"
89
"strconv"
910
"strings"
11+
"sync"
1012
"time"
1113

1214
"github.com/go-logr/logr"
@@ -26,9 +28,11 @@ import (
2628
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2729
"k8s.io/apimachinery/pkg/runtime"
2830
"k8s.io/apimachinery/pkg/runtime/schema"
31+
"k8s.io/apimachinery/pkg/util/wait"
2932
"k8s.io/apimachinery/pkg/watch"
3033
"k8s.io/client-go/dynamic"
3134
"k8s.io/client-go/rest"
35+
"k8s.io/client-go/util/retry"
3236
"k8s.io/utils/ptr"
3337
ctrl "sigs.k8s.io/controller-runtime"
3438
"sigs.k8s.io/controller-runtime/pkg/builder"
@@ -323,10 +327,14 @@ func (r *NamespaceReconciler) Reconcile(
323327
}
324328
}
325329
if t.newDebt != "" || t.newNetwork != "" {
326-
logger.Info(
330+
r.Log.Info(
327331
"update namespace anno ",
328-
"debt status",
332+
"old debt status",
333+
debtStatus,
334+
"new debt status",
329335
t.newDebt,
336+
"old network status",
337+
networkStatus,
330338
"network status",
331339
t.newNetwork,
332340
)
@@ -370,8 +378,12 @@ func (r *NamespaceReconciler) SuspendUserResource(ctx context.Context, namespace
370378
}
371379

372380
func (r *NamespaceReconciler) DeleteUserResource(_ context.Context, namespace string) error {
381+
err := deleteResource(r.dynamicClient, "backup", namespace)
382+
if err != nil {
383+
return err
384+
}
373385
deleteResources := []string{
374-
"backup", "cluster.apps.kubeblocks.io", "backupschedules", "devboxes", "devboxreleases", "cronjob",
386+
"cluster.apps.kubeblocks.io", "backupschedules", "devboxes", "devboxreleases", "cronjob",
375387
"objectstorageuser", "deploy", "sts", "pvc", "Service", "Ingress",
376388
"Issuer", "Certificate", "HorizontalPodAutoscaler", "instance",
377389
"job", "app",
@@ -2139,6 +2151,7 @@ func deleteResource(dynamicClient dynamic.Interface, resource, namespace string)
21392151
Version: "v1alpha1",
21402152
Resource: "backups",
21412153
}
2154+
return deleteResourceListAndWait(dynamicClient, gvr, namespace)
21422155
case "cluster.apps.kubeblocks.io":
21432156
gvr = schema.GroupVersionResource{
21442157
Group: "apps.kubeblocks.io",
@@ -2528,3 +2541,109 @@ func (r *NamespaceReconciler) resumeOrphanJob(ctx context.Context, namespace str
25282541
}
25292542
return nil
25302543
}
2544+
2545+
func deleteResourceListAndWait(
2546+
dynamicClient dynamic.Interface,
2547+
gvr schema.GroupVersionResource,
2548+
namespace string,
2549+
) error {
2550+
ctx := context.Background()
2551+
// 列出所有资源
2552+
list, err := dynamicClient.Resource(gvr).Namespace(namespace).List(ctx, v12.ListOptions{})
2553+
if err != nil {
2554+
return fmt.Errorf("failed to list %s in namespace %s: %w", gvr, namespace, err)
2555+
}
2556+
2557+
if len(list.Items) == 0 {
2558+
return nil // 无资源需要删除
2559+
}
2560+
2561+
// 并发删除:使用WaitGroup和error channel收集错误
2562+
var wg sync.WaitGroup
2563+
errCh := make(chan error, len(list.Items)) // 缓冲channel,避免阻塞
2564+
allErrors := []error{}
2565+
2566+
for _, item := range list.Items {
2567+
name := item.GetName()
2568+
wg.Add(1)
2569+
go func(resName string) {
2570+
defer wg.Done()
2571+
if deleteErr := deleteResourceAndWait(dynamicClient, gvr, namespace, resName); deleteErr != nil {
2572+
errCh <- fmt.Errorf("failed to delete %s/%s: %w", gvr, resName, deleteErr)
2573+
}
2574+
}(name)
2575+
}
2576+
2577+
// 等待所有Goroutine完成,并收集错误
2578+
go func() {
2579+
wg.Wait()
2580+
close(errCh)
2581+
}()
2582+
2583+
for deleteErr := range errCh {
2584+
allErrors = append(allErrors, deleteErr)
2585+
}
2586+
2587+
if len(allErrors) > 0 {
2588+
return fmt.Errorf("failed to delete some %s resources: %v", gvr, allErrors)
2589+
}
2590+
2591+
return nil
2592+
}
2593+
2594+
func deleteResourceAndWait(
2595+
dynamicClient dynamic.Interface,
2596+
gvr schema.GroupVersionResource,
2597+
namespace, name string,
2598+
) error {
2599+
ctx := context.Background()
2600+
deletePolicy := v12.DeletePropagationForeground // 前台删除,等待子资源
2601+
2602+
// 执行删除(针对单个资源)
2603+
err := dynamicClient.Resource(gvr).Namespace(namespace).Delete(ctx, name, v12.DeleteOptions{
2604+
PropagationPolicy: &deletePolicy,
2605+
})
2606+
if err != nil && !errors.IsNotFound(err) {
2607+
return fmt.Errorf("failed to delete %s/%s: %w", gvr, name, err)
2608+
}
2609+
if errors.IsNotFound(err) {
2610+
return nil // 已不存在,无需等待
2611+
}
2612+
2613+
// 等待删除完成:轮询Get直到NotFound
2614+
pollInterval := 5 * time.Second
2615+
timeout := 5 * time.Minute // 根据finalizer复杂度调整
2616+
err = wait.PollUntilContextTimeout(ctx, pollInterval, timeout, true,
2617+
func(ctx context.Context) (bool, error) {
2618+
// 使用retry.Backoff可选重试Get(处理临时错误)
2619+
dErr := retry.OnError(wait.Backoff{
2620+
Steps: 5,
2621+
Duration: 10 * time.Second,
2622+
Factor: 1.0,
2623+
Jitter: 0.1,
2624+
}, func(err error) bool {
2625+
return errors.IsServerTimeout(err) || errors.IsServiceUnavailable(err)
2626+
}, func() error {
2627+
_, getErr := dynamicClient.Resource(gvr).
2628+
Namespace(namespace).
2629+
Get(ctx, name, v12.GetOptions{})
2630+
if errors.IsNotFound(getErr) {
2631+
return nil // 成功:资源已删除
2632+
}
2633+
if getErr != nil {
2634+
// 其它错误:继续轮询
2635+
return getErr
2636+
}
2637+
// 资源仍存在:继续轮询
2638+
return errors2.New("resource still exists")
2639+
})
2640+
return dErr == nil, dErr
2641+
})
2642+
if err != nil {
2643+
if errors2.Is(ctx.Err(), context.DeadlineExceeded) {
2644+
return fmt.Errorf("timeout waiting for %s/%s to delete after %v", gvr, name, timeout)
2645+
}
2646+
return fmt.Errorf("error waiting for %s/%s to delete: %w", gvr, name, err)
2647+
}
2648+
return nil
2649+
}

0 commit comments

Comments
 (0)