mirror of
https://github.com/kubernetes-sigs/descheduler.git
synced 2026-02-14 18:09:55 +00:00
add namespaceLabelSelector
Signed-off-by: Danila Bobkov <danila.bobkov@flant.com>
This commit is contained in:
@@ -158,6 +158,7 @@ The Default Evictor Plugin is used by default for filtering pods before processi
|
||||
| `ignorePvcPods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"PodsWithPVC"` instead]**<br>Sets whether PVC pods should be evicted or ignored. |
|
||||
| `evictFailedBarePods` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"FailedBarePods"` instead]**<br>Allows eviction of pods without owner references and in a failed phase. |
|
||||
| `ignorePodsWithoutPDB` | `bool` | `false` | **[Deprecated: Use `podProtections` with `"PodsWithoutPDB"` instead]**<br>Sets whether pods without PodDisruptionBudget should be evicted or ignored. |
|
||||
| `namespaceLabelSelector` | `metav1.LabelSelector` | | limiting the pods which are processed by namespace (see [label filtering](#label-filtering)) |
|
||||
| `labelSelector` | `metav1.LabelSelector` | | (See [label filtering](#label-filtering)) |
|
||||
| `priorityThreshold` | `priorityThreshold` | | (See [priority filtering](#priority-filtering)) |
|
||||
| `nodeFit` | `bool` | `false` | (See [node fit filtering](#node-fit-filtering)) |
|
||||
@@ -1202,4 +1203,4 @@ This roadmap is not in any particular order.
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
||||
Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md).
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
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.
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"slices"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/client-go/informers"
|
||||
@@ -28,6 +30,7 @@ import (
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
evictionutils "sigs.k8s.io/descheduler/pkg/descheduler/evictions/utils"
|
||||
|
||||
nodeutil "sigs.k8s.io/descheduler/pkg/descheduler/node"
|
||||
podutil "sigs.k8s.io/descheduler/pkg/descheduler/pod"
|
||||
frameworktypes "sigs.k8s.io/descheduler/pkg/framework/types"
|
||||
@@ -35,8 +38,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PluginName = "DefaultEvictor"
|
||||
evictPodAnnotationKey = "descheduler.alpha.kubernetes.io/evict"
|
||||
PluginName = "DefaultEvictor"
|
||||
evictPodAnnotationKey = "descheduler.alpha.kubernetes.io/evict"
|
||||
namespaceWithLabelSelector = "namespaceWithLabelSelector-"
|
||||
)
|
||||
|
||||
var _ frameworktypes.EvictorPlugin = &DefaultEvictor{}
|
||||
@@ -85,9 +89,43 @@ func New(ctx context.Context, args runtime.Object, handle frameworktypes.Handle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ev.args.NamespaceLabelSelector != nil && len(ev.args.NamespaceLabelSelector.MatchLabels) > 0 {
|
||||
selector, nslErr := metav1.LabelSelectorAsSelector(ev.args.NamespaceLabelSelector)
|
||||
if nslErr != nil {
|
||||
return nil, fmt.Errorf("unable to convert namespaceLabelSelector to label selector: %w", nslErr)
|
||||
}
|
||||
indexName := namespaceWithLabelSelector + ev.handle.PluginInstanceID()
|
||||
if nslErr := addNamespaceLabelSelectorIndexer(ev.handle.SharedInformerFactory().Core().V1().Namespaces().Informer(), indexName, selector); nslErr != nil {
|
||||
return nil, fmt.Errorf("failed to add namespace label selector indexer: %w", nslErr)
|
||||
}
|
||||
}
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
func addNamespaceLabelSelectorIndexer(informer cache.SharedIndexInformer, indexName string, selector labels.Selector) error {
|
||||
indexer := informer.GetIndexer()
|
||||
for name := range indexer.GetIndexers() {
|
||||
if name == indexName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return informer.AddIndexers(cache.Indexers{
|
||||
indexName: func(obj interface{}) ([]string, error) {
|
||||
ns, ok := obj.(*v1.Namespace)
|
||||
if !ok {
|
||||
return []string{}, errors.New("unexpected object")
|
||||
}
|
||||
if !selector.Empty() {
|
||||
if !selector.Matches(labels.Set(ns.Labels)) {
|
||||
return []string{}, nil
|
||||
}
|
||||
}
|
||||
return []string{ns.GetName()}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DefaultEvictor) addAllConstraints(logger klog.Logger, handle frameworktypes.Handle) error {
|
||||
args := d.args
|
||||
// Determine effective protected policies based on the provided arguments.
|
||||
@@ -407,8 +445,21 @@ func (d *DefaultEvictor) PreEvictionFilter(pod *v1.Pod) bool {
|
||||
logger.Info("pod does not fit on any other node because of nodeSelector(s), Taint(s), or nodes marked as unschedulable", "pod", klog.KObj(pod))
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if d.args.NamespaceLabelSelector == nil || len(d.args.NamespaceLabelSelector.MatchLabels) == 0 {
|
||||
return true
|
||||
}
|
||||
indexName := namespaceWithLabelSelector + d.handle.PluginInstanceID()
|
||||
objs, err := d.handle.SharedInformerFactory().Core().V1().Namespaces().Informer().GetIndexer().ByIndex(indexName, pod.Namespace)
|
||||
if err != nil {
|
||||
logger.Error(err, "unable to list namespaces for namespaceLabelSelector filter in the policy parameter", "pod", klog.KObj(pod))
|
||||
return false
|
||||
}
|
||||
if len(objs) == 0 {
|
||||
logger.Info("pod namespace do not match the namespaceLabelSelector filter in the policy parameter", "pod", klog.KObj(pod))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -473,6 +524,5 @@ func getPodIndexerByOwnerRefs(indexName string, handle frameworktypes.Handle) (c
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return indexer, nil
|
||||
}
|
||||
|
||||
@@ -39,10 +39,20 @@ import (
|
||||
"sigs.k8s.io/descheduler/test"
|
||||
)
|
||||
|
||||
var (
|
||||
namespace = "test"
|
||||
namespaceSelector = &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"kubernetes.io/metadata.name": namespace,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
description string
|
||||
pods []*v1.Pod
|
||||
nodes []*v1.Node
|
||||
namespaces []*v1.Namespace
|
||||
pdbs []*policyv1.PodDisruptionBudget
|
||||
evictFailedBarePods bool
|
||||
evictLocalStoragePods bool
|
||||
@@ -50,6 +60,7 @@ type testCase struct {
|
||||
ignorePvcPods bool
|
||||
priorityThreshold *int32
|
||||
nodeFit bool
|
||||
useNamespaceSelector bool
|
||||
minReplicas uint
|
||||
minPodAge *metav1.Duration
|
||||
result bool
|
||||
@@ -121,7 +132,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
buildTestNode("node3", setNodeTaint),
|
||||
},
|
||||
nodeFit: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with correct tolerations running on normal node, all other nodes tainted",
|
||||
pods: []*v1.Pod{
|
||||
buildTestPod("p1", n1.Name, func(pod *v1.Pod) {
|
||||
@@ -141,7 +153,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
},
|
||||
nodeFit: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with incorrect node selector",
|
||||
pods: []*v1.Pod{
|
||||
buildTestPod("p1", n1.Name, func(pod *v1.Pod) {
|
||||
@@ -156,7 +169,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
buildTestNode("node3", setNodeLabel),
|
||||
},
|
||||
nodeFit: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with correct node selector",
|
||||
pods: []*v1.Pod{
|
||||
buildTestPod("p1", n1.Name, func(pod *v1.Pod) {
|
||||
@@ -170,7 +184,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
},
|
||||
nodeFit: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with correct node selector, but only available node doesn't have enough CPU",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 12, 8, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -183,7 +198,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
test.BuildTestNode("node3-TEST", 10, 16, 10, setNodeLabel),
|
||||
},
|
||||
nodeFit: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with correct node selector, and one node has enough memory",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 12, 8, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -207,7 +223,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
},
|
||||
nodeFit: true,
|
||||
result: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with correct node selector, but both nodes don't have enough memory",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 12, 8, n1.Name, func(pod *v1.Pod) {
|
||||
@@ -230,7 +247,8 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
test.BuildTestNode("node3", 100, 16, 10, setNodeLabel),
|
||||
},
|
||||
nodeFit: true,
|
||||
}, {
|
||||
},
|
||||
{
|
||||
description: "Pod with incorrect node selector, but nodefit false, should still be evicted",
|
||||
pods: []*v1.Pod{
|
||||
buildTestPod("p1", n1.Name, func(pod *v1.Pod) {
|
||||
@@ -246,6 +264,71 @@ func TestDefaultEvictorPreEvictionFilter(t *testing.T) {
|
||||
},
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
description: "Pod with namespace matched namespace selector, should be evicted",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.Namespace = namespace
|
||||
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod.Spec.NodeSelector = map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
}
|
||||
}),
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
test.BuildTestNode("node2", 1000, 2000, 13, func(node *v1.Node) {
|
||||
node.ObjectMeta.Labels = map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
}
|
||||
}),
|
||||
test.BuildTestNode("node3", 1000, 2000, 13, func(node *v1.Node) {
|
||||
node.ObjectMeta.Labels = map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
}
|
||||
}),
|
||||
},
|
||||
namespaces: []*v1.Namespace{
|
||||
test.BuildTestNamespace("default"),
|
||||
test.BuildTestNamespace(namespace),
|
||||
},
|
||||
evictLocalStoragePods: false,
|
||||
evictSystemCriticalPods: false,
|
||||
nodeFit: true,
|
||||
useNamespaceSelector: true,
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
description: "Pod with namespace does not matched namespace selector, should not be evicted",
|
||||
pods: []*v1.Pod{
|
||||
test.BuildTestPod("p1", 400, 0, n1.Name, func(pod *v1.Pod) {
|
||||
pod.ObjectMeta.OwnerReferences = test.GetNormalPodOwnerRefList()
|
||||
pod.Spec.NodeSelector = map[string]string{
|
||||
nodeLabelKey: "fail",
|
||||
}
|
||||
}),
|
||||
},
|
||||
nodes: []*v1.Node{
|
||||
test.BuildTestNode("node2", 1000, 2000, 13, func(node *v1.Node) {
|
||||
node.ObjectMeta.Labels = map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
}
|
||||
}),
|
||||
test.BuildTestNode("node3", 1000, 2000, 13, func(node *v1.Node) {
|
||||
node.ObjectMeta.Labels = map[string]string{
|
||||
nodeLabelKey: nodeLabelValue,
|
||||
}
|
||||
}),
|
||||
},
|
||||
namespaces: []*v1.Namespace{
|
||||
test.BuildTestNamespace("default"),
|
||||
test.BuildTestNamespace(namespace),
|
||||
},
|
||||
evictLocalStoragePods: false,
|
||||
evictSystemCriticalPods: false,
|
||||
nodeFit: true,
|
||||
useNamespaceSelector: true,
|
||||
result: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -928,9 +1011,11 @@ func TestReinitialization(t *testing.T) {
|
||||
|
||||
func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin, error) {
|
||||
var objs []runtime.Object
|
||||
|
||||
for _, node := range test.nodes {
|
||||
objs = append(objs, node)
|
||||
}
|
||||
|
||||
for _, pod := range test.pods {
|
||||
objs = append(objs, pod)
|
||||
}
|
||||
@@ -941,13 +1026,17 @@ func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin
|
||||
objs = append(objs, pvc)
|
||||
}
|
||||
|
||||
for _, ns := range test.namespaces {
|
||||
objs = append(objs, ns)
|
||||
}
|
||||
|
||||
fakeClient := fake.NewSimpleClientset(objs...)
|
||||
|
||||
sharedInformerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
|
||||
podInformer := sharedInformerFactory.Core().V1().Pods().Informer()
|
||||
_ = sharedInformerFactory.Policy().V1().PodDisruptionBudgets().Lister()
|
||||
_ = sharedInformerFactory.Core().V1().PersistentVolumeClaims().Lister()
|
||||
|
||||
_ = sharedInformerFactory.Core().V1().Namespaces().Lister()
|
||||
getPodsAssignedToNode, err := podutil.BuildGetPodsAssignedToNodeFunc(podInformer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("build get pods assigned to node function error: %v", err)
|
||||
@@ -972,6 +1061,10 @@ func initializePlugin(ctx context.Context, test testCase) (frameworktypes.Plugin
|
||||
PodProtections: test.podProtections,
|
||||
}
|
||||
|
||||
if test.useNamespaceSelector {
|
||||
defaultEvictorArgs.NamespaceLabelSelector = namespaceSelector
|
||||
}
|
||||
|
||||
evictorPlugin, err := New(
|
||||
ctx,
|
||||
defaultEvictorArgs,
|
||||
@@ -1153,3 +1246,119 @@ func Test_protectedPVCStorageClasses(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleProfilesWithDifferentNamespaceLabelSelectors(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
node := test.BuildTestNode("node1", 1000, 2000, 10, nil)
|
||||
const (
|
||||
nsProdName = "ns-prod"
|
||||
nsTestName = "ns-test"
|
||||
nsBackendName = "ns-backend"
|
||||
)
|
||||
nsProd := test.BuildTestNamespace(nsProdName)
|
||||
nsProd.Labels["env"] = "prod"
|
||||
|
||||
nsTest := test.BuildTestNamespace(nsTestName)
|
||||
nsTest.Labels["env"] = "test"
|
||||
|
||||
nsBackend := test.BuildTestNamespace(nsBackendName)
|
||||
nsBackend.Labels["team"] = "backend"
|
||||
|
||||
podInProd := test.BuildTestPod("pod-in-prod", 100, 100, node.Name, func(pod *v1.Pod) {
|
||||
pod.Namespace = nsProdName
|
||||
test.SetNormalOwnerRef(pod)
|
||||
})
|
||||
|
||||
podInTest := test.BuildTestPod("pod-in-test", 100, 100, node.Name, func(pod *v1.Pod) {
|
||||
pod.Namespace = nsTestName
|
||||
test.SetNormalOwnerRef(pod)
|
||||
})
|
||||
|
||||
podInBackend := test.BuildTestPod("pod-in-backend", 100, 100, node.Name, func(pod *v1.Pod) {
|
||||
pod.Namespace = nsBackendName
|
||||
test.SetNormalOwnerRef(pod)
|
||||
})
|
||||
|
||||
fakeClient := fake.NewClientset(node, nsProd, nsBackend, nsTest, podInProd, podInBackend, podInTest)
|
||||
sharedInformerFactory := informers.NewSharedInformerFactory(fakeClient, 0)
|
||||
|
||||
_ = sharedInformerFactory.Core().V1().Namespaces().Lister()
|
||||
sharedInformerFactory.Start(ctx.Done())
|
||||
sharedInformerFactory.WaitForCacheSync(ctx.Done())
|
||||
|
||||
// Create Profile 1: targets namespaces with env=prod
|
||||
profile1Args := &DefaultEvictorArgs{
|
||||
NamespaceLabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"env": "prod",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
getPodAssignedToNode, err := podutil.BuildGetPodsAssignedToNodeFunc(sharedInformerFactory.Core().V1().Pods().Informer())
|
||||
if err != nil {
|
||||
t.Fatalf("build get pods assigned to node function error: %v", err)
|
||||
}
|
||||
|
||||
profile1Plugin, err := New(ctx, profile1Args, &frameworkfake.HandleImpl{
|
||||
ClientsetImpl: fakeClient,
|
||||
GetPodsAssignedToNodeFuncImpl: getPodAssignedToNode,
|
||||
SharedInformerFactoryImpl: sharedInformerFactory,
|
||||
PluginInstanceIDImpl: nsProdName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize profile1 plugin: %v", err)
|
||||
}
|
||||
|
||||
profile2Args := &DefaultEvictorArgs{
|
||||
NamespaceLabelSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"team": "backend",
|
||||
},
|
||||
},
|
||||
}
|
||||
profile2Plugin, err := New(ctx, profile2Args, &frameworkfake.HandleImpl{
|
||||
ClientsetImpl: fakeClient,
|
||||
GetPodsAssignedToNodeFuncImpl: getPodAssignedToNode,
|
||||
SharedInformerFactoryImpl: sharedInformerFactory,
|
||||
PluginInstanceIDImpl: nsBackendName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize profile2 plugin: %v", err)
|
||||
}
|
||||
|
||||
// Test Profile 1: evicts pods in ns-prod, reject others
|
||||
t.Run("profile1", func(t *testing.T) {
|
||||
profile1 := profile1Plugin.(*DefaultEvictor)
|
||||
|
||||
if profile1.PreEvictionFilter(podInBackend) {
|
||||
t.Errorf("podInBackend should be rejected by profile1")
|
||||
}
|
||||
|
||||
if profile1.PreEvictionFilter(podInTest) {
|
||||
t.Errorf("podInTest should be rejected by profile1")
|
||||
}
|
||||
|
||||
if !profile1.PreEvictionFilter(podInProd) {
|
||||
t.Errorf("podInProd should not be rejected by profile1")
|
||||
}
|
||||
})
|
||||
|
||||
// Test Profile 2: evicts pods in ns-backend, reject others
|
||||
t.Run("profile2", func(t *testing.T) {
|
||||
profile2 := profile2Plugin.(*DefaultEvictor)
|
||||
|
||||
if profile2.PreEvictionFilter(podInProd) {
|
||||
t.Errorf("podInProd should be rejected by profile2")
|
||||
}
|
||||
|
||||
if profile2.PreEvictionFilter(podInTest) {
|
||||
t.Errorf("podInTest should be rejected by profile2")
|
||||
}
|
||||
|
||||
if !profile2.PreEvictionFilter(podInBackend) {
|
||||
t.Errorf("podInBackend should not be rejected by profile2")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,5 +23,4 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
|
||||
// SetDefaults_DefaultEvictorArgs sets the default values for the
|
||||
// DefaultEvictorArgs configuration.
|
||||
func SetDefaults_DefaultEvictorArgs(obj runtime.Object) {
|
||||
}
|
||||
func SetDefaults_DefaultEvictorArgs(obj runtime.Object) {}
|
||||
|
||||
@@ -15,6 +15,7 @@ package defaultevictor
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"sigs.k8s.io/descheduler/pkg/api"
|
||||
)
|
||||
|
||||
@@ -25,13 +26,14 @@ import (
|
||||
type DefaultEvictorArgs struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
NodeSelector string `json:"nodeSelector,omitempty"`
|
||||
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
|
||||
PriorityThreshold *api.PriorityThreshold `json:"priorityThreshold,omitempty"`
|
||||
NodeFit bool `json:"nodeFit,omitempty"`
|
||||
MinReplicas uint `json:"minReplicas,omitempty"`
|
||||
MinPodAge *metav1.Duration `json:"minPodAge,omitempty"`
|
||||
NoEvictionPolicy NoEvictionPolicy `json:"noEvictionPolicy,omitempty"`
|
||||
NodeSelector string `json:"nodeSelector,omitempty"`
|
||||
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
|
||||
NamespaceLabelSelector *metav1.LabelSelector `json:"namespaceLabelSelector,omitempty"`
|
||||
PriorityThreshold *api.PriorityThreshold `json:"priorityThreshold,omitempty"`
|
||||
NodeFit bool `json:"nodeFit,omitempty"`
|
||||
MinReplicas uint `json:"minReplicas,omitempty"`
|
||||
MinPodAge *metav1.Duration `json:"minPodAge,omitempty"`
|
||||
NoEvictionPolicy NoEvictionPolicy `json:"noEvictionPolicy,omitempty"`
|
||||
|
||||
// PodProtections holds the list of enabled and disabled protection policies.
|
||||
// Users can selectively disable certain default protection rules or enable extra ones.
|
||||
|
||||
@@ -36,6 +36,11 @@ func (in *DefaultEvictorArgs) DeepCopyInto(out *DefaultEvictorArgs) {
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NamespaceLabelSelector != nil {
|
||||
in, out := &in.NamespaceLabelSelector, &out.NamespaceLabelSelector
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PriorityThreshold != nil {
|
||||
in, out := &in.PriorityThreshold, &out.PriorityThreshold
|
||||
*out = new(api.PriorityThreshold)
|
||||
|
||||
@@ -40,6 +40,22 @@ import (
|
||||
utilptr "k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
// BuildTestNamespace creates a test namespace with given parameters.
|
||||
func BuildTestNamespace(name string) *v1.Namespace {
|
||||
namespace := &v1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Namespace",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
UID: uuid.NewUUID(),
|
||||
Labels: map[string]string{"kubernetes.io/metadata.name": name},
|
||||
},
|
||||
}
|
||||
return namespace
|
||||
}
|
||||
|
||||
// BuildTestPod creates a test pod with given parameters.
|
||||
func BuildTestPod(name string, cpu, memory int64, nodeName string, apply func(*v1.Pod)) *v1.Pod {
|
||||
pod := &v1.Pod{
|
||||
|
||||
Reference in New Issue
Block a user