Commit beb5b9dd authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #47869 from timothysc/affinity_cleanup

Automatic merge from submit-queue Removes alpha feature gate for affinity annotations. **What this PR does / why we need it**: In 1.5 we added a backstop to support alpha affinity annotations. This PR removes that support in favor of the Beta fields per discussions. It also serves as a precursor to some of the component config work that @ncdc has done around @mikedanese design proposal. xref: https://github.com/kubernetes/kubernetes/pull/41617 **Special notes for your reviewer**: **Release note**: ``` Removes alpha feature gate for pod affinity annotations. ``` /cc @kubernetes/sig-scheduling-pr-reviews @kubernetes/sig-cluster-lifecycle-misc
parents d39115a9 4aea6269
...@@ -72,11 +72,6 @@ const ( ...@@ -72,11 +72,6 @@ const (
// This annotation can be attached to node. // This annotation can be attached to node.
ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl" ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
// AffinityAnnotationKey represents the key of affinity data (json serialized)
// in the Annotations of a Pod.
// TODO: remove when alpha support for affinity is removed
AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
// annotation key prefix used to identify non-convertible json paths. // annotation key prefix used to identify non-convertible json paths.
NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io" NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io"
......
...@@ -519,21 +519,6 @@ func PodAnnotationsFromSysctls(sysctls []api.Sysctl) string { ...@@ -519,21 +519,6 @@ func PodAnnotationsFromSysctls(sysctls []api.Sysctl) string {
return strings.Join(kvs, ",") return strings.Join(kvs, ",")
} }
// GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations
// and converts it to the Affinity type in api.
// TODO: remove when alpha support for affinity is removed
func GetAffinityFromPodAnnotations(annotations map[string]string) (*api.Affinity, error) {
if len(annotations) > 0 && annotations[api.AffinityAnnotationKey] != "" {
var affinity api.Affinity
err := json.Unmarshal([]byte(annotations[api.AffinityAnnotationKey]), &affinity)
if err != nil {
return nil, err
}
return &affinity, nil
}
return nil, nil
}
// GetPersistentVolumeClass returns StorageClassName. // GetPersistentVolumeClass returns StorageClassName.
func GetPersistentVolumeClass(volume *api.PersistentVolume) string { func GetPersistentVolumeClass(volume *api.PersistentVolume) string {
// Use beta annotation first // Use beta annotation first
......
...@@ -445,21 +445,6 @@ func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) { ...@@ -445,21 +445,6 @@ func RemoveTaint(node *v1.Node, taint *v1.Taint) (*v1.Node, bool, error) {
return newNode, true, nil return newNode, true, nil
} }
// GetAffinityFromPodAnnotations gets the json serialized affinity data from Pod.Annotations
// and converts it to the Affinity type in api.
// TODO: remove when alpha support for affinity is removed
func GetAffinityFromPodAnnotations(annotations map[string]string) (*v1.Affinity, error) {
if len(annotations) > 0 && annotations[v1.AffinityAnnotationKey] != "" {
var affinity v1.Affinity
err := json.Unmarshal([]byte(annotations[v1.AffinityAnnotationKey]), &affinity)
if err != nil {
return nil, err
}
return &affinity, nil
}
return nil, nil
}
// GetPersistentVolumeClass returns StorageClassName. // GetPersistentVolumeClass returns StorageClassName.
func GetPersistentVolumeClass(volume *v1.PersistentVolume) string { func GetPersistentVolumeClass(volume *v1.PersistentVolume) string {
// Use beta annotation first // Use beta annotation first
......
...@@ -443,63 +443,6 @@ func TestSysctlsFromPodAnnotation(t *testing.T) { ...@@ -443,63 +443,6 @@ func TestSysctlsFromPodAnnotation(t *testing.T) {
} }
} }
// TODO: remove when alpha support for affinity is removed
func TestGetAffinityFromPodAnnotations(t *testing.T) {
testCases := []struct {
pod *v1.Pod
expectErr bool
}{
{
pod: &v1.Pod{},
expectErr: false,
},
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
v1.AffinityAnnotationKey: `
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
"matchExpressions": [{
"key": "foo",
"operator": "In",
"values": ["value1", "value2"]
}]
}]
}}}`,
},
},
},
expectErr: false,
},
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{
v1.AffinityAnnotationKey: `
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
"matchExpressions": [{
"key": "foo",
`,
},
},
},
expectErr: true,
},
}
for i, tc := range testCases {
_, err := GetAffinityFromPodAnnotations(tc.pod.Annotations)
if err == nil && tc.expectErr {
t.Errorf("[%v]expected error but got none.", i)
}
if err != nil && !tc.expectErr {
t.Errorf("[%v]did not expect error but got: %v", i, err)
}
}
}
// TODO: remove when alpha support for topology constraints is removed // TODO: remove when alpha support for topology constraints is removed
func TestGetNodeAffinityFromAnnotations(t *testing.T) { func TestGetNodeAffinityFromAnnotations(t *testing.T) {
testCases := []struct { testCases := []struct {
......
...@@ -110,10 +110,6 @@ func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList ...@@ -110,10 +110,6 @@ func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList
func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList { func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if annotations[api.AffinityAnnotationKey] != "" {
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
}
if value, isMirror := annotations[api.MirrorPodAnnotationKey]; isMirror { if value, isMirror := annotations[api.MirrorPodAnnotationKey]; isMirror {
if len(spec.NodeName) == 0 { if len(spec.NodeName) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Key(api.MirrorPodAnnotationKey), value, "must set spec.nodeName if mirror pod annotation is set")) allErrs = append(allErrs, field.Invalid(fldPath.Key(api.MirrorPodAnnotationKey), value, "must set spec.nodeName if mirror pod annotation is set"))
...@@ -164,23 +160,6 @@ func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath ...@@ -164,23 +160,6 @@ func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath
return allErrs return allErrs
} }
// ValidateAffinityInPodAnnotations tests that the serialized Affinity in Pod.Annotations has valid data
func ValidateAffinityInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
affinity, err := helper.GetAffinityFromPodAnnotations(annotations)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, api.AffinityAnnotationKey, err.Error()))
return allErrs
}
if affinity == nil {
return allErrs
}
allErrs = append(allErrs, validateAffinity(affinity, fldPath.Child("affinity"))...)
return allErrs
}
func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *api.Pod, fldPath *field.Path) field.ErrorList { func ValidatePodSpecificAnnotationUpdates(newPod, oldPod *api.Pod, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
newAnnotations := newPod.Annotations newAnnotations := newPod.Annotations
...@@ -2430,9 +2409,7 @@ func ValidatePreferredSchedulingTerms(terms []api.PreferredSchedulingTerm, fldPa ...@@ -2430,9 +2409,7 @@ func ValidatePreferredSchedulingTerms(terms []api.PreferredSchedulingTerm, fldPa
// validatePodAffinityTerm tests that the specified podAffinityTerm fields have valid data // validatePodAffinityTerm tests that the specified podAffinityTerm fields have valid data
func validatePodAffinityTerm(podAffinityTerm api.PodAffinityTerm, allowEmptyTopologyKey bool, fldPath *field.Path) field.ErrorList { func validatePodAffinityTerm(podAffinityTerm api.PodAffinityTerm, allowEmptyTopologyKey bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if !utilfeature.DefaultFeatureGate.Enabled(features.AffinityInAnnotations) && len(podAffinityTerm.TopologyKey) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("topologyKey"), "can not be empty"))
}
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, fldPath.Child("matchExpressions"))...) allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, fldPath.Child("matchExpressions"))...)
for _, name := range podAffinityTerm.Namespaces { for _, name := range podAffinityTerm.Namespaces {
for _, msg := range ValidateNamespaceName(name, false) { for _, msg := range ValidateNamespaceName(name, false) {
......
...@@ -5044,7 +5044,7 @@ func TestValidatePod(t *testing.T) { ...@@ -5044,7 +5044,7 @@ func TestValidatePod(t *testing.T) {
} }
} }
func TestValidatePodWithDisabledAffinityInAnnotations(t *testing.T) { func TestValidatePodWithAffinity(t *testing.T) {
validPodSpec := func(affinity *api.Affinity) api.PodSpec { validPodSpec := func(affinity *api.Affinity) api.PodSpec {
spec := api.PodSpec{ spec := api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
...@@ -5057,7 +5057,6 @@ func TestValidatePodWithDisabledAffinityInAnnotations(t *testing.T) { ...@@ -5057,7 +5057,6 @@ func TestValidatePodWithDisabledAffinityInAnnotations(t *testing.T) {
return spec return spec
} }
utilfeature.DefaultFeatureGate.Set("AffinityInAnnotations=False")
errorCases := []api.Pod{ errorCases := []api.Pod{
{ {
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"}, ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
...@@ -5129,29 +5128,30 @@ func TestValidatePodWithDisabledAffinityInAnnotations(t *testing.T) { ...@@ -5129,29 +5128,30 @@ func TestValidatePodWithDisabledAffinityInAnnotations(t *testing.T) {
}), }),
}, },
{ {
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"}, /* TODO: Re-enable if/when topologykey is required.
Spec: validPodSpec(&api.Affinity{ ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
PodAntiAffinity: &api.PodAntiAffinity{ Spec: validPodSpec(&api.Affinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{ PodAntiAffinity: &api.PodAntiAffinity{
{ PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
Weight: 10, {
PodAffinityTerm: api.PodAffinityTerm{ Weight: 10,
LabelSelector: &metav1.LabelSelector{ PodAffinityTerm: api.PodAffinityTerm{
MatchExpressions: []metav1.LabelSelectorRequirement{ LabelSelector: &metav1.LabelSelector{
{ MatchExpressions: []metav1.LabelSelectorRequirement{
Key: "key2", {
Operator: metav1.LabelSelectorOpNotIn, Key: "key2",
Values: []string{"value1", "value2"}, Operator: metav1.LabelSelectorOpNotIn,
}, Values: []string{"value1", "value2"},
}, },
}, },
Namespaces: []string{"ns"},
TopologyKey: "",
}, },
Namespaces: []string{"ns"},
TopologyKey: "",
}, },
}, },
}, },
}), },
}),*/
}, },
} }
......
...@@ -67,13 +67,6 @@ const ( ...@@ -67,13 +67,6 @@ const (
// Note: This feature is not supported for `BestEffort` pods. // Note: This feature is not supported for `BestEffort` pods.
ExperimentalCriticalPodAnnotation utilfeature.Feature = "ExperimentalCriticalPodAnnotation" ExperimentalCriticalPodAnnotation utilfeature.Feature = "ExperimentalCriticalPodAnnotation"
// 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"
// owner: @vishh // owner: @vishh
// alpha: v1.6 // alpha: v1.6
// //
...@@ -137,7 +130,6 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS ...@@ -137,7 +130,6 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
DynamicVolumeProvisioning: {Default: true, PreRelease: utilfeature.Alpha}, DynamicVolumeProvisioning: {Default: true, PreRelease: utilfeature.Alpha},
ExperimentalHostUserNamespaceDefaultingGate: {Default: false, PreRelease: utilfeature.Beta}, ExperimentalHostUserNamespaceDefaultingGate: {Default: false, PreRelease: utilfeature.Beta},
ExperimentalCriticalPodAnnotation: {Default: false, PreRelease: utilfeature.Alpha}, ExperimentalCriticalPodAnnotation: {Default: false, PreRelease: utilfeature.Alpha},
AffinityInAnnotations: {Default: false, PreRelease: utilfeature.Alpha},
Accelerators: {Default: false, PreRelease: utilfeature.Alpha}, Accelerators: {Default: false, PreRelease: utilfeature.Alpha},
TaintBasedEvictions: {Default: false, PreRelease: utilfeature.Alpha}, TaintBasedEvictions: {Default: false, PreRelease: utilfeature.Alpha},
RotateKubeletServerCertificate: {Default: false, PreRelease: utilfeature.Alpha}, RotateKubeletServerCertificate: {Default: false, PreRelease: utilfeature.Alpha},
......
...@@ -59,7 +59,6 @@ go_test( ...@@ -59,7 +59,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
......
...@@ -663,7 +663,7 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool { ...@@ -663,7 +663,7 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool {
// 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity // 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity
// 6. non-nil empty NodeSelectorRequirement is not allowed // 6. non-nil empty NodeSelectorRequirement is not allowed
nodeAffinityMatches := true nodeAffinityMatches := true
affinity := schedulercache.ReconcileAffinity(pod) affinity := pod.Spec.Affinity
if affinity != nil && affinity.NodeAffinity != nil { if affinity != nil && affinity.NodeAffinity != nil {
nodeAffinity := affinity.NodeAffinity nodeAffinity := affinity.NodeAffinity
// if no required NodeAffinity requirements, will do no-op, means select all nodes. // if no required NodeAffinity requirements, will do no-op, means select all nodes.
...@@ -985,7 +985,7 @@ func (c *PodAffinityChecker) InterPodAffinityMatches(pod *v1.Pod, meta interface ...@@ -985,7 +985,7 @@ func (c *PodAffinityChecker) InterPodAffinityMatches(pod *v1.Pod, meta interface
} }
// Now check if <pod> requirements will be satisfied on this node. // Now check if <pod> requirements will be satisfied on this node.
affinity := schedulercache.ReconcileAffinity(pod) affinity := pod.Spec.Affinity
if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) { if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) {
return true, nil, nil return true, nil, nil
} }
...@@ -1089,7 +1089,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler ...@@ -1089,7 +1089,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler
} }
var nodeResult []matchingPodAntiAffinityTerm var nodeResult []matchingPodAntiAffinityTerm
for _, existingPod := range nodeInfo.PodsWithAffinity() { for _, existingPod := range nodeInfo.PodsWithAffinity() {
affinity := schedulercache.ReconcileAffinity(existingPod) affinity := existingPod.Spec.Affinity
if affinity == nil { if affinity == nil {
continue continue
} }
...@@ -1116,7 +1116,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler ...@@ -1116,7 +1116,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler
func (c *PodAffinityChecker) getMatchingAntiAffinityTerms(pod *v1.Pod, allPods []*v1.Pod) ([]matchingPodAntiAffinityTerm, error) { func (c *PodAffinityChecker) getMatchingAntiAffinityTerms(pod *v1.Pod, allPods []*v1.Pod) ([]matchingPodAntiAffinityTerm, error) {
var result []matchingPodAntiAffinityTerm var result []matchingPodAntiAffinityTerm
for _, existingPod := range allPods { for _, existingPod := range allPods {
affinity := schedulercache.ReconcileAffinity(existingPod) affinity := existingPod.Spec.Affinity
if affinity != nil && affinity.PodAntiAffinity != nil { if affinity != nil && affinity.PodAntiAffinity != nil {
existingPodNode, err := c.info.GetNodeInfo(existingPod.Spec.NodeName) existingPodNode, err := c.info.GetNodeInfo(existingPod.Spec.NodeName)
if err != nil { if err != nil {
......
...@@ -71,7 +71,6 @@ go_test( ...@@ -71,7 +71,6 @@ go_test(
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
......
...@@ -117,7 +117,7 @@ func (p *podAffinityPriorityMap) processTerms(terms []v1.WeightedPodAffinityTerm ...@@ -117,7 +117,7 @@ func (p *podAffinityPriorityMap) processTerms(terms []v1.WeightedPodAffinityTerm
// Symmetry need to be considered for preferredDuringSchedulingIgnoredDuringExecution from podAffinity & podAntiAffinity, // Symmetry need to be considered for preferredDuringSchedulingIgnoredDuringExecution from podAffinity & podAntiAffinity,
// symmetry need to be considered for hard requirements from podAffinity // 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) { func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.NodeInfo, nodes []*v1.Node) (schedulerapi.HostPriorityList, error) {
affinity := schedulercache.ReconcileAffinity(pod) affinity := pod.Spec.Affinity
hasAffinityConstraints := affinity != nil && affinity.PodAffinity != nil hasAffinityConstraints := affinity != nil && affinity.PodAffinity != nil
hasAntiAffinityConstraints := affinity != nil && affinity.PodAntiAffinity != nil hasAntiAffinityConstraints := affinity != nil && affinity.PodAntiAffinity != nil
...@@ -138,7 +138,7 @@ func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, node ...@@ -138,7 +138,7 @@ func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, node
if err != nil { if err != nil {
return err return err
} }
existingPodAffinity := schedulercache.ReconcileAffinity(existingPod) existingPodAffinity := existingPod.Spec.Affinity
existingHasAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAffinity != nil existingHasAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAffinity != nil
existingHasAntiAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAntiAffinity != nil existingHasAntiAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAntiAffinity != nil
......
...@@ -38,6 +38,6 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod ...@@ -38,6 +38,6 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod
return &priorityMetadata{ return &priorityMetadata{
nonZeroRequest: getNonZeroRequests(pod), nonZeroRequest: getNonZeroRequests(pod),
podTolerations: tolerationsPreferNoSchedule, podTolerations: tolerationsPreferNoSchedule,
affinity: schedulercache.ReconcileAffinity(pod), affinity: pod.Spec.Affinity,
} }
} }
...@@ -43,7 +43,7 @@ func CalculateNodeAffinityPriorityMap(pod *v1.Pod, meta interface{}, nodeInfo *s ...@@ -43,7 +43,7 @@ func CalculateNodeAffinityPriorityMap(pod *v1.Pod, meta interface{}, nodeInfo *s
affinity = priorityMeta.affinity affinity = priorityMeta.affinity
} else { } else {
// We couldn't parse metadata - fallback to the podspec. // We couldn't parse metadata - fallback to the podspec.
affinity = schedulercache.ReconcileAffinity(pod) affinity = pod.Spec.Affinity
} }
var count int32 var count int32
......
...@@ -22,7 +22,6 @@ import ( ...@@ -22,7 +22,6 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api" schedulerapi "k8s.io/kubernetes/plugin/pkg/scheduler/api"
"k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache" "k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache"
) )
...@@ -178,147 +177,3 @@ func TestNodeAffinityPriority(t *testing.T) { ...@@ -178,147 +177,3 @@ 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"}
label2 := map[string]string{"key": "value"}
label3 := map[string]string{"az": "az1"}
label4 := map[string]string{"abc": "az11", "def": "az22"}
label5 := map[string]string{"foo": "bar", "key": "value", "az": "az1"}
affinity1 := map[string]string{
v1.AffinityAnnotationKey: `
{"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [
{
"weight": 2,
"preference": {
"matchExpressions": [
{
"key": "foo",
"operator": "In", "values": ["bar"]
}
]
}
}
]}}`,
}
affinity2 := map[string]string{
v1.AffinityAnnotationKey: `
{"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [
{
"weight": 2,
"preference": {"matchExpressions": [
{
"key": "foo",
"operator": "In", "values": ["bar"]
}
]}
},
{
"weight": 4,
"preference": {"matchExpressions": [
{
"key": "key",
"operator": "In", "values": ["value"]
}
]}
},
{
"weight": 5,
"preference": {"matchExpressions": [
{
"key": "foo",
"operator": "In", "values": ["bar"]
},
{
"key": "key",
"operator": "In", "values": ["value"]
},
{
"key": "az",
"operator": "In", "values": ["az1"]
}
]}
}
]}}`,
}
tests := []struct {
pod *v1.Pod
nodes []*v1.Node
expectedList schedulerapi.HostPriorityList
test string
}{
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
},
nodes: []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
},
expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 0}, {Host: "machine2", Score: 0}, {Host: "machine3", Score: 0}},
test: "all machines are same priority as NodeAffinity is nil",
},
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: affinity1,
},
},
nodes: []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label4}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
},
expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 0}, {Host: "machine2", Score: 0}, {Host: "machine3", Score: 0}},
test: "no machine macthes preferred scheduling requirements in NodeAffinity of pod so all machines' priority is zero",
},
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: affinity1,
},
},
nodes: []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine3", Labels: label3}},
},
expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 10}, {Host: "machine2", Score: 0}, {Host: "machine3", Score: 0}},
test: "only machine1 matches the preferred scheduling requirements of pod",
},
{
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Annotations: affinity2,
},
},
nodes: []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: "machine1", Labels: label1}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine5", Labels: label5}},
{ObjectMeta: metav1.ObjectMeta{Name: "machine2", Labels: label2}},
},
expectedList: []schedulerapi.HostPriority{{Host: "machine1", Score: 1}, {Host: "machine5", Score: 10}, {Host: "machine2", Score: 3}},
test: "all machines matches the preferred scheduling requirements of pod but with different priorities ",
},
}
for _, test := range tests {
nodeNameToInfo := schedulercache.CreateNodeNameToInfoMap(nil, test.nodes)
nap := priorityFunction(CalculateNodeAffinityPriorityMap, CalculateNodeAffinityPriorityReduce)
list, err := nap(test.pod, nodeNameToInfo, test.nodes)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !reflect.DeepEqual(test.expectedList, list) {
t.Errorf("%s: \nexpected %#v, \ngot %#v", test.test, test.expectedList, list)
}
}
}
...@@ -8,6 +8,17 @@ load( ...@@ -8,6 +8,17 @@ load(
"go_test", "go_test",
) )
go_test(
name = "go_default_test",
srcs = ["topologies_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
],
)
go_library( go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
...@@ -17,12 +28,10 @@ go_library( ...@@ -17,12 +28,10 @@ go_library(
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/features:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
...@@ -38,14 +47,3 @@ filegroup( ...@@ -38,14 +47,3 @@ filegroup(
srcs = [":package-srcs"], srcs = [":package-srcs"],
tags = ["automanaged"], tags = ["automanaged"],
) )
go_test(
name = "go_default_test",
srcs = ["topologies_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
],
)
...@@ -20,8 +20,6 @@ import ( ...@@ -20,8 +20,6 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features"
) )
// GetNamespacesFromPodAffinityTerm returns a set of names // GetNamespacesFromPodAffinityTerm returns a set of names
...@@ -79,15 +77,5 @@ type Topologies struct { ...@@ -79,15 +77,5 @@ type Topologies struct {
// NodesHaveSameTopologyKey checks if nodeA and nodeB have same label value with given topologyKey as label key. // NodesHaveSameTopologyKey checks if nodeA and nodeB have same label value with given topologyKey as label key.
// If the topologyKey is empty, check if the two nodes have any of the default topologyKeys, and have same corresponding label value. // If the topologyKey is empty, check if the two nodes have any of the default topologyKeys, and have same corresponding label value.
func (tps *Topologies) NodesHaveSameTopologyKey(nodeA, nodeB *v1.Node, topologyKey string) bool { func (tps *Topologies) NodesHaveSameTopologyKey(nodeA, nodeB *v1.Node, topologyKey string) bool {
if utilfeature.DefaultFeatureGate.Enabled(features.AffinityInAnnotations) && len(topologyKey) == 0 { return NodesHaveSameTopologyKey(nodeA, nodeB, topologyKey)
// assumes this is allowed only for PreferredDuringScheduling pod anti-affinity (ensured by api/validation)
for _, defaultKey := range tps.DefaultKeys {
if NodesHaveSameTopologyKey(nodeA, nodeB, defaultKey) {
return true
}
}
return false
} else {
return NodesHaveSameTopologyKey(nodeA, nodeB, topologyKey)
}
} }
...@@ -14,30 +14,24 @@ go_library( ...@@ -14,30 +14,24 @@ go_library(
"cache.go", "cache.go",
"interface.go", "interface.go",
"node_info.go", "node_info.go",
"reconcile_affinity.go",
"util.go", "util.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1/helper:go_default_library", "//pkg/api/v1/helper:go_default_library",
"//pkg/features:go_default_library",
"//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library", "//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library",
], ],
) )
go_test( go_test(
name = "go_default_test", name = "go_default_test",
srcs = [ srcs = ["cache_test.go"],
"cache_test.go",
"reconcile_affinity_test.go",
],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
...@@ -48,7 +42,6 @@ go_test( ...@@ -48,7 +42,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
], ],
) )
......
...@@ -257,7 +257,7 @@ func (n *NodeInfo) String() string { ...@@ -257,7 +257,7 @@ func (n *NodeInfo) String() string {
} }
func hasPodAffinityConstraints(pod *v1.Pod) bool { func hasPodAffinityConstraints(pod *v1.Pod) bool {
affinity := ReconcileAffinity(pod) affinity := pod.Spec.Affinity
return affinity != nil && (affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil) return affinity != nil && (affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil)
} }
......
/*
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 (
"k8s.io/api/core/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
"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, _ := v1helper.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
}
/*
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"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
)
// 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)
}
}
}
...@@ -72,11 +72,6 @@ const ( ...@@ -72,11 +72,6 @@ const (
// This annotation can be attached to node. // This annotation can be attached to node.
ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl" ObjectTTLAnnotationKey string = "node.alpha.kubernetes.io/ttl"
// AffinityAnnotationKey represents the key of affinity data (json serialized)
// in the Annotations of a Pod.
// TODO: remove when alpha support for affinity is removed
AffinityAnnotationKey string = "scheduler.alpha.kubernetes.io/affinity"
// annotation key prefix used to identify non-convertible json paths. // annotation key prefix used to identify non-convertible json paths.
NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io" NonConvertibleAnnotationPrefix = "non-convertible.kubernetes.io"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment