[scheduling] Moved pod affinity and anti-affinity from annotations to api

fields. #25319
This commit is contained in:
Robert Rati 2016-12-08 10:14:21 -05:00
parent 1854d48238
commit 6a3ad93d6c
26 changed files with 1245 additions and 1309 deletions

View File

@ -486,20 +486,6 @@ const (
UnsafeSysctlsPodAnnotationKey string = "security.alpha.kubernetes.io/unsafe-sysctls"
)
// GetAffinityFromPod gets the json serialized affinity data from Pod.Annotations
// and converts it to the Affinity type in api.
func GetAffinityFromPodAnnotations(annotations map[string]string) (*Affinity, error) {
if len(annotations) > 0 && annotations[AffinityAnnotationKey] != "" {
var affinity Affinity
err := json.Unmarshal([]byte(annotations[AffinityAnnotationKey]), &affinity)
if err != nil {
return nil, err
}
return &affinity, nil
}
return nil, nil
}
// GetTolerationsFromPodAnnotations gets the json serialized tolerations data from Pod.Annotations
// and converts it to the []Toleration type in api.
func GetTolerationsFromPodAnnotations(annotations map[string]string) ([]Toleration, error) {

View File

@ -241,62 +241,6 @@ func TestNodeSelectorRequirementsAsSelector(t *testing.T) {
}
}
func TestGetAffinityFromPod(t *testing.T) {
testCases := []struct {
pod *Pod
expectErr bool
}{
{
pod: &Pod{},
expectErr: false,
},
{
pod: &Pod{
ObjectMeta: ObjectMeta{
Annotations: map[string]string{
AffinityAnnotationKey: `
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
"matchExpressions": [{
"key": "foo",
"operator": "In",
"values": ["value1", "value2"]
}]
}]
}}}`,
},
},
},
expectErr: false,
},
{
pod: &Pod{
ObjectMeta: ObjectMeta{
Annotations: map[string]string{
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)
}
}
}
func TestTaintToString(t *testing.T) {
testCases := []struct {
taint *Taint

View File

@ -331,7 +331,6 @@ func TestEncode_Ptr(t *testing.T) {
TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
}
obj := runtime.Object(pod)
@ -345,7 +344,6 @@ func TestEncode_Ptr(t *testing.T) {
}
if !api.Semantic.DeepEqual(obj2, pod) {
t.Errorf("\nExpected:\n\n %#v,\n\nGot:\n\n %#vDiff: %v\n\n", pod, obj2, diff.ObjectDiff(obj2, pod))
}
}

View File

@ -29,7 +29,6 @@ func DeepEqualSafePodSpec() api.PodSpec {
DNSPolicy: api.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}
}

View File

@ -174,9 +174,6 @@ func SetDefaults_PodSpec(obj *PodSpec) {
period := int64(DefaultTerminationGracePeriodSeconds)
obj.TerminationGracePeriodSeconds = &period
}
if obj.Affinity == nil {
obj.Affinity = &Affinity{}
}
}
func SetDefaults_Probe(obj *Probe) {
if obj.TimeoutSeconds == 0 {

View File

@ -101,10 +101,6 @@ func ValidateDNS1123Subdomain(value string, fldPath *field.Path) field.ErrorList
func ValidatePodSpecificAnnotations(annotations map[string]string, spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if annotations[api.AffinityAnnotationKey] != "" {
allErrs = append(allErrs, ValidateAffinityInPodAnnotations(annotations, fldPath)...)
}
if annotations[api.TolerationsAnnotationKey] != "" {
allErrs = append(allErrs, ValidateTolerationsInPodAnnotations(annotations, fldPath)...)
}
@ -1672,7 +1668,14 @@ func validateAffinity(affinity *api.Affinity, fldPath *field.Path) field.ErrorLi
allErrs = append(allErrs, ValidatePreferredSchedulingTerms(na.PreferredDuringSchedulingIgnoredDuringExecution, fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
}
}
if affinity.PodAffinity != nil {
allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, fldPath.Child("podAffinity"))...)
}
if affinity.PodAntiAffinity != nil {
allErrs = append(allErrs, validatePodAntiAffinity(affinity.PodAntiAffinity, fldPath.Child("podAntiAffinity"))...)
}
}
return allErrs
}
@ -1968,30 +1971,6 @@ func validatePodAffinity(podAffinity *api.PodAffinity, fldPath *field.Path) fiel
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 := api.GetAffinityFromPodAnnotations(annotations)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, api.AffinityAnnotationKey, err.Error()))
return allErrs
}
if affinity == nil {
return allErrs
}
affinityFldPath := fldPath.Child(api.AffinityAnnotationKey)
if affinity.PodAffinity != nil {
allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, affinityFldPath.Child("podAffinity"))...)
}
if affinity.PodAntiAffinity != nil {
allErrs = append(allErrs, validatePodAntiAffinity(affinity.PodAntiAffinity, affinityFldPath.Child("podAntiAffinity"))...)
}
return allErrs
}
// ValidateTolerationsInPodAnnotations tests that the serialized tolerations in Pod.Annotations has valid data
func ValidateTolerationsInPodAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}

View File

