Cleanup work to enable feature gating annotations

pull/6/head
Timothy St. Clair 2017-02-16 11:38:03 -06:00
parent 32c4683242
commit 2bcd63c524
17 changed files with 242 additions and 167 deletions

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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"
)

View File

@ -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",
],
)

View File

@ -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 {

View File

@ -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{

View File

@ -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",
],
)

View File

@ -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

View File

@ -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{

View File

@ -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),
}
}

View File

@ -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

View File

@ -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"}

View File

@ -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",
],
)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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.