From 6844eef4ca0e0705a35e9116a21f657a4a785d0e Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sun, 5 Apr 2020 10:02:06 -0700 Subject: [PATCH] Switch to the k/k implementation of drain.Helper --- hack/.packages | 1 - pkg/drain/BUILD.bazel | 30 --- pkg/drain/README.md | 7 - pkg/drain/cordon.go | 94 --------- pkg/drain/default.go | 69 ------- pkg/drain/drain.go | 292 --------------------------- pkg/drain/filters.go | 223 -------------------- pkg/instancegroups/instancegroups.go | 2 +- 8 files changed, 1 insertion(+), 717 deletions(-) delete mode 100644 pkg/drain/BUILD.bazel delete mode 100644 pkg/drain/README.md delete mode 100644 pkg/drain/cordon.go delete mode 100644 pkg/drain/default.go delete mode 100644 pkg/drain/drain.go delete mode 100644 pkg/drain/filters.go diff --git a/hack/.packages b/hack/.packages index 93a41890c1..641524423c 100644 --- a/hack/.packages +++ b/hack/.packages @@ -80,7 +80,6 @@ k8s.io/kops/pkg/commands k8s.io/kops/pkg/configbuilder k8s.io/kops/pkg/diff k8s.io/kops/pkg/dns -k8s.io/kops/pkg/drain k8s.io/kops/pkg/edit k8s.io/kops/pkg/featureflag k8s.io/kops/pkg/flagbuilder diff --git a/pkg/drain/BUILD.bazel b/pkg/drain/BUILD.bazel deleted file mode 100644 index 273508b34c..0000000000 --- a/pkg/drain/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "cordon.go", - "default.go", - "drain.go", - "filters.go", - ], - importpath = "k8s.io/kops/pkg/drain", - visibility = ["//visibility:public"], - deps = [ - "//vendor/k8s.io/api/apps/v1:go_default_library", - "//vendor/k8s.io/api/core/v1:go_default_library", - "//vendor/k8s.io/api/policy/v1beta1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//vendor/k8s.io/client-go/kubernetes:go_default_library", - ], -) diff --git a/pkg/drain/README.md b/pkg/drain/README.md deleted file mode 100644 index 5110dd03ce..0000000000 --- a/pkg/drain/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Drain code - -This is the drain code copied from k8s.io/kubernetes, after the extraction in -https://github.com/kubernetes/kubernetes/pull/80045/files - -Once we are on that version of k/k (1.16), we can replace with the upstream -version. diff --git a/pkg/drain/cordon.go b/pkg/drain/cordon.go deleted file mode 100644 index 63d7502aa2..0000000000 --- a/pkg/drain/cordon.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package drain - -import ( - "fmt" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/json" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/client-go/kubernetes" -) - -// CordonHelper wraps functionality to cordon/uncordon nodes -type CordonHelper struct { - node *corev1.Node - desired bool -} - -// NewCordonHelper returns a new CordonHelper -func NewCordonHelper(node *corev1.Node) *CordonHelper { - return &CordonHelper{ - node: node, - } -} - -// NewCordonHelperFromRuntimeObject returns a new CordonHelper, or an error if given object is not a -// node or cannot be encoded as JSON -func NewCordonHelperFromRuntimeObject(nodeObject runtime.Object, scheme *runtime.Scheme, gvk schema.GroupVersionKind) (*CordonHelper, error) { - nodeObject, err := scheme.ConvertToVersion(nodeObject, gvk.GroupVersion()) - if err != nil { - return nil, err - } - - node, ok := nodeObject.(*corev1.Node) - if !ok { - return nil, fmt.Errorf("unexpected type %T", nodeObject) - } - - return NewCordonHelper(node), nil -} - -// UpdateIfRequired returns true if c.node.Spec.Unschedulable isn't already set, -// or false when no change is needed -func (c *CordonHelper) UpdateIfRequired(desired bool) bool { - c.desired = desired - return c.node.Spec.Unschedulable != c.desired -} - -// PatchOrReplace uses given clientset to update the node status, either by patching or -// updating the given node object; it may return error if the object cannot be encoded as -// JSON, or if either patch or update calls fail; it will also return a second error -// whenever creating a patch has failed -func (c *CordonHelper) PatchOrReplace(clientset kubernetes.Interface) (error, error) { - client := clientset.CoreV1().Nodes() - - oldData, err := json.Marshal(c.node) - if err != nil { - return err, nil - } - - c.node.Spec.Unschedulable = c.desired - - newData, err := json.Marshal(c.node) - if err != nil { - return err, nil - } - - patchBytes, patchErr := strategicpatch.CreateTwoWayMergePatch(oldData, newData, c.node) - if patchErr == nil { - _, err = client.Patch(c.node.Name, types.StrategicMergePatchType, patchBytes) - } else { - _, err = client.Update(c.node) - } - return err, patchErr -} diff --git a/pkg/drain/default.go b/pkg/drain/default.go deleted file mode 100644 index ec0351b0ff..0000000000 --- a/pkg/drain/default.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package drain - -import ( - "fmt" - - corev1 "k8s.io/api/core/v1" - utilerrors "k8s.io/apimachinery/pkg/util/errors" -) - -// This file contains default implementations of how to -// drain/cordon/uncordon nodes. These functions may be called -// directly, or their functionality copied into your own code, for -// example if you want different output behaviour. - -// RunNodeDrain shows the canonical way to drain a node. -// You should first cordon the node, e.g. using RunCordonOrUncordon -func RunNodeDrain(drainer *Helper, nodeName string) error { - // TODO(justinsb): Ensure we have adequate e2e coverage of this function in library consumers - list, errs := drainer.GetPodsForDeletion(nodeName) - if errs != nil { - return utilerrors.NewAggregate(errs) - } - if warnings := list.Warnings(); warnings != "" { - fmt.Fprintf(drainer.ErrOut, "WARNING: %s\n", warnings) - } - - if err := drainer.DeleteOrEvictPods(list.Pods()); err != nil { - // Maybe warn about non-deleted pods here - return err - } - return nil -} - -// RunCordonOrUncordon demonstrates the canonical way to cordon or uncordon a Node -func RunCordonOrUncordon(drainer *Helper, node *corev1.Node, desired bool) error { - // TODO(justinsb): Ensure we have adequate e2e coverage of this function in library consumers - c := NewCordonHelper(node) - - if updateRequired := c.UpdateIfRequired(desired); !updateRequired { - // Already done - return nil - } - - err, patchErr := c.PatchOrReplace(drainer.Client) - if patchErr != nil { - return patchErr - } - if err != nil { - return err - } - - return nil -} diff --git a/pkg/drain/drain.go b/pkg/drain/drain.go deleted file mode 100644 index bca3fba4b9..0000000000 --- a/pkg/drain/drain.go +++ /dev/null @@ -1,292 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package drain - -import ( - "fmt" - "io" - "math" - "time" - - corev1 "k8s.io/api/core/v1" - policyv1beta1 "k8s.io/api/policy/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/kubernetes" -) - -const ( - // EvictionKind represents the kind of evictions object - EvictionKind = "Eviction" - // EvictionSubresource represents the kind of evictions object as pod's subresource - EvictionSubresource = "pods/eviction" -) - -// Helper contains the parameters to control the behaviour of drainer -type Helper struct { - Client kubernetes.Interface - Force bool - GracePeriodSeconds int - IgnoreAllDaemonSets bool - Timeout time.Duration - DeleteLocalData bool - Selector string - PodSelector string - Out io.Writer - ErrOut io.Writer - - // TODO(justinsb): unnecessary? - DryRun bool - - // OnPodDeletedOrEvicted is called when a pod is evicted/deleted; for printing progress output - OnPodDeletedOrEvicted func(pod *corev1.Pod, usingEviction bool) -} - -// CheckEvictionSupport uses Discovery API to find out if the server support -// eviction subresource If support, it will return its groupVersion; Otherwise, -// it will return an empty string -func CheckEvictionSupport(clientset kubernetes.Interface) (string, error) { - discoveryClient := clientset.Discovery() - groupList, err := discoveryClient.ServerGroups() - if err != nil { - return "", err - } - foundPolicyGroup := false - var policyGroupVersion string - for _, group := range groupList.Groups { - if group.Name == "policy" { - foundPolicyGroup = true - policyGroupVersion = group.PreferredVersion.GroupVersion - break - } - } - if !foundPolicyGroup { - return "", nil - } - resourceList, err := discoveryClient.ServerResourcesForGroupVersion("v1") - if err != nil { - return "", err - } - for _, resource := range resourceList.APIResources { - if resource.Name == EvictionSubresource && resource.Kind == EvictionKind { - return policyGroupVersion, nil - } - } - return "", nil -} - -func (d *Helper) makeDeleteOptions() *metav1.DeleteOptions { - deleteOptions := &metav1.DeleteOptions{} - if d.GracePeriodSeconds >= 0 { - gracePeriodSeconds := int64(d.GracePeriodSeconds) - deleteOptions.GracePeriodSeconds = &gracePeriodSeconds - } - return deleteOptions -} - -// DeletePod will delete the given pod, or return an error if it couldn't -func (d *Helper) DeletePod(pod corev1.Pod) error { - return d.Client.CoreV1().Pods(pod.Namespace).Delete(pod.Name, d.makeDeleteOptions()) -} - -// EvictPod will evict the give pod, or return an error if it couldn't -func (d *Helper) EvictPod(pod corev1.Pod, policyGroupVersion string) error { - eviction := &policyv1beta1.Eviction{ - TypeMeta: metav1.TypeMeta{ - APIVersion: policyGroupVersion, - Kind: EvictionKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: pod.Name, - Namespace: pod.Namespace, - }, - DeleteOptions: d.makeDeleteOptions(), - } - // Remember to change the URL manipulation func when Eviction's version change - return d.Client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(eviction) -} - -// GetPodsForDeletion receives resource info for a node, and returns those pods as PodDeleteList, -// or error if it cannot list pods. All pods that are ready to be deleted can be obtained with .Pods(), -// and string with all warning can be obtained with .Warnings(), and .Errors() for all errors that -// occurred during deletion. -func (d *Helper) GetPodsForDeletion(nodeName string) (*podDeleteList, []error) { - labelSelector, err := labels.Parse(d.PodSelector) - if err != nil { - return nil, []error{err} - } - - podList, err := d.Client.CoreV1().Pods(metav1.NamespaceAll).List(metav1.ListOptions{ - LabelSelector: labelSelector.String(), - FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": nodeName}).String()}) - if err != nil { - return nil, []error{err} - } - - pods := []podDelete{} - - for _, pod := range podList.Items { - var status podDeleteStatus - for _, filter := range d.makeFilters() { - status = filter(pod) - if !status.delete { - // short-circuit as soon as pod is filtered out - // at that point, there is no reason to run pod - // through any additional filters - break - } - } - pods = append(pods, podDelete{ - pod: pod, - status: status, - }) - } - - list := &podDeleteList{items: pods} - - if errs := list.errors(); len(errs) > 0 { - return list, errs - } - - return list, nil -} - -// DeleteOrEvictPods deletes or evicts the pods on the api server -func (d *Helper) DeleteOrEvictPods(pods []corev1.Pod) error { - if len(pods) == 0 { - 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) - } - - return d.deletePods(pods, getPodFn) -} - -func (d *Helper) evictPods(pods []corev1.Pod, policyGroupVersion string, getPodFn func(namespace, name string) (*corev1.Pod, error)) error { - returnCh := make(chan error, 1) - - for _, pod := range pods { - go func(pod corev1.Pod, returnCh chan error) { - for { - fmt.Fprintf(d.Out, "evicting pod %q\n", pod.Name) - err := d.EvictPod(pod, policyGroupVersion) - if err == nil { - break - } else if apierrors.IsNotFound(err) { - returnCh <- nil - return - } else if apierrors.IsTooManyRequests(err) { - fmt.Fprintf(d.ErrOut, "error when evicting pod %q (will retry after 5s): %v\n", pod.Name, err) - time.Sleep(5 * time.Second) - } else { - returnCh <- fmt.Errorf("error when evicting pod %q: %v", pod.Name, err) - return - } - } - _, err := waitForDelete([]corev1.Pod{pod}, 1*time.Second, time.Duration(math.MaxInt64), true, getPodFn, d.OnPodDeletedOrEvicted) - if err == nil { - returnCh <- nil - } else { - returnCh <- fmt.Errorf("error when waiting for pod %q terminating: %v", pod.Name, err) - } - }(pod, returnCh) - } - - doneCount := 0 - var errors []error - - // 0 timeout means infinite, we use MaxInt64 to represent it. - var globalTimeout time.Duration - if d.Timeout == 0 { - globalTimeout = time.Duration(math.MaxInt64) - } else { - globalTimeout = d.Timeout - } - globalTimeoutCh := time.After(globalTimeout) - numPods := len(pods) - for doneCount < numPods { - select { - case err := <-returnCh: - doneCount++ - if err != nil { - errors = append(errors, err) - } - case <-globalTimeoutCh: - return fmt.Errorf("drain did not complete within %v", globalTimeout) - } - } - return utilerrors.NewAggregate(errors) -} - -func (d *Helper) deletePods(pods []corev1.Pod, getPodFn func(namespace, name string) (*corev1.Pod, error)) error { - // 0 timeout means infinite, we use MaxInt64 to represent it. - var globalTimeout time.Duration - if d.Timeout == 0 { - globalTimeout = time.Duration(math.MaxInt64) - } else { - globalTimeout = d.Timeout - } - for _, pod := range pods { - err := d.DeletePod(pod) - if err != nil && !apierrors.IsNotFound(err) { - return err - } - } - _, err := waitForDelete(pods, 1*time.Second, globalTimeout, false, getPodFn, d.OnPodDeletedOrEvicted) - return err -} - -func waitForDelete(pods []corev1.Pod, interval, timeout time.Duration, usingEviction bool, getPodFn func(string, string) (*corev1.Pod, error), onDoneFn func(pod *corev1.Pod, usingEviction bool)) ([]corev1.Pod, error) { - err := wait.PollImmediate(interval, timeout, func() (bool, error) { - pendingPods := []corev1.Pod{} - for i, pod := range pods { - p, err := getPodFn(pod.Namespace, pod.Name) - if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) { - if onDoneFn != nil { - onDoneFn(&pod, usingEviction) - } - continue - } else if err != nil { - return false, err - } else { - pendingPods = append(pendingPods, pods[i]) - } - } - pods = pendingPods - if len(pendingPods) > 0 { - return false, nil - } - return true, nil - }) - return pods, err -} diff --git a/pkg/drain/filters.go b/pkg/drain/filters.go deleted file mode 100644 index 2cbba24563..0000000000 --- a/pkg/drain/filters.go +++ /dev/null @@ -1,223 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package drain - -import ( - "fmt" - "strings" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - daemonSetFatal = "DaemonSet-managed Pods (use --ignore-daemonsets to ignore)" - daemonSetWarning = "ignoring DaemonSet-managed Pods" - localStorageFatal = "Pods with local storage (use --delete-local-data to override)" - localStorageWarning = "deleting Pods with local storage" - unmanagedFatal = "Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet (use --force to override)" - unmanagedWarning = "deleting Pods not managed by ReplicationController, ReplicaSet, Job, DaemonSet or StatefulSet" -) - -type podDelete struct { - pod corev1.Pod - status podDeleteStatus -} - -type podDeleteList struct { - items []podDelete -} - -func (l *podDeleteList) Pods() []corev1.Pod { - pods := []corev1.Pod{} - for _, i := range l.items { - if i.status.delete { - pods = append(pods, i.pod) - } - } - return pods -} - -func (l *podDeleteList) Warnings() string { - ps := make(map[string][]string) - for _, i := range l.items { - if i.status.reason == podDeleteStatusTypeWarning { - ps[i.status.message] = append(ps[i.status.message], fmt.Sprintf("%s/%s", i.pod.Namespace, i.pod.Name)) - } - } - - msgs := []string{} - for key, pods := range ps { - msgs = append(msgs, fmt.Sprintf("%s: %s", key, strings.Join(pods, ", "))) - } - return strings.Join(msgs, "; ") -} - -func (l *podDeleteList) errors() []error { - failedPods := make(map[string][]string) - for _, i := range l.items { - if i.status.reason == podDeleteStatusTypeError { - msg := i.status.message - if msg == "" { - msg = "unexpected error" - } - failedPods[msg] = append(failedPods[msg], fmt.Sprintf("%s/%s", i.pod.Namespace, i.pod.Name)) - } - } - errs := make([]error, 0) - for msg, pods := range failedPods { - errs = append(errs, fmt.Errorf("cannot delete %s: %s", msg, strings.Join(pods, ", "))) - } - return errs -} - -type podDeleteStatus struct { - delete bool - reason string - message string -} - -// Takes a pod and returns a PodDeleteStatus -type podFilter func(corev1.Pod) podDeleteStatus - -const ( - podDeleteStatusTypeOkay = "Okay" - podDeleteStatusTypeSkip = "Skip" - podDeleteStatusTypeWarning = "Warning" - podDeleteStatusTypeError = "Error" -) - -func makePodDeleteStatusOkay() podDeleteStatus { - return podDeleteStatus{ - delete: true, - reason: podDeleteStatusTypeOkay, - } -} - -func makePodDeleteStatusSkip() podDeleteStatus { - return podDeleteStatus{ - delete: false, - reason: podDeleteStatusTypeSkip, - } -} - -func makePodDeleteStatusWithWarning(delete bool, message string) podDeleteStatus { - return podDeleteStatus{ - delete: delete, - reason: podDeleteStatusTypeWarning, - message: message, - } -} - -func makePodDeleteStatusWithError(message string) podDeleteStatus { - return podDeleteStatus{ - delete: false, - reason: podDeleteStatusTypeError, - message: message, - } -} - -func (d *Helper) makeFilters() []podFilter { - return []podFilter{ - d.daemonSetFilter, - d.mirrorPodFilter, - d.localStorageFilter, - d.unreplicatedFilter, - } -} - -func hasLocalStorage(pod corev1.Pod) bool { - for _, volume := range pod.Spec.Volumes { - if volume.EmptyDir != nil { - return true - } - } - - return false -} - -func (d *Helper) daemonSetFilter(pod corev1.Pod) podDeleteStatus { - // Note that we return false in cases where the pod is DaemonSet managed, - // regardless of flags. - // - // The exception is for pods that are orphaned (the referencing - // management resource - including DaemonSet - is not found). - // Such pods will be deleted if --force is used. - controllerRef := metav1.GetControllerOf(&pod) - if controllerRef == nil || controllerRef.Kind != appsv1.SchemeGroupVersion.WithKind("DaemonSet").Kind { - return makePodDeleteStatusOkay() - } - // Any finished pod can be removed. - if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed { - return makePodDeleteStatusOkay() - } - - if _, err := d.Client.AppsV1().DaemonSets(pod.Namespace).Get(controllerRef.Name, metav1.GetOptions{}); err != nil { - // remove orphaned pods with a warning if --force is used - if apierrors.IsNotFound(err) && d.Force { - return makePodDeleteStatusWithWarning(true, err.Error()) - } - - return makePodDeleteStatusWithError(err.Error()) - } - - if !d.IgnoreAllDaemonSets { - return makePodDeleteStatusWithError(daemonSetFatal) - } - - return makePodDeleteStatusWithWarning(false, daemonSetWarning) -} - -func (d *Helper) mirrorPodFilter(pod corev1.Pod) podDeleteStatus { - if _, found := pod.ObjectMeta.Annotations[corev1.MirrorPodAnnotationKey]; found { - return makePodDeleteStatusSkip() - } - return makePodDeleteStatusOkay() -} - -func (d *Helper) localStorageFilter(pod corev1.Pod) podDeleteStatus { - if !hasLocalStorage(pod) { - return makePodDeleteStatusOkay() - } - // Any finished pod can be removed. - if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed { - return makePodDeleteStatusOkay() - } - if !d.DeleteLocalData { - return makePodDeleteStatusWithError(localStorageFatal) - } - - return makePodDeleteStatusWithWarning(true, localStorageWarning) -} - -func (d *Helper) unreplicatedFilter(pod corev1.Pod) podDeleteStatus { - // any finished pod can be removed - if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed { - return makePodDeleteStatusOkay() - } - - controllerRef := metav1.GetControllerOf(&pod) - if controllerRef != nil { - return makePodDeleteStatusOkay() - } - if d.Force { - return makePodDeleteStatusWithWarning(true, unmanagedWarning) - } - return makePodDeleteStatusWithError(unmanagedFatal) -} diff --git a/pkg/instancegroups/instancegroups.go b/pkg/instancegroups/instancegroups.go index 4545a25720..a34f503b3e 100644 --- a/pkg/instancegroups/instancegroups.go +++ b/pkg/instancegroups/instancegroups.go @@ -32,7 +32,7 @@ import ( "k8s.io/klog" api "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/pkg/cloudinstances" - "k8s.io/kops/pkg/drain" + "k8s.io/kubectl/pkg/drain" ) const rollingUpdateTaintKey = "kops.k8s.io/scheduled-for-update"