@ -3076,11 +3076,18 @@ func TestValidatePodSpec(t *testing.T) {
}
func TestValidatePod(t *testing.T) {
validPodSpec := api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
validPodSpec := func(affinity *api.Affinity) api.PodSpec {
spec := api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
}
if affinity != nil {
spec.Affinity = affinity
}
return spec
}
successCases := []api.Pod{
{ // Basic fields.
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
@ -3111,10 +3118,7 @@ func TestValidatePod(t *testing.T) {
Name: "123",
Namespace: "ns",
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Spec: validPodSpec(
// TODO: Uncomment and move this block and move inside NodeAffinity once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// RequiredDuringSchedulingRequiredDuringExecution: &api.NodeSelector{
@ -3129,7 +3133,7 @@ func TestValidatePod(t *testing.T) {
// },
// },
// },
Affinity: &api.Affinity{
&api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
@ -3160,7 +3164,7 @@ func TestValidatePod(t *testing.T) {
},
},
},
},
),
},
{ // Serialized pod affinity in affinity requirements in annotations.
ObjectMeta: api.ObjectMeta{
@ -3179,38 +3183,44 @@ func TestValidatePod(t *testing.T) {
// "namespaces":["ns"],
// "topologyKey": "zone"
// }]
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "In",
"values": ["value1", "value2"]
}]
},
"topologyKey": "zone",
"namespaces": ["ns"]
}],
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "NotIn",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": "region"
}
}]
}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value1", "value2"},
},
},
},
TopologyKey: "zone",
Namespaces: []string{"ns"},
},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 10,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
TopologyKey: "region",
},
},
},
},
}),
},
{ // Serialized pod anti affinity with different Label Operators in affinity requirements in annotations.
ObjectMeta: api.ObjectMeta{
@ -3229,36 +3239,42 @@ func TestValidatePod(t *testing.T) {
// "namespaces":["ns"],
// "topologyKey": "zone"
// }]
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "Exists"
}]
},
"topologyKey": "zone",
"namespaces": ["ns"]
}],
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "DoesNotExist"
}]
},
"namespaces": ["ns"],
"topologyKey": "region"
}
}]
}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpExists,
},
},
},
TopologyKey: "zone",
Namespaces: []string{"ns"},
},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 10,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
Namespaces: []string{"ns"},
TopologyKey: "region",
},
},
},
},
}),
},
{ // populate tolerations equal operator in annotations.
ObjectMeta: api.ObjectMeta{
@ -3274,7 +3290,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // populate tolerations exists operator in annotations.
ObjectMeta: api.ObjectMeta{
@ -3289,7 +3305,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // empty operator is ok for toleration
ObjectMeta: api.ObjectMeta{
@ -3304,7 +3320,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // empty efffect is ok for toleration
ObjectMeta: api.ObjectMeta{
@ -3319,7 +3335,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // docker default seccomp profile
ObjectMeta: api.ObjectMeta{
@ -3329,7 +3345,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "docker/default",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // unconfined seccomp profile
ObjectMeta: api.ObjectMeta{
@ -3339,7 +3355,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "unconfined",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // localhost seccomp profile
ObjectMeta: api.ObjectMeta{
@ -3349,7 +3365,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "localhost/foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // localhost seccomp profile for a container
ObjectMeta: api.ObjectMeta{
@ -3359,7 +3375,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompContainerAnnotationKeyPrefix + "foo": "localhost/foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // default AppArmor profile for a container
ObjectMeta: api.ObjectMeta{
@ -3369,7 +3385,7 @@ func TestValidatePod(t *testing.T) {
apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileRuntimeDefault,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // default AppArmor profile for an init container
ObjectMeta: api.ObjectMeta{
@ -3394,7 +3410,7 @@ func TestValidatePod(t *testing.T) {
apparmor.ContainerAnnotationKeyPrefix + "ctr": apparmor.ProfileNamePrefix + "foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // syntactically valid sysctls
ObjectMeta: api.ObjectMeta{
@ -3405,7 +3421,7 @@ func TestValidatePod(t *testing.T) {
api.UnsafeSysctlsPodAnnotationKey: "knet.ipv4.route.min_pmtu=1000",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
{ // valid opaque integer resources for init container
ObjectMeta: api.ObjectMeta{Name: "valid-opaque-int", Namespace: "ns"},
@ -3502,271 +3518,234 @@ func TestValidatePod(t *testing.T) {
Name: "123",
Namespace: "ns",
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Spec: validPodSpec(&api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "key1",
},
Key: "key1",
},
},
},
},
},
},
},
}),
},
"invalid preferredSchedulingTerm in node affinity, weight should be in range 1-100": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
{
Weight: 199,
Preference: api.NodeSelectorTerm{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "foo",
Operator: api.NodeSelectorOpIn,
Values: []string{"bar"},
},
Spec: validPodSpec(&api.Affinity{
NodeAffinity: &api.NodeAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
{
Weight: 199,
Preference: api.NodeSelectorTerm{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "foo",
Operator: api.NodeSelectorOpIn,
Values: []string{"bar"},
},
},
},
},
},
},
},
}),
},
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{},
},
Spec: validPodSpec(&api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{},
},
},
},
}),
},
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
},
Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{},
},
Spec: validPodSpec(&api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{},
},
},
},
},
},
}),
},
"invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 109,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "NotIn",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": "region"
}
}]}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAffinity: &api.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 109,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
TopologyKey: "region",
},
},
},
},
}),
},
"invalid labelSelector in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, values should be empty if the operator is Exists": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "Exists",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": "region"
}
}]}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 10,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpExists,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
TopologyKey: "region",
},
},
},
},
}),
},
"invalid name space in preferredDuringSchedulingIgnoredDuringExecution in podaffinity annotations, name space shouldbe valid": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "Exists",
"values": ["value1", "value2"]
}]
},
"namespaces": ["INVALID_NAMESPACE"],
"topologyKey": "region"
}
}]}}`,
},
},
Spec: validPodSpec,
},
"invalid labelOperator in preferredDuringSchedulingIgnoredDuringExecution in podantiaffinity annotations, labelOperator should be proper": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
Spec: validPodSpec(&api.Affinity{
PodAffinity: &api.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "WrongOp",
"values": ["value1", "value2"]
}]
Weight: 10,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpExists,
},
},
},
Namespaces: []string{"INVALID_NAMESPACE"},
TopologyKey: "region",
},
"namespaces": ["ns"],
"topologyKey": "region"
}
}]}}`,
},
},
},
},
Spec: validPodSpec,
}),
},
"invalid pod affinity, empty topologyKey is not allowed for hard pod affinity": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {"requiredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "In",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": ""
}
}]}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
},
},
},
}),
},
"invalid pod anti-affinity, empty topologyKey is not allowed for hard pod anti-affinity": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {"requiredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "In",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": ""
}
}]}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
},
},
},
}),
},
"invalid pod anti-affinity, empty topologyKey is not allowed for soft pod affinity": {
ObjectMeta: api.ObjectMeta{
Name: "123",
Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"podAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 10,
"podAffinityTerm":
{
"labelSelector": {
"matchExpressions": [{
"key": "key2",
"operator": "In",
"values": ["value1", "value2"]
}]
},
"namespaces": ["ns"],
"topologyKey": ""
}
}]}}`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(&api.Affinity{
PodAffinity: &api.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 10,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "key2",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value1", "value2"},
},
},
},
Namespaces: []string{"ns"},
},
},
},
},
}),
},
"invalid toleration key": {
ObjectMeta: api.ObjectMeta{
@ -3782,7 +3761,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"invalid toleration operator": {
ObjectMeta: api.ObjectMeta{
@ -3798,7 +3777,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"value must be empty when `operator` is 'Exists'": {
ObjectMeta: api.ObjectMeta{
@ -3814,7 +3793,7 @@ func TestValidatePod(t *testing.T) {
}]`,
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must be a valid pod seccomp profile": {
ObjectMeta: api.ObjectMeta{
@ -3824,7 +3803,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must be a valid container seccomp profile": {
ObjectMeta: api.ObjectMeta{
@ -3834,7 +3813,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompContainerAnnotationKeyPrefix + "foo": "foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must be a non-empty container name in seccomp annotation": {
ObjectMeta: api.ObjectMeta{
@ -3844,7 +3823,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompContainerAnnotationKeyPrefix: "foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must be a non-empty container profile in seccomp annotation": {
ObjectMeta: api.ObjectMeta{
@ -3854,7 +3833,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompContainerAnnotationKeyPrefix + "foo": "",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must be a relative path in a node-local seccomp profile annotation": {
ObjectMeta: api.ObjectMeta{
@ -3864,7 +3843,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "localhost//foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"must not start with '../'": {
ObjectMeta: api.ObjectMeta{
@ -3874,7 +3853,7 @@ func TestValidatePod(t *testing.T) {
api.SeccompPodAnnotationKey: "localhost/../foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"AppArmor profile must apply to a container": {
ObjectMeta: api.ObjectMeta{
@ -3901,7 +3880,7 @@ func TestValidatePod(t *testing.T) {
apparmor.ContainerAnnotationKeyPrefix + "ctr": "bad-name",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"only default AppArmor profile may start with runtime/": {
ObjectMeta: api.ObjectMeta{
@ -3911,7 +3890,7 @@ func TestValidatePod(t *testing.T) {
apparmor.ContainerAnnotationKeyPrefix + "ctr": "runtime/foo",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"invalid sysctl annotation": {
ObjectMeta: api.ObjectMeta{
@ -3921,7 +3900,7 @@ func TestValidatePod(t *testing.T) {
api.SysctlsPodAnnotationKey: "foo:",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"invalid comma-separated sysctl annotation": {
ObjectMeta: api.ObjectMeta{
@ -3931,7 +3910,7 @@ func TestValidatePod(t *testing.T) {
api.SysctlsPodAnnotationKey: "kernel.msgmax,",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"invalid unsafe sysctl annotation": {
ObjectMeta: api.ObjectMeta{
@ -3941,7 +3920,7 @@ func TestValidatePod(t *testing.T) {
api.SysctlsPodAnnotationKey: "foo:",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"intersecting safe sysctls and unsafe sysctls annotations": {
ObjectMeta: api.ObjectMeta{
@ -3952,7 +3931,7 @@ func TestValidatePod(t *testing.T) {
api.UnsafeSysctlsPodAnnotationKey: "kernel.shmmax=10000000",
},
},
Spec: validPodSpec,
Spec: validPodSpec(nil),
},
"invalid opaque integer resource requirement: request must be <= limit": {
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},

View File

@ -40,7 +40,6 @@ func TestSetDefaultDaemonSet(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
},
ObjectMeta: v1.ObjectMeta{
Labels: defaultLabels,
@ -52,7 +51,6 @@ func TestSetDefaultDaemonSet(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
},
}
tests := []struct {
@ -157,7 +155,6 @@ func TestSetDefaultDeployment(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
},
}
tests := []struct {

View File

@ -132,7 +132,6 @@ func TestMerge(t *testing.T) {
DNSPolicy: api.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
},
},

View File

@ -54,7 +54,6 @@ func TestDecodeSinglePod(t *testing.T) {
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
}
json, err := runtime.Encode(testapi.Default.Codec(), pod)
@ -115,7 +114,6 @@ func TestDecodePodList(t *testing.T) {
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
}
podList := &v1.PodList{

View File

@ -188,7 +188,6 @@ func getTestCases(hostname types.NodeName) []*testCase {
Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,
@ -214,7 +213,6 @@ func getTestCases(hostname types.NodeName) []*testCase {
ImagePullPolicy: "Always",
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,

View File

@ -148,7 +148,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: string(nodeName),
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,
@ -170,7 +169,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst,
SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &grace,
Affinity: &v1.Affinity{},
Containers: []v1.Container{{
Name: "1",
@ -201,7 +199,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: nodeName,
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,
@ -216,7 +213,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: nodeName,
Containers: []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: ""}},
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
},
Status: v1.PodStatus{
Phase: v1.PodPending,
@ -240,7 +236,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace,
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
Containers: []v1.Container{{
Name: "1",
@ -267,7 +262,6 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace,
SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
Containers: []v1.Container{{
Name: "2",

View File

@ -484,7 +484,8 @@ func TestCriticalPrioritySorting(t *testing.T) {
Requests: v1.ResourceList{
"memory": resource.MustParse("90"),
},
}}}}
}}},
}
pods := []*v1.Pod{
podWithUidNameNsSpec("000000000", "newpod", "foo", spec),
podWithUidNameNsSpec("987654321", "oldpod", "foo", spec),
@ -637,7 +638,8 @@ func TestHandleMemExceeded(t *testing.T) {
Requests: v1.ResourceList{
"memory": resource.MustParse("90"),
},
}}}}
}}},
}
pods := []*v1.Pod{
podWithUidNameNsSpec("123456789", "newpod", "foo", spec),
podWithUidNameNsSpec("987654321", "oldpod", "foo", spec),

View File

@ -73,7 +73,6 @@ func validNewPod() *api.Pod {
},
},
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
}
}
@ -660,7 +659,6 @@ func TestEtcdUpdateScheduled(t *testing.T) {
},
},
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
}, nil, 1)
if err != nil {
@ -689,7 +687,6 @@ func TestEtcdUpdateScheduled(t *testing.T) {
TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
}
_, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn, api.Scheme))
@ -730,7 +727,6 @@ func TestEtcdUpdateStatus(t *testing.T) {
},
},
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
}
err := storage.Storage.Create(ctx, key, &podStart, nil, 0)
@ -755,7 +751,6 @@ func TestEtcdUpdateStatus(t *testing.T) {
},
},
SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
},
Status: api.PodStatus{
Phase: api.PodRunning,

View File

@ -19,7 +19,6 @@ go_library(
"//pkg/admission:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
],
)

View File

@ -20,7 +20,6 @@ import (
"fmt"
"io"
"github.com/golang/glog"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
@ -56,11 +55,7 @@ func (p *plugin) Admit(attributes admission.Attributes) (err error) {
if !ok {
return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
}
affinity, err := api.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
glog.V(5).Infof("Invalid Affinity detected, but we will leave handling of this to validation phase")
return nil
}
affinity := pod.Spec.Affinity
if affinity != nil && affinity.PodAntiAffinity != nil {
var podAntiAffinityTerms []api.PodAffinityTerm
if len(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {

View File

@ -32,109 +32,120 @@ func TestInterPodAffinityAdmission(t *testing.T) {
Spec: api.PodSpec{},
}
tests := []struct {
affinity map[string]string
affinity *api.Affinity
errorExpected bool
}{
// empty affinity its success.
{
affinity: map[string]string{},
affinity: &api.Affinity{},
errorExpected: false,
},
// what ever topologyKey in preferredDuringSchedulingIgnoredDuringExecution, the admission should success.
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
affinity: &api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
"namespaces": [],
"topologyKey": "az"
}
}]
}}`,
},
TopologyKey: "az",
},
},
},
},
},
errorExpected: false,
},
// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution,
// plus any topologyKey in preferredDuringSchedulingIgnoredDuringExecution, then admission success.
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
affinity: &api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: api.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
"namespaces": [],
"topologyKey": "az"
}
}],
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
TopologyKey: "az",
},
"namespaces": [],
"topologyKey": "` + metav1.LabelHostname + `"
}]
}}`,
},
},
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: metav1.LabelHostname,
},
},
},
},
errorExpected: false,
},
// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission success.
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
affinity: &api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
"namespaces":[],
"topologyKey": "` + metav1.LabelHostname + `"
}]
}}`,
TopologyKey: metav1.LabelHostname,
},
},
},
},
errorExpected: false,
},
// invalid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission fails.
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
affinity: &api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
"namespaces":[],
"topologyKey": " zone "
}]
}}`,
TopologyKey: " zone ",
},
},
},
},
errorExpected: true,
},
@ -161,59 +172,51 @@ func TestInterPodAffinityAdmission(t *testing.T) {
// }
// list of requiredDuringSchedulingIgnoredDuringExecution middle element topologyKey is not valid.
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
},
"namespaces":[],
"topologyKey": "` + metav1.LabelHostname + `"
},
affinity: &api.Affinity{
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
"namespaces":[],
"topologyKey": " zone "
TopologyKey: metav1.LabelHostname,
}, {
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: " zone ",
}, {
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: metav1.LabelHostname,
},
{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
},
"namespaces": [],
"topologyKey": "` + metav1.LabelHostname + `"
}]
}}`,
},
},
},
errorExpected: true,
},
{
affinity: map[string]string{
api.AffinityAnnotationKey: `
{"podAntiAffinity": {
"thisIsAInvalidAffinity": [{}
}}`,
},
// however, we should not get error here
errorExpected: false,
},
}
for _, test := range tests {
pod.ObjectMeta.Annotations = test.affinity
pod.Spec.Affinity = test.affinity
err := handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil))
if test.errorExpected && err == nil {

View File

@ -46,6 +46,7 @@ go_test(
"//plugin/pkg/scheduler/algorithm:go_default_library",
"//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library",
"//plugin/pkg/scheduler/schedulercache:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",
],
)

View File

@ -904,10 +904,7 @@ func (c *PodAffinityChecker) InterPodAffinityMatches(pod *v1.Pod, meta interface
}
// Now check if <pod> requirements will be satisfied on this node.
affinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
return false, nil, err
}
affinity := pod.Spec.Affinity
if affinity == nil || (affinity.PodAffinity == nil && affinity.PodAntiAffinity == nil) {
return true, nil, nil
}
@ -1008,11 +1005,7 @@ func getMatchingAntiAffinityTerms(pod *v1.Pod, nodeInfoMap map[string]*scheduler
}
var nodeResult []matchingPodAntiAffinityTerm
for _, existingPod := range nodeInfo.PodsWithAffinity() {
affinity, err := v1.GetAffinityFromPodAnnotations(existingPod.Annotations)
if err != nil {
catchError(err)
return
}
affinity := existingPod.Spec.Affinity
if affinity == nil {
continue
}
@ -1040,10 +1033,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, err := v1.GetAffinityFromPodAnnotations(existingPod.Annotations)
if err != nil {
return nil, err
}
affinity := pod.Spec.Affinity
if affinity != nil && affinity.PodAntiAffinity != nil {
existingPodNode, err := c.info.GetNodeInfo(existingPod.Spec.NodeName)
if err != nil {

File diff suppressed because it is too large Load Diff

View File

@ -118,10 +118,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, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
return nil, err
}
affinity := pod.Spec.Affinity
hasAffinityConstraints := affinity != nil && affinity.PodAffinity != nil
hasAntiAffinityConstraints := affinity != nil && affinity.PodAntiAffinity != nil
@ -142,10 +139,7 @@ func (ipa *InterPodAffinity) CalculateInterPodAffinityPriority(pod *v1.Pod, node
if err != nil {
return err
}
existingPodAffinity, err := v1.GetAffinityFromPodAnnotations(existingPod.Annotations)
if err != nil {
return err
}
existingPodAffinity := existingPod.Spec.Affinity
existingHasAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAffinity != nil
existingHasAntiAffinityConstraints := existingPodAffinity != nil && existingPodAffinity.PodAntiAffinity != nil

View File

@ -65,188 +65,203 @@ func TestInterPodAffinityPriority(t *testing.T) {
"security": "S2",
}
// considered only preferredDuringSchedulingIgnoredDuringExecution in pod affinity
stayWithS1InRegion := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S1"]
}]
},
"namespaces": [],
"topologyKey": "region"
}
}]
}}`,
}
stayWithS2InRegion := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 6,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
},
"namespaces": [],
"topologyKey": "region"
}
}]
}}`,
}
affinity3 := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [
stayWithS1InRegion := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
"weight": 8,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "NotIn",
"values":["S1"]
}, {
"key": "security",
"operator": "In",
"values":["S2"]
}]
Weight: 5,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
"namespaces": [],
"topologyKey": "region"
}
}, {
"weight": 2,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "Exists"
}, {
"key": "wrongkey",
"operator": "DoesNotExist"
}]
},
"namespaces": [],
"topologyKey": "region"
}
}
]
}}`,
TopologyKey: "region",
},
},
},
},
}
hardAffinity := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [
stayWithS2InRegion := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
"labelSelector":{
"matchExpressions": [{
"key": "security",
"operator": "In",
"values": ["S1", "value2"]
}]
Weight: 6,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: "region",
},
"namespaces": [],
"topologyKey": "region"
}, {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "Exists"
}, {
"key": "wrongkey",
"operator": "DoesNotExist"
}]
},
"namespaces": [],
"topologyKey": "region"
}
]
}}`,
},
},
},
}
awayFromS1InAz := map[string]string{
v1.AffinityAnnotationKey: `
{"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S1"]
}]
affinity3 := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 8,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"S1"},
}, {
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: "region",
},
"namespaces": [],
"topologyKey": "az"
}
}]
}}`,
}, {
Weight: 2,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpExists,
}, {
Key: "wrongkey",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
TopologyKey: "region",
},
},
},
},
}
hardAffinity := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1", "value2"},
},
},
},
TopologyKey: "region",
}, {
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpExists,
}, {
Key: "wrongkey",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
TopologyKey: "region",
},
},
},
}
awayFromS1InAz := &v1.Affinity{
PodAntiAffinity: &v1.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
TopologyKey: "az",
},
},
},
},
}
// to stay away from security S2 in any az.
awayFromS2InAz := map[string]string{
v1.AffinityAnnotationKey: `
{"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
awayFromS2InAz := &v1.Affinity{
PodAntiAffinity: &v1.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: "az",
},
"namespaces": [],
"topologyKey": "az"
}
}]
}}`,
},
},
},
}
// to stay with security S1 in same region, stay away from security S2 in any az.
stayWithS1InRegionAwayFromS2InAz := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 8,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S1"]
}]
stayWithS1InRegionAwayFromS2InAz := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 8,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
TopologyKey: "region",
},
"namespaces": [],
"topologyKey": "region"
}
}]
},
},
},
"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
PodAntiAffinity: &v1.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: "az",
},
"namespaces": [],
"topologyKey": "az"
}
}]
}}`,
},
},
},
}
tests := []struct {
@ -257,7 +272,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
test string
}{
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: map[string]string{}}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
{ObjectMeta: v1.ObjectMeta{Name: "machine2", Labels: labelRgIndia}},
@ -270,7 +285,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
// the node(machine3) that don't have the label {"region": "whatever the value is"} (mismatch the topology key) but that have existing pods that match the labelSelector get low score
// the node(machine2) that have the label {"region": "China"} (match the topology key) but that have existing pods that mismatch the labelSelector get low score
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS1InRegion}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
@ -290,7 +305,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
// the node3(machine3) that have the label {"region": "India"}, match the topology key but have a different label value, don't have existing pods that match the labelSelector,
// get a low score.
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Annotations: stayWithS1InRegion}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
},
@ -307,7 +322,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
// Then, nodes in regionChina get higher score than nodes in regionIndia, and all the nodes in regionChina should get a same score(high score),
// while all the nodes in regionIndia should get another same score(low score).
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS2InRegion}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS2InRegion}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
@ -328,7 +343,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
},
// Test with the different operators and values for pod affinity scheduling preference, including some match failures.
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: affinity3}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: affinity3}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
@ -347,8 +362,8 @@ func TestInterPodAffinityPriority(t *testing.T) {
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS1InRegion}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2, Annotations: stayWithS2InRegion}},
{Spec: v1.PodSpec{NodeName: "machine1", Affinity: stayWithS1InRegion}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2", Affinity: stayWithS2InRegion}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
@ -361,8 +376,8 @@ func TestInterPodAffinityPriority(t *testing.T) {
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: hardAffinity}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2, Annotations: hardAffinity}},
{Spec: v1.PodSpec{NodeName: "machine1", Affinity: hardAffinity}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2", Affinity: hardAffinity}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
@ -380,7 +395,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
// there are 2 nodes, say node1 and node2, both nodes have pods that match the labelSelector and have topology-key in node.Labels.
// But there are more pods on node1 that match the preference than node2. Then, node1 get a lower score than node2.
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: awayFromS1InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
@ -393,7 +408,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
test: "Anti Affinity: pod that doesnot match existing pods in node will get high score ",
},
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: awayFromS1InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
@ -406,7 +421,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
test: "Anti Affinity: pod that does not matches topology key & matches the pods in nodes will get higher score comparing to others ",
},
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: awayFromS1InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
@ -423,8 +438,8 @@ func TestInterPodAffinityPriority(t *testing.T) {
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: awayFromS2InAz}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2, Annotations: awayFromS1InAz}},
{Spec: v1.PodSpec{NodeName: "machine1", Affinity: awayFromS2InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2", Affinity: awayFromS1InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelAzAz1}},
@ -435,7 +450,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
},
// Test both affinity and anti-affinity
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS1InRegionAwayFromS2InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
@ -452,7 +467,7 @@ func TestInterPodAffinityPriority(t *testing.T) {
// so that all the pods of a RC/service can stay in a same region but trying to separate with each other
// machine-1,machine-3,machine-4 are in ChinaRegion others machin-2,machine-5 are in IndiaRegion
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS1InRegionAwayFromS2InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
@ -478,12 +493,12 @@ func TestInterPodAffinityPriority(t *testing.T) {
// for Affinity symmetry, the weights are: 0, 0, 8, 0
// for Anti Affinity symmetry, the weights are: 0, 0, 0, -5
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1, Annotations: stayWithS1InRegionAwayFromS2InAz}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabelSecurityS2}},
{Spec: v1.PodSpec{NodeName: "machine3"}, ObjectMeta: v1.ObjectMeta{Annotations: stayWithS1InRegionAwayFromS2InAz}},
{Spec: v1.PodSpec{NodeName: "machine4"}, ObjectMeta: v1.ObjectMeta{Annotations: awayFromS1InAz}},
{Spec: v1.PodSpec{NodeName: "machine3", Affinity: stayWithS1InRegionAwayFromS2InAz}},
{Spec: v1.PodSpec{NodeName: "machine4", Affinity: awayFromS1InAz}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
@ -527,23 +542,23 @@ func TestHardPodAffinitySymmetricWeight(t *testing.T) {
labelAzAz1 := map[string]string{
"az": "az1",
}
hardPodAffinity := map[string]string{
v1.AffinityAnnotationKey: `
{"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [
hardPodAffinity := &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
"labelSelector":{
"matchExpressions": [{
"key": "service",
"operator": "In",
"values": ["S1"]
}]
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "service",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
"namespaces": [],
"topologyKey": "region"
}
]
}}`,
TopologyKey: "region",
},
},
},
}
tests := []struct {
pod *v1.Pod
@ -556,8 +571,8 @@ func TestHardPodAffinitySymmetricWeight(t *testing.T) {
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelServiceS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Annotations: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Annotations: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine1", Affinity: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine2", Affinity: hardPodAffinity}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
@ -571,8 +586,8 @@ func TestHardPodAffinitySymmetricWeight(t *testing.T) {
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabelServiceS1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Annotations: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Annotations: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine1", Affinity: hardPodAffinity}},
{Spec: v1.PodSpec{NodeName: "machine2", Affinity: hardPodAffinity}},
},
nodes: []*v1.Node{
{ObjectMeta: v1.ObjectMeta{Name: "machine1", Labels: labelRgChina}},
@ -612,24 +627,26 @@ func TestSoftPodAntiAffinityWithFailureDomains(t *testing.T) {
podLabel1 := map[string]string{
"security": "S1",
}
antiAffinity1 := map[string]string{
v1.AffinityAnnotationKey: `
{"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 5,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S1"]
}]
antiAffinity1 := &v1.Affinity{
PodAntiAffinity: &v1.PodAntiAffinity{
PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
{
Weight: 5,
PodAffinityTerm: v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
TopologyKey: "",
},
"namespaces": [],
"topologyKey": ""
}
}]
}}`,
},
},
},
}
tests := []struct {
pod *v1.Pod
@ -640,7 +657,7 @@ func TestSoftPodAntiAffinityWithFailureDomains(t *testing.T) {
test string
}{
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1, Annotations: antiAffinity1}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: antiAffinity1}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},
@ -654,7 +671,7 @@ func TestSoftPodAntiAffinityWithFailureDomains(t *testing.T) {
test: "Soft Pod Anti Affinity: when the topologyKey is emtpy, match among topologyKeys indicated by failure domains.",
},
{
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1, Annotations: antiAffinity1}},
pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: antiAffinity1}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},
pods: []*v1.Pod{
{Spec: v1.PodSpec{NodeName: "machine1"}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},
{Spec: v1.PodSpec{NodeName: "machine2"}, ObjectMeta: v1.ObjectMeta{Labels: podLabel1}},

View File

@ -38,18 +38,9 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod
if err != nil {
return nil
}
affinity := pod.Spec.Affinity
annotationAffinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
return nil
}
if annotationAffinity != nil {
affinity.PodAffinity = annotationAffinity.PodAffinity
affinity.PodAntiAffinity = annotationAffinity.PodAntiAffinity
}
return &priorityMetadata{
nonZeroRequest: getNonZeroRequests(pod),
podTolerations: tolerations,
affinity: affinity,
affinity: pod.Spec.Affinity,
}
}

