diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go index f88fd1a1f71..3c719517934 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/drain/drain.go @@ -192,6 +192,7 @@ func NewCmdDrain(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr cmd.Flags().DurationVar(&o.drainer.Timeout, "timeout", o.drainer.Timeout, "The length of time to wait before giving up, zero means infinite") cmd.Flags().StringVarP(&o.drainer.Selector, "selector", "l", o.drainer.Selector, "Selector (label query) to filter on") cmd.Flags().StringVarP(&o.drainer.PodSelector, "pod-selector", "", o.drainer.PodSelector, "Label selector to filter pods on the node") + cmd.Flags().BoolVar(&o.drainer.DisableEviction, "disable-eviction", o.drainer.DisableEviction, "Force drain to use delete, even if eviction is supported. This will bypass checking PodDisruptionBudgets, use with caution.") cmdutil.AddDryRunFlag(cmd) return cmd diff --git a/staging/src/k8s.io/kubectl/pkg/drain/drain.go b/staging/src/k8s.io/kubectl/pkg/drain/drain.go index c922c6aa5d7..1763770ba3e 100644 --- a/staging/src/k8s.io/kubectl/pkg/drain/drain.go +++ b/staging/src/k8s.io/kubectl/pkg/drain/drain.go @@ -52,8 +52,12 @@ type Helper struct { DeleteLocalData bool Selector string PodSelector string - Out io.Writer - ErrOut io.Writer + + // DisableEviction forces drain to use delete rather than evict + DisableEviction bool + + Out io.Writer + ErrOut io.Writer // TODO(justinsb): unnecessary? DryRun bool @@ -179,17 +183,20 @@ func (d *Helper) DeleteOrEvictPods(pods []corev1.Pod) error { return nil } - policyGroupVersion, err := CheckEvictionSupport(d.Client) - if err != nil { - return err - } - // TODO(justinsb): unnecessary? getPodFn := func(namespace, name string) (*corev1.Pod, error) { return d.Client.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{}) } - if len(policyGroupVersion) > 0 { - return d.evictPods(pods, policyGroupVersion, getPodFn) + + if !d.DisableEviction { + policyGroupVersion, err := CheckEvictionSupport(d.Client) + if err != nil { + return err + } + + if len(policyGroupVersion) > 0 { + return d.evictPods(pods, policyGroupVersion, getPodFn) + } } return d.deletePods(pods, getPodFn) 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 bdbf8d43237..75e86342a8d 100644 --- a/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go +++ b/staging/src/k8s.io/kubectl/pkg/drain/drain_test.go @@ -245,90 +245,114 @@ func TestCheckEvictionSupport(t *testing.T) { } func TestDeleteOrEvict(t *testing.T) { - for _, evictionSupported := range []bool{true, false} { - evictionSupported := evictionSupported - t.Run(fmt.Sprintf("evictionSupported=%v", evictionSupported), - func(t *testing.T) { - h := &Helper{ - Out: os.Stdout, - GracePeriodSeconds: 10, - } + tests := []struct { + description string + evictionSupported bool + disableEviction bool + }{ + { + description: "eviction supported/enabled", + evictionSupported: true, + disableEviction: false, + }, + { + description: "eviction unsupported/disabled", + evictionSupported: false, + disableEviction: false, + }, + { + description: "eviction supported/disabled", + evictionSupported: true, + disableEviction: true, + }, + { + description: "eviction unsupported/disabled", + evictionSupported: false, + disableEviction: false, + }, + } + for _, tc := range tests { + t.Run(tc.description, func(t *testing.T) { + h := &Helper{ + Out: os.Stdout, + GracePeriodSeconds: 10, + } - // Create 4 pods, and try to remove the first 2 - var expectedEvictions []policyv1beta1.Eviction - var create []runtime.Object - deletePods := []corev1.Pod{} - for i := 1; i <= 4; i++ { - pod := &corev1.Pod{} - pod.Name = fmt.Sprintf("mypod-%d", i) - pod.Namespace = "default" + // Create 4 pods, and try to remove the first 2 + var expectedEvictions []policyv1beta1.Eviction + var create []runtime.Object + deletePods := []corev1.Pod{} + for i := 1; i <= 4; i++ { + pod := &corev1.Pod{} + pod.Name = fmt.Sprintf("mypod-%d", i) + pod.Namespace = "default" - create = append(create, pod) - if i <= 2 { - deletePods = append(deletePods, *pod) + create = append(create, pod) + if i <= 2 { + deletePods = append(deletePods, *pod) - if evictionSupported { - eviction := policyv1beta1.Eviction{} - eviction.Kind = "Eviction" - eviction.APIVersion = "policy/v1" - eviction.Namespace = pod.Namespace - eviction.Name = pod.Name + if tc.evictionSupported && !tc.disableEviction { + eviction := policyv1beta1.Eviction{} + eviction.Kind = "Eviction" + eviction.APIVersion = "policy/v1" + eviction.Namespace = pod.Namespace + eviction.Name = pod.Name - gracePeriodSeconds := int64(h.GracePeriodSeconds) - eviction.DeleteOptions = &metav1.DeleteOptions{ - GracePeriodSeconds: &gracePeriodSeconds, - } - - expectedEvictions = append(expectedEvictions, eviction) + gracePeriodSeconds := int64(h.GracePeriodSeconds) + eviction.DeleteOptions = &metav1.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, } + + expectedEvictions = append(expectedEvictions, eviction) } } + } - // Build the fake client - k := fake.NewSimpleClientset(create...) - if evictionSupported { - addEvictionSupport(t, k) - } - h.Client = k + // Build the fake client + k := fake.NewSimpleClientset(create...) + if tc.evictionSupported { + addEvictionSupport(t, k) + } + h.Client = k + h.DisableEviction = tc.disableEviction + // Do the eviction + if err := h.DeleteOrEvictPods(deletePods); err != nil { + t.Fatalf("error from DeleteOrEvictPods: %v", err) + } - // Do the eviction - if err := h.DeleteOrEvictPods(deletePods); err != nil { - t.Fatalf("error from DeleteOrEvictPods: %v", err) + // Test that other pods are still there + var remainingPods []string + { + podList, err := k.CoreV1().Pods("").List(metav1.ListOptions{}) + if err != nil { + t.Fatalf("error listing pods: %v", err) } - // Test that other pods are still there - var remainingPods []string - { - podList, err := k.CoreV1().Pods("").List(metav1.ListOptions{}) - if err != nil { - t.Fatalf("error listing pods: %v", err) - } + for _, pod := range podList.Items { + remainingPods = append(remainingPods, pod.Namespace+"/"+pod.Name) + } + sort.Strings(remainingPods) + } + expected := []string{"default/mypod-3", "default/mypod-4"} + if !reflect.DeepEqual(remainingPods, expected) { + t.Errorf("%s: unexpected remaining pods after DeleteOrEvictPods; actual %v; expected %v", tc.description, remainingPods, expected) + } - for _, pod := range podList.Items { - remainingPods = append(remainingPods, pod.Namespace+"/"+pod.Name) - } - sort.Strings(remainingPods) - } - expected := []string{"default/mypod-3", "default/mypod-4"} - if !reflect.DeepEqual(remainingPods, expected) { - t.Errorf("unexpected remaining pods after DeleteOrEvictPods; actual %v; expected %v", remainingPods, expected) - } - - // Test that pods were evicted as expected - var actualEvictions []policyv1beta1.Eviction - for _, action := range k.Actions() { - if action.GetVerb() != "create" || action.GetResource().Resource != "pods" || action.GetSubresource() != "eviction" { - continue - } - eviction := *action.(ktest.CreateAction).GetObject().(*policyv1beta1.Eviction) - actualEvictions = append(actualEvictions, eviction) - } - sort.Slice(actualEvictions, func(i, j int) bool { - return actualEvictions[i].Name < actualEvictions[j].Name - }) - if !reflect.DeepEqual(actualEvictions, expectedEvictions) { - t.Errorf("unexpected evictions; actual %v; expected %v", actualEvictions, expectedEvictions) + // Test that pods were evicted as expected + var actualEvictions []policyv1beta1.Eviction + for _, action := range k.Actions() { + if action.GetVerb() != "create" || action.GetResource().Resource != "pods" || action.GetSubresource() != "eviction" { + continue } + eviction := *action.(ktest.CreateAction).GetObject().(*policyv1beta1.Eviction) + actualEvictions = append(actualEvictions, eviction) + } + sort.Slice(actualEvictions, func(i, j int) bool { + return actualEvictions[i].Name < actualEvictions[j].Name }) + if !reflect.DeepEqual(actualEvictions, expectedEvictions) { + t.Errorf("%s: unexpected evictions; actual %v; expected %v", tc.description, actualEvictions, expectedEvictions) + } + }) } }