diff --git a/go.mod b/go.mod index 3131c4078..6148fa09b 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.19 require ( github.com/client9/misspell v0.3.4 + github.com/google/go-cmp v0.5.6 github.com/spf13/cobra v1.4.0 github.com/spf13/pflag v1.0.5 k8s.io/api v0.25.0 @@ -51,7 +52,6 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/gomarkdown/markdown v0.0.0-20200824053859-8c8b3816f167 // indirect github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.6 // indirect github.com/google/gofuzz v1.1.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect diff --git a/hack/lib/generator-help.sh b/hack/lib/generator-help.sh new file mode 100644 index 000000000..a79bf584a --- /dev/null +++ b/hack/lib/generator-help.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# Utility command based on 'find' command. The pipeline is as following: +# 1. find all the go files; (exclude specific path: vendor etc) +# 2. find all the files containing specific tags in contents; +# 3. extract related dirs; +# 4. remove duplicated paths; +# 5. replace . with ${PRJ_PREFIX} +# 6. merge all dirs in array with delimiter ,; +# +# Example: +# find_dirs_containing_comment_tags("+k8s:") +# Return: +# sigs.k8s.io/descheduler/a,sigs.k8s.io/descheduler/b,sigs.k8s.io/descheduler/c +function find_dirs_containing_comment_tags() { + array=() + while IFS='' read -r line; do array+=("$line"); done < <( \ + find . -type f -name \*.go -not -path "./vendor/*" -print0 \ + | xargs -0 grep --color=never -l "$@" \ + | xargs -n1 dirname \ + | LC_ALL=C sort -u \ + | sed "s|.|${PRJ_PREFIX}|" \ + ) + + IFS=","; + printf '%s' "${array[*]}"; +} diff --git a/hack/update-generated-conversions.sh b/hack/update-generated-conversions.sh index ebf51280b..4edd5c967 100755 --- a/hack/update-generated-conversions.sh +++ b/hack/update-generated-conversions.sh @@ -1,9 +1,10 @@ #!/bin/bash source "$(dirname "${BASH_SOURCE}")/lib/init.sh" +source "$(dirname "${BASH_SOURCE}")/lib/generator-help.sh" go build -o "${OS_OUTPUT_BINPATH}/conversion-gen" "k8s.io/code-generator/cmd/conversion-gen" ${OS_OUTPUT_BINPATH}/conversion-gen \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \ - --input-dirs "${PRJ_PREFIX}/pkg/apis/componentconfig/v1alpha1,${PRJ_PREFIX}/pkg/api/v1alpha1" \ + --input-dirs "$(find_dirs_containing_comment_tags "+k8s:conversion-gen=")" \ --output-file-base zz_generated.conversion diff --git a/hack/update-generated-deep-copies.sh b/hack/update-generated-deep-copies.sh index 880961f70..3f2f67e26 100755 --- a/hack/update-generated-deep-copies.sh +++ b/hack/update-generated-deep-copies.sh @@ -1,10 +1,11 @@ #!/bin/bash source "$(dirname "${BASH_SOURCE}")/lib/init.sh" +source "$(dirname "${BASH_SOURCE}")/lib/generator-help.sh" go build -o "${OS_OUTPUT_BINPATH}/deepcopy-gen" "k8s.io/code-generator/cmd/deepcopy-gen" ${OS_OUTPUT_BINPATH}/deepcopy-gen \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \ - --input-dirs "${PRJ_PREFIX}/pkg/apis/componentconfig,${PRJ_PREFIX}/pkg/apis/componentconfig/v1alpha1,${PRJ_PREFIX}/pkg/api,${PRJ_PREFIX}/pkg/api/v1alpha1,${PRJ_PREFIX}/pkg/framework/plugins/defaultevictor/" \ + --input-dirs "$(find_dirs_containing_comment_tags "+k8s:deepcopy-gen=")" \ --output-file-base zz_generated.deepcopy diff --git a/hack/update-generated-defaulters.sh b/hack/update-generated-defaulters.sh index 1c88e1d11..52e0f42f2 100755 --- a/hack/update-generated-defaulters.sh +++ b/hack/update-generated-defaulters.sh @@ -1,10 +1,11 @@ #!/bin/bash source "$(dirname "${BASH_SOURCE}")/lib/init.sh" +source "$(dirname "${BASH_SOURCE}")/lib/generator-help.sh" go build -o "${OS_OUTPUT_BINPATH}/defaulter-gen" "k8s.io/code-generator/cmd/defaulter-gen" ${OS_OUTPUT_BINPATH}/defaulter-gen \ --go-header-file "hack/boilerplate/boilerplate.go.txt" \ - --input-dirs "${PRJ_PREFIX}/pkg/apis/componentconfig/v1alpha1,${PRJ_PREFIX}/pkg/api/v1alpha1" \ + --input-dirs "$(find_dirs_containing_comment_tags "+k8s:defaulter-gen=")" \ --extra-peer-dirs "${PRJ_PREFIX}/pkg/apis/componentconfig/v1alpha1,${PRJ_PREFIX}/pkg/api/v1alpha1" \ --output-file-base zz_generated.defaults diff --git a/pkg/apis/componentconfig/types_pluginargs.go b/pkg/apis/componentconfig/types_pluginargs.go deleted file mode 100644 index d47a35eb9..000000000 --- a/pkg/apis/componentconfig/types_pluginargs.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Copyright 2022 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 componentconfig - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/descheduler/pkg/api" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemovePodsViolatingNodeTaintsArgs holds arguments used to configure the RemovePodsViolatingNodeTaints plugin. -type RemovePodsViolatingNodeTaintsArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - IncludePreferNoSchedule bool - ExcludedTaints []string -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemoveFailedPodsArgs holds arguments used to configure RemoveFailedPods plugin. -type RemoveFailedPodsArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - ExcludeOwnerKinds []string - MinPodLifetimeSeconds *uint - Reasons []string - IncludingInitContainers bool -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemovePodsViolatingNodeAffinityArgs holds arguments used to configure RemovePodsViolatingNodeAffinity plugin. -type RemovePodsViolatingNodeAffinityArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - NodeAffinityType []string -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemovePodsHavingTooManyRestartsArgs holds arguments used to configure RemovePodsHavingTooManyRestarts plugin. -type RemovePodsHavingTooManyRestartsArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - PodRestartThreshold int32 - IncludingInitContainers bool -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type RemoveDuplicatesArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - ExcludeOwnerKinds []string -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// PodLifeTimeArgs holds arguments used to configure PodLifeTime plugin. -type PodLifeTimeArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - MaxPodLifeTimeSeconds *uint - States []string -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemovePodsViolatingTopologySpreadConstraintArgs holds arguments used to configure RemovePodsViolatingTopologySpreadConstraint plugin. -type RemovePodsViolatingTopologySpreadConstraintArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector - IncludeSoftConstraints bool -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// RemovePodsViolatingInterPodAntiAffinity holds arguments used to configure RemovePodsViolatingInterPodAntiAffinity plugin. -type RemovePodsViolatingInterPodAntiAffinityArgs struct { - metav1.TypeMeta - - Namespaces *api.Namespaces - LabelSelector *metav1.LabelSelector -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type LowNodeUtilizationArgs struct { - metav1.TypeMeta - - UseDeviationThresholds bool - Thresholds api.ResourceThresholds - TargetThresholds api.ResourceThresholds - NumberOfNodes int -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -type HighNodeUtilizationArgs struct { - metav1.TypeMeta - - Thresholds api.ResourceThresholds - NumberOfNodes int -} diff --git a/pkg/apis/componentconfig/validation/validation_pluginargs.go b/pkg/apis/componentconfig/validation/validation_pluginargs.go deleted file mode 100644 index 8107e8f12..000000000 --- a/pkg/apis/componentconfig/validation/validation_pluginargs.go +++ /dev/null @@ -1,203 +0,0 @@ -/* -Copyright 2022 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 validation - -import ( - "fmt" - - v1 "k8s.io/api/core/v1" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/sets" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" -) - -const ( - // MinResourcePercentage is the minimum value of a resource's percentage - MinResourcePercentage = 0 - // MaxResourcePercentage is the maximum value of a resource's percentage - MaxResourcePercentage = 100 -) - -// ValidateRemovePodsViolatingNodeTaintsArgs validates RemovePodsViolatingNodeTaints arguments -func ValidateRemovePodsViolatingNodeTaintsArgs(args *componentconfig.RemovePodsViolatingNodeTaintsArgs) error { - return errorsAggregate( - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - ) -} - -// ValidateRemoveFailedPodsArgs validates RemoveFailedPods arguments -func ValidateRemoveFailedPodsArgs(args *componentconfig.RemoveFailedPodsArgs) error { - return errorsAggregate( - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - ) -} - -// ValidateRemovePodsHavingTooManyRestartsArgs validates RemovePodsHavingTooManyRestarts arguments -func ValidateRemovePodsHavingTooManyRestartsArgs(args *componentconfig.RemovePodsHavingTooManyRestartsArgs) error { - return errorsAggregate( - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - validatePodRestartThreshold(args.PodRestartThreshold), - ) -} - -// ValidateRemovePodsViolatingNodeAffinityArgs validates RemovePodsViolatingNodeAffinity arguments -func ValidateRemovePodsViolatingNodeAffinityArgs(args *componentconfig.RemovePodsViolatingNodeAffinityArgs) error { - var err error - if args == nil || len(args.NodeAffinityType) == 0 { - err = fmt.Errorf("nodeAffinityType needs to be set") - } - - return errorsAggregate( - err, - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - ) -} - -// ValidatePodLifeTimeArgs validates PodLifeTime arguments -func ValidatePodLifeTimeArgs(args *componentconfig.PodLifeTimeArgs) error { - var err error - if args.MaxPodLifeTimeSeconds == nil { - err = fmt.Errorf("MaxPodLifeTimeSeconds not set") - } - - return errorsAggregate( - err, - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - validatePodLifeTimeStates(args.States), - ) -} - -func ValidateRemoveDuplicatesArgs(args *componentconfig.RemoveDuplicatesArgs) error { - return validateNamespaceArgs(args.Namespaces) -} - -// ValidateRemovePodsViolatingTopologySpreadConstraintArgs validates RemovePodsViolatingTopologySpreadConstraint arguments -func ValidateRemovePodsViolatingTopologySpreadConstraintArgs(args *componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs) error { - return errorsAggregate( - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - ) -} - -// ValidateRemovePodsViolatingInterPodAntiAffinityArgs validates ValidateRemovePodsViolatingInterPodAntiAffinity arguments -func ValidateRemovePodsViolatingInterPodAntiAffinityArgs(args *componentconfig.RemovePodsViolatingInterPodAntiAffinityArgs) error { - return errorsAggregate( - validateNamespaceArgs(args.Namespaces), - validateLabelSelectorArgs(args.LabelSelector), - ) -} - -// errorsAggregate converts all arg validation errors to a single error interface. -// if no errors, it will return nil. -func errorsAggregate(errors ...error) error { - return utilerrors.NewAggregate(errors) -} - -func validateNamespaceArgs(namespaces *api.Namespaces) error { - // At most one of include/exclude can be set - if namespaces != nil && len(namespaces.Include) > 0 && len(namespaces.Exclude) > 0 { - return fmt.Errorf("only one of Include/Exclude namespaces can be set") - } - - return nil -} - -func validateLabelSelectorArgs(labelSelector *metav1.LabelSelector) error { - if labelSelector != nil { - if _, err := metav1.LabelSelectorAsSelector(labelSelector); err != nil { - return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) - } - } - - return nil -} - -func validatePodRestartThreshold(podRestartThreshold int32) error { - if podRestartThreshold < 1 { - return fmt.Errorf("PodsHavingTooManyRestarts threshold not set") - } - return nil -} - -func validatePodLifeTimeStates(states []string) error { - podLifeTimeAllowedStates := sets.NewString( - string(v1.PodRunning), - string(v1.PodPending), - - // Container state reasons: https://github.com/kubernetes/kubernetes/blob/release-1.24/pkg/kubelet/kubelet_pods.go#L76-L79 - "PodInitializing", - "ContainerCreating", - ) - - if !podLifeTimeAllowedStates.HasAll(states...) { - return fmt.Errorf("states must be one of %v", podLifeTimeAllowedStates.List()) - } - - return nil -} - -func ValidateHighNodeUtilizationArgs(args *componentconfig.HighNodeUtilizationArgs) error { - return validateThresholds(args.Thresholds) -} - -func ValidateLowNodeUtilizationArgs(args *componentconfig.LowNodeUtilizationArgs) error { - return validateLowNodeUtilizationThresholds(args.Thresholds, args.TargetThresholds, args.UseDeviationThresholds) -} - -func validateLowNodeUtilizationThresholds(thresholds, targetThresholds api.ResourceThresholds, useDeviationThresholds bool) error { - // validate thresholds and targetThresholds config - if err := validateThresholds(thresholds); err != nil { - return fmt.Errorf("thresholds config is not valid: %v", err) - } - if err := validateThresholds(targetThresholds); err != nil { - return fmt.Errorf("targetThresholds config is not valid: %v", err) - } - - // validate if thresholds and targetThresholds have same resources configured - if len(thresholds) != len(targetThresholds) { - return fmt.Errorf("thresholds and targetThresholds configured different resources") - } - for resourceName, value := range thresholds { - if targetValue, ok := targetThresholds[resourceName]; !ok { - return fmt.Errorf("thresholds and targetThresholds configured different resources") - } else if value > targetValue && !useDeviationThresholds { - return fmt.Errorf("thresholds' %v percentage is greater than targetThresholds'", resourceName) - } - } - return nil -} - -// validateThresholds checks if thresholds have valid resource name and resource percentage configured -func validateThresholds(thresholds api.ResourceThresholds) error { - if len(thresholds) == 0 { - return fmt.Errorf("no resource threshold is configured") - } - for name, percent := range thresholds { - if percent < MinResourcePercentage || percent > MaxResourcePercentage { - return fmt.Errorf("%v threshold not in [%v, %v] range", name, MinResourcePercentage, MaxResourcePercentage) - } - } - return nil -} diff --git a/pkg/apis/componentconfig/zz_generated.deepcopy.go b/pkg/apis/componentconfig/zz_generated.deepcopy.go index 096c4f358..f450e704c 100644 --- a/pkg/apis/componentconfig/zz_generated.deepcopy.go +++ b/pkg/apis/componentconfig/zz_generated.deepcopy.go @@ -22,9 +22,7 @@ limitations under the License. package componentconfig import ( - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" - api "sigs.k8s.io/descheduler/pkg/api" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -53,389 +51,3 @@ func (in *DeschedulerConfiguration) DeepCopyObject() runtime.Object { } return nil } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HighNodeUtilizationArgs) DeepCopyInto(out *HighNodeUtilizationArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Thresholds != nil { - in, out := &in.Thresholds, &out.Thresholds - *out = make(api.ResourceThresholds, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HighNodeUtilizationArgs. -func (in *HighNodeUtilizationArgs) DeepCopy() *HighNodeUtilizationArgs { - if in == nil { - return nil - } - out := new(HighNodeUtilizationArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *HighNodeUtilizationArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *LowNodeUtilizationArgs) DeepCopyInto(out *LowNodeUtilizationArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Thresholds != nil { - in, out := &in.Thresholds, &out.Thresholds - *out = make(api.ResourceThresholds, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.TargetThresholds != nil { - in, out := &in.TargetThresholds, &out.TargetThresholds - *out = make(api.ResourceThresholds, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LowNodeUtilizationArgs. -func (in *LowNodeUtilizationArgs) DeepCopy() *LowNodeUtilizationArgs { - if in == nil { - return nil - } - out := new(LowNodeUtilizationArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *LowNodeUtilizationArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PodLifeTimeArgs) DeepCopyInto(out *PodLifeTimeArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.MaxPodLifeTimeSeconds != nil { - in, out := &in.MaxPodLifeTimeSeconds, &out.MaxPodLifeTimeSeconds - *out = new(uint) - **out = **in - } - if in.States != nil { - in, out := &in.States, &out.States - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodLifeTimeArgs. -func (in *PodLifeTimeArgs) DeepCopy() *PodLifeTimeArgs { - if in == nil { - return nil - } - out := new(PodLifeTimeArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *PodLifeTimeArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemoveDuplicatesArgs) DeepCopyInto(out *RemoveDuplicatesArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.ExcludeOwnerKinds != nil { - in, out := &in.ExcludeOwnerKinds, &out.ExcludeOwnerKinds - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoveDuplicatesArgs. -func (in *RemoveDuplicatesArgs) DeepCopy() *RemoveDuplicatesArgs { - if in == nil { - return nil - } - out := new(RemoveDuplicatesArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemoveDuplicatesArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemoveFailedPodsArgs) DeepCopyInto(out *RemoveFailedPodsArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.ExcludeOwnerKinds != nil { - in, out := &in.ExcludeOwnerKinds, &out.ExcludeOwnerKinds - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.MinPodLifetimeSeconds != nil { - in, out := &in.MinPodLifetimeSeconds, &out.MinPodLifetimeSeconds - *out = new(uint) - **out = **in - } - if in.Reasons != nil { - in, out := &in.Reasons, &out.Reasons - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoveFailedPodsArgs. -func (in *RemoveFailedPodsArgs) DeepCopy() *RemoveFailedPodsArgs { - if in == nil { - return nil - } - out := new(RemoveFailedPodsArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemoveFailedPodsArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopyInto(out *RemovePodsHavingTooManyRestartsArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsHavingTooManyRestartsArgs. -func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopy() *RemovePodsHavingTooManyRestartsArgs { - if in == nil { - return nil - } - out := new(RemovePodsHavingTooManyRestartsArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopyInto(out *RemovePodsViolatingInterPodAntiAffinityArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingInterPodAntiAffinityArgs. -func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopy() *RemovePodsViolatingInterPodAntiAffinityArgs { - if in == nil { - return nil - } - out := new(RemovePodsViolatingInterPodAntiAffinityArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopyInto(out *RemovePodsViolatingNodeAffinityArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.NodeAffinityType != nil { - in, out := &in.NodeAffinityType, &out.NodeAffinityType - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingNodeAffinityArgs. -func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopy() *RemovePodsViolatingNodeAffinityArgs { - if in == nil { - return nil - } - out := new(RemovePodsViolatingNodeAffinityArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopyInto(out *RemovePodsViolatingNodeTaintsArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - if in.ExcludedTaints != nil { - in, out := &in.ExcludedTaints, &out.ExcludedTaints - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingNodeTaintsArgs. -func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopy() *RemovePodsViolatingNodeTaintsArgs { - if in == nil { - return nil - } - out := new(RemovePodsViolatingNodeTaintsArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopyInto(out *RemovePodsViolatingTopologySpreadConstraintArgs) { - *out = *in - out.TypeMeta = in.TypeMeta - if in.Namespaces != nil { - in, out := &in.Namespaces, &out.Namespaces - *out = new(api.Namespaces) - (*in).DeepCopyInto(*out) - } - if in.LabelSelector != nil { - in, out := &in.LabelSelector, &out.LabelSelector - *out = new(v1.LabelSelector) - (*in).DeepCopyInto(*out) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingTopologySpreadConstraintArgs. -func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopy() *RemovePodsViolatingTopologySpreadConstraintArgs { - if in == nil { - return nil - } - out := new(RemovePodsViolatingTopologySpreadConstraintArgs) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} diff --git a/pkg/descheduler/strategy_migration.go b/pkg/descheduler/strategy_migration.go index 410f1577f..bb2be1686 100644 --- a/pkg/descheduler/strategy_migration.go +++ b/pkg/descheduler/strategy_migration.go @@ -22,8 +22,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/klog/v2" "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig/validation" "sigs.k8s.io/descheduler/pkg/framework" "sigs.k8s.io/descheduler/pkg/framework/plugins/nodeutilization" "sigs.k8s.io/descheduler/pkg/framework/plugins/podlifetime" @@ -42,13 +40,13 @@ import ( var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl){ "RemovePodsViolatingNodeTaints": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.RemovePodsViolatingNodeTaintsArgs{ + args := &removepodsviolatingnodetaints.RemovePodsViolatingNodeTaintsArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, IncludePreferNoSchedule: params.IncludePreferNoSchedule, ExcludedTaints: params.ExcludedTaints, } - if err := validation.ValidateRemovePodsViolatingNodeTaintsArgs(args); err != nil { + if err := removepodsviolatingnodetaints.ValidateRemovePodsViolatingNodeTaintsArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removepodsviolatingnodetaints.PluginName) return } @@ -67,7 +65,7 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * if failedPodsParams == nil { failedPodsParams = &api.FailedPods{} } - args := &componentconfig.RemoveFailedPodsArgs{ + args := &removefailedpods.RemoveFailedPodsArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, IncludingInitContainers: failedPodsParams.IncludingInitContainers, @@ -75,7 +73,7 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * ExcludeOwnerKinds: failedPodsParams.ExcludeOwnerKinds, Reasons: failedPodsParams.Reasons, } - if err := validation.ValidateRemoveFailedPodsArgs(args); err != nil { + if err := removefailedpods.ValidateRemoveFailedPodsArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removefailedpods.PluginName) return } @@ -90,12 +88,12 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "RemovePodsViolatingNodeAffinity": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args := &removepodsviolatingnodeaffinity.RemovePodsViolatingNodeAffinityArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, NodeAffinityType: params.NodeAffinityType, } - if err := validation.ValidateRemovePodsViolatingNodeAffinityArgs(args); err != nil { + if err := removepodsviolatingnodeaffinity.ValidateRemovePodsViolatingNodeAffinityArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removepodsviolatingnodeaffinity.PluginName) return } @@ -110,11 +108,11 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "RemovePodsViolatingInterPodAntiAffinity": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.RemovePodsViolatingInterPodAntiAffinityArgs{ + args := &removepodsviolatinginterpodantiaffinity.RemovePodsViolatingInterPodAntiAffinityArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, } - if err := validation.ValidateRemovePodsViolatingInterPodAntiAffinityArgs(args); err != nil { + if err := removepodsviolatinginterpodantiaffinity.ValidateRemovePodsViolatingInterPodAntiAffinityArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removepodsviolatinginterpodantiaffinity.PluginName) return } @@ -133,13 +131,13 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * if tooManyRestartsParams == nil { tooManyRestartsParams = &api.PodsHavingTooManyRestarts{} } - args := &componentconfig.RemovePodsHavingTooManyRestartsArgs{ + args := &removepodshavingtoomanyrestarts.RemovePodsHavingTooManyRestartsArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, PodRestartThreshold: tooManyRestartsParams.PodRestartThreshold, IncludingInitContainers: tooManyRestartsParams.IncludingInitContainers, } - if err := validation.ValidateRemovePodsHavingTooManyRestartsArgs(args); err != nil { + if err := removepodshavingtoomanyrestarts.ValidateRemovePodsHavingTooManyRestartsArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removepodshavingtoomanyrestarts.PluginName) return } @@ -167,13 +165,13 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * states = append(states, podLifeTimeParams.States...) } - args := &componentconfig.PodLifeTimeArgs{ + args := &podlifetime.PodLifeTimeArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, MaxPodLifeTimeSeconds: podLifeTimeParams.MaxPodLifeTimeSeconds, States: states, } - if err := validation.ValidatePodLifeTimeArgs(args); err != nil { + if err := podlifetime.ValidatePodLifeTimeArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", podlifetime.PluginName) return } @@ -188,13 +186,13 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "RemoveDuplicates": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.RemoveDuplicatesArgs{ + args := &removeduplicates.RemoveDuplicatesArgs{ Namespaces: params.Namespaces, } if params.RemoveDuplicates != nil { args.ExcludeOwnerKinds = params.RemoveDuplicates.ExcludeOwnerKinds } - if err := validation.ValidateRemoveDuplicatesArgs(args); err != nil { + if err := removeduplicates.ValidateRemoveDuplicatesArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removeduplicates.PluginName) return } @@ -209,12 +207,12 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "RemovePodsViolatingTopologySpreadConstraint": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{ + args := &removepodsviolatingtopologyspreadconstraint.RemovePodsViolatingTopologySpreadConstraintArgs{ Namespaces: params.Namespaces, LabelSelector: params.LabelSelector, IncludeSoftConstraints: params.IncludePreferNoSchedule, } - if err := validation.ValidateRemovePodsViolatingTopologySpreadConstraintArgs(args); err != nil { + if err := removepodsviolatingtopologyspreadconstraint.ValidateRemovePodsViolatingTopologySpreadConstraintArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", removepodsviolatingtopologyspreadconstraint.PluginName) return } @@ -229,12 +227,12 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "HighNodeUtilization": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.HighNodeUtilizationArgs{ + args := &nodeutilization.HighNodeUtilizationArgs{ Thresholds: params.NodeResourceUtilizationThresholds.Thresholds, NumberOfNodes: params.NodeResourceUtilizationThresholds.NumberOfNodes, } - if err := validation.ValidateHighNodeUtilizationArgs(args); err != nil { + if err := nodeutilization.ValidateHighNodeUtilizationArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", nodeutilization.HighNodeUtilizationPluginName) return } @@ -249,14 +247,14 @@ var pluginsMap = map[string]func(ctx context.Context, nodes []*v1.Node, params * } }, "LowNodeUtilization": func(ctx context.Context, nodes []*v1.Node, params *api.StrategyParameters, handle *handleImpl) { - args := &componentconfig.LowNodeUtilizationArgs{ + args := &nodeutilization.LowNodeUtilizationArgs{ Thresholds: params.NodeResourceUtilizationThresholds.Thresholds, TargetThresholds: params.NodeResourceUtilizationThresholds.TargetThresholds, UseDeviationThresholds: params.NodeResourceUtilizationThresholds.UseDeviationThresholds, NumberOfNodes: params.NodeResourceUtilizationThresholds.NumberOfNodes, } - if err := validation.ValidateLowNodeUtilizationArgs(args); err != nil { + if err := nodeutilization.ValidateLowNodeUtilizationArgs(args); err != nil { klog.V(1).ErrorS(err, "unable to validate plugin arguments", "pluginName", nodeutilization.LowNodeUtilizationPluginName) return } diff --git a/pkg/framework/plugins/defaultevictor/defaults.go b/pkg/framework/plugins/defaultevictor/defaults.go new file mode 100644 index 000000000..f71731c45 --- /dev/null +++ b/pkg/framework/plugins/defaultevictor/defaults.go @@ -0,0 +1,51 @@ +/* +Copyright 2022 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 defaultevictor + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_DefaultEvictorArgs +// TODO: the final default values would be discussed in community +func SetDefaults_DefaultEvictorArgs(obj *DefaultEvictorArgs) { + if obj.NodeSelector == "" { + obj.NodeSelector = "" + } + if !obj.EvictLocalStoragePods { + obj.EvictSystemCriticalPods = false + } + if !obj.EvictSystemCriticalPods { + obj.EvictSystemCriticalPods = false + } + if !obj.IgnorePvcPods { + obj.IgnorePvcPods = false + } + if !obj.EvictFailedBarePods { + obj.EvictFailedBarePods = false + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if obj.PriorityThreshold == nil { + obj.PriorityThreshold = nil + } + if !obj.NodeFit { + obj.NodeFit = false + } +} diff --git a/pkg/framework/plugins/defaultevictor/defaults_test.go b/pkg/framework/plugins/defaultevictor/defaults_test.go new file mode 100644 index 000000000..2a4072be6 --- /dev/null +++ b/pkg/framework/plugins/defaultevictor/defaults_test.go @@ -0,0 +1,83 @@ +/* +Copyright 2022 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 defaultevictor + +import ( + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_DefaultEvictorArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "DefaultEvictorArgs empty", + in: &DefaultEvictorArgs{}, + want: &DefaultEvictorArgs{ + NodeSelector: "", + EvictLocalStoragePods: false, + EvictSystemCriticalPods: false, + IgnorePvcPods: false, + EvictFailedBarePods: false, + LabelSelector: nil, + PriorityThreshold: nil, + NodeFit: false, + }, + }, + { + name: "DefaultEvictorArgs with value", + in: &DefaultEvictorArgs{ + NodeSelector: "NodeSelector", + EvictLocalStoragePods: true, + EvictSystemCriticalPods: true, + IgnorePvcPods: true, + EvictFailedBarePods: true, + LabelSelector: nil, + PriorityThreshold: &api.PriorityThreshold{ + Value: pointer.Int32(800), + }, + NodeFit: true, + }, + want: &DefaultEvictorArgs{ + NodeSelector: "NodeSelector", + EvictLocalStoragePods: true, + EvictSystemCriticalPods: true, + IgnorePvcPods: true, + EvictFailedBarePods: true, + LabelSelector: nil, + PriorityThreshold: &api.PriorityThreshold{ + Value: pointer.Int32(800), + }, + NodeFit: true, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/defaultevictor/doc.go b/pkg/framework/plugins/defaultevictor/doc.go new file mode 100644 index 000000000..aee6a176f --- /dev/null +++ b/pkg/framework/plugins/defaultevictor/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package defaultevictor diff --git a/pkg/framework/plugins/defaultevictor/register.go b/pkg/framework/plugins/defaultevictor/register.go new file mode 100644 index 000000000..ded9f76c6 --- /dev/null +++ b/pkg/framework/plugins/defaultevictor/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 defaultevictor + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/defaultevictor/zz_generated.defaults.go b/pkg/framework/plugins/defaultevictor/zz_generated.defaults.go new file mode 100644 index 000000000..6e662a5a9 --- /dev/null +++ b/pkg/framework/plugins/defaultevictor/zz_generated.defaults.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package defaultevictor + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&DefaultEvictorArgs{}, func(obj interface{}) { SetObjectDefaults_DefaultEvictorArgs(obj.(*DefaultEvictorArgs)) }) + return nil +} + +func SetObjectDefaults_DefaultEvictorArgs(in *DefaultEvictorArgs) { + SetDefaults_DefaultEvictorArgs(in) +} diff --git a/pkg/framework/plugins/nodeutilization/defaults.go b/pkg/framework/plugins/nodeutilization/defaults.go new file mode 100644 index 000000000..6621a1863 --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/defaults.go @@ -0,0 +1,50 @@ +/* +Copyright 2022 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 nodeutilization + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_LowNodeUtilizationArgs +// TODO: the final default values would be discussed in community +func SetDefaults_LowNodeUtilizationArgs(obj *LowNodeUtilizationArgs) { + if !obj.UseDeviationThresholds { + obj.UseDeviationThresholds = false + } + if obj.Thresholds == nil { + obj.Thresholds = nil + } + if obj.TargetThresholds == nil { + obj.TargetThresholds = nil + } + if obj.NumberOfNodes == 0 { + obj.NumberOfNodes = 0 + } +} + +// SetDefaults_HighNodeUtilizationArgs +// TODO: the final default values would be discussed in community +func SetDefaults_HighNodeUtilizationArgs(obj *HighNodeUtilizationArgs) { + if obj.Thresholds == nil { + obj.Thresholds = nil + } + if obj.NumberOfNodes == 0 { + obj.NumberOfNodes = 0 + } +} diff --git a/pkg/framework/plugins/nodeutilization/defaults_test.go b/pkg/framework/plugins/nodeutilization/defaults_test.go new file mode 100644 index 000000000..d3eb339a8 --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/defaults_test.go @@ -0,0 +1,123 @@ +/* +Copyright 2022 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 nodeutilization + +import ( + "github.com/google/go-cmp/cmp" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_LowNodeUtilizationArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "LowNodeUtilizationArgs empty", + in: &LowNodeUtilizationArgs{}, + want: &LowNodeUtilizationArgs{ + UseDeviationThresholds: false, + Thresholds: nil, + TargetThresholds: nil, + NumberOfNodes: 0, + }, + }, + { + name: "LowNodeUtilizationArgs with value", + in: &LowNodeUtilizationArgs{ + UseDeviationThresholds: true, + Thresholds: api.ResourceThresholds{ + v1.ResourceCPU: 20, + v1.ResourceMemory: 120, + }, + TargetThresholds: api.ResourceThresholds{ + v1.ResourceCPU: 80, + v1.ResourceMemory: 80, + }, + NumberOfNodes: 10, + }, + want: &LowNodeUtilizationArgs{ + UseDeviationThresholds: true, + Thresholds: api.ResourceThresholds{ + v1.ResourceCPU: 20, + v1.ResourceMemory: 120, + }, + TargetThresholds: api.ResourceThresholds{ + v1.ResourceCPU: 80, + v1.ResourceMemory: 80, + }, + NumberOfNodes: 10, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} + +func TestSetDefaults_HighNodeUtilizationArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "HighNodeUtilizationArgs empty", + in: &HighNodeUtilizationArgs{}, + want: &HighNodeUtilizationArgs{ + Thresholds: nil, + NumberOfNodes: 0, + }, + }, + { + name: "HighNodeUtilizationArgs with value", + in: &HighNodeUtilizationArgs{ + Thresholds: api.ResourceThresholds{ + v1.ResourceCPU: 20, + v1.ResourceMemory: 120, + }, + NumberOfNodes: 10, + }, + want: &HighNodeUtilizationArgs{ + Thresholds: api.ResourceThresholds{ + v1.ResourceCPU: 20, + v1.ResourceMemory: 120, + }, + NumberOfNodes: 10, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/nodeutilization/doc.go b/pkg/framework/plugins/nodeutilization/doc.go new file mode 100644 index 000000000..09d3bbc6d --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package nodeutilization diff --git a/pkg/framework/plugins/nodeutilization/highnodeutilization.go b/pkg/framework/plugins/nodeutilization/highnodeutilization.go index 1eb47254c..0c3977153 100644 --- a/pkg/framework/plugins/nodeutilization/highnodeutilization.go +++ b/pkg/framework/plugins/nodeutilization/highnodeutilization.go @@ -27,7 +27,6 @@ import ( "sigs.k8s.io/descheduler/pkg/api" nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" ) @@ -39,7 +38,7 @@ const HighNodeUtilizationPluginName = "HighNodeUtilization" type HighNodeUtilization struct { handle framework.Handle - args *componentconfig.HighNodeUtilizationArgs + args *HighNodeUtilizationArgs podFilter func(pod *v1.Pod) bool } @@ -47,7 +46,7 @@ var _ framework.BalancePlugin = &HighNodeUtilization{} // NewHighNodeUtilization builds plugin from its arguments while passing a handle func NewHighNodeUtilization(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - highNodeUtilizatioArgs, ok := args.(*componentconfig.HighNodeUtilizationArgs) + highNodeUtilizatioArgs, ok := args.(*HighNodeUtilizationArgs) if !ok { return nil, fmt.Errorf("want args to be of type HighNodeUtilizationArgs, got %T", args) } diff --git a/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go b/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go index a4053f8f5..def0ec44b 100644 --- a/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go +++ b/pkg/framework/plugins/nodeutilization/highnodeutilization_test.go @@ -31,7 +31,6 @@ import ( "k8s.io/client-go/tools/events" "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -524,7 +523,7 @@ func TestHighNodeUtilization(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := NewHighNodeUtilization(&componentconfig.HighNodeUtilizationArgs{ + plugin, err := NewHighNodeUtilization(&HighNodeUtilizationArgs{ Thresholds: testCase.thresholds, }, handle) @@ -676,7 +675,7 @@ func TestHighNodeUtilizationWithTaints(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := NewHighNodeUtilization(&componentconfig.HighNodeUtilizationArgs{ + plugin, err := NewHighNodeUtilization(&HighNodeUtilizationArgs{ Thresholds: api.ResourceThresholds{ v1.ResourceCPU: 40, }, diff --git a/pkg/framework/plugins/nodeutilization/lownodeutilization.go b/pkg/framework/plugins/nodeutilization/lownodeutilization.go index cc3ff2bff..b11ff4dce 100644 --- a/pkg/framework/plugins/nodeutilization/lownodeutilization.go +++ b/pkg/framework/plugins/nodeutilization/lownodeutilization.go @@ -24,7 +24,6 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -37,7 +36,7 @@ const LowNodeUtilizationPluginName = "LowNodeUtilization" type LowNodeUtilization struct { handle framework.Handle - args *componentconfig.LowNodeUtilizationArgs + args *LowNodeUtilizationArgs podFilter func(pod *v1.Pod) bool } @@ -45,7 +44,7 @@ var _ framework.BalancePlugin = &LowNodeUtilization{} // NewLowNodeUtilization builds plugin from its arguments while passing a handle func NewLowNodeUtilization(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - lowNodeUtilizationArgsArgs, ok := args.(*componentconfig.LowNodeUtilizationArgs) + lowNodeUtilizationArgsArgs, ok := args.(*LowNodeUtilizationArgs) if !ok { return nil, fmt.Errorf("want args to be of type LowNodeUtilizationArgs, got %T", args) } diff --git a/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go b/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go index 30f5401a6..8cf7be5d8 100644 --- a/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go +++ b/pkg/framework/plugins/nodeutilization/lownodeutilization_test.go @@ -22,7 +22,6 @@ import ( "testing" "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" frameworkfake "sigs.k8s.io/descheduler/pkg/framework/fake" "sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor" @@ -790,7 +789,7 @@ func TestLowNodeUtilization(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := NewLowNodeUtilization(&componentconfig.LowNodeUtilizationArgs{ + plugin, err := NewLowNodeUtilization(&LowNodeUtilizationArgs{ Thresholds: test.thresholds, TargetThresholds: test.targetThresholds, @@ -963,7 +962,7 @@ func TestLowNodeUtilizationWithTaints(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := NewLowNodeUtilization(&componentconfig.LowNodeUtilizationArgs{ + plugin, err := NewLowNodeUtilization(&LowNodeUtilizationArgs{ Thresholds: api.ResourceThresholds{ v1.ResourcePods: 20, diff --git a/pkg/framework/plugins/nodeutilization/register.go b/pkg/framework/plugins/nodeutilization/register.go new file mode 100644 index 000000000..5d9df2dbc --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 nodeutilization + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/nodeutilization/types.go b/pkg/framework/plugins/nodeutilization/types.go new file mode 100644 index 000000000..f850f6470 --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/types.go @@ -0,0 +1,41 @@ +/* +Copyright 2022 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 nodeutilization + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type LowNodeUtilizationArgs struct { + metav1.TypeMeta + + UseDeviationThresholds bool + Thresholds api.ResourceThresholds + TargetThresholds api.ResourceThresholds + NumberOfNodes int +} + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type HighNodeUtilizationArgs struct { + metav1.TypeMeta + + Thresholds api.ResourceThresholds + NumberOfNodes int +} diff --git a/pkg/framework/plugins/nodeutilization/validation.go b/pkg/framework/plugins/nodeutilization/validation.go new file mode 100644 index 000000000..89969a8d9 --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/validation.go @@ -0,0 +1,63 @@ +/* +Copyright 2022 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 nodeutilization + +import ( + "fmt" + "sigs.k8s.io/descheduler/pkg/api" +) + +func ValidateHighNodeUtilizationArgs(args *HighNodeUtilizationArgs) error { + return validateThresholds(args.Thresholds) +} + +func ValidateLowNodeUtilizationArgs(args *LowNodeUtilizationArgs) error { + return validateLowNodeUtilizationThresholds(args.Thresholds, args.TargetThresholds, args.UseDeviationThresholds) +} + +func validateLowNodeUtilizationThresholds(thresholds, targetThresholds api.ResourceThresholds, useDeviationThresholds bool) error { + // validate thresholds and targetThresholds config + if err := validateThresholds(thresholds); err != nil { + return fmt.Errorf("thresholds config is not valid: %v", err) + } + if err := validateThresholds(targetThresholds); err != nil { + return fmt.Errorf("targetThresholds config is not valid: %v", err) + } + + // validate if thresholds and targetThresholds have same resources configured + if len(thresholds) != len(targetThresholds) { + return fmt.Errorf("thresholds and targetThresholds configured different resources") + } + for resourceName, value := range thresholds { + if targetValue, ok := targetThresholds[resourceName]; !ok { + return fmt.Errorf("thresholds and targetThresholds configured different resources") + } else if value > targetValue && !useDeviationThresholds { + return fmt.Errorf("thresholds' %v percentage is greater than targetThresholds'", resourceName) + } + } + return nil +} + +// validateThresholds checks if thresholds have valid resource name and resource percentage configured +func validateThresholds(thresholds api.ResourceThresholds) error { + if len(thresholds) == 0 { + return fmt.Errorf("no resource threshold is configured") + } + for name, percent := range thresholds { + if percent < MinResourcePercentage || percent > MaxResourcePercentage { + return fmt.Errorf("%v threshold not in [%v, %v] range", name, MinResourcePercentage, MaxResourcePercentage) + } + } + return nil +} diff --git a/pkg/apis/componentconfig/validation/validation_pluginargs_test.go b/pkg/framework/plugins/nodeutilization/validation_test.go similarity index 59% rename from pkg/apis/componentconfig/validation/validation_pluginargs_test.go rename to pkg/framework/plugins/nodeutilization/validation_test.go index e78c69ed4..655c19cad 100644 --- a/pkg/apis/componentconfig/validation/validation_pluginargs_test.go +++ b/pkg/framework/plugins/nodeutilization/validation_test.go @@ -14,162 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -package validation +package nodeutilization import ( "fmt" - "testing" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" + "testing" ) -func TestValidateRemovePodsViolatingNodeTaintsArgs(t *testing.T) { - testCases := []struct { - description string - args *componentconfig.RemovePodsViolatingNodeTaintsArgs - expectError bool - }{ - { - description: "valid namespace args, no errors", - args: &componentconfig.RemovePodsViolatingNodeTaintsArgs{ - Namespaces: &api.Namespaces{ - Include: []string{"default"}, - }, - }, - expectError: false, - }, - { - description: "invalid namespaces args, expects error", - args: &componentconfig.RemovePodsViolatingNodeTaintsArgs{ - Namespaces: &api.Namespaces{ - Include: []string{"default"}, - Exclude: []string{"kube-system"}, - }, - }, - expectError: true, - }, - { - description: "valid label selector args, no errors", - args: &componentconfig.RemovePodsViolatingNodeTaintsArgs{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{"role.kubernetes.io/node": ""}, - }, - }, - expectError: false, - }, - { - description: "invalid label selector args, expects errors", - args: &componentconfig.RemovePodsViolatingNodeTaintsArgs{ - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Operator: metav1.LabelSelectorOpIn, - }, - }, - }, - }, - expectError: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - err := ValidateRemovePodsViolatingNodeTaintsArgs(tc.args) - - hasError := err != nil - if tc.expectError != hasError { - t.Error("unexpected arg validation behavior") - } - }) - } -} - -func TestValidateRemovePodsViolatingNodeAffinityArgs(t *testing.T) { - testCases := []struct { - description string - args *componentconfig.RemovePodsViolatingNodeAffinityArgs - expectError bool - }{ - { - description: "nil NodeAffinityType args, expects errors", - args: &componentconfig.RemovePodsViolatingNodeAffinityArgs{ - NodeAffinityType: nil, - }, - expectError: true, - }, - { - description: "empty NodeAffinityType args, expects errors", - args: &componentconfig.RemovePodsViolatingNodeAffinityArgs{ - NodeAffinityType: []string{}, - }, - expectError: true, - }, - { - description: "valid NodeAffinityType args, no errors", - args: &componentconfig.RemovePodsViolatingNodeAffinityArgs{ - NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, - }, - expectError: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - err := ValidateRemovePodsViolatingNodeAffinityArgs(tc.args) - - hasError := err != nil - if tc.expectError != hasError { - t.Error("unexpected arg validation behavior") - } - }) - } -} - -func TestValidateRemovePodLifeTimeArgs(t *testing.T) { - testCases := []struct { - description string - args *componentconfig.PodLifeTimeArgs - expectError bool - }{ - { - description: "valid arg, no errors", - args: &componentconfig.PodLifeTimeArgs{ - MaxPodLifeTimeSeconds: func(i uint) *uint { return &i }(1), - States: []string{string(v1.PodRunning)}, - }, - expectError: false, - }, - { - description: "nil MaxPodLifeTimeSeconds arg, expects errors", - args: &componentconfig.PodLifeTimeArgs{ - MaxPodLifeTimeSeconds: nil, - }, - expectError: true, - }, - { - description: "invalid pod state arg, expects errors", - args: &componentconfig.PodLifeTimeArgs{ - States: []string{string(v1.NodeRunning)}, - }, - expectError: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.description, func(t *testing.T) { - err := ValidatePodLifeTimeArgs(tc.args) - - hasError := err != nil - if tc.expectError != hasError { - t.Error("unexpected arg validation behavior") - } - }) - } -} - func TestValidateLowNodeUtilizationPluginConfig(t *testing.T) { var extendedResource = v1.ResourceName("example.com/foo") tests := []struct { @@ -311,7 +164,7 @@ func TestValidateLowNodeUtilizationPluginConfig(t *testing.T) { } for _, testCase := range tests { - args := &componentconfig.LowNodeUtilizationArgs{ + args := &LowNodeUtilizationArgs{ Thresholds: testCase.thresholds, TargetThresholds: testCase.targetThresholds, diff --git a/pkg/framework/plugins/nodeutilization/zz_generated.deepcopy.go b/pkg/framework/plugins/nodeutilization/zz_generated.deepcopy.go new file mode 100644 index 000000000..faab6a47c --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/zz_generated.deepcopy.go @@ -0,0 +1,98 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package nodeutilization + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HighNodeUtilizationArgs) DeepCopyInto(out *HighNodeUtilizationArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Thresholds != nil { + in, out := &in.Thresholds, &out.Thresholds + *out = make(api.ResourceThresholds, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HighNodeUtilizationArgs. +func (in *HighNodeUtilizationArgs) DeepCopy() *HighNodeUtilizationArgs { + if in == nil { + return nil + } + out := new(HighNodeUtilizationArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *HighNodeUtilizationArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LowNodeUtilizationArgs) DeepCopyInto(out *LowNodeUtilizationArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Thresholds != nil { + in, out := &in.Thresholds, &out.Thresholds + *out = make(api.ResourceThresholds, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.TargetThresholds != nil { + in, out := &in.TargetThresholds, &out.TargetThresholds + *out = make(api.ResourceThresholds, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LowNodeUtilizationArgs. +func (in *LowNodeUtilizationArgs) DeepCopy() *LowNodeUtilizationArgs { + if in == nil { + return nil + } + out := new(LowNodeUtilizationArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *LowNodeUtilizationArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/nodeutilization/zz_generated.defaults.go b/pkg/framework/plugins/nodeutilization/zz_generated.defaults.go new file mode 100644 index 000000000..ede106f47 --- /dev/null +++ b/pkg/framework/plugins/nodeutilization/zz_generated.defaults.go @@ -0,0 +1,43 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package nodeutilization + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&HighNodeUtilizationArgs{}, func(obj interface{}) { SetObjectDefaults_HighNodeUtilizationArgs(obj.(*HighNodeUtilizationArgs)) }) + scheme.AddTypeDefaultingFunc(&LowNodeUtilizationArgs{}, func(obj interface{}) { SetObjectDefaults_LowNodeUtilizationArgs(obj.(*LowNodeUtilizationArgs)) }) + return nil +} + +func SetObjectDefaults_HighNodeUtilizationArgs(in *HighNodeUtilizationArgs) { + SetDefaults_HighNodeUtilizationArgs(in) +} + +func SetObjectDefaults_LowNodeUtilizationArgs(in *LowNodeUtilizationArgs) { + SetDefaults_LowNodeUtilizationArgs(in) +} diff --git a/pkg/framework/plugins/podlifetime/defaults.go b/pkg/framework/plugins/podlifetime/defaults.go new file mode 100644 index 000000000..b4aaa3686 --- /dev/null +++ b/pkg/framework/plugins/podlifetime/defaults.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 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 podlifetime + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_PodLifeTimeArgs +// TODO: the final default values would be discussed in community +func SetDefaults_PodLifeTimeArgs(obj *PodLifeTimeArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if obj.MaxPodLifeTimeSeconds == nil { + obj.MaxPodLifeTimeSeconds = nil + } + if obj.States == nil { + obj.States = nil + } +} diff --git a/pkg/framework/plugins/podlifetime/defaults_test.go b/pkg/framework/plugins/podlifetime/defaults_test.go new file mode 100644 index 000000000..67882979b --- /dev/null +++ b/pkg/framework/plugins/podlifetime/defaults_test.go @@ -0,0 +1,72 @@ +/* +Copyright 2022 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 podlifetime + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_PodLifeTimeArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "PodLifeTimeArgs empty", + in: &PodLifeTimeArgs{}, + want: &PodLifeTimeArgs{ + Namespaces: nil, + LabelSelector: nil, + MaxPodLifeTimeSeconds: nil, + States: nil, + }, + }, + { + name: "PodLifeTimeArgs with value", + in: &PodLifeTimeArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + MaxPodLifeTimeSeconds: pointer.Uint(600), + States: []string{"Pending"}, + }, + want: &PodLifeTimeArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + MaxPodLifeTimeSeconds: pointer.Uint(600), + States: []string{"Pending"}, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/podlifetime/doc.go b/pkg/framework/plugins/podlifetime/doc.go new file mode 100644 index 000000000..a345e2bb6 --- /dev/null +++ b/pkg/framework/plugins/podlifetime/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package podlifetime diff --git a/pkg/framework/plugins/podlifetime/pod_lifetime.go b/pkg/framework/plugins/podlifetime/pod_lifetime.go index 894b09da8..3c1e13ee5 100644 --- a/pkg/framework/plugins/podlifetime/pod_lifetime.go +++ b/pkg/framework/plugins/podlifetime/pod_lifetime.go @@ -25,7 +25,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" @@ -39,13 +38,13 @@ var _ framework.DeschedulePlugin = &PodLifeTime{} // PodLifeTime evicts pods on the node that violate the max pod lifetime threshold type PodLifeTime struct { handle framework.Handle - args *componentconfig.PodLifeTimeArgs + args *PodLifeTimeArgs podFilter podutil.FilterFunc } // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - podLifeTimeArgs, ok := args.(*componentconfig.PodLifeTimeArgs) + podLifeTimeArgs, ok := args.(*PodLifeTimeArgs) if !ok { return nil, fmt.Errorf("want args to be of type PodLifeTimeArgs, got %T", args) } diff --git a/pkg/framework/plugins/podlifetime/pod_lifetime_test.go b/pkg/framework/plugins/podlifetime/pod_lifetime_test.go index 5e859bd65..75960a152 100644 --- a/pkg/framework/plugins/podlifetime/pod_lifetime_test.go +++ b/pkg/framework/plugins/podlifetime/pod_lifetime_test.go @@ -28,7 +28,6 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -143,7 +142,7 @@ func TestPodLifeTime(t *testing.T) { var maxLifeTime uint = 600 testCases := []struct { description string - args *componentconfig.PodLifeTimeArgs + args *PodLifeTimeArgs pods []*v1.Pod nodes []*v1.Node expectedEvictedPodCount uint @@ -153,7 +152,7 @@ func TestPodLifeTime(t *testing.T) { }{ { description: "Two pods in the `dev` Namespace, 1 is new and 1 very is old. 1 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p1, p2}, @@ -162,7 +161,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two pods in the `dev` Namespace, 2 are new and 0 are old. 0 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p3, p4}, @@ -171,7 +170,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two pods in the `dev` Namespace, 1 created 605 seconds ago. 1 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p5, p6}, @@ -180,7 +179,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two pods in the `dev` Namespace, 1 created 595 seconds ago. 0 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p7, p8}, @@ -189,7 +188,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two pods, one with ContainerCreating state. 1 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, States: []string{"ContainerCreating"}, }, @@ -212,7 +211,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two pods, one with PodInitializing state. 1 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, States: []string{"PodInitializing"}, }, @@ -235,7 +234,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "Two old pods with different states. 1 should be evicted.", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, States: []string{"Pending"}, }, @@ -245,7 +244,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "does not evict pvc pods with ignorePvcPods set to true", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p11}, @@ -255,7 +254,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "evicts pvc pods with ignorePvcPods set to false (or unset)", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p11}, @@ -264,7 +263,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "No pod to evicted since all pod terminating", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, LabelSelector: &metav1.LabelSelector{ MatchLabels: map[string]string{"foo": "bar"}, @@ -276,7 +275,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "No pod should be evicted since pod terminating", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, LabelSelector: &metav1.LabelSelector{ MatchLabels: map[string]string{"foo": "bar"}, @@ -288,7 +287,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "2 Oldest pods should be evicted when maxPodsToEvictPerNode and maxPodsToEvictPerNamespace are not set", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p1, p2, p9}, @@ -299,7 +298,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "1 Oldest pod should be evicted when maxPodsToEvictPerNamespace is set to 1", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p1, p2, p9}, @@ -309,7 +308,7 @@ func TestPodLifeTime(t *testing.T) { }, { description: "1 Oldest pod should be evicted when maxPodsToEvictPerNode is set to 1", - args: &componentconfig.PodLifeTimeArgs{ + args: &PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxLifeTime, }, pods: []*v1.Pod{p1, p2, p9}, diff --git a/pkg/framework/plugins/podlifetime/register.go b/pkg/framework/plugins/podlifetime/register.go new file mode 100644 index 000000000..d42ebd34a --- /dev/null +++ b/pkg/framework/plugins/podlifetime/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 podlifetime + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/podlifetime/types.go b/pkg/framework/plugins/podlifetime/types.go new file mode 100644 index 000000000..c95d0ea3d --- /dev/null +++ b/pkg/framework/plugins/podlifetime/types.go @@ -0,0 +1,32 @@ +/* +Copyright 2022 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 podlifetime + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodLifeTimeArgs holds arguments used to configure PodLifeTime plugin. +type PodLifeTimeArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + MaxPodLifeTimeSeconds *uint + States []string +} diff --git a/pkg/framework/plugins/podlifetime/validation.go b/pkg/framework/plugins/podlifetime/validation.go new file mode 100644 index 000000000..4ef151764 --- /dev/null +++ b/pkg/framework/plugins/podlifetime/validation.go @@ -0,0 +1,56 @@ +/* +Copyright 2022 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 podlifetime + +import ( + "fmt" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +// ValidatePodLifeTimeArgs validates PodLifeTime arguments +func ValidatePodLifeTimeArgs(args *PodLifeTimeArgs) error { + if args.MaxPodLifeTimeSeconds == nil { + return fmt.Errorf("MaxPodLifeTimeSeconds not set") + } + + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + podLifeTimeAllowedStates := sets.NewString( + string(v1.PodRunning), + string(v1.PodPending), + + // Container state reasons: https://github.com/kubernetes/kubernetes/blob/release-1.24/pkg/kubelet/kubelet_pods.go#L76-L79 + "PodInitializing", + "ContainerCreating", + ) + + if !podLifeTimeAllowedStates.HasAll(args.States...) { + return fmt.Errorf("states must be one of %v", podLifeTimeAllowedStates.List()) + } + + return nil +} diff --git a/pkg/framework/plugins/podlifetime/validation_test.go b/pkg/framework/plugins/podlifetime/validation_test.go new file mode 100644 index 000000000..4c5b097d1 --- /dev/null +++ b/pkg/framework/plugins/podlifetime/validation_test.go @@ -0,0 +1,64 @@ +/* +Copyright 2022 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 podlifetime + +import ( + v1 "k8s.io/api/core/v1" + "testing" +) + +func TestValidateRemovePodLifeTimeArgs(t *testing.T) { + testCases := []struct { + description string + args *PodLifeTimeArgs + expectError bool + }{ + { + description: "valid arg, no errors", + args: &PodLifeTimeArgs{ + MaxPodLifeTimeSeconds: func(i uint) *uint { return &i }(1), + States: []string{string(v1.PodRunning)}, + }, + expectError: false, + }, + { + description: "nil MaxPodLifeTimeSeconds arg, expects errors", + args: &PodLifeTimeArgs{ + MaxPodLifeTimeSeconds: nil, + }, + expectError: true, + }, + { + description: "invalid pod state arg, expects errors", + args: &PodLifeTimeArgs{ + States: []string{string(v1.NodeRunning)}, + }, + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + err := ValidatePodLifeTimeArgs(tc.args) + + hasError := err != nil + if tc.expectError != hasError { + t.Error("unexpected arg validation behavior") + } + }) + } +} diff --git a/pkg/framework/plugins/podlifetime/zz_generated.deepcopy.go b/pkg/framework/plugins/podlifetime/zz_generated.deepcopy.go new file mode 100644 index 000000000..277c92ba9 --- /dev/null +++ b/pkg/framework/plugins/podlifetime/zz_generated.deepcopy.go @@ -0,0 +1,73 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package podlifetime + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodLifeTimeArgs) DeepCopyInto(out *PodLifeTimeArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.MaxPodLifeTimeSeconds != nil { + in, out := &in.MaxPodLifeTimeSeconds, &out.MaxPodLifeTimeSeconds + *out = new(uint) + **out = **in + } + if in.States != nil { + in, out := &in.States, &out.States + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodLifeTimeArgs. +func (in *PodLifeTimeArgs) DeepCopy() *PodLifeTimeArgs { + if in == nil { + return nil + } + out := new(PodLifeTimeArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodLifeTimeArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/podlifetime/zz_generated.defaults.go b/pkg/framework/plugins/podlifetime/zz_generated.defaults.go new file mode 100644 index 000000000..fc593d17d --- /dev/null +++ b/pkg/framework/plugins/podlifetime/zz_generated.defaults.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package podlifetime + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&PodLifeTimeArgs{}, func(obj interface{}) { SetObjectDefaults_PodLifeTimeArgs(obj.(*PodLifeTimeArgs)) }) + return nil +} + +func SetObjectDefaults_PodLifeTimeArgs(in *PodLifeTimeArgs) { + SetDefaults_PodLifeTimeArgs(in) +} diff --git a/pkg/framework/plugins/removeduplicates/defaults.go b/pkg/framework/plugins/removeduplicates/defaults.go new file mode 100644 index 000000000..7da4f6898 --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2022 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 removeduplicates + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemoveDuplicatesArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemoveDuplicatesArgs(obj *RemoveDuplicatesArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.ExcludeOwnerKinds == nil { + obj.ExcludeOwnerKinds = nil + } +} diff --git a/pkg/framework/plugins/removeduplicates/defaults_test.go b/pkg/framework/plugins/removeduplicates/defaults_test.go new file mode 100644 index 000000000..b594b4318 --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/defaults_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2022 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 removeduplicates + +import ( + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemoveDuplicatesArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemoveDuplicatesArgs empty", + in: &RemoveDuplicatesArgs{}, + want: &RemoveDuplicatesArgs{ + Namespaces: nil, + ExcludeOwnerKinds: nil, + }, + }, + { + name: "RemoveDuplicatesArgs with value", + in: &RemoveDuplicatesArgs{ + Namespaces: &api.Namespaces{}, + ExcludeOwnerKinds: []string{"ReplicaSet"}, + }, + want: &RemoveDuplicatesArgs{ + Namespaces: &api.Namespaces{}, + ExcludeOwnerKinds: []string{"ReplicaSet"}, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removeduplicates/doc.go b/pkg/framework/plugins/removeduplicates/doc.go new file mode 100644 index 000000000..b54e0840b --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removeduplicates diff --git a/pkg/framework/plugins/removeduplicates/register.go b/pkg/framework/plugins/removeduplicates/register.go new file mode 100644 index 000000000..02dd932b6 --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removeduplicates + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removeduplicates/removeduplicates.go b/pkg/framework/plugins/removeduplicates/removeduplicates.go index b808bebaf..afe2f5f03 100644 --- a/pkg/framework/plugins/removeduplicates/removeduplicates.go +++ b/pkg/framework/plugins/removeduplicates/removeduplicates.go @@ -32,7 +32,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -47,7 +46,7 @@ const PluginName = "RemoveDuplicates" type RemoveDuplicates struct { handle framework.Handle - args *componentconfig.RemoveDuplicatesArgs + args *RemoveDuplicatesArgs podFilter podutil.FilterFunc } @@ -60,7 +59,7 @@ type podOwner struct { // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - removeDuplicatesArgs, ok := args.(*componentconfig.RemoveDuplicatesArgs) + removeDuplicatesArgs, ok := args.(*RemoveDuplicatesArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemoveDuplicatesArgs, got %T", args) } diff --git a/pkg/framework/plugins/removeduplicates/removeduplicates_test.go b/pkg/framework/plugins/removeduplicates/removeduplicates_test.go index ec022a9d6..c7d5ce155 100644 --- a/pkg/framework/plugins/removeduplicates/removeduplicates_test.go +++ b/pkg/framework/plugins/removeduplicates/removeduplicates_test.go @@ -21,7 +21,6 @@ import ( "testing" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" frameworkfake "sigs.k8s.io/descheduler/pkg/framework/fake" "sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor" @@ -351,7 +350,7 @@ func TestFindDuplicatePods(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := New(&componentconfig.RemoveDuplicatesArgs{ + plugin, err := New(&RemoveDuplicatesArgs{ ExcludeOwnerKinds: testCase.excludeOwnerKinds, }, handle, @@ -802,7 +801,7 @@ func TestRemoveDuplicatesUniformly(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := New(&componentconfig.RemoveDuplicatesArgs{}, + plugin, err := New(&RemoveDuplicatesArgs{}, handle, ) if err != nil { diff --git a/pkg/framework/plugins/removeduplicates/types.go b/pkg/framework/plugins/removeduplicates/types.go new file mode 100644 index 000000000..981ddba0e --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/types.go @@ -0,0 +1,29 @@ +/* +Copyright 2022 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 removeduplicates + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +type RemoveDuplicatesArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + ExcludeOwnerKinds []string +} diff --git a/pkg/framework/plugins/removeduplicates/validation.go b/pkg/framework/plugins/removeduplicates/validation.go new file mode 100644 index 000000000..240bd36e9 --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/validation.go @@ -0,0 +1,27 @@ +/* +Copyright 2022 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 removeduplicates + +import ( + "fmt" +) + +func ValidateRemoveDuplicatesArgs(args *RemoveDuplicatesArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + return nil +} diff --git a/pkg/framework/plugins/removeduplicates/zz_generated.deepcopy.go b/pkg/framework/plugins/removeduplicates/zz_generated.deepcopy.go new file mode 100644 index 000000000..bc3652f1d --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/zz_generated.deepcopy.go @@ -0,0 +1,62 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removeduplicates + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoveDuplicatesArgs) DeepCopyInto(out *RemoveDuplicatesArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.ExcludeOwnerKinds != nil { + in, out := &in.ExcludeOwnerKinds, &out.ExcludeOwnerKinds + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoveDuplicatesArgs. +func (in *RemoveDuplicatesArgs) DeepCopy() *RemoveDuplicatesArgs { + if in == nil { + return nil + } + out := new(RemoveDuplicatesArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemoveDuplicatesArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removeduplicates/zz_generated.defaults.go b/pkg/framework/plugins/removeduplicates/zz_generated.defaults.go new file mode 100644 index 000000000..2ee3e64b7 --- /dev/null +++ b/pkg/framework/plugins/removeduplicates/zz_generated.defaults.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removeduplicates + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemoveDuplicatesArgs{}, func(obj interface{}) { SetObjectDefaults_RemoveDuplicatesArgs(obj.(*RemoveDuplicatesArgs)) }) + return nil +} + +func SetObjectDefaults_RemoveDuplicatesArgs(in *RemoveDuplicatesArgs) { + SetDefaults_RemoveDuplicatesArgs(in) +} diff --git a/pkg/framework/plugins/removefailedpods/defaults.go b/pkg/framework/plugins/removefailedpods/defaults.go new file mode 100644 index 000000000..52f95e1c6 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/defaults.go @@ -0,0 +1,45 @@ +/* +Copyright 2022 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 removefailedpods + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemoveFailedPodsArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemoveFailedPodsArgs(obj *RemoveFailedPodsArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if obj.ExcludeOwnerKinds == nil { + obj.ExcludeOwnerKinds = nil + } + if obj.MinPodLifetimeSeconds == nil { + obj.MinPodLifetimeSeconds = nil + } + if obj.Reasons == nil { + obj.Reasons = nil + } + if !obj.IncludingInitContainers { + obj.IncludingInitContainers = false + } +} diff --git a/pkg/framework/plugins/removefailedpods/defaults_test.go b/pkg/framework/plugins/removefailedpods/defaults_test.go new file mode 100644 index 000000000..dfef43736 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/defaults_test.go @@ -0,0 +1,74 @@ +/* +Copyright 2022 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 removefailedpods + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/utils/pointer" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemoveFailedPodsArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemoveFailedPodsArgs empty", + in: &RemoveFailedPodsArgs{}, + want: &RemoveFailedPodsArgs{ + Namespaces: nil, + LabelSelector: nil, + ExcludeOwnerKinds: nil, + MinPodLifetimeSeconds: nil, + Reasons: nil, + IncludingInitContainers: false, + }, + }, + { + name: "RemoveFailedPodsArgs with value", + in: &RemoveFailedPodsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + ExcludeOwnerKinds: []string{"ReplicaSet"}, + MinPodLifetimeSeconds: pointer.Uint(0), + Reasons: []string{"reason"}, + IncludingInitContainers: true, + }, + want: &RemoveFailedPodsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + ExcludeOwnerKinds: []string{"ReplicaSet"}, + MinPodLifetimeSeconds: pointer.Uint(0), + Reasons: []string{"reason"}, + IncludingInitContainers: true, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removefailedpods/doc.go b/pkg/framework/plugins/removefailedpods/doc.go new file mode 100644 index 000000000..3951b1d10 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removefailedpods diff --git a/pkg/framework/plugins/removefailedpods/failedpods.go b/pkg/framework/plugins/removefailedpods/failedpods.go index 87435d455..9b7fb57ca 100644 --- a/pkg/framework/plugins/removefailedpods/failedpods.go +++ b/pkg/framework/plugins/removefailedpods/failedpods.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -38,7 +37,7 @@ const PluginName = "RemoveFailedPods" // RemoveFailedPods evicts pods in failed status phase that match the given args criteria type RemoveFailedPods struct { handle framework.Handle - args *componentconfig.RemoveFailedPodsArgs + args *RemoveFailedPodsArgs podFilter podutil.FilterFunc } @@ -46,7 +45,7 @@ var _ framework.DeschedulePlugin = &RemoveFailedPods{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - failedPodsArgs, ok := args.(*componentconfig.RemoveFailedPodsArgs) + failedPodsArgs, ok := args.(*RemoveFailedPodsArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemoveFailedPodsArgs, got %T", args) } @@ -114,7 +113,7 @@ func (d *RemoveFailedPods) Deschedule(ctx context.Context, nodes []*v1.Node) *fr } // validateCanEvict looks at failedPodArgs to see if pod can be evicted given the args. -func validateCanEvict(pod *v1.Pod, failedPodArgs *componentconfig.RemoveFailedPodsArgs) error { +func validateCanEvict(pod *v1.Pod, failedPodArgs *RemoveFailedPodsArgs) error { var errs []error if failedPodArgs.MinPodLifetimeSeconds != nil { diff --git a/pkg/framework/plugins/removefailedpods/failedpods_test.go b/pkg/framework/plugins/removefailedpods/failedpods_test.go index 561f97066..b0b33cce5 100644 --- a/pkg/framework/plugins/removefailedpods/failedpods_test.go +++ b/pkg/framework/plugins/removefailedpods/failedpods_test.go @@ -27,7 +27,6 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" @@ -45,8 +44,8 @@ func TestRemoveFailedPods(t *testing.T) { createRemoveFailedPodsArgs := func( includingInitContainers bool, reasons, excludeKinds []string, - minAgeSeconds *uint) componentconfig.RemoveFailedPodsArgs { - return componentconfig.RemoveFailedPodsArgs{ + minAgeSeconds *uint) RemoveFailedPodsArgs { + return RemoveFailedPodsArgs{ IncludingInitContainers: includingInitContainers, Reasons: reasons, MinPodLifetimeSeconds: minAgeSeconds, @@ -57,14 +56,14 @@ func TestRemoveFailedPods(t *testing.T) { tests := []struct { description string nodes []*v1.Node - args componentconfig.RemoveFailedPodsArgs + args RemoveFailedPodsArgs expectedEvictedPodCount uint pods []*v1.Pod nodeFit bool }{ { description: "default empty args, 0 failures, 0 evictions", - args: componentconfig.RemoveFailedPodsArgs{}, + args: RemoveFailedPodsArgs{}, nodes: []*v1.Node{test.BuildTestNode("node1", 2000, 3000, 10, nil)}, expectedEvictedPodCount: 0, pods: []*v1.Pod{}, // no pods come back with field selector phase=Failed @@ -323,7 +322,7 @@ func TestRemoveFailedPods(t *testing.T) { t.Fatalf("Unable to initialize the plugin: %v", err) } - plugin, err := New(&componentconfig.RemoveFailedPodsArgs{ + plugin, err := New(&RemoveFailedPodsArgs{ Reasons: tc.args.Reasons, MinPodLifetimeSeconds: tc.args.MinPodLifetimeSeconds, IncludingInitContainers: tc.args.IncludingInitContainers, diff --git a/pkg/framework/plugins/removefailedpods/register.go b/pkg/framework/plugins/removefailedpods/register.go new file mode 100644 index 000000000..3a8559510 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removefailedpods + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removefailedpods/types.go b/pkg/framework/plugins/removefailedpods/types.go new file mode 100644 index 000000000..985f83398 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/types.go @@ -0,0 +1,34 @@ +/* +Copyright 2022 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 removefailedpods + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemoveFailedPodsArgs holds arguments used to configure RemoveFailedPods plugin. +type RemoveFailedPodsArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + ExcludeOwnerKinds []string + MinPodLifetimeSeconds *uint + Reasons []string + IncludingInitContainers bool +} diff --git a/pkg/framework/plugins/removefailedpods/validation.go b/pkg/framework/plugins/removefailedpods/validation.go new file mode 100644 index 000000000..11aa6c7d3 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/validation.go @@ -0,0 +1,36 @@ +/* +Copyright 2022 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 removefailedpods + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemoveFailedPodsArgs validates RemoveFailedPods arguments +func ValidateRemoveFailedPodsArgs(args *RemoveFailedPodsArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + return nil +} diff --git a/pkg/framework/plugins/removefailedpods/zz_generated.deepcopy.go b/pkg/framework/plugins/removefailedpods/zz_generated.deepcopy.go new file mode 100644 index 000000000..67fbf5f8f --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/zz_generated.deepcopy.go @@ -0,0 +1,78 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removefailedpods + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemoveFailedPodsArgs) DeepCopyInto(out *RemoveFailedPodsArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.ExcludeOwnerKinds != nil { + in, out := &in.ExcludeOwnerKinds, &out.ExcludeOwnerKinds + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.MinPodLifetimeSeconds != nil { + in, out := &in.MinPodLifetimeSeconds, &out.MinPodLifetimeSeconds + *out = new(uint) + **out = **in + } + if in.Reasons != nil { + in, out := &in.Reasons, &out.Reasons + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemoveFailedPodsArgs. +func (in *RemoveFailedPodsArgs) DeepCopy() *RemoveFailedPodsArgs { + if in == nil { + return nil + } + out := new(RemoveFailedPodsArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemoveFailedPodsArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removefailedpods/zz_generated.defaults.go b/pkg/framework/plugins/removefailedpods/zz_generated.defaults.go new file mode 100644 index 000000000..62c6d8052 --- /dev/null +++ b/pkg/framework/plugins/removefailedpods/zz_generated.defaults.go @@ -0,0 +1,38 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removefailedpods + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemoveFailedPodsArgs{}, func(obj interface{}) { SetObjectDefaults_RemoveFailedPodsArgs(obj.(*RemoveFailedPodsArgs)) }) + return nil +} + +func SetObjectDefaults_RemoveFailedPodsArgs(in *RemoveFailedPodsArgs) { + SetDefaults_RemoveFailedPodsArgs(in) +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults.go new file mode 100644 index 000000000..e62af8e72 --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 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 removepodshavingtoomanyrestarts + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemovePodsHavingTooManyRestartsArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemovePodsHavingTooManyRestartsArgs(obj *RemovePodsHavingTooManyRestartsArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if obj.PodRestartThreshold == 0 { + obj.PodRestartThreshold = 0 + } + if !obj.IncludingInitContainers { + obj.IncludingInitContainers = false + } +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults_test.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults_test.go new file mode 100644 index 000000000..25cd3f5dc --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/defaults_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2022 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 removepodshavingtoomanyrestarts + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemovePodsHavingTooManyRestartsArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemovePodsHavingTooManyRestartsArgs empty", + in: &RemovePodsHavingTooManyRestartsArgs{}, + want: &RemovePodsHavingTooManyRestartsArgs{ + Namespaces: nil, + LabelSelector: nil, + PodRestartThreshold: 0, + IncludingInitContainers: false, + }, + }, + { + name: "RemovePodsHavingTooManyRestartsArgs with value", + in: &RemovePodsHavingTooManyRestartsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + PodRestartThreshold: 10, + IncludingInitContainers: true, + }, + want: &RemovePodsHavingTooManyRestartsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + PodRestartThreshold: 10, + IncludingInitContainers: true, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/doc.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/doc.go new file mode 100644 index 000000000..954422b2d --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removepodshavingtoomanyrestarts diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/register.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/register.go new file mode 100644 index 000000000..a4033889c --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removepodshavingtoomanyrestarts + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts.go index 7814736cf..30624a055 100644 --- a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts.go +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts.go @@ -25,7 +25,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -38,7 +37,7 @@ const PluginName = "RemovePodsHavingTooManyRestarts" // As of now, this strategy won't evict daemonsets, mirror pods, critical pods and pods with local storages. type RemovePodsHavingTooManyRestarts struct { handle framework.Handle - args *componentconfig.RemovePodsHavingTooManyRestartsArgs + args *RemovePodsHavingTooManyRestartsArgs podFilter podutil.FilterFunc } @@ -46,7 +45,7 @@ var _ framework.DeschedulePlugin = &RemovePodsHavingTooManyRestarts{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - tooManyRestartsArgs, ok := args.(*componentconfig.RemovePodsHavingTooManyRestartsArgs) + tooManyRestartsArgs, ok := args.(*RemovePodsHavingTooManyRestartsArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemovePodsHavingTooManyRestartsArgs, got %T", args) } @@ -111,7 +110,7 @@ func (d *RemovePodsHavingTooManyRestarts) Deschedule(ctx context.Context, nodes } // validateCanEvict looks at tooManyRestartsArgs to see if pod can be evicted given the args. -func validateCanEvict(pod *v1.Pod, tooManyRestartsArgs *componentconfig.RemovePodsHavingTooManyRestartsArgs) error { +func validateCanEvict(pod *v1.Pod, tooManyRestartsArgs *RemovePodsHavingTooManyRestartsArgs) error { var err error restarts := calcContainerRestartsFromStatuses(pod.Status.ContainerStatuses) diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go index 78650f8a9..5ac6af505 100644 --- a/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/toomanyrestarts_test.go @@ -29,7 +29,6 @@ import ( "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -109,8 +108,8 @@ func TestRemovePodsHavingTooManyRestarts(t *testing.T) { createRemovePodsHavingTooManyRestartsAgrs := func( podRestartThresholds int32, includingInitContainers bool, - ) componentconfig.RemovePodsHavingTooManyRestartsArgs { - return componentconfig.RemovePodsHavingTooManyRestartsArgs{ + ) RemovePodsHavingTooManyRestartsArgs { + return RemovePodsHavingTooManyRestartsArgs{ PodRestartThreshold: podRestartThresholds, IncludingInitContainers: includingInitContainers, } @@ -121,7 +120,7 @@ func TestRemovePodsHavingTooManyRestarts(t *testing.T) { tests := []struct { description string nodes []*v1.Node - args componentconfig.RemovePodsHavingTooManyRestartsArgs + args RemovePodsHavingTooManyRestartsArgs expectedEvictedPodCount uint maxPodsToEvictPerNode *uint maxNoOfPodsToEvictPerNamespace *uint diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/types.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/types.go new file mode 100644 index 000000000..bc159a31a --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/types.go @@ -0,0 +1,32 @@ +/* +Copyright 2022 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 removepodshavingtoomanyrestarts + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemovePodsHavingTooManyRestartsArgs holds arguments used to configure RemovePodsHavingTooManyRestarts plugin. +type RemovePodsHavingTooManyRestartsArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + PodRestartThreshold int32 + IncludingInitContainers bool +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/validation.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/validation.go new file mode 100644 index 000000000..142869649 --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/validation.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 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 removepodshavingtoomanyrestarts + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemovePodsHavingTooManyRestartsArgs validates RemovePodsHavingTooManyRestarts arguments +func ValidateRemovePodsHavingTooManyRestartsArgs(args *RemovePodsHavingTooManyRestartsArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + if args.PodRestartThreshold < 1 { + return fmt.Errorf("PodsHavingTooManyRestarts threshold not set") + } + + return nil +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.deepcopy.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.deepcopy.go new file mode 100644 index 000000000..f4e25f17a --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.deepcopy.go @@ -0,0 +1,63 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removepodshavingtoomanyrestarts + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopyInto(out *RemovePodsHavingTooManyRestartsArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsHavingTooManyRestartsArgs. +func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopy() *RemovePodsHavingTooManyRestartsArgs { + if in == nil { + return nil + } + out := new(RemovePodsHavingTooManyRestartsArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemovePodsHavingTooManyRestartsArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.defaults.go b/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.defaults.go new file mode 100644 index 000000000..7705a78a9 --- /dev/null +++ b/pkg/framework/plugins/removepodshavingtoomanyrestarts/zz_generated.defaults.go @@ -0,0 +1,40 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removepodshavingtoomanyrestarts + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemovePodsHavingTooManyRestartsArgs{}, func(obj interface{}) { + SetObjectDefaults_RemovePodsHavingTooManyRestartsArgs(obj.(*RemovePodsHavingTooManyRestartsArgs)) + }) + return nil +} + +func SetObjectDefaults_RemovePodsHavingTooManyRestartsArgs(in *RemovePodsHavingTooManyRestartsArgs) { + SetDefaults_RemovePodsHavingTooManyRestartsArgs(in) +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults.go new file mode 100644 index 000000000..75a51362f --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2022 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 removepodsviolatinginterpodantiaffinity + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemovePodsViolatingInterPodAntiAffinityArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemovePodsViolatingInterPodAntiAffinityArgs(obj *RemovePodsViolatingInterPodAntiAffinityArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults_test.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults_test.go new file mode 100644 index 000000000..e731e4b8f --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/defaults_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2022 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 removepodsviolatinginterpodantiaffinity + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemovePodsViolatingInterPodAntiAffinityArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemovePodsViolatingInterPodAntiAffinityArgs empty", + in: &RemovePodsViolatingInterPodAntiAffinityArgs{}, + want: &RemovePodsViolatingInterPodAntiAffinityArgs{ + Namespaces: nil, + LabelSelector: nil, + }, + }, + { + name: "RemovePodsViolatingInterPodAntiAffinityArgs with value", + in: &RemovePodsViolatingInterPodAntiAffinityArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + }, + want: &RemovePodsViolatingInterPodAntiAffinityArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/doc.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/doc.go new file mode 100644 index 000000000..f3ad3425b --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removepodsviolatinginterpodantiaffinity diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity.go index 25916a3b6..509fbc334 100644 --- a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity.go +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity.go @@ -22,7 +22,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -38,7 +37,7 @@ const PluginName = "RemovePodsViolatingInterPodAntiAffinity" // RemovePodsViolatingInterPodAntiAffinity evicts pods on the node which violate inter pod anti affinity type RemovePodsViolatingInterPodAntiAffinity struct { handle framework.Handle - args *componentconfig.RemovePodsViolatingInterPodAntiAffinityArgs + args *RemovePodsViolatingInterPodAntiAffinityArgs podFilter podutil.FilterFunc } @@ -46,7 +45,7 @@ var _ framework.DeschedulePlugin = &RemovePodsViolatingInterPodAntiAffinity{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - interPodAntiAffinityArgs, ok := args.(*componentconfig.RemovePodsViolatingInterPodAntiAffinityArgs) + interPodAntiAffinityArgs, ok := args.(*RemovePodsViolatingInterPodAntiAffinityArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemovePodsViolatingInterPodAntiAffinityArgs, got %T", args) } diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go index eacc70475..ad3b042af 100644 --- a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/pod_antiaffinity_test.go @@ -27,7 +27,6 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" @@ -257,7 +256,7 @@ func TestPodAntiAffinity(t *testing.T) { EvictorFilterImpl: evictorFilter.(framework.EvictorPlugin), } plugin, err := New( - &componentconfig.RemovePodsViolatingInterPodAntiAffinityArgs{}, + &RemovePodsViolatingInterPodAntiAffinityArgs{}, handle, ) diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/register.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/register.go new file mode 100644 index 000000000..4df448944 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removepodsviolatinginterpodantiaffinity + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/types.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/types.go new file mode 100644 index 000000000..fbaa585cf --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/types.go @@ -0,0 +1,33 @@ +/* +Copyright 2022 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 removepodsviolatinginterpodantiaffinity + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemovePodsViolatingInterPodAntiAffinity holds arguments used to configure RemovePodsViolatingInterPodAntiAffinity plugin. +type RemovePodsViolatingInterPodAntiAffinityArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/validation.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/validation.go new file mode 100644 index 000000000..5f6c42ead --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/validation.go @@ -0,0 +1,35 @@ +/* +Copyright 2022 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 removepodsviolatinginterpodantiaffinity + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemovePodsViolatingInterPodAntiAffinityArgs validates ValidateRemovePodsViolatingInterPodAntiAffinity arguments +func ValidateRemovePodsViolatingInterPodAntiAffinityArgs(args *RemovePodsViolatingInterPodAntiAffinityArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.deepcopy.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.deepcopy.go new file mode 100644 index 000000000..0a3cee85a --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.deepcopy.go @@ -0,0 +1,63 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removepodsviolatinginterpodantiaffinity + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopyInto(out *RemovePodsViolatingInterPodAntiAffinityArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingInterPodAntiAffinityArgs. +func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopy() *RemovePodsViolatingInterPodAntiAffinityArgs { + if in == nil { + return nil + } + out := new(RemovePodsViolatingInterPodAntiAffinityArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemovePodsViolatingInterPodAntiAffinityArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.defaults.go b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.defaults.go new file mode 100644 index 000000000..95c93e12d --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatinginterpodantiaffinity/zz_generated.defaults.go @@ -0,0 +1,40 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removepodsviolatinginterpodantiaffinity + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemovePodsViolatingInterPodAntiAffinityArgs{}, func(obj interface{}) { + SetObjectDefaults_RemovePodsViolatingInterPodAntiAffinityArgs(obj.(*RemovePodsViolatingInterPodAntiAffinityArgs)) + }) + return nil +} + +func SetObjectDefaults_RemovePodsViolatingInterPodAntiAffinityArgs(in *RemovePodsViolatingInterPodAntiAffinityArgs) { + SetDefaults_RemovePodsViolatingInterPodAntiAffinityArgs(in) +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults.go new file mode 100644 index 000000000..e8f5cf2cb --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemovePodsViolatingNodeAffinityArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemovePodsViolatingNodeAffinityArgs(obj *RemovePodsViolatingNodeAffinityArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults_test.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults_test.go new file mode 100644 index 000000000..c500b4cd9 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/defaults_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemovePodsViolatingNodeAffinityArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemovePodsViolatingNodeAffinityArgs empty", + in: &RemovePodsViolatingNodeAffinityArgs{}, + want: &RemovePodsViolatingNodeAffinityArgs{ + Namespaces: nil, + LabelSelector: nil, + }, + }, + { + name: "RemovePodsViolatingNodeAffinityArgs with value", + in: &RemovePodsViolatingNodeAffinityArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + }, + want: &RemovePodsViolatingNodeAffinityArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/doc.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/doc.go new file mode 100644 index 000000000..4fc110e22 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removepodsviolatingnodeaffinity diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity.go index 23fc418b7..388dd2f3c 100644 --- a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity.go +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity.go @@ -23,7 +23,6 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" @@ -35,7 +34,7 @@ const PluginName = "RemovePodsViolatingNodeAffinity" // RemovePodsViolatingNodeAffinity evicts pods on the node which violate node affinity type RemovePodsViolatingNodeAffinity struct { handle framework.Handle - args *componentconfig.RemovePodsViolatingNodeAffinityArgs + args *RemovePodsViolatingNodeAffinityArgs podFilter podutil.FilterFunc } @@ -43,7 +42,7 @@ var _ framework.DeschedulePlugin = &RemovePodsViolatingNodeAffinity{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - nodeAffinityArgs, ok := args.(*componentconfig.RemovePodsViolatingNodeAffinityArgs) + nodeAffinityArgs, ok := args.(*RemovePodsViolatingNodeAffinityArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemovePodsViolatingNodeAffinityArgs, got %T", args) } diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go index 221e919df..e15c28c88 100644 --- a/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/node_affinity_test.go @@ -27,7 +27,6 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" @@ -96,12 +95,12 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { expectedEvictedPodCount uint maxPodsToEvictPerNode *uint maxNoOfPodsToEvictPerNamespace *uint - args componentconfig.RemovePodsViolatingNodeAffinityArgs + args RemovePodsViolatingNodeAffinityArgs nodefit bool }{ { description: "Invalid Affinity type, should not evict any pods", - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingRequiredDuringExecution"}, }, expectedEvictedPodCount: 0, @@ -110,7 +109,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { }, { description: "Pod is correctly scheduled on node, no eviction expected", - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, expectedEvictedPodCount: 0, @@ -120,7 +119,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, another schedulable node available, should be evicted", expectedEvictedPodCount: 1, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, nil), @@ -129,7 +128,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, another schedulable node available, maxPodsToEvictPerNode set to 1, should not be evicted", expectedEvictedPodCount: 1, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, nil), @@ -139,7 +138,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, another schedulable node available, maxPodsToEvictPerNode set to 1, no pod evicted since pod terminting", expectedEvictedPodCount: 1, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, &metav1.Time{}), @@ -149,7 +148,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, another schedulable node available, maxNoOfPodsToEvictPerNamespace set to 1, should not be evicted", expectedEvictedPodCount: 1, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, nil), @@ -159,7 +158,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, another schedulable node available, maxNoOfPodsToEvictPerNamespace set to 1, no pod evicted since pod terminting", expectedEvictedPodCount: 1, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, &metav1.Time{}), @@ -169,7 +168,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, but no node where pod fits is available, should not evict", expectedEvictedPodCount: 0, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, nil), @@ -179,7 +178,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { { description: "Pod is scheduled on node without matching labels, and node where pod fits is available, should evict", expectedEvictedPodCount: 0, - args: componentconfig.RemovePodsViolatingNodeAffinityArgs{ + args: RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, }, pods: addPodsToNode(nodeWithoutLabels, nil), @@ -257,7 +256,7 @@ func TestRemovePodsViolatingNodeAffinity(t *testing.T) { } plugin, err := New( - &componentconfig.RemovePodsViolatingNodeAffinityArgs{ + &RemovePodsViolatingNodeAffinityArgs{ NodeAffinityType: tc.args.NodeAffinityType, }, handle, diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/register.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/register.go new file mode 100644 index 000000000..7198cb695 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/types.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/types.go new file mode 100644 index 000000000..a0836d81b --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/types.go @@ -0,0 +1,34 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemovePodsViolatingNodeAffinityArgs holds arguments used to configure RemovePodsViolatingNodeAffinity plugin. +type RemovePodsViolatingNodeAffinityArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + NodeAffinityType []string +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation.go new file mode 100644 index 000000000..6ab03948f --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation.go @@ -0,0 +1,42 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemovePodsViolatingNodeAffinityArgs validates RemovePodsViolatingNodeAffinity arguments +func ValidateRemovePodsViolatingNodeAffinityArgs(args *RemovePodsViolatingNodeAffinityArgs) error { + if args == nil || len(args.NodeAffinityType) == 0 { + return fmt.Errorf("nodeAffinityType needs to be set") + } + + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation_test.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation_test.go new file mode 100644 index 000000000..b8106608c --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/validation_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2022 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 removepodsviolatingnodeaffinity + +import "testing" + +func TestValidateRemovePodsViolatingNodeAffinityArgs(t *testing.T) { + testCases := []struct { + description string + args *RemovePodsViolatingNodeAffinityArgs + expectError bool + }{ + { + description: "nil NodeAffinityType args, expects errors", + args: &RemovePodsViolatingNodeAffinityArgs{ + NodeAffinityType: nil, + }, + expectError: true, + }, + { + description: "empty NodeAffinityType args, expects errors", + args: &RemovePodsViolatingNodeAffinityArgs{ + NodeAffinityType: []string{}, + }, + expectError: true, + }, + { + description: "valid NodeAffinityType args, no errors", + args: &RemovePodsViolatingNodeAffinityArgs{ + NodeAffinityType: []string{"requiredDuringSchedulingIgnoredDuringExecution"}, + }, + expectError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + err := ValidateRemovePodsViolatingNodeAffinityArgs(tc.args) + + hasError := err != nil + if tc.expectError != hasError { + t.Error("unexpected arg validation behavior") + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.deepcopy.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.deepcopy.go new file mode 100644 index 000000000..4b74d52cc --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.deepcopy.go @@ -0,0 +1,68 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removepodsviolatingnodeaffinity + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopyInto(out *RemovePodsViolatingNodeAffinityArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.NodeAffinityType != nil { + in, out := &in.NodeAffinityType, &out.NodeAffinityType + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingNodeAffinityArgs. +func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopy() *RemovePodsViolatingNodeAffinityArgs { + if in == nil { + return nil + } + out := new(RemovePodsViolatingNodeAffinityArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemovePodsViolatingNodeAffinityArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.defaults.go b/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.defaults.go new file mode 100644 index 000000000..610db082f --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodeaffinity/zz_generated.defaults.go @@ -0,0 +1,40 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removepodsviolatingnodeaffinity + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemovePodsViolatingNodeAffinityArgs{}, func(obj interface{}) { + SetObjectDefaults_RemovePodsViolatingNodeAffinityArgs(obj.(*RemovePodsViolatingNodeAffinityArgs)) + }) + return nil +} + +func SetObjectDefaults_RemovePodsViolatingNodeAffinityArgs(in *RemovePodsViolatingNodeAffinityArgs) { + SetDefaults_RemovePodsViolatingNodeAffinityArgs(in) +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/defaults.go b/pkg/framework/plugins/removepodsviolatingnodetaints/defaults.go new file mode 100644 index 000000000..12eebb91b --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/defaults.go @@ -0,0 +1,39 @@ +/* +Copyright 2022 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 removepodsviolatingnodetaints + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemovePodsViolatingNodeTaintsArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemovePodsViolatingNodeTaintsArgs(obj *RemovePodsViolatingNodeTaintsArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if !obj.IncludePreferNoSchedule { + obj.IncludePreferNoSchedule = false + } + if obj.ExcludedTaints == nil { + obj.ExcludedTaints = nil + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/defaults_test.go b/pkg/framework/plugins/removepodsviolatingnodetaints/defaults_test.go new file mode 100644 index 000000000..e43163131 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/defaults_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2022 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 removepodsviolatingnodetaints + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemovePodsViolatingNodeTaintsArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemovePodsViolatingNodeTaintsArgs empty", + in: &RemovePodsViolatingNodeTaintsArgs{}, + want: &RemovePodsViolatingNodeTaintsArgs{ + Namespaces: nil, + LabelSelector: nil, + IncludePreferNoSchedule: false, + ExcludedTaints: nil, + }, + }, + { + name: "RemovePodsViolatingNodeTaintsArgs with value", + in: &RemovePodsViolatingNodeTaintsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + IncludePreferNoSchedule: false, + ExcludedTaints: []string{"ExcludedTaints"}, + }, + want: &RemovePodsViolatingNodeTaintsArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + IncludePreferNoSchedule: false, + ExcludedTaints: []string{"ExcludedTaints"}, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/doc.go b/pkg/framework/plugins/removepodsviolatingnodetaints/doc.go new file mode 100644 index 000000000..9b9e6ec8c --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removepodsviolatingnodetaints diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint.go b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint.go index da7fbd2c1..a9eee7b8a 100644 --- a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint.go +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint.go @@ -25,7 +25,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -37,7 +36,7 @@ const PluginName = "RemovePodsViolatingNodeTaints" // RemovePodsViolatingNodeTaints evicts pods on the node which violate NoSchedule Taints on nodes type RemovePodsViolatingNodeTaints struct { handle framework.Handle - args *componentconfig.RemovePodsViolatingNodeTaintsArgs + args *RemovePodsViolatingNodeTaintsArgs taintFilterFnc func(taint *v1.Taint) bool podFilter podutil.FilterFunc } @@ -46,7 +45,7 @@ var _ framework.DeschedulePlugin = &RemovePodsViolatingNodeTaints{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - nodeTaintsArgs, ok := args.(*componentconfig.RemovePodsViolatingNodeTaintsArgs) + nodeTaintsArgs, ok := args.(*RemovePodsViolatingNodeTaintsArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemovePodsViolatingNodeTaintsArgs, got %T", args) } diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go index a778bffe4..43464217e 100644 --- a/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/node_taint_test.go @@ -29,7 +29,6 @@ import ( "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/events" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -393,7 +392,7 @@ func TestDeletePodsViolatingNodeTaints(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := New(&componentconfig.RemovePodsViolatingNodeTaintsArgs{ + plugin, err := New(&RemovePodsViolatingNodeTaintsArgs{ IncludePreferNoSchedule: tc.includePreferNoSchedule, ExcludedTaints: tc.excludedTaints, }, diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/register.go b/pkg/framework/plugins/removepodsviolatingnodetaints/register.go new file mode 100644 index 000000000..974d0d094 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removepodsviolatingnodetaints + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/types.go b/pkg/framework/plugins/removepodsviolatingnodetaints/types.go new file mode 100644 index 000000000..41a72b21f --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/types.go @@ -0,0 +1,35 @@ +/* +Copyright 2022 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 removepodsviolatingnodetaints + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemovePodsViolatingNodeTaintsArgs holds arguments used to configure the RemovePodsViolatingNodeTaints plugin. +type RemovePodsViolatingNodeTaintsArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + IncludePreferNoSchedule bool + ExcludedTaints []string +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/validation.go b/pkg/framework/plugins/removepodsviolatingnodetaints/validation.go new file mode 100644 index 000000000..ee54e7bb6 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/validation.go @@ -0,0 +1,38 @@ +/* +Copyright 2022 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 removepodsviolatingnodetaints + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemovePodsViolatingNodeTaintsArgs validates RemovePodsViolatingNodeTaints arguments +func ValidateRemovePodsViolatingNodeTaintsArgs(args *RemovePodsViolatingNodeTaintsArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/validation_test.go b/pkg/framework/plugins/removepodsviolatingnodetaints/validation_test.go new file mode 100644 index 000000000..d5b2a59c7 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/validation_test.go @@ -0,0 +1,68 @@ +package removepodsviolatingnodetaints + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestValidateRemovePodsViolatingNodeTaintsArgs(t *testing.T) { + testCases := []struct { + description string + args *RemovePodsViolatingNodeTaintsArgs + expectError bool + }{ + { + description: "valid namespace args, no errors", + args: &RemovePodsViolatingNodeTaintsArgs{ + Namespaces: &api.Namespaces{ + Include: []string{"default"}, + }, + }, + expectError: false, + }, + { + description: "invalid namespaces args, expects error", + args: &RemovePodsViolatingNodeTaintsArgs{ + Namespaces: &api.Namespaces{ + Include: []string{"default"}, + Exclude: []string{"kube-system"}, + }, + }, + expectError: true, + }, + { + description: "valid label selector args, no errors", + args: &RemovePodsViolatingNodeTaintsArgs{ + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"role.kubernetes.io/node": ""}, + }, + }, + expectError: false, + }, + { + description: "invalid label selector args, expects errors", + args: &RemovePodsViolatingNodeTaintsArgs{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Operator: metav1.LabelSelectorOpIn, + }, + }, + }, + }, + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + err := ValidateRemovePodsViolatingNodeTaintsArgs(tc.args) + + hasError := err != nil + if tc.expectError != hasError { + t.Error("unexpected arg validation behavior") + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.deepcopy.go b/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.deepcopy.go new file mode 100644 index 000000000..5efb12af6 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.deepcopy.go @@ -0,0 +1,68 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removepodsviolatingnodetaints + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopyInto(out *RemovePodsViolatingNodeTaintsArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + if in.ExcludedTaints != nil { + in, out := &in.ExcludedTaints, &out.ExcludedTaints + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingNodeTaintsArgs. +func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopy() *RemovePodsViolatingNodeTaintsArgs { + if in == nil { + return nil + } + out := new(RemovePodsViolatingNodeTaintsArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemovePodsViolatingNodeTaintsArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.defaults.go b/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.defaults.go new file mode 100644 index 000000000..0ea6f06be --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingnodetaints/zz_generated.defaults.go @@ -0,0 +1,40 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removepodsviolatingnodetaints + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemovePodsViolatingNodeTaintsArgs{}, func(obj interface{}) { + SetObjectDefaults_RemovePodsViolatingNodeTaintsArgs(obj.(*RemovePodsViolatingNodeTaintsArgs)) + }) + return nil +} + +func SetObjectDefaults_RemovePodsViolatingNodeTaintsArgs(in *RemovePodsViolatingNodeTaintsArgs) { + SetDefaults_RemovePodsViolatingNodeTaintsArgs(in) +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults.go new file mode 100644 index 000000000..6d62ee684 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults.go @@ -0,0 +1,36 @@ +/* +Copyright 2022 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 removepodsviolatingtopologyspreadconstraint + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +// SetDefaults_RemovePodsViolatingTopologySpreadConstraintArgs +// TODO: the final default values would be discussed in community +func SetDefaults_RemovePodsViolatingTopologySpreadConstraintArgs(obj *RemovePodsViolatingTopologySpreadConstraintArgs) { + if obj.Namespaces == nil { + obj.Namespaces = nil + } + if obj.LabelSelector == nil { + obj.LabelSelector = nil + } + if !obj.IncludeSoftConstraints { + obj.IncludeSoftConstraints = false + } +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults_test.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults_test.go new file mode 100644 index 000000000..44cfafbaf --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/defaults_test.go @@ -0,0 +1,64 @@ +/* +Copyright 2022 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 removepodsviolatingtopologyspreadconstraint + +import ( + "github.com/google/go-cmp/cmp" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "sigs.k8s.io/descheduler/pkg/api" + "testing" +) + +func TestSetDefaults_RemovePodsViolatingTopologySpreadConstraintArgs(t *testing.T) { + tests := []struct { + name string + in runtime.Object + want runtime.Object + }{ + { + name: "RemovePodsViolatingTopologySpreadConstraintArgs empty", + in: &RemovePodsViolatingTopologySpreadConstraintArgs{}, + want: &RemovePodsViolatingTopologySpreadConstraintArgs{ + Namespaces: nil, + LabelSelector: nil, + IncludeSoftConstraints: false, + }, + }, + { + name: "RemovePodsViolatingTopologySpreadConstraintArgs with value", + in: &RemovePodsViolatingTopologySpreadConstraintArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + IncludeSoftConstraints: true, + }, + want: &RemovePodsViolatingTopologySpreadConstraintArgs{ + Namespaces: &api.Namespaces{}, + LabelSelector: &metav1.LabelSelector{}, + IncludeSoftConstraints: true, + }, + }, + } + for _, tc := range tests { + scheme := runtime.NewScheme() + utilruntime.Must(AddToScheme(scheme)) + t.Run(tc.name, func(t *testing.T) { + scheme.Default(tc.in) + if diff := cmp.Diff(tc.in, tc.want); diff != "" { + t.Errorf("Got unexpected defaults (-want, +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/doc.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/doc.go new file mode 100644 index 000000000..482c3072d --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/doc.go @@ -0,0 +1,16 @@ +/* +Copyright 2022 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. +*/ + +// +k8s:defaulter-gen=TypeMeta + +package removepodsviolatingtopologyspreadconstraint diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/register.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/register.go new file mode 100644 index 000000000..755bdfc21 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/register.go @@ -0,0 +1,31 @@ +/* +Copyright 2022 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 removepodsviolatingtopologyspreadconstraint + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +var ( + SchemeBuilder = runtime.NewSchemeBuilder() + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint.go index 034e4d09b..730bdc365 100644 --- a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint.go +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint.go @@ -27,7 +27,6 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" "sigs.k8s.io/descheduler/pkg/descheduler/node" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" @@ -51,7 +50,7 @@ type topology struct { // RemovePodsViolatingTopologySpreadConstraint evicts pods which violate their topology spread constraints type RemovePodsViolatingTopologySpreadConstraint struct { handle framework.Handle - args *componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs + args *RemovePodsViolatingTopologySpreadConstraintArgs podFilter podutil.FilterFunc } @@ -59,7 +58,7 @@ var _ framework.BalancePlugin = &RemovePodsViolatingTopologySpreadConstraint{} // New builds plugin from its arguments while passing a handle func New(args runtime.Object, handle framework.Handle) (framework.Plugin, error) { - pluginArgs, ok := args.(*componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs) + pluginArgs, ok := args.(*RemovePodsViolatingTopologySpreadConstraintArgs) if !ok { return nil, fmt.Errorf("want args to be of type RemovePodsViolatingTopologySpreadConstraintArgs, got %T", args) } diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go index e2158ede5..0ba10cf1b 100644 --- a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/topologyspreadconstraint_test.go @@ -16,7 +16,6 @@ import ( "k8s.io/client-go/tools/events" "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod" "sigs.k8s.io/descheduler/pkg/framework" @@ -33,7 +32,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedPods []string // if specified, will assert specific pods were evicted nodes []*v1.Node namespaces []string - args componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs + args RemovePodsViolatingTopologySpreadConstraintArgs nodeFit bool }{ { @@ -69,7 +68,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [3,1], maxSkew=1, move 1 pod to achieve [2,2]", @@ -97,7 +96,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [3,1], maxSkew=1, move 1 pod to achieve [2,2] (soft constraints)", @@ -132,7 +131,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{IncludeSoftConstraints: true}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{IncludeSoftConstraints: true}, }, { name: "2 domains, sizes [3,1], maxSkew=1, no pods eligible, move 0 pods", @@ -163,7 +162,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [3,1], maxSkew=1, move 1 pod to achieve [2,2], exclude kube-system namespace", @@ -191,7 +190,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{Namespaces: &api.Namespaces{Exclude: []string{"kube-system"}}}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{Namespaces: &api.Namespaces{Exclude: []string{"kube-system"}}}, nodeFit: true, }, { @@ -220,7 +219,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [4,0], maxSkew=1, move 2 pods to achieve [2,2]", @@ -243,7 +242,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 2, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [4,0], maxSkew=1, only move 1 pod since pods with nodeSelector and nodeAffinity aren't evicted", @@ -283,7 +282,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -327,7 +326,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 2, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "3 domains, sizes [0, 1, 100], maxSkew=1, move 66 pods to get [34, 33, 34]", @@ -351,7 +350,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 66, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "4 domains, sizes [0, 1, 3, 5], should move 3 to get [2, 2, 3, 2]", @@ -381,7 +380,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 3, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains size [2 6], maxSkew=2, should move 1 to get [3 5]", @@ -409,7 +408,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains size [2 6], maxSkew=2, can't move any because of node taints", @@ -453,7 +452,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -482,7 +481,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -588,7 +587,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 1, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{IncludeSoftConstraints: true}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{IncludeSoftConstraints: true}, }, { name: "3 domains size [8 7 0], maxSkew=1, should move 5 to get [5 5 5]", @@ -614,7 +613,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 5, expectedEvictedPods: []string{"pod-5", "pod-6", "pod-7", "pod-8"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "3 domains size [5 5 5], maxSkew=1, should move 0 to retain [5 5 5]", @@ -645,7 +644,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 1 pod since pod tolerates the node with taint", @@ -687,7 +686,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 1, expectedEvictedPods: []string{"pod-0"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 0 pods since pod does not tolerate the tainted node", @@ -720,7 +719,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 0 pods since pod does not tolerate the tainted node, and NodeFit is enabled", @@ -753,7 +752,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -788,7 +787,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 1, expectedEvictedPods: []string{"pod-0"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 0 pod for node with unmatched label filtering", @@ -811,7 +810,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"baz"}, metav1.LabelSelectorOpIn)}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"baz"}, metav1.LabelSelectorOpIn)}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 1 pod for node with matched label filtering", @@ -835,7 +834,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 1, expectedEvictedPods: []string{"pod-1"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"bar"}, metav1.LabelSelectorOpIn)}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"bar"}, metav1.LabelSelectorOpIn)}, }, { name: "2 domains, sizes [2,0], maxSkew=1, move 1 pod for node with matched label filtering (NotIn op)", @@ -859,7 +858,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 1, expectedEvictedPods: []string{"pod-1"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"baz"}, metav1.LabelSelectorOpNotIn)}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"baz"}, metav1.LabelSelectorOpNotIn)}, }, { name: "2 domains, sizes [4,2], maxSkew=1, 2 pods in termination; nothing should be moved", @@ -890,7 +889,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"bar"}, metav1.LabelSelectorOpIn)}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{LabelSelector: getLabelSelector("foo", []string{"bar"}, metav1.LabelSelectorOpIn)}, }, { name: "3 domains, sizes [2,3,4], maxSkew=1, NodeFit is enabled, and not enough cpu on zoneA; nothing should be moved", @@ -921,7 +920,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -968,7 +967,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -1008,7 +1007,7 @@ func TestTopologySpreadConstraint(t *testing.T) { expectedEvictedCount: 1, expectedEvictedPods: []string{"pod-4"}, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, { @@ -1115,7 +1114,7 @@ func TestTopologySpreadConstraint(t *testing.T) { }), expectedEvictedCount: 0, namespaces: []string{"ns1"}, - args: componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{}, + args: RemovePodsViolatingTopologySpreadConstraintArgs{}, nodeFit: true, }, } diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/types.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/types.go new file mode 100644 index 000000000..40f2dec86 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/types.go @@ -0,0 +1,34 @@ +/* +Copyright 2022 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 removepodsviolatingtopologyspreadconstraint + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/descheduler/pkg/api" +) + +// +k8s:deepcopy-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RemovePodsViolatingTopologySpreadConstraintArgs holds arguments used to configure RemovePodsViolatingTopologySpreadConstraint plugin. +type RemovePodsViolatingTopologySpreadConstraintArgs struct { + metav1.TypeMeta + + Namespaces *api.Namespaces + LabelSelector *metav1.LabelSelector + IncludeSoftConstraints bool +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/validation.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/validation.go new file mode 100644 index 000000000..2f30ba507 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/validation.go @@ -0,0 +1,38 @@ +/* +Copyright 2022 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 removepodsviolatingtopologyspreadconstraint + +import ( + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ValidateRemovePodsViolatingTopologySpreadConstraintArgs validates RemovePodsViolatingTopologySpreadConstraint arguments +func ValidateRemovePodsViolatingTopologySpreadConstraintArgs(args *RemovePodsViolatingTopologySpreadConstraintArgs) error { + // At most one of include/exclude can be set + if args.Namespaces != nil && len(args.Namespaces.Include) > 0 && len(args.Namespaces.Exclude) > 0 { + return fmt.Errorf("only one of Include/Exclude namespaces can be set") + } + + if args.LabelSelector != nil { + if _, err := metav1.LabelSelectorAsSelector(args.LabelSelector); err != nil { + return fmt.Errorf("failed to get label selectors from strategy's params: %+v", err) + } + } + + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.deepcopy.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.deepcopy.go new file mode 100644 index 000000000..93f242a1e --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.deepcopy.go @@ -0,0 +1,63 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package removepodsviolatingtopologyspreadconstraint + +import ( + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + api "sigs.k8s.io/descheduler/pkg/api" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopyInto(out *RemovePodsViolatingTopologySpreadConstraintArgs) { + *out = *in + out.TypeMeta = in.TypeMeta + if in.Namespaces != nil { + in, out := &in.Namespaces, &out.Namespaces + *out = new(api.Namespaces) + (*in).DeepCopyInto(*out) + } + if in.LabelSelector != nil { + in, out := &in.LabelSelector, &out.LabelSelector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RemovePodsViolatingTopologySpreadConstraintArgs. +func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopy() *RemovePodsViolatingTopologySpreadConstraintArgs { + if in == nil { + return nil + } + out := new(RemovePodsViolatingTopologySpreadConstraintArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RemovePodsViolatingTopologySpreadConstraintArgs) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.defaults.go b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.defaults.go new file mode 100644 index 000000000..2659305b2 --- /dev/null +++ b/pkg/framework/plugins/removepodsviolatingtopologyspreadconstraint/zz_generated.defaults.go @@ -0,0 +1,40 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 2022 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. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package removepodsviolatingtopologyspreadconstraint + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&RemovePodsViolatingTopologySpreadConstraintArgs{}, func(obj interface{}) { + SetObjectDefaults_RemovePodsViolatingTopologySpreadConstraintArgs(obj.(*RemovePodsViolatingTopologySpreadConstraintArgs)) + }) + return nil +} + +func SetObjectDefaults_RemovePodsViolatingTopologySpreadConstraintArgs(in *RemovePodsViolatingTopologySpreadConstraintArgs) { + SetDefaults_RemovePodsViolatingTopologySpreadConstraintArgs(in) +} diff --git a/test/e2e/e2e_duplicatepods_test.go b/test/e2e/e2e_duplicatepods_test.go index f7554f7a1..8bf97811e 100644 --- a/test/e2e/e2e_duplicatepods_test.go +++ b/test/e2e/e2e_duplicatepods_test.go @@ -21,7 +21,6 @@ import ( "strings" "testing" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" frameworkfake "sigs.k8s.io/descheduler/pkg/framework/fake" "sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor" @@ -186,7 +185,7 @@ func TestRemoveDuplicates(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := removeduplicates.New(&componentconfig.RemoveDuplicatesArgs{}, + plugin, err := removeduplicates.New(&removeduplicates.RemoveDuplicatesArgs{}, handle, ) if err != nil { diff --git a/test/e2e/e2e_failedpods_test.go b/test/e2e/e2e_failedpods_test.go index e052601d3..4c26be053 100644 --- a/test/e2e/e2e_failedpods_test.go +++ b/test/e2e/e2e_failedpods_test.go @@ -13,7 +13,6 @@ import ( "k8s.io/apimachinery/pkg/util/wait" clientset "k8s.io/client-go/kubernetes" "k8s.io/utils/pointer" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" frameworkfake "sigs.k8s.io/descheduler/pkg/framework/fake" "sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor" @@ -39,27 +38,27 @@ func TestFailedPods(t *testing.T) { defer clientSet.CoreV1().Namespaces().Delete(ctx, testNamespace.Name, metav1.DeleteOptions{}) testCases := map[string]struct { expectedEvictedCount uint - args *componentconfig.RemoveFailedPodsArgs + args *removefailedpods.RemoveFailedPodsArgs }{ "test-failed-pods-default-args": { expectedEvictedCount: 1, - args: &componentconfig.RemoveFailedPodsArgs{}, + args: &removefailedpods.RemoveFailedPodsArgs{}, }, "test-failed-pods-reason-unmatched": { expectedEvictedCount: 0, - args: &componentconfig.RemoveFailedPodsArgs{ + args: &removefailedpods.RemoveFailedPodsArgs{ Reasons: []string{"ReasonDoesNotMatch"}, }, }, "test-failed-pods-min-age-unmet": { expectedEvictedCount: 0, - args: &componentconfig.RemoveFailedPodsArgs{ + args: &removefailedpods.RemoveFailedPodsArgs{ MinPodLifetimeSeconds: &oneHourPodLifetimeSeconds, }, }, "test-failed-pods-exclude-job-kind": { expectedEvictedCount: 0, - args: &componentconfig.RemoveFailedPodsArgs{ + args: &removefailedpods.RemoveFailedPodsArgs{ ExcludeOwnerKinds: []string{"Job"}, }, }, @@ -100,7 +99,7 @@ func TestFailedPods(t *testing.T) { t.Logf("Running RemoveFailedPods strategy for %s", name) - plugin, err := removefailedpods.New(&componentconfig.RemoveFailedPodsArgs{ + plugin, err := removefailedpods.New(&removefailedpods.RemoveFailedPodsArgs{ Reasons: tc.args.Reasons, MinPodLifetimeSeconds: tc.args.MinPodLifetimeSeconds, IncludingInitContainers: tc.args.IncludingInitContainers, diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index aa62b6c3e..fdac3fbaa 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -41,7 +41,6 @@ import ( "sigs.k8s.io/descheduler/cmd/descheduler/app/options" "sigs.k8s.io/descheduler/pkg/api" deschedulerapi "sigs.k8s.io/descheduler/pkg/api" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler" "sigs.k8s.io/descheduler/pkg/descheduler/client" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" @@ -224,7 +223,7 @@ func runPodLifetimePlugin( maxPodLifeTimeSeconds := uint(1) - plugin, err := podlifetime.New(&componentconfig.PodLifeTimeArgs{ + plugin, err := podlifetime.New(&podlifetime.PodLifeTimeArgs{ MaxPodLifeTimeSeconds: &maxPodLifeTimeSeconds, LabelSelector: labelSelector, Namespaces: namespaces, @@ -393,7 +392,7 @@ func TestLowNodeUtilization(t *testing.T) { SharedInformerFactoryImpl: sharedInformerFactory, } - plugin, err := nodeutilization.NewLowNodeUtilization(&componentconfig.LowNodeUtilizationArgs{ + plugin, err := nodeutilization.NewLowNodeUtilization(&nodeutilization.LowNodeUtilizationArgs{ Thresholds: api.ResourceThresholds{ v1.ResourceCPU: 70, }, diff --git a/test/e2e/e2e_toomanyrestarts_test.go b/test/e2e/e2e_toomanyrestarts_test.go index 2e436ba03..2765f4f4a 100644 --- a/test/e2e/e2e_toomanyrestarts_test.go +++ b/test/e2e/e2e_toomanyrestarts_test.go @@ -31,7 +31,6 @@ import ( "k8s.io/client-go/tools/events" "k8s.io/utils/pointer" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/descheduler/evictions" eutils "sigs.k8s.io/descheduler/pkg/descheduler/evictions/utils" "sigs.k8s.io/descheduler/pkg/framework" @@ -114,8 +113,8 @@ func TestTooManyRestarts(t *testing.T) { createRemovePodsHavingTooManyRestartsAgrs := func( podRestartThresholds int32, includingInitContainers bool, - ) componentconfig.RemovePodsHavingTooManyRestartsArgs { - return componentconfig.RemovePodsHavingTooManyRestartsArgs{ + ) removepodshavingtoomanyrestarts.RemovePodsHavingTooManyRestartsArgs { + return removepodshavingtoomanyrestarts.RemovePodsHavingTooManyRestartsArgs{ PodRestartThreshold: podRestartThresholds, IncludingInitContainers: includingInitContainers, } @@ -123,7 +122,7 @@ func TestTooManyRestarts(t *testing.T) { tests := []struct { name string - args componentconfig.RemovePodsHavingTooManyRestartsArgs + args removepodshavingtoomanyrestarts.RemovePodsHavingTooManyRestartsArgs expectedEvictedPodCount uint }{ { diff --git a/test/e2e/e2e_topologyspreadconstraint_test.go b/test/e2e/e2e_topologyspreadconstraint_test.go index e436006a2..cf7147edc 100644 --- a/test/e2e/e2e_topologyspreadconstraint_test.go +++ b/test/e2e/e2e_topologyspreadconstraint_test.go @@ -10,7 +10,6 @@ import ( v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/descheduler/pkg/apis/componentconfig" "sigs.k8s.io/descheduler/pkg/framework" frameworkfake "sigs.k8s.io/descheduler/pkg/framework/fake" "sigs.k8s.io/descheduler/pkg/framework/plugins/defaultevictor" @@ -103,7 +102,7 @@ func TestTopologySpreadConstraint(t *testing.T) { t.Fatalf("Unable to initialize the plugin: %v", err) } - plugin, err := removepodsviolatingtopologyspreadconstraint.New(&componentconfig.RemovePodsViolatingTopologySpreadConstraintArgs{ + plugin, err := removepodsviolatingtopologyspreadconstraint.New(&removepodsviolatingtopologyspreadconstraint.RemovePodsViolatingTopologySpreadConstraintArgs{ IncludeSoftConstraints: tc.constraint != v1.DoNotSchedule, }, &frameworkfake.HandleImpl{