From bc5302ec93b280d2816522b8a4b9b41e0b24f965 Mon Sep 17 00:00:00 2001 From: kita456 Date: Sun, 8 Mar 2026 12:05:32 +0900 Subject: [PATCH] fix: kubectl drain --disable-eviction --dry-run=server hanging indefinitely --- staging/src/k8s.io/kubectl/pkg/drain/drain.go | 5 ++ .../k8s.io/kubectl/pkg/drain/drain_test.go | 65 +++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/staging/src/k8s.io/kubectl/pkg/drain/drain.go b/staging/src/k8s.io/kubectl/pkg/drain/drain.go index b280d20ba96..061972493bf 100644 --- a/staging/src/k8s.io/kubectl/pkg/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/drain/drain.go @@ -394,6 +394,11 @@ func (d *Helper) deletePods(pods []corev1.Pod, getPodFn func(namespace, name str d.OnPodDeletionOrEvictionStarted(&pod, false) } } + + if d.DryRunStrategy == cmdutil.DryRunServer { + return nil + } + ctx := d.getContext() params := waitForDeleteParams{ ctx: ctx, diff --git a/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go b/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go index 373241054b6..edb250ceb3e 100644 --- a/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go +++ b/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go @@ -17,6 +17,7 @@ limitations under the License. package drain import ( + "bytes" "context" "errors" "fmt" @@ -38,6 +39,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/fake" ktest "k8s.io/client-go/testing" + cmdutil "k8s.io/kubectl/pkg/cmd/util" ) func TestDeletePods(t *testing.T) { @@ -461,6 +463,69 @@ func TestDeleteOrEvict(t *testing.T) { } } +func TestDeleteOrEvictWithDryRunServer(t *testing.T) { + testCases := []struct { + description string + disableEviction bool + }{ + { + description: "Eviction enabled with server dry-run", + disableEviction: false, + }, + { + description: "Eviction disabled with server dry-run", + disableEviction: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + var buf bytes.Buffer + h := &Helper{ + Out: &buf, + GracePeriodSeconds: 10, + DisableEviction: tc.disableEviction, + DryRunStrategy: cmdutil.DryRunServer, + } + + var allPods []runtime.Object + var podsToDelete []corev1.Pod + + for i := 1; i <= 4; i++ { + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("mypod-%d", i), + Namespace: "default", + }, + } + allPods = append(allPods, &pod) + if i <= 2 { + podsToDelete = append(podsToDelete, pod) + } + } + + k := fake.NewSimpleClientset(allPods...) + + // fake clientset will actually delete objects from the in-memory store. + // This reactor intercepts delete requests with DryRun set and returns success without + // removing the object, simulating real API server dry-run behavior. + k.PrependReactor("delete", "pods", func(actions ktest.Action) (bool, runtime.Object, error) { + deleteAction := actions.(ktest.DeleteAction) + if len(deleteAction.GetDeleteOptions().DryRun) > 0 { + return true, nil, nil + } + return false, nil, nil + }) + addEvictionSupport(t, k, "v1") + h.Client = k + + if err := h.DeleteOrEvictPods(podsToDelete); err != nil { + t.Fatalf("error from DeleteOrEvictPods: %v", err) + } + }) + } +} + func mockFilterSkip(_ corev1.Pod) PodDeleteStatus { return MakePodDeleteStatusSkip() }