anycast: drop pods from nexthop set on DeletionTimestamp
Build flock Image / build (push) Has been cancelled
Build flock Image / build (push) Has been cancelled
Previously the AnycastReconciler kept a pod in the nexthop set as long as its PodReady condition was True. During a rolling restart that produces a window after kubelet has accepted SIGTERM (DeletionTimestamp set, pod still Ready until probes observe shutdown) where BGP still advertises a path through the dying pod's veth — in-flight requests get RST'd when the container actually exits. Fix: introduce podAnycastEligible(pod) = !DeletionTimestamp && Ready, swap it in at the AnycastReconciler's isReady callback, and fire the ready-change callback when DeletionTimestamp transitions (the informer UpdateFunc previously only fired on Ready transitions). Result: as soon as the apiserver marks a pod for deletion, the reconciler withdraws the local nexthop and BIRD reannounces the route without it. Sibling replicas absorb traffic before the pod's terminationGracePeriod elapses. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func readyPod(deletionTimestamp *metav1.Time) *corev1.Pod {
|
||||
return &corev1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{DeletionTimestamp: deletionTimestamp},
|
||||
Status: corev1.PodStatus{
|
||||
Conditions: []corev1.PodCondition{
|
||||
{Type: corev1.PodReady, Status: corev1.ConditionTrue},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestPodAnycastEligible(t *testing.T) {
|
||||
now := metav1.Now()
|
||||
cases := []struct {
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
want bool
|
||||
}{
|
||||
{"ready, not deleting", readyPod(nil), true},
|
||||
{"ready, but deleting", readyPod(&now), false},
|
||||
{
|
||||
"not ready, not deleting",
|
||||
&corev1.Pod{Status: corev1.PodStatus{Conditions: []corev1.PodCondition{
|
||||
{Type: corev1.PodReady, Status: corev1.ConditionFalse},
|
||||
}}},
|
||||
false,
|
||||
},
|
||||
{"no conditions, not deleting", &corev1.Pod{}, false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
if got := podAnycastEligible(c.pod); got != c.want {
|
||||
t.Fatalf("got %v want %v", got, c.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user