diff --git a/pkg/api/v1/helpers.go b/pkg/api/v1/helpers.go index 005346bbc9..7308431f0e 100644 --- a/pkg/api/v1/helpers.go +++ b/pkg/api/v1/helpers.go @@ -27,9 +27,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/features" ) // IsOpaqueIntResourceName returns true if the resource name has the opaque @@ -281,7 +279,8 @@ const ( ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl" // AffinityAnnotationKey represents the key of affinity data (json serialized) - // in the Annotations of a Pod. TODO remove in 1.7 + // in the Annotations of a Pod. + // TODO: remove when alpha support for affinity is removed AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity" ) @@ -606,7 +605,7 @@ func RemoveTaint(node *Node, taint *Taint) (*Node, bool, error) { // GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations // and converts it to the Affinity type in api. -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, error) { if len(annotations) > 0 && annotations[AffinityAnnotationKey] != "" { var affinity Affinity @@ -618,26 +617,3 @@ func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, er } return nil, nil } - -// Reconcile api and annotation affinity definitions. -// TODO remove for 1.7 -func ReconcileAffinity(pod *Pod) *Affinity { - affinity := pod.Spec.Affinity - if utilfeature.DefaultFeatureGate.Enabled(features.AffinityInAnnotations) { - annotationsAffinity, _ := GetAffinityFromPodAnnotations(pod.Annotations) - if affinity == nil && annotationsAffinity != nil { - affinity = annotationsAffinity - } else if annotationsAffinity != nil { - if affinity != nil && affinity.NodeAffinity == nil && annotationsAffinity.NodeAffinity != nil { - affinity.NodeAffinity = annotationsAffinity.NodeAffinity - } - if affinity != nil && affinity.PodAffinity == nil && annotationsAffinity.PodAffinity != nil { - affinity.PodAffinity = annotationsAffinity.PodAffinity - } - if affinity != nil && affinity.PodAntiAffinity == nil && annotationsAffinity.PodAntiAffinity != nil { - affinity.PodAntiAffinity = annotationsAffinity.PodAntiAffinity - } - } - } - return affinity -} diff --git a/pkg/api/v1/helpers_test.go b/pkg/api/v1/helpers_test.go index 7d46ce2813..c1496dbe36 100644 --- a/pkg/api/v1/helpers_test.go +++ b/pkg/api/v1/helpers_test.go @@ -17,14 +17,12 @@ limitations under the License. package v1 import ( - "fmt" "reflect" "testing" apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - utilfeature "k8s.io/apiserver/pkg/util/feature" ) func TestAddToNodeAddresses(t *testing.T) { @@ -647,6 +645,7 @@ func TestSysctlsFromPodAnnotation(t *testing.T) { } } +// TODO: remove when alpha support for affinity is removed func TestGetAffinityFromPodAnnotations(t *testing.T) { testCases := []struct { pod *Pod @@ -702,124 +701,3 @@ func TestGetAffinityFromPodAnnotations(t *testing.T) { } } } - -func TestReconcileAffinity(t *testing.T) { - baseAffinity := &Affinity{ - NodeAffinity: &NodeAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: &NodeSelector{ - NodeSelectorTerms: []NodeSelectorTerm{ - { - MatchExpressions: []NodeSelectorRequirement{ - { - Key: "foo", - Operator: NodeSelectorOpIn, - Values: []string{"bar", "value2"}, - }, - }, - }, - }, - }, - }, - PodAffinity: &PodAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "security", - Operator: metav1.LabelSelectorOpDoesNotExist, - Values: []string{"securityscan"}, - }, - }, - }, - TopologyKey: "topologyKey1", - }, - }, - }, - PodAntiAffinity: &PodAntiAffinity{ - RequiredDuringSchedulingIgnoredDuringExecution: []PodAffinityTerm{ - { - LabelSelector: &metav1.LabelSelector{ - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "service", - Operator: metav1.LabelSelectorOpIn, - Values: []string{"S1", "value2"}, - }, - }, - }, - TopologyKey: "topologyKey2", - Namespaces: []string{"ns1"}, - }, - }, - }, - } - - nodeAffinityAnnotation := map[string]string{ - AffinityAnnotationKey: ` - {"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ - { - "weight": 2, - "preference": {"matchExpressions": [ - { - "key": "foo", - "operator": "In", "values": ["bar"] - } - ]} - } - ]}}`, - } - - testCases := []struct { - pod *Pod - expected *Affinity - annotationsEnabled bool - }{ - { - pod: &Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: nodeAffinityAnnotation, - }, - Spec: PodSpec{ - Affinity: baseAffinity, - }, - }, - expected: baseAffinity, - annotationsEnabled: true, - }, - { - pod: &Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: nodeAffinityAnnotation, - }, - }, - expected: &Affinity{ - NodeAffinity: &NodeAffinity{ - PreferredDuringSchedulingIgnoredDuringExecution: []PreferredSchedulingTerm{ - { - Weight: 2, - Preference: NodeSelectorTerm{ - MatchExpressions: []NodeSelectorRequirement{ - { - Key: "foo", - Operator: NodeSelectorOpIn, - Values: []string{"bar"}, - }, - }, - }, - }, - }, - }, - }, - annotationsEnabled: true, - }, - } - - for i, tc := range testCases { - utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("AffinityInAnnotations=%t", tc.annotationsEnabled)) - affinity := ReconcileAffinity(tc.pod) - if affinity != tc.expected { - t.Errorf("[%v]did not get expected affinity. got: %v instead of %v", i, affinity, tc.expected) - } - } -} diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 82f2194cb7..4bf9fedea5 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -67,7 +67,11 @@ const ( // Note: This feature is not supported for `BestEffort` pods. ExperimentalCriticalPodAnnotation utilfeature.Feature = "ExperimentalCriticalPodAnnotation" - // Determines if affinity defined in annotations should bep rocessed + // owner: @davidopp + // alpha: v1.6 + // + // Determines if affinity defined in annotations should be processed + // TODO: remove when alpha support for affinity is removed AffinityInAnnotations utilfeature.Feature = "AffinityInAnnotations" ) diff --git a/plugin/pkg/scheduler/algorithm/predicates/BUILD b/plugin/pkg/scheduler/algorithm/predicates/BUILD index 44cc969fd0..b77a17843a 100644 --- a/plugin/pkg/scheduler/algorithm/predicates/BUILD +++ b/plugin/pkg/scheduler/algorithm/predicates/BUILD @@ -47,6 +47,7 @@ go_test( "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/labels", + "//vendor:k8s.io/apiserver/pkg/util/feature", ], ) diff --git a/plugin/pkg/scheduler/algorithm/predicates/predicates.go b/plugin/pkg/scheduler/algorithm/predicates/predicates.go index 7bc3c22cbb..cc6b2dd5f3 100644 --- a/plugin/pkg/scheduler/algorithm/predicates/predicates.go +++ b/plugin/pkg/scheduler/algorithm/predicates/predicates.go @@ -585,7 +585,7 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool { // 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity // 6. non-nil empty NodeSelectorRequirement is not allowed nodeAffinityMatches := true - affinity := v1.ReconcileAffinity(pod) + affinity := schedulercache.ReconcileAffinity(pod) if affinity != nil && affinity.NodeAffinity != nil { nodeAffinity := affinity.NodeAffinity // if no required NodeAffinity requirements, will do no-op, means select all nodes. @@ -897,7 +897,7 @@ func (c *PodAffinityChecker) InterPodAffinityMatches(pod *v1.Pod, meta interface } // Now check if requirements will be satisfied on this node. - affinity := v1.ReconcileAffinity(pod) + affinity := schedulercache.ReconcileAffinity(pod) if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) { return true, nil, nil } @@ -1001,7 +1001,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler } var nodeResult []matchingPodAntiAffinityTerm for _, existingPod := range nodeInfo.PodsWithAffinity() { - affinity := v1.ReconcileAffinity(existingPod) + affinity := schedulercache.ReconcileAffinity(existingPod) if affinity == nil { continue } @@ -1029,7 +1029,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler func (c *PodAffinityChecker) getMatchingAntiAffinityTerms(pod *v1.Pod, allPods []*v1.Pod) ([]matchingPodAntiAffinityTerm, error) { var result []matchingPodAntiAffinityTerm for _, existingPod := range allPods { - affinity := v1.ReconcileAffinity(existingPod) + affinity := schedulercache.ReconcileAffinity(existingPod) if affinity != nil && affinity.PodAntiAffinity != nil { existingPodNode, err := c.info.GetNodeInfo(existingPod.Spec.NodeName) if err != nil { diff --git a/plugin/pkg/scheduler/algorithm/predicates/predicates_test.go b/plugin/pkg/scheduler/algorithm/predicates/predicates_test.go index 5564352551..f6c2b5250f 100644 --- a/plugin/pkg/scheduler/algorithm/predicates/predicates_test.go +++ b/plugin/pkg/scheduler/algorithm/predicates/predicates_test.go @@ -3581,7 +3581,7 @@ func TestVolumeZonePredicate(t *testing.T) { } } -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func TestPodAnnotationFitsSelector(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") tests := []struct { @@ -4000,7 +4000,7 @@ func TestPodAnnotationFitsSelector(t *testing.T) { } } -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func TestInterPodAffinityAnnotations(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") podLabel := map[string]string{"service": "securityscan"} @@ -4555,7 +4555,7 @@ func TestInterPodAffinityAnnotations(t *testing.T) { } } -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func TestInterPodAffinityAnnotationsWithMultipleNodes(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") podLabelA := map[string]string{ diff --git a/plugin/pkg/scheduler/algorithm/priorities/BUILD b/plugin/pkg/scheduler/algorithm/priorities/BUILD index f3a18dd86b..13c91320c4 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/BUILD +++ b/plugin/pkg/scheduler/algorithm/priorities/BUILD @@ -67,6 +67,7 @@ go_test( "//plugin/pkg/scheduler/schedulercache:go_default_library", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", + "//vendor:k8s.io/apiserver/pkg/util/feature", ], ) diff --git a/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go b/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go index c71bf35495..75cdcd0cc2 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go +++ b/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go @@ -116,7 +116,7 @@ func (p *podAffinityPriorityMap) processTerms(terms []v1.WeightedPodAffinityTerm // Symmetry need to be considered for preferredDuringSchedulingIgnoredDuringExecution from podAffinity & podAntiAffinity, // symmetry need to be considered for hard requirements from podAffinity func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) { - affinity := v1.ReconcileAffinity(pod) + affinity := schedulercache.ReconcileAffinity(pod) hasAffinityConstraints := affinity != nil && affinity.PodAffinity != nil hasAntiAffinityConstraints := affinity != nil && affinity.PodAntiAffinity != nil @@ -137,7 +137,7 @@ func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, node if err != nil { return err } - existingPodAffinity := v1.ReconcileAffinity(existingPod) + existingPodAffinity := schedulercache.ReconcileAffinity(existingPod) existingHasAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAffinity != nil existingHasAntiAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAntiAffinity != nil diff --git a/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity_test.go b/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity_test.go index 6fef421359..d2247b459f 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity_test.go +++ b/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity_test.go @@ -615,7 +615,7 @@ func TestHardPodAffinitySymmetricWeight(t *testing.T) { } } -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func TestInterPodAffinityAnnotationsPriority(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") labelRgChina := map[string]string{ @@ -1089,7 +1089,7 @@ func TestInterPodAffinityAnnotationsPriority(t *testing.T) { } } -// TODO remove for 1.7 +// TODO: remove when alpha support for affinity is removed func TestHardPodAffinityAnnotationsSymmetricWeight(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") podLabelServiceS1 := map[string]string{ diff --git a/plugin/pkg/scheduler/algorithm/priorities/metadata.go b/plugin/pkg/scheduler/algorithm/priorities/metadata.go index 3d7738087d..112501bb31 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/metadata.go +++ b/plugin/pkg/scheduler/algorithm/priorities/metadata.go @@ -41,6 +41,6 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod return &priorityMetadata{ nonZeroRequest: getNonZeroRequests(pod), podTolerations: tolerations, - affinity: v1.ReconcileAffinity(pod), + affinity: schedulercache.ReconcileAffinity(pod), } } diff --git a/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go b/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go index 010d8d05b0..4ee8a80a84 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go +++ b/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go @@ -42,7 +42,7 @@ func CalculateNodeAffinityPriorityMap(pod *v1.Pod, meta interface{}, nodeInfo *s affinity = priorityMeta.affinity } else { // We couldn't parse metadata - fallback to the podspec. - affinity = v1.ReconcileAffinity(pod) + affinity = schedulercache.ReconcileAffinity(pod) } var count int32 diff --git a/plugin/pkg/scheduler/algorithm/priorities/node_affinity_test.go b/plugin/pkg/scheduler/algorithm/priorities/node_affinity_test.go index 64a06c3f8d..66776eeacd 100644 --- a/plugin/pkg/scheduler/algorithm/priorities/node_affinity_test.go +++ b/plugin/pkg/scheduler/algorithm/priorities/node_affinity_test.go @@ -179,6 +179,7 @@ func TestNodeAffinityPriority(t *testing.T) { } } +// TODO: remove when alpha support for affinity is removed func TestNodeAffinityAnnotationsPriority(t *testing.T) { utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=true") label1 := map[string]string{"foo": "bar"} diff --git a/plugin/pkg/scheduler/schedulercache/BUILD b/plugin/pkg/scheduler/schedulercache/BUILD index 214ad6dea1..85633d416d 100644 --- a/plugin/pkg/scheduler/schedulercache/BUILD +++ b/plugin/pkg/scheduler/schedulercache/BUILD @@ -14,23 +14,29 @@ go_library( "cache.go", "interface.go", "node_info.go", + "reconcile_affinity.go", "util.go", ], tags = ["automanaged"], deps = [ "//pkg/api/v1:go_default_library", + "//pkg/features:go_default_library", "//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library", "//vendor:github.com/golang/glog", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/labels", "//vendor:k8s.io/apimachinery/pkg/util/wait", + "//vendor:k8s.io/apiserver/pkg/util/feature", "//vendor:k8s.io/client-go/tools/cache", ], ) go_test( name = "go_default_test", - srcs = ["cache_test.go"], + srcs = [ + "cache_test.go", + "reconcile_affinity_test.go", + ], library = ":go_default_library", tags = ["automanaged"], deps = [ @@ -39,6 +45,7 @@ go_test( "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/labels", + "//vendor:k8s.io/apiserver/pkg/util/feature", ], ) diff --git a/plugin/pkg/scheduler/schedulercache/node_info.go b/plugin/pkg/scheduler/schedulercache/node_info.go index 5c5307642d..dd4ccf02b2 100644 --- a/plugin/pkg/scheduler/schedulercache/node_info.go +++ b/plugin/pkg/scheduler/schedulercache/node_info.go @@ -217,7 +217,7 @@ func (n *NodeInfo) String() string { } func hasPodAffinityConstraints(pod *v1.Pod) bool { - affinity := v1.ReconcileAffinity(pod) + affinity := ReconcileAffinity(pod) return affinity != nil && (affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil) } diff --git a/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go b/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go new file mode 100644 index 0000000000..8009075571 --- /dev/null +++ b/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go @@ -0,0 +1,53 @@ +/* +Copyright 2017 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 schedulercache + +import ( + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/features" +) + +// Reconcile api and annotation affinity definitions. +// When alpha affinity feature is not enabled, always take affinity +// from PodSpec.When alpha affinity feature is enabled, if affinity +// is not set in PodSpec, take affinity from annotation. +// When alpha affinity feature is enabled, if affinity is set in PodSpec, +// take node affinity, pod affinity, and pod anti-affinity individually +// using the following rule: take affinity from PodSpec if it is defined, +// otherwise take from annotation if it is defined. +// TODO: remove when alpha support for affinity is removed +func ReconcileAffinity(pod *v1.Pod) *v1.Affinity { + affinity := pod.Spec.Affinity + if utilfeature.DefaultFeatureGate.Enabled(features.AffinityInAnnotations) { + annotationsAffinity, _ := v1.GetAffinityFromPodAnnotations(pod.Annotations) + if affinity == nil && annotationsAffinity != nil { + affinity = annotationsAffinity + } else if annotationsAffinity != nil { + if affinity != nil && affinity.NodeAffinity == nil && annotationsAffinity.NodeAffinity != nil { + affinity.NodeAffinity = annotationsAffinity.NodeAffinity + } + if affinity != nil && affinity.PodAffinity == nil && annotationsAffinity.PodAffinity != nil { + affinity.PodAffinity = annotationsAffinity.PodAffinity + } + if affinity != nil && affinity.PodAntiAffinity == nil && annotationsAffinity.PodAntiAffinity != nil { + affinity.PodAntiAffinity = annotationsAffinity.PodAntiAffinity + } + } + } + return affinity +} diff --git a/plugin/pkg/scheduler/schedulercache/reconcile_affinity_test.go b/plugin/pkg/scheduler/schedulercache/reconcile_affinity_test.go new file mode 100644 index 0000000000..c8c7627d80 --- /dev/null +++ b/plugin/pkg/scheduler/schedulercache/reconcile_affinity_test.go @@ -0,0 +1,151 @@ +/* +Copyright 2017 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 schedulercache + +import ( + "fmt" + "reflect" + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/api/v1" +) + +// TODO: remove when alpha support for affinity is removed +func TestReconcileAffinity(t *testing.T) { + baseAffinity := &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ + NodeSelectorTerms: []v1.NodeSelectorTerm{ + { + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "foo", + Operator: v1.NodeSelectorOpIn, + Values: []string{"bar", "value2"}, + }, + }, + }, + }, + }, + }, + PodAffinity: &v1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ + { + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "security", + Operator: metav1.LabelSelectorOpDoesNotExist, + Values: []string{"securityscan"}, + }, + }, + }, + TopologyKey: "topologyKey1", + }, + }, + }, + PodAntiAffinity: &v1.PodAntiAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{ + { + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "service", + Operator: metav1.LabelSelectorOpIn, + Values: []string{"S1", "value2"}, + }, + }, + }, + TopologyKey: "topologyKey2", + Namespaces: []string{"ns1"}, + }, + }, + }, + } + + nodeAffinityAnnotation := map[string]string{ + v1.AffinityAnnotationKey: ` + {"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ + { + "weight": 2, + "preference": {"matchExpressions": [ + { + "key": "foo", + "operator": "In", "values": ["bar"] + } + ]} + } + ]}}`, + } + + testCases := []struct { + pod *v1.Pod + expected *v1.Affinity + annotationsEnabled bool + }{ + { + // affinity is set in both PodSpec and annotations; take from PodSpec. + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: nodeAffinityAnnotation, + }, + Spec: v1.PodSpec{ + Affinity: baseAffinity, + }, + }, + expected: baseAffinity, + annotationsEnabled: true, + }, + { + // affinity is only set in annotation; take from annotation. + pod: &v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: nodeAffinityAnnotation, + }, + }, + expected: &v1.Affinity{ + NodeAffinity: &v1.NodeAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{ + { + Weight: 2, + Preference: v1.NodeSelectorTerm{ + MatchExpressions: []v1.NodeSelectorRequirement{ + { + Key: "foo", + Operator: v1.NodeSelectorOpIn, + Values: []string{"bar"}, + }, + }, + }, + }, + }, + }, + }, + annotationsEnabled: true, + }, + } + + for i, tc := range testCases { + utilfeature.DefaultFeatureGate.Set(fmt.Sprintf("AffinityInAnnotations=%t", tc.annotationsEnabled)) + affinity := ReconcileAffinity(tc.pod) + if !reflect.DeepEqual(affinity, tc.expected) { + t.Errorf("[%v] Did not get expected affinity:\n\n%v\n\n. got:\n\n %v", i, tc.expected, affinity) + } + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/util/feature/feature_gate.go b/staging/src/k8s.io/apiserver/pkg/util/feature/feature_gate.go index facac044b1..ebb81425d6 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/feature/feature_gate.go +++ b/staging/src/k8s.io/apiserver/pkg/util/feature/feature_gate.go @@ -108,7 +108,10 @@ type FeatureGate interface { // alpha: v1.5 ExperimentalHostUserNamespaceDefaulting() bool - AnninityInAnnotations() bool + // owner: @davidopp + // alpha: v1.6 + // TODO: remove when alpha support for affinity is removed + AffinityInAnnotations() bool } // featureGate implements FeatureGate as well as pflag.Value for flag parsing.