diff --git a/go.mod b/go.mod index 945c5699c..9dbc57a15 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( golang.org/x/sys v0.43.0 golang.org/x/text v0.36.0 gopkg.in/evanphx/json-patch.v4 v4.13.0 - k8s.io/api v0.0.0-20260504204501-f40e8ff20019 + k8s.io/api v0.0.0-20260505124447-9279b4068be3 k8s.io/apimachinery v0.0.0-20260504204121-2fca5de43c56 k8s.io/cli-runtime v0.0.0-20260504213337-8858f6b36386 k8s.io/client-go v0.0.0-20260504205003-535f2d0806f7 diff --git a/go.sum b/go.sum index b356b1421..07b2dc8a4 100644 --- a/go.sum +++ b/go.sum @@ -205,8 +205,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.0.0-20260504204501-f40e8ff20019 h1:ldAUH3G96shgtYJbZwndQTKhRAMZ2dDrkC7Pw0u15uM= -k8s.io/api v0.0.0-20260504204501-f40e8ff20019/go.mod h1:3USlyQsXhfXGbJzZevPnCx4ZCGaYM/xzUYOlYcB/eUE= +k8s.io/api v0.0.0-20260505124447-9279b4068be3 h1:DhwY3OTw7UCwd88KVVm2jbnPFLTRy27ZvMSI2MBPGFk= +k8s.io/api v0.0.0-20260505124447-9279b4068be3/go.mod h1:3USlyQsXhfXGbJzZevPnCx4ZCGaYM/xzUYOlYcB/eUE= k8s.io/apimachinery v0.0.0-20260504204121-2fca5de43c56 h1:JN0n7OEc9lF1Rnk+09tWPA9FC2v7+wxxR+3lgBo4uNU= k8s.io/apimachinery v0.0.0-20260504204121-2fca5de43c56/go.mod h1:VERTuh5iDSri6+w9SKXTmWbqrGWrdBWrNaC/gYIcRTY= k8s.io/cli-runtime v0.0.0-20260504213337-8858f6b36386 h1:71wIHWEqonNQNCV4fZUZfNy85N1fvFW7jw2Xc7DVk1A= diff --git a/pkg/drain/drain.go b/pkg/drain/drain.go index b280d20ba..9f2e36b37 100644 --- a/pkg/drain/drain.go +++ b/pkg/drain/drain.go @@ -390,10 +390,18 @@ func (d *Helper) deletePods(pods []corev1.Pod, getPodFn func(namespace, name str if err != nil && !apierrors.IsNotFound(err) { return err } + if d.DryRunStrategy == cmdutil.DryRunServer { + continue + } if d.OnPodDeletionOrEvictionStarted != nil { d.OnPodDeletionOrEvictionStarted(&pod, false) } } + + if d.DryRunStrategy == cmdutil.DryRunServer { + return nil + } + ctx := d.getContext() params := waitForDeleteParams{ ctx: ctx, diff --git a/pkg/drain/drain_test.go b/pkg/drain/drain_test.go index 60fabb9f0..ac3e2cb42 100644 --- a/pkg/drain/drain_test.go +++ b/pkg/drain/drain_test.go @@ -17,12 +17,14 @@ limitations under the License. package drain import ( + "bytes" "context" "errors" "fmt" "math" "os" "reflect" + "slices" "sort" "strconv" "testing" @@ -38,6 +40,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 +464,66 @@ 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 <= 2; i++ { + pod := corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("mypod-%d", i), + Namespace: "default", + }, + } + allPods = append(allPods, &pod) + podsToDelete = append(podsToDelete, pod) + } + + k := fake.NewSimpleClientset(allPods...) + + // 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 slices.Contains(deleteAction.GetDeleteOptions().DryRun, metav1.DryRunAll) { + 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() }