mirror of https://github.com/k3s-io/k3s
Cleanup work to enable feature gating annotations
parent
32c4683242
commit
2bcd63c524
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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 <pod> 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 {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue