From c0668a0dbf3ae48dc0f8953ef76860bd6c656bd8 Mon Sep 17 00:00:00 2001 From: Meghana Jangi Date: Sun, 13 Feb 2022 13:36:10 +0530 Subject: [PATCH] Removes code deprecated with v1.1.0 Signed-off-by: Meghana Jangi --- .../src/developer/providers/v1.1-to-v1.2.md | 5 +- third_party/kubernetes-drain/README.md | 4 - third_party/kubernetes-drain/cordon.go | 96 ----- third_party/kubernetes-drain/default.go | 74 ---- third_party/kubernetes-drain/drain.go | 374 ------------------ third_party/kubernetes-drain/filters.go | 244 ------------ util/annotations/helpers.go | 12 - util/version/version.go | 23 +- util/version/version_test.go | 235 ++++------- 9 files changed, 93 insertions(+), 974 deletions(-) delete mode 100644 third_party/kubernetes-drain/README.md delete mode 100644 third_party/kubernetes-drain/cordon.go delete mode 100644 third_party/kubernetes-drain/default.go delete mode 100644 third_party/kubernetes-drain/drain.go delete mode 100644 third_party/kubernetes-drain/filters.go diff --git a/docs/book/src/developer/providers/v1.1-to-v1.2.md b/docs/book/src/developer/providers/v1.1-to-v1.2.md index b3b1ab7f78a4..b46eb5cd674d 100644 --- a/docs/book/src/developer/providers/v1.1-to-v1.2.md +++ b/docs/book/src/developer/providers/v1.1-to-v1.2.md @@ -21,7 +21,10 @@ in ClusterAPI are kept in sync with the versions used by `sigs.k8s.io/controller * `util.MachinesByCreationTimestamp` has been deprecated and will be removed in a future release. ### Removals - +* The `third_party/kubernetes-drain` package has been removed, as we're now using `k8s.io/kubectl/pkg/drain` instead ([PR](https://github.com/kubernetes-sigs/cluster-api/pull/5440)). +* `util/version.CompareWithBuildIdentifiers` has been removed, please use `util/version.Compare(a, b, WithBuildTags())` instead. +* The functions `annotations.HasPausedAnnotation` and `annotations.HasSkipRemediationAnnotation` have been removed, please use + `annotations.HasPaused` and `annotations.HasSkipRemediation` respectively instead. - ### API Changes diff --git a/third_party/kubernetes-drain/README.md b/third_party/kubernetes-drain/README.md deleted file mode 100644 index 13a590a030b9..000000000000 --- a/third_party/kubernetes-drain/README.md +++ /dev/null @@ -1,4 +0,0 @@ -NOTE: The types and methods in this directory have been deprecated and will be removed in a future release. - -The code in this directory has been copied from: -github.com/kubernetes/kubectl/pkg/drain@a17d91f9f5b34c73bed0bfc75b70bd762b725231 diff --git a/third_party/kubernetes-drain/cordon.go b/third_party/kubernetes-drain/cordon.go deleted file mode 100644 index eb972147bef0..000000000000 --- a/third_party/kubernetes-drain/cordon.go +++ /dev/null @@ -1,96 +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 ( - "context" - "fmt" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/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(ctx context.Context, 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(ctx, c.node.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}) - } else { - _, err = client.Update(ctx, c.node, metav1.UpdateOptions{}) - } - return err, patchErr -} diff --git a/third_party/kubernetes-drain/default.go b/third_party/kubernetes-drain/default.go deleted file mode 100644 index b31d8a448658..000000000000 --- a/third_party/kubernetes-drain/default.go +++ /dev/null @@ -1,74 +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 ( - "context" - "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 -// Deprecated: This method has been deprecated in favor of using the -// same method from the upstream kubectl codebase. -func RunNodeDrain(ctx context.Context, drainer *Helper, nodeName string) error { - // TODO(justinsb): Ensure we have adequate e2e coverage of this function in library consumers - list, errs := drainer.GetPodsForDeletion(ctx, 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(ctx, 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 -// Deprecated: This method has been deprecated in favor of using the -// same method from the upstream kubectl codebase. -func RunCordonOrUncordon(ctx context.Context, 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(ctx, drainer.Client) - if patchErr != nil { - return patchErr - } - if err != nil { - return err - } - - return nil -} diff --git a/third_party/kubernetes-drain/drain.go b/third_party/kubernetes-drain/drain.go deleted file mode 100644 index 97d6d3e678fb..000000000000 --- a/third_party/kubernetes-drain/drain.go +++ /dev/null @@ -1,374 +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 ( - "context" - "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" - podSkipMsgTemplate = "pod %q has DeletionTimestamp older than %v seconds, skipping\n" -) - -// Helper contains the parameters to control the behaviour of drainer -// Deprecated: This has been deprecated in favor of using the -// same type from the upstream kubectl codebase. -type Helper struct { - Ctx context.Context - Client kubernetes.Interface - Force bool - GracePeriodSeconds int - IgnoreAllDaemonSets bool - Timeout time.Duration - DeleteLocalData bool - Selector string - PodSelector string - - // DisableEviction forces drain to use delete rather than evict - DisableEviction bool - - // SkipWaitForDeleteTimeoutSeconds ignores pods that have a - // DeletionTimeStamp > N seconds. It's up to the user to decide when this - // option is appropriate; examples include the Node is unready and the pods - // won't drain otherwise - SkipWaitForDeleteTimeoutSeconds int - - 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) -} - -type waitForDeleteParams struct { - ctx context.Context - pods []corev1.Pod - interval time.Duration - timeout time.Duration - usingEviction bool - getPodFn func(string, string) (*corev1.Pod, error) - onDoneFn func(pod *corev1.Pod, usingEviction bool) - globalTimeout time.Duration - skipWaitForDeleteTimeoutSeconds int - out io.Writer -} - -// 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(ctx context.Context, pod corev1.Pod) error { - return d.Client.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, *d.makeDeleteOptions()) -} - -// EvictPod will evict the give pod, or return an error if it couldn't -func (d *Helper) EvictPod(ctx context.Context, 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 change the URL manipulation func when Eviction's version change - return d.Client.PolicyV1beta1().Evictions(eviction.Namespace).Evict(ctx, 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(ctx context.Context, 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(ctx, 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 - } - } - if status.delete { - 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(ctx context.Context, pods []corev1.Pod) error { - if len(pods) == 0 { - return nil - } - - // TODO(justinsb): unnecessary? - getPodFn := func(namespace, name string) (*corev1.Pod, error) { - return d.Client.CoreV1().Pods(namespace).Get(ctx, name, metav1.GetOptions{}) - } - - if !d.DisableEviction { - policyGroupVersion, err := CheckEvictionSupport(d.Client) - if err != nil { - return err - } - - if len(policyGroupVersion) > 0 { - return d.evictPods(ctx, pods, policyGroupVersion, getPodFn) - } - } - - return d.deletePods(ctx, pods, getPodFn) -} - -func (d *Helper) evictPods(ctx context.Context, pods []corev1.Pod, policyGroupVersion string, getPodFn func(namespace, name string) (*corev1.Pod, error)) error { - returnCh := make(chan error, 1) - // 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 - } - ctx, cancel := context.WithTimeout(d.getContext(), globalTimeout) - defer cancel() - for _, pod := range pods { - go func(pod corev1.Pod, returnCh chan error) { - for { - fmt.Fprintf(d.Out, "evicting pod %s/%s\n", pod.Namespace, pod.Name) - select { - case <-ctx.Done(): - // return here or we'll leak a goroutine. - returnCh <- fmt.Errorf("error when evicting pod %q: global timeout reached: %v", pod.Name, globalTimeout) - return - default: - } - err := d.EvictPod(ctx, 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 - } - } - params := waitForDeleteParams{ - ctx: ctx, - pods: []corev1.Pod{pod}, - interval: 1 * time.Second, - timeout: time.Duration(math.MaxInt64), - usingEviction: true, - getPodFn: getPodFn, - onDoneFn: d.OnPodDeletedOrEvicted, - globalTimeout: globalTimeout, - skipWaitForDeleteTimeoutSeconds: d.SkipWaitForDeleteTimeoutSeconds, - out: d.Out, - } - _, err := waitForDelete(params) - 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 - - numPods := len(pods) - for doneCount < numPods { - select { - case err := <-returnCh: - doneCount++ - if err != nil { - errors = append(errors, err) - } - } - } - - return utilerrors.NewAggregate(errors) -} - -func (d *Helper) deletePods(ctx context.Context, 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(ctx, pod) - if err != nil && !apierrors.IsNotFound(err) { - return err - } - } - params := waitForDeleteParams{ - ctx: ctx, - pods: pods, - interval: 1 * time.Second, - timeout: globalTimeout, - usingEviction: false, - getPodFn: getPodFn, - onDoneFn: d.OnPodDeletedOrEvicted, - globalTimeout: globalTimeout, - skipWaitForDeleteTimeoutSeconds: d.SkipWaitForDeleteTimeoutSeconds, - out: d.Out, - } - _, err := waitForDelete(params) - return err -} - -func waitForDelete(params waitForDeleteParams) ([]corev1.Pod, error) { - pods := params.pods - err := wait.PollImmediate(params.interval, params.timeout, func() (bool, error) { - pendingPods := []corev1.Pod{} - for i, pod := range pods { - p, err := params.getPodFn(pod.Namespace, pod.Name) - if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) { - if params.onDoneFn != nil { - params.onDoneFn(&pod, params.usingEviction) - } - continue - } else if err != nil { - return false, err - } else { - if shouldSkipPod(*p, params.skipWaitForDeleteTimeoutSeconds) { - fmt.Fprintf(params.out, podSkipMsgTemplate, pod.Name, params.skipWaitForDeleteTimeoutSeconds) - continue - } - pendingPods = append(pendingPods, pods[i]) - } - } - pods = pendingPods - if len(pendingPods) > 0 { - select { - case <-params.ctx.Done(): - return false, fmt.Errorf("global timeout reached: %v", params.globalTimeout) - default: - return false, nil - } - } - return true, nil - }) - return pods, err -} - -// Since Helper does not have a constructor, we can't enforce Helper.Ctx != nil -// Multiple public methods prevent us from initializing the context in a single -// place as well. -func (d *Helper) getContext() context.Context { - if d.Ctx != nil { - return d.Ctx - } - return context.Background() -} diff --git a/third_party/kubernetes-drain/filters.go b/third_party/kubernetes-drain/filters.go deleted file mode 100644 index 1ffbbbe5330f..000000000000 --- a/third_party/kubernetes-drain/filters.go +++ /dev/null @@ -1,244 +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 ( - "context" - "fmt" - "strings" - "time" - - 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, - } -} - -// The filters are applied in a specific order, only the last filter's -// message will be retained if there are any warnings. -func (d *Helper) makeFilters() []podFilter { - return []podFilter{ - d.skipDeletedFilter, - 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(context.TODO(), 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) - } - - // TODO: this warning gets dropped by subsequent filters; - // consider accounting for multiple warning conditions or at least - // preserving the last warning message. - 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) -} - -func shouldSkipPod(pod corev1.Pod, skipDeletedTimeoutSeconds int) bool { - return skipDeletedTimeoutSeconds > 0 && - !pod.ObjectMeta.DeletionTimestamp.IsZero() && - int(time.Now().Sub(pod.ObjectMeta.GetDeletionTimestamp().Time).Seconds()) > skipDeletedTimeoutSeconds -} - -func (d *Helper) skipDeletedFilter(pod corev1.Pod) podDeleteStatus { - if shouldSkipPod(pod, d.SkipWaitForDeleteTimeoutSeconds) { - return makePodDeleteStatusSkip() - } - return makePodDeleteStatusOkay() -} diff --git a/util/annotations/helpers.go b/util/annotations/helpers.go index 07f2c6a506b0..de07f7a55b92 100644 --- a/util/annotations/helpers.go +++ b/util/annotations/helpers.go @@ -38,23 +38,11 @@ func IsExternallyManaged(o metav1.Object) bool { return hasAnnotation(o, clusterv1.ManagedByAnnotation) } -// HasPausedAnnotation returns true if the object has the `paused` annotation. -// Deprecated: this is going to be removed in a next release. Please use `HasPaused` instead. -func HasPausedAnnotation(o metav1.Object) bool { - return hasAnnotation(o, clusterv1.PausedAnnotation) -} - // HasPaused returns true if the object has the `paused` annotation. func HasPaused(o metav1.Object) bool { return hasAnnotation(o, clusterv1.PausedAnnotation) } -// HasSkipRemediationAnnotation returns true if the object has the `skip-remediation` annotation. -// Deprecated: this is going to be removed in a next release. Please use `HasSkipRemediation` instead. -func HasSkipRemediationAnnotation(o metav1.Object) bool { - return hasAnnotation(o, clusterv1.MachineSkipRemediationAnnotation) -} - // HasSkipRemediation returns true if the object has the `skip-remediation` annotation. func HasSkipRemediation(o metav1.Object) bool { return hasAnnotation(o, clusterv1.MachineSkipRemediationAnnotation) diff --git a/util/version/version.go b/util/version/version.go index c0e49365f760..cfd4392abd62 100644 --- a/util/version/version.go +++ b/util/version/version.go @@ -173,22 +173,6 @@ func (v buildIdentifier) compare(o buildIdentifier) int { } } -// CompareWithBuildIdentifiers compares two versions a and b. -// Perfoms a standard version compare between a and b. If the versions -// are equal, build identifiers will be used to compare further. -// -1 == a is less than b. -// 0 == a is equal to b. -// 1 == a is greater than b. -// Deprecated: Use Compare(a, b, WithBuildTags()) instead. -func CompareWithBuildIdentifiers(a semver.Version, b semver.Version) int { - if comp := a.Compare(b); comp != 0 { - return comp - } - biA := newBuildIdentifiers(a.Build) - biB := newBuildIdentifiers(b.Build) - return biA.compare(biB) -} - type comparer struct { buildTags bool } @@ -229,7 +213,12 @@ func Compare(a, b semver.Version, options ...CompareOption) int { } if c.buildTags { - return CompareWithBuildIdentifiers(a, b) + if comp := a.Compare(b); comp != 0 { + return comp + } + biA := newBuildIdentifiers(a.Build) + biB := newBuildIdentifiers(b.Build) + return biA.compare(biB) } return a.Compare(b) } diff --git a/util/version/version_test.go b/util/version/version_test.go index 195c2b0091c5..a76ea9d2c7e5 100644 --- a/util/version/version_test.go +++ b/util/version/version_test.go @@ -124,191 +124,114 @@ func TestParseMajorMinorPatchTolerant(t *testing.T) { } } -func TestCompareWithBuildIdentifiers(t *testing.T) { +func TestCompare(t *testing.T) { tests := []struct { name string - a semver.Version - b semver.Version - expected int + aVersion semver.Version + bVersion semver.Version + options []CompareOption + want int }{ { - name: "compare with no build identifiers", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.2") - return v - }(), - expected: -1, + name: "comparing with no options should perform standard compare", + aVersion: semver.MustParse("1.2.3"), + bVersion: semver.MustParse("1.3.1"), + want: -1, }, { - name: "compare with pre release versions and no build identifiers", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1-alpha.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1-alpha.2") - return v - }(), - expected: -1, + name: "comparing with no options should perform standard compare - equal versions", + aVersion: semver.MustParse("1.2.3+xyz.1"), + bVersion: semver.MustParse("1.2.3+xyz.2"), + want: 0, }, { - name: "compare with pre release versions and build identifiers", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1-alpha.1+xyz.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1-alpha.1+xyz.2") - return v - }(), - expected: -1, + name: "compare with build tags using the WithBuildTags option", + aVersion: semver.MustParse("1.2.3+xyz.1"), + bVersion: semver.MustParse("1.2.3+xyz.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - smaller", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.2") - return v - }(), - expected: -1, + name: "compare with no build identifiers", + aVersion: mustParseTolerant("v1.20.1"), + bVersion: mustParseTolerant("v1.20.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - equal", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1") - return v - }(), - expected: 0, + name: "compare with pre release versions and no build identifiers", + aVersion: mustParseTolerant("v1.20.1-alpha.1"), + bVersion: mustParseTolerant("v1.20.1-alpha.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - greater", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.3") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.2") - return v - }(), - expected: 1, + name: "compare with pre release versions and build identifiers", + aVersion: mustParseTolerant("v1.20.1-alpha.1+xyz.1"), + bVersion: mustParseTolerant("v1.20.1-alpha.1+xyz.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - smaller by sub version", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1.0") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1.1") - return v - }(), - expected: -1, + name: "compare with build identifiers - smaller", + aVersion: mustParseTolerant("v1.20.1+xyz.1"), + bVersion: mustParseTolerant("v1.20.1+xyz.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - smaller - different version lengths", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.2") - return v - }(), - expected: -1, + name: "compare with build identifiers - equal", + aVersion: mustParseTolerant("v1.20.1+xyz.1"), + bVersion: mustParseTolerant("v1.20.1+xyz.1"), + options: []CompareOption{WithBuildTags()}, + want: 0, }, { - name: "compare with build identifiers - greater by length", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1") - return v - }(), - expected: 1, + name: "compare with build identifiers - greater", + aVersion: mustParseTolerant("v1.20.1+xyz.3"), + bVersion: mustParseTolerant("v1.20.1+xyz.2"), + options: []CompareOption{WithBuildTags()}, + want: 1, }, { - name: "compare with build identifiers - different non numeric", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.a") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.b") - return v - }(), - expected: 2, + name: "compare with build identifiers - smaller by sub version", + aVersion: mustParseTolerant("v1.20.1+xyz.1.0"), + bVersion: mustParseTolerant("v1.20.1+xyz.1.1"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - equal non numeric", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.a") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.a") - return v - }(), - expected: 0, + name: "compare with build identifiers - smaller - different version lengths", + aVersion: mustParseTolerant("v1.20.1+xyz.1.1"), + bVersion: mustParseTolerant("v1.20.1+xyz.2"), + options: []CompareOption{WithBuildTags()}, + want: -1, }, { - name: "compare with build identifiers - smaller - a is numeric b is not", - a: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.1") - return v - }(), - b: func() semver.Version { - v, _ := semver.ParseTolerant("v1.20.1+xyz.abc") - return v - }(), - expected: -1, + name: "compare with build identifiers - greater by length", + aVersion: mustParseTolerant("v1.20.1+xyz.1.1"), + bVersion: mustParseTolerant("v1.20.1+xyz.1"), + options: []CompareOption{WithBuildTags()}, + want: 1, }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - g := NewWithT(t) - g.Expect(CompareWithBuildIdentifiers(tt.a, tt.b)).To(Equal(tt.expected)) - }) - } -} - -func TestCompare(t *testing.T) { - tests := []struct { - name string - aVersion semver.Version - bVersion semver.Version - options []CompareOption - want int - }{ { - name: "comparing with no options should perform standard compare", - aVersion: semver.MustParse("1.2.3"), - bVersion: semver.MustParse("1.3.1"), - want: -1, + name: "compare with build identifiers - different non numeric", + aVersion: mustParseTolerant("v1.20.1+xyz.a"), + bVersion: mustParseTolerant("v1.20.1+xyz.b"), + options: []CompareOption{WithBuildTags()}, + want: 2, }, { - name: "comparing with no options should perform standard compare - equal versions", - aVersion: semver.MustParse("1.2.3+xyz.1"), - bVersion: semver.MustParse("1.2.3+xyz.2"), + name: "compare with build identifiers - equal non numeric", + aVersion: mustParseTolerant("v1.20.1+xyz.a"), + bVersion: mustParseTolerant("v1.20.1+xyz.a"), + options: []CompareOption{WithBuildTags()}, want: 0, }, { - name: "compare with build tags using the WithBuildTags option", - aVersion: semver.MustParse("1.2.3+xyz.1"), - bVersion: semver.MustParse("1.2.3+xyz.2"), + name: "compare with build identifiers - smaller - a is numeric b is not", + aVersion: mustParseTolerant("v1.20.1+xyz.1"), + bVersion: mustParseTolerant("v1.20.1+xyz.abc"), options: []CompareOption{WithBuildTags()}, want: -1, }, @@ -321,3 +244,11 @@ func TestCompare(t *testing.T) { }) } } + +func mustParseTolerant(s string) semver.Version { + v, err := semver.ParseTolerant(s) + if err != nil { + panic(`semver: ParseTolerant(` + s + `): ` + err.Error()) + } + return v +}