mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-01-26 05:14:13 +01:00
ListPodsOnANode: allow to include/exclude namespaces
Info: field selector is still not properly mocked so it's not possible to unit test it
This commit is contained in:
@@ -44,13 +44,23 @@ type DeschedulerStrategy struct {
|
|||||||
Params *StrategyParameters
|
Params *StrategyParameters
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only one of its members may be specified
|
// Namespaces carries a list of included/excluded namespaces
|
||||||
|
// for which a given strategy is applicable
|
||||||
|
type Namespaces struct {
|
||||||
|
Include []string
|
||||||
|
Exclude []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Besides Namespaces only one of its members may be specified
|
||||||
|
// TODO(jchaloup): move Namespaces to individual strategies once the policy
|
||||||
|
// version is bumped to v1alpha2
|
||||||
type StrategyParameters struct {
|
type StrategyParameters struct {
|
||||||
NodeResourceUtilizationThresholds *NodeResourceUtilizationThresholds
|
NodeResourceUtilizationThresholds *NodeResourceUtilizationThresholds
|
||||||
NodeAffinityType []string
|
NodeAffinityType []string
|
||||||
PodsHavingTooManyRestarts *PodsHavingTooManyRestarts
|
PodsHavingTooManyRestarts *PodsHavingTooManyRestarts
|
||||||
MaxPodLifeTimeSeconds *uint
|
MaxPodLifeTimeSeconds *uint
|
||||||
RemoveDuplicates *RemoveDuplicates
|
RemoveDuplicates *RemoveDuplicates
|
||||||
|
Namespaces Namespaces
|
||||||
}
|
}
|
||||||
|
|
||||||
type Percentage float64
|
type Percentage float64
|
||||||
|
|||||||
@@ -44,13 +44,21 @@ type DeschedulerStrategy struct {
|
|||||||
Params *StrategyParameters `json:"params,omitempty"`
|
Params *StrategyParameters `json:"params,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only one of its members may be specified
|
// Namespaces carries a list of included/excluded namespaces
|
||||||
|
// for which a given strategy is applicable.
|
||||||
|
type Namespaces struct {
|
||||||
|
Include []string `json:"include"`
|
||||||
|
Exclude []string `json:"exclude"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Besides Namespaces only one of its members may be specified
|
||||||
type StrategyParameters struct {
|
type StrategyParameters struct {
|
||||||
NodeResourceUtilizationThresholds *NodeResourceUtilizationThresholds `json:"nodeResourceUtilizationThresholds,omitempty"`
|
NodeResourceUtilizationThresholds *NodeResourceUtilizationThresholds `json:"nodeResourceUtilizationThresholds,omitempty"`
|
||||||
NodeAffinityType []string `json:"nodeAffinityType,omitempty"`
|
NodeAffinityType []string `json:"nodeAffinityType,omitempty"`
|
||||||
PodsHavingTooManyRestarts *PodsHavingTooManyRestarts `json:"podsHavingTooManyRestarts,omitempty"`
|
PodsHavingTooManyRestarts *PodsHavingTooManyRestarts `json:"podsHavingTooManyRestarts,omitempty"`
|
||||||
MaxPodLifeTimeSeconds *uint `json:"maxPodLifeTimeSeconds,omitempty"`
|
MaxPodLifeTimeSeconds *uint `json:"maxPodLifeTimeSeconds,omitempty"`
|
||||||
RemoveDuplicates *RemoveDuplicates `json:"removeDuplicates,omitempty"`
|
RemoveDuplicates *RemoveDuplicates `json:"removeDuplicates,omitempty"`
|
||||||
|
Namespaces Namespaces `json:"namespaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Percentage float64
|
type Percentage float64
|
||||||
|
|||||||
@@ -55,6 +55,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.AddGeneratedConversionFunc((*Namespaces)(nil), (*api.Namespaces)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_v1alpha1_Namespaces_To_api_Namespaces(a.(*Namespaces), b.(*api.Namespaces), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.AddGeneratedConversionFunc((*api.Namespaces)(nil), (*Namespaces)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
|
return Convert_api_Namespaces_To_v1alpha1_Namespaces(a.(*api.Namespaces), b.(*Namespaces), scope)
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := s.AddGeneratedConversionFunc((*NodeResourceUtilizationThresholds)(nil), (*api.NodeResourceUtilizationThresholds)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
if err := s.AddGeneratedConversionFunc((*NodeResourceUtilizationThresholds)(nil), (*api.NodeResourceUtilizationThresholds)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||||
return Convert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(a.(*NodeResourceUtilizationThresholds), b.(*api.NodeResourceUtilizationThresholds), scope)
|
return Convert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(a.(*NodeResourceUtilizationThresholds), b.(*api.NodeResourceUtilizationThresholds), scope)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@@ -142,6 +152,28 @@ func Convert_api_DeschedulerStrategy_To_v1alpha1_DeschedulerStrategy(in *api.Des
|
|||||||
return autoConvert_api_DeschedulerStrategy_To_v1alpha1_DeschedulerStrategy(in, out, s)
|
return autoConvert_api_DeschedulerStrategy_To_v1alpha1_DeschedulerStrategy(in, out, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func autoConvert_v1alpha1_Namespaces_To_api_Namespaces(in *Namespaces, out *api.Namespaces, s conversion.Scope) error {
|
||||||
|
out.Include = *(*[]string)(unsafe.Pointer(&in.Include))
|
||||||
|
out.Exclude = *(*[]string)(unsafe.Pointer(&in.Exclude))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_v1alpha1_Namespaces_To_api_Namespaces is an autogenerated conversion function.
|
||||||
|
func Convert_v1alpha1_Namespaces_To_api_Namespaces(in *Namespaces, out *api.Namespaces, s conversion.Scope) error {
|
||||||
|
return autoConvert_v1alpha1_Namespaces_To_api_Namespaces(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func autoConvert_api_Namespaces_To_v1alpha1_Namespaces(in *api.Namespaces, out *Namespaces, s conversion.Scope) error {
|
||||||
|
out.Include = *(*[]string)(unsafe.Pointer(&in.Include))
|
||||||
|
out.Exclude = *(*[]string)(unsafe.Pointer(&in.Exclude))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert_api_Namespaces_To_v1alpha1_Namespaces is an autogenerated conversion function.
|
||||||
|
func Convert_api_Namespaces_To_v1alpha1_Namespaces(in *api.Namespaces, out *Namespaces, s conversion.Scope) error {
|
||||||
|
return autoConvert_api_Namespaces_To_v1alpha1_Namespaces(in, out, s)
|
||||||
|
}
|
||||||
|
|
||||||
func autoConvert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(in *NodeResourceUtilizationThresholds, out *api.NodeResourceUtilizationThresholds, s conversion.Scope) error {
|
func autoConvert_v1alpha1_NodeResourceUtilizationThresholds_To_api_NodeResourceUtilizationThresholds(in *NodeResourceUtilizationThresholds, out *api.NodeResourceUtilizationThresholds, s conversion.Scope) error {
|
||||||
out.Thresholds = *(*api.ResourceThresholds)(unsafe.Pointer(&in.Thresholds))
|
out.Thresholds = *(*api.ResourceThresholds)(unsafe.Pointer(&in.Thresholds))
|
||||||
out.TargetThresholds = *(*api.ResourceThresholds)(unsafe.Pointer(&in.TargetThresholds))
|
out.TargetThresholds = *(*api.ResourceThresholds)(unsafe.Pointer(&in.TargetThresholds))
|
||||||
@@ -214,6 +246,9 @@ func autoConvert_v1alpha1_StrategyParameters_To_api_StrategyParameters(in *Strat
|
|||||||
out.PodsHavingTooManyRestarts = (*api.PodsHavingTooManyRestarts)(unsafe.Pointer(in.PodsHavingTooManyRestarts))
|
out.PodsHavingTooManyRestarts = (*api.PodsHavingTooManyRestarts)(unsafe.Pointer(in.PodsHavingTooManyRestarts))
|
||||||
out.MaxPodLifeTimeSeconds = (*uint)(unsafe.Pointer(in.MaxPodLifeTimeSeconds))
|
out.MaxPodLifeTimeSeconds = (*uint)(unsafe.Pointer(in.MaxPodLifeTimeSeconds))
|
||||||
out.RemoveDuplicates = (*api.RemoveDuplicates)(unsafe.Pointer(in.RemoveDuplicates))
|
out.RemoveDuplicates = (*api.RemoveDuplicates)(unsafe.Pointer(in.RemoveDuplicates))
|
||||||
|
if err := Convert_v1alpha1_Namespaces_To_api_Namespaces(&in.Namespaces, &out.Namespaces, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +263,9 @@ func autoConvert_api_StrategyParameters_To_v1alpha1_StrategyParameters(in *api.S
|
|||||||
out.PodsHavingTooManyRestarts = (*PodsHavingTooManyRestarts)(unsafe.Pointer(in.PodsHavingTooManyRestarts))
|
out.PodsHavingTooManyRestarts = (*PodsHavingTooManyRestarts)(unsafe.Pointer(in.PodsHavingTooManyRestarts))
|
||||||
out.MaxPodLifeTimeSeconds = (*uint)(unsafe.Pointer(in.MaxPodLifeTimeSeconds))
|
out.MaxPodLifeTimeSeconds = (*uint)(unsafe.Pointer(in.MaxPodLifeTimeSeconds))
|
||||||
out.RemoveDuplicates = (*RemoveDuplicates)(unsafe.Pointer(in.RemoveDuplicates))
|
out.RemoveDuplicates = (*RemoveDuplicates)(unsafe.Pointer(in.RemoveDuplicates))
|
||||||
|
if err := Convert_api_Namespaces_To_v1alpha1_Namespaces(&in.Namespaces, &out.Namespaces, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,32 @@ func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Namespaces) DeepCopyInto(out *Namespaces) {
|
||||||
|
*out = *in
|
||||||
|
if in.Include != nil {
|
||||||
|
in, out := &in.Include, &out.Include
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Exclude != nil {
|
||||||
|
in, out := &in.Exclude, &out.Exclude
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Namespaces.
|
||||||
|
func (in *Namespaces) DeepCopy() *Namespaces {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Namespaces)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -216,6 +242,7 @@ func (in *StrategyParameters) DeepCopyInto(out *StrategyParameters) {
|
|||||||
*out = new(RemoveDuplicates)
|
*out = new(RemoveDuplicates)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
in.Namespaces.DeepCopyInto(&out.Namespaces)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,32 @@ func (in *DeschedulerStrategy) DeepCopy() *DeschedulerStrategy {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
|
func (in *Namespaces) DeepCopyInto(out *Namespaces) {
|
||||||
|
*out = *in
|
||||||
|
if in.Include != nil {
|
||||||
|
in, out := &in.Include, &out.Include
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
if in.Exclude != nil {
|
||||||
|
in, out := &in.Exclude, &out.Exclude
|
||||||
|
*out = make([]string, len(*in))
|
||||||
|
copy(*out, *in)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Namespaces.
|
||||||
|
func (in *Namespaces) DeepCopy() *Namespaces {
|
||||||
|
if in == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out := new(Namespaces)
|
||||||
|
in.DeepCopyInto(out)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
func (in *NodeResourceUtilizationThresholds) DeepCopyInto(out *NodeResourceUtilizationThresholds) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -216,6 +242,7 @@ func (in *StrategyParameters) DeepCopyInto(out *StrategyParameters) {
|
|||||||
*out = new(RemoveDuplicates)
|
*out = new(RemoveDuplicates)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
in.Namespaces.DeepCopyInto(&out.Namespaces)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
filter func(pod *v1.Pod) bool
|
filter func(pod *v1.Pod) bool
|
||||||
|
includedNamespaces []string
|
||||||
|
excludedNamespaces []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFilter sets a pod filter.
|
// WithFilter sets a pod filter.
|
||||||
@@ -39,6 +41,20 @@ func WithFilter(filter func(pod *v1.Pod) bool) func(opts *Options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithNamespaces sets included namespaces
|
||||||
|
func WithNamespaces(namespaces []string) func(opts *Options) {
|
||||||
|
return func(opts *Options) {
|
||||||
|
opts.includedNamespaces = namespaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithoutNamespaces sets excluded namespaces
|
||||||
|
func WithoutNamespaces(namespaces []string) func(opts *Options) {
|
||||||
|
return func(opts *Options) {
|
||||||
|
opts.excludedNamespaces = namespaces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ListPodsOnANode lists all of the pods on a node
|
// ListPodsOnANode lists all of the pods on a node
|
||||||
// It also accepts an optional "filter" function which can be used to further limit the pods that are returned.
|
// It also accepts an optional "filter" function which can be used to further limit the pods that are returned.
|
||||||
// (Usually this is podEvictor.IsEvictable, in order to only list the evictable pods on a node, but can
|
// (Usually this is podEvictor.IsEvictable, in order to only list the evictable pods on a node, but can
|
||||||
@@ -54,18 +70,52 @@ func ListPodsOnANode(
|
|||||||
opt(options)
|
opt(options)
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldSelector, err := fields.ParseSelector("spec.nodeName=" + node.Name + ",status.phase!=" + string(v1.PodSucceeded) + ",status.phase!=" + string(v1.PodFailed))
|
pods := make([]*v1.Pod, 0)
|
||||||
|
|
||||||
|
fieldSelectorString := "spec.nodeName=" + node.Name + ",status.phase!=" + string(v1.PodSucceeded) + ",status.phase!=" + string(v1.PodFailed)
|
||||||
|
|
||||||
|
if len(options.includedNamespaces) > 0 {
|
||||||
|
fieldSelector, err := fields.ParseSelector(fieldSelectorString)
|
||||||
|
if err != nil {
|
||||||
|
return []*v1.Pod{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, namespace := range options.includedNamespaces {
|
||||||
|
podList, err := client.CoreV1().Pods(namespace).List(ctx,
|
||||||
|
metav1.ListOptions{FieldSelector: fieldSelector.String()})
|
||||||
|
if err != nil {
|
||||||
|
return []*v1.Pod{}, err
|
||||||
|
}
|
||||||
|
for i := range podList.Items {
|
||||||
|
if options.filter != nil && !options.filter(&podList.Items[i]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pods = append(pods, &podList.Items[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options.excludedNamespaces) > 0 {
|
||||||
|
for _, namespace := range options.excludedNamespaces {
|
||||||
|
fieldSelectorString += ",metadata.namespace!=" + namespace
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldSelector, err := fields.ParseSelector(fieldSelectorString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*v1.Pod{}, err
|
return []*v1.Pod{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// INFO(jchaloup): field selectors do not work properly with listers
|
||||||
|
// Once the descheduler switcheds to pod listers (through informers),
|
||||||
|
// We need to flip to client-side filtering.
|
||||||
podList, err := client.CoreV1().Pods(v1.NamespaceAll).List(ctx,
|
podList, err := client.CoreV1().Pods(v1.NamespaceAll).List(ctx,
|
||||||
metav1.ListOptions{FieldSelector: fieldSelector.String()})
|
metav1.ListOptions{FieldSelector: fieldSelector.String()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*v1.Pod{}, err
|
return []*v1.Pod{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pods := make([]*v1.Pod, 0)
|
|
||||||
for i := range podList.Items {
|
for i := range podList.Items {
|
||||||
if options.filter != nil && !options.filter(&podList.Items[i]) {
|
if options.filter != nil && !options.filter(&podList.Items[i]) {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package strategies
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
@@ -29,10 +30,22 @@ import (
|
|||||||
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validatePodsViolatingNodeAffinityParams(params *api.StrategyParameters) error {
|
||||||
|
if params == nil || len(params.NodeAffinityType) == 0 {
|
||||||
|
return fmt.Errorf("NodeAffinityType is empty")
|
||||||
|
}
|
||||||
|
// At most one of include/exclude can be set
|
||||||
|
if len(params.Namespaces.Include) > 0 && len(params.Namespaces.Exclude) > 0 {
|
||||||
|
return fmt.Errorf("only one of Include/Exclude namespaces can be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemovePodsViolatingNodeAffinity evicts pods on nodes which violate node affinity
|
// RemovePodsViolatingNodeAffinity evicts pods on nodes which violate node affinity
|
||||||
func RemovePodsViolatingNodeAffinity(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
func RemovePodsViolatingNodeAffinity(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
||||||
if strategy.Params == nil {
|
if err := validatePodsViolatingNodeAffinityParams(strategy.Params); err != nil {
|
||||||
klog.V(1).Infof("NodeAffinityType not set")
|
klog.V(1).Info(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, nodeAffinity := range strategy.Params.NodeAffinityType {
|
for _, nodeAffinity := range strategy.Params.NodeAffinityType {
|
||||||
@@ -43,11 +56,18 @@ func RemovePodsViolatingNodeAffinity(ctx context.Context, client clientset.Inter
|
|||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
||||||
|
|
||||||
pods, err := podutil.ListPodsOnANode(ctx, client, node, podutil.WithFilter(func(pod *v1.Pod) bool {
|
pods, err := podutil.ListPodsOnANode(
|
||||||
return podEvictor.IsEvictable(pod) &&
|
ctx,
|
||||||
!nodeutil.PodFitsCurrentNode(pod, node) &&
|
client,
|
||||||
nodeutil.PodFitsAnyNode(pod, nodes)
|
node,
|
||||||
}))
|
podutil.WithFilter(func(pod *v1.Pod) bool {
|
||||||
|
return podEvictor.IsEvictable(pod) &&
|
||||||
|
!nodeutil.PodFitsCurrentNode(pod, node) &&
|
||||||
|
nodeutil.PodFitsAnyNode(pod, nodes)
|
||||||
|
}),
|
||||||
|
podutil.WithNamespaces(strategy.Params.Namespaces.Include),
|
||||||
|
podutil.WithoutNamespaces(strategy.Params.Namespaces.Exclude),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("failed to get pods from %v: %v", node.Name, err)
|
klog.Errorf("failed to get pods from %v: %v", node.Name, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package strategies
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"sigs.k8s.io/descheduler/pkg/api"
|
"sigs.k8s.io/descheduler/pkg/api"
|
||||||
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
|
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
|
||||||
@@ -29,11 +30,40 @@ import (
|
|||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validateRemovePodsViolatingNodeTaintsParams(params *api.StrategyParameters) error {
|
||||||
|
if params == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// At most one of include/exclude can be set
|
||||||
|
if len(params.Namespaces.Include) > 0 && len(params.Namespaces.Exclude) > 0 {
|
||||||
|
return fmt.Errorf("only one of Include/Exclude namespaces can be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemovePodsViolatingNodeTaints evicts pods on the node which violate NoSchedule Taints on nodes
|
// RemovePodsViolatingNodeTaints evicts pods on the node which violate NoSchedule Taints on nodes
|
||||||
func RemovePodsViolatingNodeTaints(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
func RemovePodsViolatingNodeTaints(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
||||||
|
if err := validateRemovePodsViolatingNodeTaintsParams(strategy.Params); err != nil {
|
||||||
|
klog.V(1).Info(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var namespaces api.Namespaces
|
||||||
|
if strategy.Params != nil {
|
||||||
|
namespaces = strategy.Params.Namespaces
|
||||||
|
}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
||||||
pods, err := podutil.ListPodsOnANode(ctx, client, node, podutil.WithFilter(podEvictor.IsEvictable))
|
pods, err := podutil.ListPodsOnANode(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
node,
|
||||||
|
podutil.WithFilter(podEvictor.IsEvictable),
|
||||||
|
podutil.WithNamespaces(namespaces.Include),
|
||||||
|
podutil.WithoutNamespaces(namespaces.Exclude),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//no pods evicted as error encountered retrieving evictable Pods
|
//no pods evicted as error encountered retrieving evictable Pods
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package strategies
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"sigs.k8s.io/descheduler/pkg/api"
|
"sigs.k8s.io/descheduler/pkg/api"
|
||||||
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
|
"sigs.k8s.io/descheduler/pkg/descheduler/evictions"
|
||||||
@@ -30,11 +31,36 @@ import (
|
|||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validateRemovePodsViolatingInterPodAntiAffinityParams(params *api.StrategyParameters) error {
|
||||||
|
if params == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// At most one of include/exclude can be set
|
||||||
|
if len(params.Namespaces.Include) > 0 && len(params.Namespaces.Exclude) > 0 {
|
||||||
|
return fmt.Errorf("only one of Include/Exclude namespaces can be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemovePodsViolatingInterPodAntiAffinity evicts pods on the node which are having a pod affinity rules.
|
// RemovePodsViolatingInterPodAntiAffinity evicts pods on the node which are having a pod affinity rules.
|
||||||
func RemovePodsViolatingInterPodAntiAffinity(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
func RemovePodsViolatingInterPodAntiAffinity(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
||||||
|
var namespaces api.Namespaces
|
||||||
|
if strategy.Params != nil {
|
||||||
|
namespaces = strategy.Params.Namespaces
|
||||||
|
}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
klog.V(1).Infof("Processing node: %#v\n", node.Name)
|
||||||
pods, err := podutil.ListPodsOnANode(ctx, client, node, podutil.WithFilter(podEvictor.IsEvictable))
|
pods, err := podutil.ListPodsOnANode(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
node,
|
||||||
|
podutil.WithFilter(podEvictor.IsEvictable),
|
||||||
|
podutil.WithNamespaces(namespaces.Include),
|
||||||
|
podutil.WithoutNamespaces(namespaces.Exclude),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package strategies
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
v1meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
v1meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -29,16 +30,30 @@ import (
|
|||||||
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validatePodLifeTimeParams(params *api.StrategyParameters) error {
|
||||||
|
if params == nil || params.MaxPodLifeTimeSeconds == nil {
|
||||||
|
return fmt.Errorf("MaxPodLifeTimeSeconds not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// At most one of include/exclude can be set
|
||||||
|
if len(params.Namespaces.Include) > 0 && len(params.Namespaces.Exclude) > 0 {
|
||||||
|
return fmt.Errorf("only one of Include/Exclude namespaces can be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PodLifeTime evicts pods on nodes that were created more than strategy.Params.MaxPodLifeTimeSeconds seconds ago.
|
// PodLifeTime evicts pods on nodes that were created more than strategy.Params.MaxPodLifeTimeSeconds seconds ago.
|
||||||
func PodLifeTime(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
func PodLifeTime(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
||||||
if strategy.Params == nil || strategy.Params.MaxPodLifeTimeSeconds == nil {
|
if err := validatePodLifeTimeParams(strategy.Params); err != nil {
|
||||||
klog.V(1).Infof("MaxPodLifeTimeSeconds not set")
|
klog.V(1).Info(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
klog.V(1).Infof("Processing node: %#v", node.Name)
|
klog.V(1).Infof("Processing node: %#v", node.Name)
|
||||||
pods := listOldPodsOnNode(ctx, client, node, *strategy.Params.MaxPodLifeTimeSeconds, podEvictor)
|
|
||||||
|
pods := listOldPodsOnNode(ctx, client, node, strategy.Params, podEvictor)
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
success, err := podEvictor.EvictPod(ctx, pod, node, "PodLifeTime")
|
success, err := podEvictor.EvictPod(ctx, pod, node, "PodLifeTime")
|
||||||
if success {
|
if success {
|
||||||
@@ -50,11 +65,19 @@ func PodLifeTime(ctx context.Context, client clientset.Interface, strategy api.D
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listOldPodsOnNode(ctx context.Context, client clientset.Interface, node *v1.Node, maxAge uint, evictor *evictions.PodEvictor) []*v1.Pod {
|
func listOldPodsOnNode(ctx context.Context, client clientset.Interface, node *v1.Node, params *api.StrategyParameters, evictor *evictions.PodEvictor) []*v1.Pod {
|
||||||
pods, err := podutil.ListPodsOnANode(ctx, client, node, podutil.WithFilter(evictor.IsEvictable))
|
pods, err := podutil.ListPodsOnANode(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
node,
|
||||||
|
podutil.WithFilter(evictor.IsEvictable),
|
||||||
|
podutil.WithNamespaces(params.Namespaces.Include),
|
||||||
|
podutil.WithoutNamespaces(params.Namespaces.Exclude),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -62,7 +85,7 @@ func listOldPodsOnNode(ctx context.Context, client clientset.Interface, node *v1
|
|||||||
var oldPods []*v1.Pod
|
var oldPods []*v1.Pod
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
podAgeSeconds := uint(v1meta.Now().Sub(pod.GetCreationTimestamp().Local()).Seconds())
|
podAgeSeconds := uint(v1meta.Now().Sub(pod.GetCreationTimestamp().Local()).Seconds())
|
||||||
if podAgeSeconds > maxAge {
|
if podAgeSeconds > *params.MaxPodLifeTimeSeconds {
|
||||||
oldPods = append(oldPods, pod)
|
oldPods = append(oldPods, pod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package strategies
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
@@ -28,17 +29,37 @@ import (
|
|||||||
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func validateRemovePodsHavingTooManyRestartsParams(params *api.StrategyParameters) error {
|
||||||
|
if params == nil || params.PodsHavingTooManyRestarts == nil || params.PodsHavingTooManyRestarts.PodRestartThreshold < 1 {
|
||||||
|
return fmt.Errorf("PodsHavingTooManyRestarts threshold not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// At most one of include/exclude can be set
|
||||||
|
if len(params.Namespaces.Include) > 0 && len(params.Namespaces.Exclude) > 0 {
|
||||||
|
return fmt.Errorf("only one of Include/Exclude namespaces can be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RemovePodsHavingTooManyRestarts removes the pods that have too many restarts on node.
|
// RemovePodsHavingTooManyRestarts removes the pods that have too many restarts on node.
|
||||||
// There are too many cases leading this issue: Volume mount failed, app error due to nodes' different settings.
|
// There are too many cases leading this issue: Volume mount failed, app error due to nodes' different settings.
|
||||||
// As of now, this strategy won't evict daemonsets, mirror pods, critical pods and pods with local storages.
|
// As of now, this strategy won't evict daemonsets, mirror pods, critical pods and pods with local storages.
|
||||||
func RemovePodsHavingTooManyRestarts(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
func RemovePodsHavingTooManyRestarts(ctx context.Context, client clientset.Interface, strategy api.DeschedulerStrategy, nodes []*v1.Node, podEvictor *evictions.PodEvictor) {
|
||||||
if strategy.Params == nil || strategy.Params.PodsHavingTooManyRestarts == nil || strategy.Params.PodsHavingTooManyRestarts.PodRestartThreshold < 1 {
|
if err := validateRemovePodsHavingTooManyRestartsParams(strategy.Params); err != nil {
|
||||||
klog.V(1).Infof("PodsHavingTooManyRestarts thresholds not set")
|
klog.V(1).Info(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, node := range nodes {
|
for _, node := range nodes {
|
||||||
klog.V(1).Infof("Processing node: %s", node.Name)
|
klog.V(1).Infof("Processing node: %s", node.Name)
|
||||||
pods, err := podutil.ListPodsOnANode(ctx, client, node, podutil.WithFilter(podEvictor.IsEvictable))
|
pods, err := podutil.ListPodsOnANode(
|
||||||
|
ctx,
|
||||||
|
client,
|
||||||
|
node,
|
||||||
|
podutil.WithFilter(podEvictor.IsEvictable),
|
||||||
|
podutil.WithNamespaces(strategy.Params.Namespaces.Include),
|
||||||
|
podutil.WithoutNamespaces(strategy.Params.Namespaces.Exclude),
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("Error when list pods at node %s", node.Name)
|
klog.Errorf("Error when list pods at node %s", node.Name)
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user