diff --git a/cmd/descheduler/app/options/options.go b/cmd/descheduler/app/options/options.go index 0dd1dfd5f..6277c2240 100644 --- a/cmd/descheduler/app/options/options.go +++ b/cmd/descheduler/app/options/options.go @@ -53,4 +53,6 @@ func (rs *DeschedulerServer) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&rs.KubeconfigFile, "kubeconfig-file", rs.KubeconfigFile, "File with kube configuration.") fs.StringVar(&rs.PolicyConfigFile, "policy-config-file", rs.PolicyConfigFile, "File with descheduler policy configuration.") fs.BoolVar(&rs.DryRun, "dry-run", rs.DryRun, "execute descheduler in dry run mode.") + // node-selector query causes descheduler to run only on nodes that matches the node labels in the query + fs.StringVar(&rs.NodeSelector, "node-selector", rs.NodeSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") } diff --git a/pkg/apis/componentconfig/types.go b/pkg/apis/componentconfig/types.go index 049fcbc50..f2df250a9 100644 --- a/pkg/apis/componentconfig/types.go +++ b/pkg/apis/componentconfig/types.go @@ -37,4 +37,7 @@ type DeschedulerConfiguration struct { // Dry run DryRun bool + + // Node selectors + NodeSelector string } diff --git a/pkg/apis/componentconfig/v1alpha1/types.go b/pkg/apis/componentconfig/v1alpha1/types.go index 017617f41..97363dab2 100644 --- a/pkg/apis/componentconfig/v1alpha1/types.go +++ b/pkg/apis/componentconfig/v1alpha1/types.go @@ -37,4 +37,7 @@ type DeschedulerConfiguration struct { // Dry run DryRun bool `json:"dryRun,omitempty"` + + // Node selectors + NodeSelector string `json:"nodeSelector,omitempty"` } diff --git a/pkg/descheduler/descheduler.go b/pkg/descheduler/descheduler.go index 7e7b33456..2ebfa5ff8 100644 --- a/pkg/descheduler/descheduler.go +++ b/pkg/descheduler/descheduler.go @@ -48,7 +48,7 @@ func Run(rs *options.DeschedulerServer) error { } stopChannel := make(chan struct{}) - nodes, err := nodeutil.ReadyNodes(rs.Client, stopChannel) + nodes, err := nodeutil.ReadyNodes(rs.Client, rs.NodeSelector, stopChannel) if err != nil { return err } diff --git a/pkg/descheduler/node/node.go b/pkg/descheduler/node/node.go index 4907e1dc6..a34c6eee0 100644 --- a/pkg/descheduler/node/node.go +++ b/pkg/descheduler/node/node.go @@ -31,16 +31,23 @@ import ( // ReadyNodes returns ready nodes irrespective of whether they are // schedulable or not. -func ReadyNodes(client clientset.Interface, stopChannel <-chan struct{}) ([]*v1.Node, error) { +func ReadyNodes(client clientset.Interface, nodeSelector string, stopChannel <-chan struct{}) ([]*v1.Node, error) { nl := GetNodeLister(client, stopChannel) - nodes, err := nl.List(labels.Everything()) + + ns, err := labels.Parse(nodeSelector) + if err != nil { + return []*v1.Node{}, err + } + + nodes, err := nl.List(ns) + if err != nil { return []*v1.Node{}, err } if len(nodes) == 0 { var err error - nItems, err := client.Core().Nodes().List(metav1.ListOptions{}) + nItems, err := client.Core().Nodes().List(metav1.ListOptions{LabelSelector: nodeSelector}) if err != nil { return []*v1.Node{}, err } diff --git a/pkg/descheduler/node/node_test.go b/pkg/descheduler/node/node_test.go index 33ad76a43..dd26fdaa7 100644 --- a/pkg/descheduler/node/node_test.go +++ b/pkg/descheduler/node/node_test.go @@ -80,3 +80,20 @@ func TestReadyNodes(t *testing.T) { } } + +func TestReadyNodesWithNodeSelector(t *testing.T) { + node1 := test.BuildTestNode("node1", 1000, 2000, 9) + node1.Labels = map[string]string{"type": "compute"} + node2 := test.BuildTestNode("node2", 1000, 2000, 9) + node2.Labels = map[string]string{"type": "infra"} + + fakeClient := fake.NewSimpleClientset(node1, node2) + + nodeSelector := "type=compute" + stopChannel := make(chan struct{}) + nodes, _ := ReadyNodes(fakeClient, nodeSelector, stopChannel) + + if nodes[0].Name != "node1" { + t.Errorf("Expected node1, got %s", nodes[0].Name) + } +}