View File

@ -209,11 +209,8 @@ func (n *NodeInfo) String() string {
}
func hasPodAffinityConstraints(pod *v1.Pod) bool {
affinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil || affinity == nil {
return false
}
return affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil
affinity := pod.Spec.Affinity
return affinity != nil && (affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil)
}
// addPod adds pod information to this NodeInfo.

View File

@ -175,9 +175,6 @@ func SetDefaults_PodSpec(obj *PodSpec) {
period := int64(DefaultTerminationGracePeriodSeconds)
obj.TerminationGracePeriodSeconds = &period
}
if obj.Affinity == nil {
obj.Affinity = &Affinity{}
}
}
func SetDefaults_Probe(obj *Probe) {
if obj.TimeoutSeconds == 0 {

View File

@ -44,8 +44,7 @@ var masterNodes sets.String
type pausePodConfig struct {
Name string
Affinity string
NodeAffinity *v1.Affinity
Affinity *v1.Affinity
Annotations, Labels, NodeSelector map[string]string
Resources *v1.ResourceRequirements
}
@ -241,7 +240,7 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
podName := "without-label"
_, err := cs.Core().Pods(ns).Create(initPausePod(f, pausePodConfig{
Name: podName,
NodeAffinity: &v1.Affinity{
Affinity: &v1.Affinity{
NodeAffinity: &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{
@ -303,7 +302,7 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
createPausePod(f, pausePodConfig{
Name: podName,
NodeAffinity: &v1.Affinity{
Affinity: &v1.Affinity{
NodeAffinity: &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{
@ -350,7 +349,7 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
labelPodName := "with-labels"
pod := createPausePod(f, pausePodConfig{
Name: labelPodName,
NodeAffinity: &v1.Affinity{
Affinity: &v1.Affinity{
NodeAffinity: &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{
@ -392,24 +391,24 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
_, err := cs.Core().Pods(ns).Create(initPausePod(f, pausePodConfig{
Name: podName,
Labels: map[string]string{"name": "without-label"},
Affinity: `{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"weight": 0,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "service",
"operator": "DoesNotExist",
"values":["securityscan"]
}]
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpDoesNotExist,
Values: []string{"securityscan"},
},
},
},
"namespaces": [],
"topologyKey": "kubernetes.io/hostname"
}
}]
}
}`,
TopologyKey: "kubernetes.io/hostname",
},
},
},
},
}))
if err == nil || !errors.IsInvalid(err) {
@ -427,20 +426,24 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
podName := "without-label-" + string(uuid.NewUUID())
createPausePod(f, pausePodConfig{
Name: podName,
Affinity: `{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector":{
"matchExpressions": [{
"key": "service",
"operator": "In",
"values": ["securityscan", "value2"]
}]
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "service",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"securityscan", "value2"},
},
},
},
TopologyKey: "kubernetes.io/hostname",
},
"topologyKey": "kubernetes.io/hostname"
}]
}
}`,
},
},
},
})
waitForScheduler()
@ -462,21 +465,25 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
labelPodName := "with-podaffinity-" + string(uuid.NewUUID())
pod := createPausePod(f, pausePodConfig{
Name: labelPodName,
Affinity: `{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values": ["S1", "value2"]
}]
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1", "value2"},
},
},
},
TopologyKey: k,
Namespaces: []string{ns},
},
"topologyKey": "` + k + `",
"namespaces":["` + ns + `"]
}]
}
}`,
},
},
},
})
// check that pod got scheduled. We intentionally DO NOT check that the
@ -529,21 +536,25 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
Name: labelPodName,
Labels: map[string]string{"service": "Diff"},
NodeSelector: map[string]string{k: v}, // only launch on our two nodes, contradicting the podAntiAffinity
Affinity: `{
"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector":{
"matchExpressions": [{
"key": "service",
"operator": "In",
"values": ["S1", "value2"]
}]
Affinity: &v1.Affinity{
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1", "value2"},
},
},
},
TopologyKey: k,
Namespaces: []string{ns},
},
"topologyKey": "` + k + `",
"namespaces": ["` + ns + `"]
}]
}
}`,
},
},
},
})
waitForScheduler()
@ -565,29 +576,31 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
labelPodName := "with-podaffinity-" + string(uuid.NewUUID())
pod := createPausePod(f, pausePodConfig{
Name: labelPodName,
Affinity: `{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector":{
"matchExpressions": [{
"key": "security",
"operator": "In",
"values": ["S1", "value2"]
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1", "value2"},
}, {
Key: "security",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"S2"},
}, {
Key: "security",
Operator: metav1.LabelSelectorOpExists,
},
},
},
{
"key": "security",
"operator": "NotIn",
"values": ["S2"]
},
{
"key": "security",
"operator":"Exists"
}]
TopologyKey: k,
},
"topologyKey": "` + k + `"
}]
}
}`,
},
},
},
})
// check that pod got scheduled. We intentionally DO NOT check that the
@ -745,15 +758,6 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
})
func initPausePod(f *framework.Framework, conf pausePodConfig) *v1.Pod {
if conf.Affinity != "" {
if conf.Annotations == nil {
conf.Annotations = map[string]string{
v1.AffinityAnnotationKey: conf.Affinity,
}
} else {
conf.Annotations[v1.AffinityAnnotationKey] = conf.Affinity
}
}
pod := &v1.Pod{
ObjectMeta: v1.ObjectMeta{
Name: conf.Name,
@ -762,7 +766,7 @@ func initPausePod(f *framework.Framework, conf pausePodConfig) *v1.Pod {
},
Spec: v1.PodSpec{
NodeSelector: conf.NodeSelector,
Affinity: conf.NodeAffinity,
Affinity: conf.Affinity,
Containers: []v1.Container{
{
Name: podName,
@ -808,7 +812,7 @@ func runPodAndGetNodeName(f *framework.Framework, conf pausePodConfig) string {
func createPodWithNodeAffinity(f *framework.Framework) *v1.Pod {
return createPausePod(f, pausePodConfig{
Name: "with-nodeaffinity-" + string(uuid.NewUUID()),
NodeAffinity: &v1.Affinity{
Affinity: &v1.Affinity{
NodeAffinity: &v1.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
NodeSelectorTerms: []v1.NodeSelectorTerm{
@ -831,32 +835,40 @@ func createPodWithNodeAffinity(f *framework.Framework) *v1.Pod {
func createPodWithPodAffinity(f *framework.Framework, topologyKey string) *v1.Pod {
return createPausePod(f, pausePodConfig{
Name: "with-podantiaffinity-" + string(uuid.NewUUID()),
Affinity: `{
"podAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S1"]
}]
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S1"},
},
},
},
TopologyKey: topologyKey,
},
},
"topologyKey": "` + topologyKey + `"
}]
},
"podAntiAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": [{
"labelSelector": {
"matchExpressions": [{
"key": "security",
"operator": "In",
"values":["S2"]
}]
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "security",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"S2"},
},
},
},
TopologyKey: topologyKey,
},
},
"topologyKey": "` + topologyKey + `"
}]
}
}`,
},
},
})
}