mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	move nodeaffinity helpers to component-helpers package
This commit is contained in:
		@@ -50,10 +50,10 @@ import (
 | 
			
		||||
	"k8s.io/client-go/util/workqueue"
 | 
			
		||||
	"k8s.io/component-base/metrics/prometheus/ratelimiter"
 | 
			
		||||
	v1helper "k8s.io/component-helpers/scheduling/corev1"
 | 
			
		||||
	"k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
 | 
			
		||||
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/controller"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/controller/daemon/util"
 | 
			
		||||
	pluginhelper "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
 | 
			
		||||
	"k8s.io/utils/integer"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -1277,7 +1277,8 @@ func (dsc *DaemonSetsController) nodeShouldRunDaemonPod(node *v1.Node, ds *apps.
 | 
			
		||||
// Predicates checks if a DaemonSet's pod can run on a node.
 | 
			
		||||
func Predicates(pod *v1.Pod, node *v1.Node, taints []v1.Taint) (fitsNodeName, fitsNodeAffinity, fitsTaints bool) {
 | 
			
		||||
	fitsNodeName = len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == node.Name
 | 
			
		||||
	fitsNodeAffinity = pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, node)
 | 
			
		||||
	// Ignore parsing errors for backwards compatibility.
 | 
			
		||||
	fitsNodeAffinity, _ = nodeaffinity.GetRequiredNodeAffinity(pod).Match(node)
 | 
			
		||||
	_, hasUntoleratedTaint := v1helper.FindMatchingUntoleratedTaint(taints, pod.Spec.Tolerations, func(t *v1.Taint) bool {
 | 
			
		||||
		return t.Effect == v1.TaintEffectNoExecute || t.Effect == v1.TaintEffectNoSchedule
 | 
			
		||||
	})
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,11 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	v1affinityhelper "k8s.io/component-helpers/scheduling/corev1/nodeaffinity"
 | 
			
		||||
	"k8s.io/klog/v2"
 | 
			
		||||
	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/kubelet/util/format"
 | 
			
		||||
	schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework"
 | 
			
		||||
	pluginhelper "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports"
 | 
			
		||||
@@ -236,7 +236,9 @@ func GeneralPredicates(pod *v1.Pod, nodeInfo *schedulerframework.NodeInfo) ([]Pr
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, nodeInfo.Node()) {
 | 
			
		||||
	// Ignore parsing errors for backwards compatibility.
 | 
			
		||||
	match, _ := v1affinityhelper.GetRequiredNodeAffinity(pod).Match(nodeInfo.Node())
 | 
			
		||||
	if !match {
 | 
			
		||||
		reasons = append(reasons, &PredicateFailureError{nodeaffinity.Name, nodeaffinity.ErrReasonPod})
 | 
			
		||||
	}
 | 
			
		||||
	if !nodename.Fits(pod, nodeInfo) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package helper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/labels"
 | 
			
		||||
	"k8s.io/component-helpers/scheduling/corev1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PodMatchesNodeSelectorAndAffinityTerms checks whether the pod is schedulable onto nodes according to
 | 
			
		||||
// the requirements in both NodeAffinity and nodeSelector.
 | 
			
		||||
func PodMatchesNodeSelectorAndAffinityTerms(pod *v1.Pod, node *v1.Node) bool {
 | 
			
		||||
	// Check if node.Labels match pod.Spec.NodeSelector.
 | 
			
		||||
	if len(pod.Spec.NodeSelector) > 0 {
 | 
			
		||||
		selector := labels.SelectorFromSet(pod.Spec.NodeSelector)
 | 
			
		||||
		if !selector.Matches(labels.Set(node.Labels)) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if pod.Spec.Affinity == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return NodeMatchesNodeAffinity(pod.Spec.Affinity.NodeAffinity, node)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NodeMatchesNodeAffinity checks whether the Node satisfy the given NodeAffinity.
 | 
			
		||||
func NodeMatchesNodeAffinity(affinity *v1.NodeAffinity, node *v1.Node) bool {
 | 
			
		||||
	// 1. nil NodeSelector matches all nodes (i.e. does not filter out any nodes)
 | 
			
		||||
	// 2. nil []NodeSelectorTerm (equivalent to non-nil empty NodeSelector) matches no nodes
 | 
			
		||||
	// 3. zero-length non-nil []NodeSelectorTerm matches no nodes also, just for simplicity
 | 
			
		||||
	// 4. nil []NodeSelectorRequirement (equivalent to non-nil empty NodeSelectorTerm) matches no nodes
 | 
			
		||||
	// 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity
 | 
			
		||||
	// 6. non-nil empty NodeSelectorRequirement is not allowed
 | 
			
		||||
	if affinity == nil {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Match node selector for requiredDuringSchedulingRequiredDuringExecution.
 | 
			
		||||
	// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
 | 
			
		||||
	// if affinity.RequiredDuringSchedulingRequiredDuringExecution != nil && !nodeMatchesNodeSelector(node, affinity.RequiredDuringSchedulingRequiredDuringExecution) {
 | 
			
		||||
	// 	return false
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	// Match node selector for requiredDuringSchedulingIgnoredDuringExecution.
 | 
			
		||||
	if affinity.RequiredDuringSchedulingIgnoredDuringExecution != nil && !nodeMatchesNodeSelector(node, affinity.RequiredDuringSchedulingIgnoredDuringExecution) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// nodeMatchesNodeSelector checks if a node's labels satisfy a list of node selector terms,
 | 
			
		||||
// terms are ORed, and an empty list of terms will match nothing.
 | 
			
		||||
func nodeMatchesNodeSelector(node *v1.Node, nodeSelector *v1.NodeSelector) bool {
 | 
			
		||||
	matches, _ := corev1.MatchNodeSelectorTerms(node, nodeSelector)
 | 
			
		||||
	return matches
 | 
			
		||||
}
 | 
			
		||||
@@ -1,711 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 The Kubernetes Authors.
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package helper
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestPodMatchesNodeSelectorAndAffinityTerms(t *testing.T) {
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		pod      *v1.Pod
 | 
			
		||||
		labels   map[string]string
 | 
			
		||||
		nodeName string
 | 
			
		||||
		want     bool
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name: "no selector",
 | 
			
		||||
			pod:  &v1.Pod{},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "missing labels",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "same labels",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "node labels are superset",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
				"baz": "blah",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "node labels are subset",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
						"baz": "blah",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchExpressions using In operator that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"bar", "value2"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchExpressions using Gt operator that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "kernel-version",
 | 
			
		||||
												Operator: v1.NodeSelectorOpGt,
 | 
			
		||||
												Values:   []string{"0204"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				// We use two digit to denote major version and two digit for minor version.
 | 
			
		||||
				"kernel-version": "0206",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchExpressions using NotIn operator that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "mem-type",
 | 
			
		||||
												Operator: v1.NodeSelectorOpNotIn,
 | 
			
		||||
												Values:   []string{"DDR", "DDR2"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"mem-type": "DDR3",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchExpressions using Exists operator that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "GPU",
 | 
			
		||||
												Operator: v1.NodeSelectorOpExists,
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"GPU": "NVIDIA-GRID-K1",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"value1", "value2"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
			name: "Pod with affinity that don't match node's labels won't schedule onto the node",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: nil,
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
			name: "Pod with a nil []NodeSelectorTerm in affinity, can't match the node's labels and won't schedule onto the node",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
			name: "Pod with an empty []NodeSelectorTerm in affinity, can't match the node's labels and won't schedule onto the node",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with empty MatchExpressions is not a valid value will match no objects and won't schedule onto the node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with no Affinity will schedule onto a node",
 | 
			
		||||
			pod:  &v1.Pod{},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with Affinity but nil NodeSelector will schedule onto a node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: nil,
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with multiple matchExpressions ANDed that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "GPU",
 | 
			
		||||
												Operator: v1.NodeSelectorOpExists,
 | 
			
		||||
											}, {
 | 
			
		||||
												Key:      "GPU",
 | 
			
		||||
												Operator: v1.NodeSelectorOpNotIn,
 | 
			
		||||
												Values:   []string{"AMD", "INTER"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"GPU": "NVIDIA-GRID-K1",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with multiple matchExpressions ANDed that doesn't match the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "GPU",
 | 
			
		||||
												Operator: v1.NodeSelectorOpExists,
 | 
			
		||||
											}, {
 | 
			
		||||
												Key:      "GPU",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"AMD", "INTER"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"GPU": "NVIDIA-GRID-K1",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with multiple NodeSelectorTerms ORed in affinity, matches the node's labels and will schedule onto the node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"bar", "value2"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "diffkey",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"wrong", "value2"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with an Affinity and a PodSpec.NodeSelector(the old thing that we are deprecating) " +
 | 
			
		||||
				"both are satisfied, will schedule onto the node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
					},
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpExists,
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with an Affinity matches node's labels but the PodSpec.NodeSelector(the old thing that we are deprecating) " +
 | 
			
		||||
				"is not satisfied, won't schedule onto the node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					NodeSelector: map[string]string{
 | 
			
		||||
						"foo": "bar",
 | 
			
		||||
					},
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpExists,
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "barrrrrr",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with an invalid value in Affinity term won't be scheduled onto the node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpNotIn,
 | 
			
		||||
												Values:   []string{"invalid value: ___@#$%^"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			labels: map[string]string{
 | 
			
		||||
				"foo": "bar",
 | 
			
		||||
			},
 | 
			
		||||
			want: false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchFields using In operator that matches the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_1",
 | 
			
		||||
			want:     true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with matchFields using In operator that does not match the existing node",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_2",
 | 
			
		||||
			want:     false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with two terms: matchFields does not match, but matchExpressions matches",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"bar"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_2",
 | 
			
		||||
			labels:   map[string]string{"foo": "bar"},
 | 
			
		||||
			want:     true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with one term: matchFields does not match, but matchExpressions matches",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"bar"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_2",
 | 
			
		||||
			labels:   map[string]string{"foo": "bar"},
 | 
			
		||||
			want:     false,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with one term: both matchFields and matchExpressions match",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"bar"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_1",
 | 
			
		||||
			labels:   map[string]string{"foo": "bar"},
 | 
			
		||||
			want:     true,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name: "Pod with two terms: both matchFields and matchExpressions do not match",
 | 
			
		||||
			pod: &v1.Pod{
 | 
			
		||||
				Spec: v1.PodSpec{
 | 
			
		||||
					Affinity: &v1.Affinity{
 | 
			
		||||
						NodeAffinity: &v1.NodeAffinity{
 | 
			
		||||
							RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
 | 
			
		||||
								NodeSelectorTerms: []v1.NodeSelectorTerm{
 | 
			
		||||
									{
 | 
			
		||||
										MatchFields: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      metav1.ObjectNameField,
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"node_1"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										MatchExpressions: []v1.NodeSelectorRequirement{
 | 
			
		||||
											{
 | 
			
		||||
												Key:      "foo",
 | 
			
		||||
												Operator: v1.NodeSelectorOpIn,
 | 
			
		||||
												Values:   []string{"not-match-to-bar"},
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			nodeName: "node_2",
 | 
			
		||||
			labels:   map[string]string{"foo": "bar"},
 | 
			
		||||
			want:     false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		t.Run(test.name, func(t *testing.T) {
 | 
			
		||||
			node := v1.Node{ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
				Name:   test.nodeName,
 | 
			
		||||
				Labels: test.labels,
 | 
			
		||||
			}}
 | 
			
		||||
			got := PodMatchesNodeSelectorAndAffinityTerms(test.pod, &node)
 | 
			
		||||
			if test.want != got {
 | 
			
		||||
				t.Errorf("expected: %v got %v", test.want, got)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user