Skip to content

Commit 872d874

Browse files
committed
fix
1 parent 83f990f commit 872d874

1 file changed

Lines changed: 47 additions & 14 deletions

File tree

test/e2e/bound_sa_token.go

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"reflect"
7+
"sort"
78
"strings"
89
"testing"
910
"time"
@@ -237,7 +238,10 @@ func testBoundTokenOperatorSecretDeletion(t testing.TB) {
237238
time.Sleep(kubeletTokenRefreshGracePeriod)
238239

239240
t.Log("bouncing remaining crash-looping pods to get fresh SA tokens")
240-
err = bounceCrashLoopingPodsWithRetry(ctx, t, kubeClient, 25*time.Minute)
241+
// Reduced from 25m to 20m to ensure we stay below monitor test thresholds.
242+
// With maxBouncesPerPod=50 and minTimeBetweenBounces=2m, max bounce time
243+
// is 100m, but most pods should recover much faster.
244+
err = bounceCrashLoopingPodsWithRetry(ctx, t, kubeClient, 20*time.Minute)
241245
require.NoError(t, err)
242246

243247
t.Log("waiting for all ClusterOperators to recover after key rotation rollouts")
@@ -267,7 +271,10 @@ func testBoundTokenOperatorSecretDeletion(t testing.TB) {
267271
time.Sleep(kubeletTokenRefreshGracePeriod)
268272

269273
t.Log("bouncing remaining crash-looping pods to get fresh SA tokens")
270-
err = bounceCrashLoopingPodsWithRetry(ctx, t, kubeClient, 25*time.Minute)
274+
// Reduced from 25m to 20m to ensure we stay below monitor test thresholds.
275+
// With maxBouncesPerPod=25 and minTimeBetweenBounces=2m, max bounce time
276+
// is 30m, but most pods should recover much faster.
277+
err = bounceCrashLoopingPodsWithRetry(ctx, t, kubeClient, 20*time.Minute)
271278
require.NoError(t, err)
272279

273280
t.Log("waiting for all ClusterOperators to stabilize after cleanup rollout")
@@ -352,13 +359,16 @@ func checkCertConfigMap(t testing.TB, kubeClient *clientcorev1.CoreV1Client, exp
352359
// - Bounces in waves by namespace to reduce cluster load
353360
// - Tracks recently bounced pods to avoid re-bouncing too quickly
354361
// - Prioritizes operator pods over infrastructure pods
362+
// - Limits max bounces per pod to prevent excessive restarts in monitor tests
355363
func bounceCrashLoopingPodsWithRetry(ctx context.Context, t testing.TB, kubeClient *clientcorev1.CoreV1Client, timeout time.Duration) error {
356-
const pollInterval = 45 * time.Second // Increased from 30s to give pods more time to recover
357-
const minTimeBetweenBounces = 90 * time.Second // Don't re-bounce the same pod within 90 seconds
364+
const pollInterval = 45 * time.Second // Increased from 30s to give pods more time to recover
365+
const minTimeBetweenBounces = 120 * time.Second // Don't re-bounce the same pod within 2 minutes
366+
const maxBouncesPerPod = 50 // Maximum bounces per pod (monitor threshold is 20 restarts, but we give extra headroom)
358367

359368
var lastUnhealthyCount int
360369
totalBounced := 0
361-
bouncedPods := make(map[string]time.Time) // Track when each pod was bounced
370+
bouncedPods := make(map[string]time.Time) // Track when each pod was last bounced
371+
bounceCount := make(map[string]int) // Track how many times each pod was bounced
362372

363373
t.Logf("bouncing crash-looping pods until healthy (timeout: %v)", timeout)
364374
err := wait.PollImmediate(pollInterval, timeout, func() (bool, error) {
@@ -387,7 +397,7 @@ func bounceCrashLoopingPodsWithRetry(ctx context.Context, t testing.TB, kubeClie
387397

388398
// First pass: bounce high-priority operator namespaces
389399
for _, nsName := range priorityNamespaces {
390-
bounced, unhealthy, err := bouncePodsInNamespace(ctx, t, kubeClient, nsName, bouncedPods, minTimeBetweenBounces)
400+
bounced, unhealthy, err := bouncePodsInNamespace(ctx, t, kubeClient, nsName, bouncedPods, bounceCount, minTimeBetweenBounces, maxBouncesPerPod)
391401
if err != nil {
392402
// Skip this namespace but continue - transient API errors shouldn't fail the whole operation
393403
continue
@@ -413,7 +423,7 @@ func bounceCrashLoopingPodsWithRetry(ctx context.Context, t testing.TB, kubeClie
413423
continue
414424
}
415425

416-
bounced, unhealthy, err := bouncePodsInNamespace(ctx, t, kubeClient, ns.Name, bouncedPods, minTimeBetweenBounces)
426+
bounced, unhealthy, err := bouncePodsInNamespace(ctx, t, kubeClient, ns.Name, bouncedPods, bounceCount, minTimeBetweenBounces, maxBouncesPerPod)
417427
if err != nil {
418428
// Skip this namespace but continue - transient API errors shouldn't fail the whole operation
419429
continue
@@ -445,7 +455,7 @@ func bounceCrashLoopingPodsWithRetry(ctx context.Context, t testing.TB, kubeClie
445455
// bouncePodsInNamespace bounces crash-looping pods in a single namespace.
446456
// Returns (number bounced, number unhealthy, error).
447457
// If an error is returned, the counts are unreliable and should not be used.
448-
func bouncePodsInNamespace(ctx context.Context, t testing.TB, kubeClient *clientcorev1.CoreV1Client, namespace string, bouncedPods map[string]time.Time, minTimeBetweenBounces time.Duration) (int, int, error) {
458+
func bouncePodsInNamespace(ctx context.Context, t testing.TB, kubeClient *clientcorev1.CoreV1Client, namespace string, bouncedPods map[string]time.Time, bounceCount map[string]int, minTimeBetweenBounces time.Duration, maxBouncesPerPod int) (int, int, error) {
449459
pods, err := kubeClient.Pods(namespace).List(ctx, metav1.ListOptions{})
450460
if err != nil {
451461
t.Logf("failed to list pods in %s: %v", namespace, err)
@@ -455,6 +465,10 @@ func bouncePodsInNamespace(ctx context.Context, t testing.TB, kubeClient *client
455465
bounced := 0
456466
unhealthy := 0
457467

468+
// Sort pods by restart count (highest first) to prioritize pods
469+
// closest to the monitor test failure threshold (20 restarts)
470+
sortPodsByRestartCount(pods.Items)
471+
458472
for _, pod := range pods.Items {
459473
if !isPodCrashLooping(pod) {
460474
continue
@@ -466,21 +480,29 @@ func bouncePodsInNamespace(ctx context.Context, t testing.TB, kubeClient *client
466480
continue
467481
}
468482

469-
// Check if we bounced this pod recently
483+
// Check if we've exceeded max bounces for this pod
470484
podKey := namespace + "/" + pod.Name
485+
if count := bounceCount[podKey]; count >= maxBouncesPerPod {
486+
t.Logf("skipping pod %s/%s: already bounced %d times (max: %d)",
487+
namespace, pod.Name, count, maxBouncesPerPod)
488+
continue
489+
}
490+
491+
// Check if we bounced this pod recently
471492
if lastBounceTime, exists := bouncedPods[podKey]; exists {
472493
if time.Since(lastBounceTime) < minTimeBetweenBounces {
473494
continue // Don't re-bounce too quickly
474495
}
475496
}
476497

477-
t.Logf("bouncing crash-looping pod %s/%s (restarts: %d)",
478-
namespace, pod.Name, getPodRestartCount(pod))
498+
t.Logf("bouncing crash-looping pod %s/%s (restarts: %d, bounce count: %d/%d)",
499+
namespace, pod.Name, getPodRestartCount(pod), bounceCount[podKey], maxBouncesPerPod)
479500

480501
if err := kubeClient.Pods(namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{}); err != nil && !apierrors.IsNotFound(err) {
481502
t.Logf("failed to delete pod %s/%s: %v", namespace, pod.Name, err)
482503
} else {
483504
bouncedPods[podKey] = time.Now()
505+
bounceCount[podKey]++
484506
bounced++
485507
}
486508
}
@@ -536,10 +558,12 @@ func isPodEligibleForBounce(pod corev1.Pod) bool {
536558
return false
537559
}
538560

539-
// Don't bounce pods with extremely high restart counts (>20)
540-
// These likely have a different issue than SA token expiry
561+
// Don't bounce pods with extremely high restart counts (>40)
562+
// These likely have a different issue than SA token expiry.
563+
// We use 40 (2x the monitor threshold of 20) to ensure we can still
564+
// bounce pods that are approaching the monitor failure threshold.
541565
restartCount := getPodRestartCount(pod)
542-
if restartCount > 20 {
566+
if restartCount > 40 {
543567
return false
544568
}
545569

@@ -555,6 +579,15 @@ func getPodRestartCount(pod corev1.Pod) int {
555579
return total
556580
}
557581

582+
// sortPodsByRestartCount sorts pods by total restart count in descending order
583+
// (highest restart count first). This prioritizes bouncing pods closest to
584+
// the monitor test failure threshold.
585+
func sortPodsByRestartCount(pods []corev1.Pod) {
586+
sort.Slice(pods, func(i, j int) bool {
587+
return getPodRestartCount(pods[i]) > getPodRestartCount(pods[j])
588+
})
589+
}
590+
558591
// waitForSATokenKeysOnAllNodes waits for the SA token signing key configmap
559592
// to exist and be stable. This configmap is used by kubelet to issue SA tokens
560593
// for application pods (it's not mounted in kube-apiserver itself).

0 commit comments

Comments
 (0)