Commit 9bc98e2a authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #37299 from rrati/node-affinity-api-fields

Automatic merge from submit-queue (batch tested with PRs 38730, 37299) [scheduling] Moved node affinity from annotations to api fields. #35518 Converted node affinity from annotations to api fields Fixes: #35518 Related: #25319 Related: #34508 **Release note**: ```release-note Node affinity has moved from annotations to api fields in the pod spec. Node affinity that is defined in the annotations will be ignored. ```
parents 46e5f216 11c577f0
...@@ -332,6 +332,7 @@ func TestEncode_Ptr(t *testing.T) { ...@@ -332,6 +332,7 @@ func TestEncode_Ptr(t *testing.T) {
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
} }
obj := runtime.Object(pod) obj := runtime.Object(pod)
......
...@@ -130,6 +130,9 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz ...@@ -130,6 +130,9 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
if s.SecurityContext == nil { if s.SecurityContext == nil {
s.SecurityContext = new(api.PodSecurityContext) s.SecurityContext = new(api.PodSecurityContext)
} }
if s.Affinity == nil {
s.Affinity = new(api.Affinity)
}
}, },
func(j *api.PodPhase, c fuzz.Continue) { func(j *api.PodPhase, c fuzz.Continue) {
statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown} statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
......
...@@ -29,6 +29,7 @@ func DeepEqualSafePodSpec() api.PodSpec { ...@@ -29,6 +29,7 @@ func DeepEqualSafePodSpec() api.PodSpec {
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
} }
} }
......
...@@ -1824,6 +1824,9 @@ type PodSpec struct { ...@@ -1824,6 +1824,9 @@ type PodSpec struct {
// If not specified, the pod will not have a domainname at all. // If not specified, the pod will not have a domainname at all.
// +optional // +optional
Subdomain string Subdomain string
// If specified, the pod's scheduling constraints
// +optional
Affinity *Affinity
} }
// Sysctl defines a kernel parameter to be set // Sysctl defines a kernel parameter to be set
......
...@@ -175,6 +175,9 @@ func SetDefaults_PodSpec(obj *PodSpec) { ...@@ -175,6 +175,9 @@ func SetDefaults_PodSpec(obj *PodSpec) {
period := int64(DefaultTerminationGracePeriodSeconds) period := int64(DefaultTerminationGracePeriodSeconds)
obj.TerminationGracePeriodSeconds = &period obj.TerminationGracePeriodSeconds = &period
} }
if obj.Affinity == nil {
obj.Affinity = &Affinity{}
}
} }
func SetDefaults_Probe(obj *Probe) { func SetDefaults_Probe(obj *Probe) {
if obj.TimeoutSeconds == 0 { if obj.TimeoutSeconds == 0 {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2517,6 +2517,10 @@ message PodSpec { ...@@ -2517,6 +2517,10 @@ message PodSpec {
// If not specified, the pod will not have a domainname at all. // If not specified, the pod will not have a domainname at all.
// +optional // +optional
optional string subdomain = 17; optional string subdomain = 17;
// If specified, the pod's scheduling constraints
// +optional
optional Affinity affinity = 18;
} }
// PodStatus represents information about the status of a pod. Status may trail the actual // PodStatus represents information about the status of a pod. Status may trail the actual
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -2107,6 +2107,9 @@ type PodSpec struct { ...@@ -2107,6 +2107,9 @@ type PodSpec struct {
// If not specified, the pod will not have a domainname at all. // If not specified, the pod will not have a domainname at all.
// +optional // +optional
Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"` Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"`
// If specified, the pod's scheduling constraints
// +optional
Affinity *Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"`
} }
// PodSecurityContext holds pod-level security attributes and common container settings. // PodSecurityContext holds pod-level security attributes and common container settings.
......
...@@ -1274,6 +1274,7 @@ var map_PodSpec = map[string]string{ ...@@ -1274,6 +1274,7 @@ var map_PodSpec = map[string]string{
"imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod", "imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://kubernetes.io/docs/user-guide/images#specifying-imagepullsecrets-on-a-pod",
"hostname": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.", "hostname": "Specifies the hostname of the Pod If not specified, the pod's hostname will be set to a system-defined value.",
"subdomain": "If specified, the fully qualified Pod hostname will be \"<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>\". If not specified, the pod will not have a domainname at all.", "subdomain": "If specified, the fully qualified Pod hostname will be \"<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>\". If not specified, the pod will not have a domainname at all.",
"affinity": "If specified, the pod's scheduling constraints",
} }
func (PodSpec) SwaggerDoc() map[string]string { func (PodSpec) SwaggerDoc() map[string]string {
......
...@@ -3064,6 +3064,7 @@ func autoConvert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conv ...@@ -3064,6 +3064,7 @@ func autoConvert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conv
out.ImagePullSecrets = *(*[]api.LocalObjectReference)(unsafe.Pointer(&in.ImagePullSecrets)) out.ImagePullSecrets = *(*[]api.LocalObjectReference)(unsafe.Pointer(&in.ImagePullSecrets))
out.Hostname = in.Hostname out.Hostname = in.Hostname
out.Subdomain = in.Subdomain out.Subdomain = in.Subdomain
out.Affinity = (*api.Affinity)(unsafe.Pointer(in.Affinity))
return nil return nil
} }
...@@ -3100,6 +3101,7 @@ func autoConvert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conv ...@@ -3100,6 +3101,7 @@ func autoConvert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conv
out.ImagePullSecrets = *(*[]LocalObjectReference)(unsafe.Pointer(&in.ImagePullSecrets)) out.ImagePullSecrets = *(*[]LocalObjectReference)(unsafe.Pointer(&in.ImagePullSecrets))
out.Hostname = in.Hostname out.Hostname = in.Hostname
out.Subdomain = in.Subdomain out.Subdomain = in.Subdomain
out.Affinity = (*Affinity)(unsafe.Pointer(in.Affinity))
return nil return nil
} }
......
...@@ -2660,6 +2660,15 @@ func DeepCopy_v1_PodSpec(in interface{}, out interface{}, c *conversion.Cloner) ...@@ -2660,6 +2660,15 @@ func DeepCopy_v1_PodSpec(in interface{}, out interface{}, c *conversion.Cloner)
} }
out.Hostname = in.Hostname out.Hostname = in.Hostname
out.Subdomain = in.Subdomain out.Subdomain = in.Subdomain
if in.Affinity != nil {
in, out := &in.Affinity, &out.Affinity
*out = new(Affinity)
if err := DeepCopy_v1_Affinity(*in, *out, c); err != nil {
return err
}
} else {
out.Affinity = nil
}
return nil return nil
} }
} }
......
...@@ -1811,6 +1811,29 @@ func validateImagePullSecrets(imagePullSecrets []api.LocalObjectReference, fldPa ...@@ -1811,6 +1811,29 @@ func validateImagePullSecrets(imagePullSecrets []api.LocalObjectReference, fldPa
return allErrors return allErrors
} }
// validateAffinity checks if given affinities are valid
func validateAffinity(affinity *api.Affinity, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if affinity != nil {
if na := affinity.NodeAffinity; na != nil {
// TODO: Uncomment the next three lines once RequiredDuringSchedulingRequiredDuringExecution is implemented.
// if na.RequiredDuringSchedulingRequiredDuringExecution != nil {
// allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingRequiredDuringExecution, fldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
// }
if na.RequiredDuringSchedulingIgnoredDuringExecution != nil {
allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingIgnoredDuringExecution, fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
}
if len(na.PreferredDuringSchedulingIgnoredDuringExecution) > 0 {
allErrs = append(allErrs, ValidatePreferredSchedulingTerms(na.PreferredDuringSchedulingIgnoredDuringExecution, fldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
}
}
}
return allErrs
}
func validateTaintEffect(effect *api.TaintEffect, allowEmpty bool, fldPath *field.Path) field.ErrorList { func validateTaintEffect(effect *api.TaintEffect, allowEmpty bool, fldPath *field.Path) field.ErrorList {
if !allowEmpty && len(*effect) == 0 { if !allowEmpty && len(*effect) == 0 {
return field.ErrorList{field.Required(fldPath, "")} return field.ErrorList{field.Required(fldPath, "")}
...@@ -1890,6 +1913,7 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList { ...@@ -1890,6 +1913,7 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...) allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"))...) allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"))...)
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...) allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
allErrs = append(allErrs, validateAffinity(spec.Affinity, fldPath.Child("affinity"))...)
if len(spec.ServiceAccountName) > 0 { if len(spec.ServiceAccountName) > 0 {
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) { for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
...@@ -2116,22 +2140,6 @@ func ValidateAffinityInPodAnnotations(annotations map[string]string, fldPath *fi ...@@ -2116,22 +2140,6 @@ func ValidateAffinityInPodAnnotations(annotations map[string]string, fldPath *fi
} }
affinityFldPath := fldPath.Child(api.AffinityAnnotationKey) affinityFldPath := fldPath.Child(api.AffinityAnnotationKey)
if affinity.NodeAffinity != nil {
na := affinity.NodeAffinity
naFldPath := affinityFldPath.Child("nodeAffinity")
// TODO: Uncomment the next three lines once RequiredDuringSchedulingRequiredDuringExecution is implemented.
// if na.RequiredDuringSchedulingRequiredDuringExecution != nil {
// allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingRequiredDuringExecution, naFldPath.Child("requiredDuringSchedulingRequiredDuringExecution"))...)
// }
if na.RequiredDuringSchedulingIgnoredDuringExecution != nil {
allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingIgnoredDuringExecution, naFldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
}
if len(na.PreferredDuringSchedulingIgnoredDuringExecution) > 0 {
allErrs = append(allErrs, ValidatePreferredSchedulingTerms(na.PreferredDuringSchedulingIgnoredDuringExecution, naFldPath.Child("preferredDuringSchedulingIgnoredDuringExecution"))...)
}
}
if affinity.PodAffinity != nil { if affinity.PodAffinity != nil {
allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, affinityFldPath.Child("podAffinity"))...) allErrs = append(allErrs, validatePodAffinity(affinity.PodAffinity, affinityFldPath.Child("podAffinity"))...)
} }
......
...@@ -3482,50 +3482,60 @@ func TestValidatePod(t *testing.T) { ...@@ -3482,50 +3482,60 @@ func TestValidatePod(t *testing.T) {
NodeName: "foobar", NodeName: "foobar",
}, },
}, },
{ // Serialized affinity requirements in annotations. { // Serialized node affinity requirements.
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "123", Name: "123",
Namespace: "ns", Namespace: "ns",
// TODO: Uncomment and move this block into Annotations map once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// "requiredDuringSchedulingRequiredDuringExecution": {
// "nodeSelectorTerms": [{
// "matchExpressions": [{
// "key": "key1",
// "operator": "Exists"
// }]
// }]
// },
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
"matchExpressions": [{
"key": "key2",
"operator": "In",
"values": ["value1", "value2"]
}]
}]
},
"preferredDuringSchedulingIgnoredDuringExecution": [
{
"weight": 10,
"preference": {"matchExpressions": [
{
"key": "foo",
"operator": "In", "values": ["bar"]
}
]}
}
]
}}`,
},
}, },
Spec: api.PodSpec{ Spec: api.PodSpec{
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
RestartPolicy: api.RestartPolicyAlways, RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
// TODO: Uncomment and move this block and move inside NodeAffinity once
// RequiredDuringSchedulingRequiredDuringExecution is implemented
// RequiredDuringSchedulingRequiredDuringExecution: &api.NodeSelector{
// NodeSelectorTerms: []api.NodeSelectorTerm{
// {
// MatchExpressions: []api.NodeSelectorRequirement{
// {
// Key: "key1",
// Operator: api.NodeSelectorOpExists
// },
// },
// },
// },
// },
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "key2",
Operator: api.NodeSelectorOpIn,
Values: []string{"value1", "value2"},
},
},
},
},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
{
Weight: 10,
Preference: api.NodeSelectorTerm{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "foo",
Operator: api.NodeSelectorOpIn,
Values: []string{"bar"},
},
},
},
},
},
},
},
}, },
}, },
{ // Serialized pod affinity in affinity requirements in annotations. { // Serialized pod affinity in affinity requirements in annotations.
...@@ -3863,90 +3873,101 @@ func TestValidatePod(t *testing.T) { ...@@ -3863,90 +3873,101 @@ func TestValidatePod(t *testing.T) {
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
}, },
}, },
"invalid json of node affinity in pod annotations": { "invalid node selector requirement in node affinity, operator can't be null": {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "123", Name: "123",
Namespace: "ns", Namespace: "ns",
Annotations: map[string]string{
api.AffinityAnnotationKey: `
{"nodeAffinity": {
"requiredDuringSchedulingIgnoredDuringExecution": {
"nodeSelectorTerms": [{
`,
},
}, },
Spec: validPodSpec, Spec: api.PodSpec{
}, Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
"invalid node selector requirement in node affinity in pod annotations, operator can't be null": { RestartPolicy: api.RestartPolicyAlways,
ObjectMeta: api.ObjectMeta{ DNSPolicy: api.DNSClusterFirst,
Name: "123", Affinity: &api.Affinity{
Namespace: "ns", NodeAffinity: &api.NodeAffinity{
Annotations: map[string]string{ RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
api.AffinityAnnotationKey: ` NodeSelectorTerms: []api.NodeSelectorTerm{
{"nodeAffinity": {"requiredDuringSchedulingIgnoredDuringExecution": { {
"nodeSelectorTerms": [{ MatchExpressions: []api.NodeSelectorRequirement{
"matchExpressions": [{ {
"key": "key1",
}] Key: "key1",
}] },
}}}`, },
},
},
},
},
}, },
}, },
Spec: validPodSpec,
}, },
"invalid preferredSchedulingTerm in node affinity in pod annotations, weight should be in range 1-100": { "invalid preferredSchedulingTerm in node affinity, weight should be in range 1-100": {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "123", Name: "123",
Namespace: "ns", Namespace: "ns",
Annotations: map[string]string{ },
api.AffinityAnnotationKey: ` Spec: api.PodSpec{
{"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
{ RestartPolicy: api.RestartPolicyAlways,
"weight": 199, DNSPolicy: api.DNSClusterFirst,
"preference": {"matchExpressions": [ Affinity: &api.Affinity{
{ NodeAffinity: &api.NodeAffinity{
"key": "foo", PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{
"operator": "In", {
"values": ["bar"] Weight: 199,
} Preference: api.NodeSelectorTerm{
]} MatchExpressions: []api.NodeSelectorRequirement{
} {
]}}`, Key: "foo",
Operator: api.NodeSelectorOpIn,
Values: []string{"bar"},
},
},
},
},
},
},
}, },
}, },
Spec: validPodSpec,
}, },
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": { "invalid requiredDuringSchedulingIgnoredDuringExecution node selector, nodeSelectorTerms must have at least one term": {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "123", Name: "123",
Namespace: "ns", Namespace: "ns",
Annotations: map[string]string{ },
api.AffinityAnnotationKey: ` Spec: api.PodSpec{
{"nodeAffinity": { Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
"requiredDuringSchedulingIgnoredDuringExecution": { RestartPolicy: api.RestartPolicyAlways,
"nodeSelectorTerms": [] DNSPolicy: api.DNSClusterFirst,
Affinity: &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{},
}, },
}}`, },
}, },
}, },
Spec: validPodSpec,
}, },
"invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement": { "invalid requiredDuringSchedulingIgnoredDuringExecution node selector term, matchExpressions must have at least one node selector requirement": {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "123", Name: "123",
Namespace: "ns", Namespace: "ns",
Annotations: map[string]string{ },
api.AffinityAnnotationKey: ` Spec: api.PodSpec{
{"nodeAffinity": { Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
"requiredDuringSchedulingIgnoredDuringExecution": { RestartPolicy: api.RestartPolicyAlways,
"nodeSelectorTerms": [{ DNSPolicy: api.DNSClusterFirst,
"matchExpressions": [] Affinity: &api.Affinity{
}] NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{},
},
},
}, },
}}`, },
}, },
}, },
Spec: validPodSpec,
}, },
"invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": { "invalid weight in preferredDuringSchedulingIgnoredDuringExecution in pod affinity annotations, weight should be in range 1-100": {
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
......
...@@ -2699,6 +2699,15 @@ func DeepCopy_api_PodSpec(in interface{}, out interface{}, c *conversion.Cloner) ...@@ -2699,6 +2699,15 @@ func DeepCopy_api_PodSpec(in interface{}, out interface{}, c *conversion.Cloner)
} }
out.Hostname = in.Hostname out.Hostname = in.Hostname
out.Subdomain = in.Subdomain out.Subdomain = in.Subdomain
if in.Affinity != nil {
in, out := &in.Affinity, &out.Affinity
*out = new(Affinity)
if err := DeepCopy_api_Affinity(*in, *out, c); err != nil {
return err
}
} else {
out.Affinity = nil
}
return nil return nil
} }
} }
......
...@@ -1577,7 +1577,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco ...@@ -1577,7 +1577,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco
yyrg131 := len(yyv131) > 0 yyrg131 := len(yyv131) > 0
yyv2131 := yyv131 yyv2131 := yyv131
yyrl131, yyrt131 = z.DecInferLen(yyl131, z.DecBasicHandle().MaxInitLen, 800) yyrl131, yyrt131 = z.DecInferLen(yyl131, z.DecBasicHandle().MaxInitLen, 808)
if yyrt131 { if yyrt131 {
if yyrl131 <= cap(yyv131) { if yyrl131 <= cap(yyv131) {
yyv131 = yyv131[:yyrl131] yyv131 = yyv131[:yyrl131]
......
...@@ -2352,7 +2352,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { ...@@ -2352,7 +2352,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
yyrg206 := len(yyv206) > 0 yyrg206 := len(yyv206) > 0
yyv2206 := yyv206 yyv2206 := yyv206
yyrl206, yyrt206 = z.DecInferLen(yyl206, z.DecBasicHandle().MaxInitLen, 824) yyrl206, yyrt206 = z.DecInferLen(yyl206, z.DecBasicHandle().MaxInitLen, 832)
if yyrt206 { if yyrt206 {
if yyrl206 <= cap(yyv206) { if yyrl206 <= cap(yyv206) {
yyv206 = yyv206[:yyrl206] yyv206 = yyv206[:yyrl206]
......
...@@ -4247,7 +4247,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { ...@@ -4247,7 +4247,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
yyrg370 := len(yyv370) > 0 yyrg370 := len(yyv370) > 0
yyv2370 := yyv370 yyv2370 := yyv370
yyrl370, yyrt370 = z.DecInferLen(yyl370, z.DecBasicHandle().MaxInitLen, 824) yyrl370, yyrt370 = z.DecInferLen(yyl370, z.DecBasicHandle().MaxInitLen, 832)
if yyrt370 { if yyrt370 {
if yyrl370 <= cap(yyv370) { if yyrl370 <= cap(yyv370) {
yyv370 = yyv370[:yyrl370] yyv370 = yyv370[:yyrl370]
...@@ -4479,7 +4479,7 @@ func (x codecSelfer1234) decSliceCronJob(v *[]CronJob, d *codec1978.Decoder) { ...@@ -4479,7 +4479,7 @@ func (x codecSelfer1234) decSliceCronJob(v *[]CronJob, d *codec1978.Decoder) {
yyrg382 := len(yyv382) > 0 yyrg382 := len(yyv382) > 0
yyv2382 := yyv382 yyv2382 := yyv382
yyrl382, yyrt382 = z.DecInferLen(yyl382, z.DecBasicHandle().MaxInitLen, 1072) yyrl382, yyrt382 = z.DecInferLen(yyl382, z.DecBasicHandle().MaxInitLen, 1080)
if yyrt382 { if yyrt382 {
if yyrl382 <= cap(yyv382) { if yyrl382 <= cap(yyv382) {
yyv382 = yyv382[:yyrl382] yyv382 = yyv382[:yyrl382]
......
...@@ -40,6 +40,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { ...@@ -40,6 +40,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways, RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period, TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
}, },
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
Labels: defaultLabels, Labels: defaultLabels,
...@@ -51,6 +52,7 @@ func TestSetDefaultDaemonSet(t *testing.T) { ...@@ -51,6 +52,7 @@ func TestSetDefaultDaemonSet(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways, RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period, TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
}, },
} }
tests := []struct { tests := []struct {
...@@ -155,6 +157,7 @@ func TestSetDefaultDeployment(t *testing.T) { ...@@ -155,6 +157,7 @@ func TestSetDefaultDeployment(t *testing.T) {
RestartPolicy: v1.RestartPolicyAlways, RestartPolicy: v1.RestartPolicyAlways,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &period, TerminationGracePeriodSeconds: &period,
Affinity: &v1.Affinity{},
}, },
} }
tests := []struct { tests := []struct {
......
...@@ -21849,7 +21849,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode ...@@ -21849,7 +21849,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode
yyrg1828 := len(yyv1828) > 0 yyrg1828 := len(yyv1828) > 0
yyv21828 := yyv1828 yyv21828 := yyv1828
yyrl1828, yyrt1828 = z.DecInferLen(yyl1828, z.DecBasicHandle().MaxInitLen, 856) yyrl1828, yyrt1828 = z.DecInferLen(yyl1828, z.DecBasicHandle().MaxInitLen, 864)
if yyrt1828 { if yyrt1828 {
if yyrl1828 <= cap(yyv1828) { if yyrl1828 <= cap(yyv1828) {
yyv1828 = yyv1828[:yyrl1828] yyv1828 = yyv1828[:yyrl1828]
...@@ -21965,7 +21965,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder) ...@@ -21965,7 +21965,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder)
yyrg1834 := len(yyv1834) > 0 yyrg1834 := len(yyv1834) > 0
yyv21834 := yyv1834 yyv21834 := yyv1834
yyrl1834, yyrt1834 = z.DecInferLen(yyl1834, z.DecBasicHandle().MaxInitLen, 752) yyrl1834, yyrt1834 = z.DecInferLen(yyl1834, z.DecBasicHandle().MaxInitLen, 760)
if yyrt1834 { if yyrt1834 {
if yyrl1834 <= cap(yyv1834) { if yyrl1834 <= cap(yyv1834) {
yyv1834 = yyv1834[:yyrl1834] yyv1834 = yyv1834[:yyrl1834]
...@@ -22197,7 +22197,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { ...@@ -22197,7 +22197,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
yyrg1846 := len(yyv1846) > 0 yyrg1846 := len(yyv1846) > 0
yyv21846 := yyv1846 yyv21846 := yyv1846
yyrl1846, yyrt1846 = z.DecInferLen(yyl1846, z.DecBasicHandle().MaxInitLen, 824) yyrl1846, yyrt1846 = z.DecInferLen(yyl1846, z.DecBasicHandle().MaxInitLen, 832)
if yyrt1846 { if yyrt1846 {
if yyrl1846 <= cap(yyv1846) { if yyrl1846 <= cap(yyv1846) {
yyv1846 = yyv1846[:yyrl1846] yyv1846 = yyv1846[:yyrl1846]
...@@ -22893,7 +22893,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode ...@@ -22893,7 +22893,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode
yyrg1882 := len(yyv1882) > 0 yyrg1882 := len(yyv1882) > 0
yyv21882 := yyv1882 yyv21882 := yyv1882
yyrl1882, yyrt1882 = z.DecInferLen(yyl1882, z.DecBasicHandle().MaxInitLen, 800) yyrl1882, yyrt1882 = z.DecInferLen(yyl1882, z.DecBasicHandle().MaxInitLen, 808)
if yyrt1882 { if yyrt1882 {
if yyrl1882 <= cap(yyv1882) { if yyrl1882 <= cap(yyv1882) {
yyv1882 = yyv1882[:yyrl1882] yyv1882 = yyv1882[:yyrl1882]
......
...@@ -553,19 +553,23 @@ func TestNodeAffinityDaemonLaunchesPods(t *testing.T) { ...@@ -553,19 +553,23 @@ func TestNodeAffinityDaemonLaunchesPods(t *testing.T) {
addNodes(manager.nodeStore.Store, 0, 4, nil) addNodes(manager.nodeStore.Store, 0, 4, nil)
addNodes(manager.nodeStore.Store, 4, 3, simpleNodeLabel) addNodes(manager.nodeStore.Store, 4, 3, simpleNodeLabel)
daemon := newDaemonSet("foo") daemon := newDaemonSet("foo")
affinity := map[string]string{ daemon.Spec.Template.Spec.Affinity = &v1.Affinity{
v1.AffinityAnnotationKey: fmt.Sprintf(` NodeAffinity: &v1.NodeAffinity{
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [{ {
"key": "color", MatchExpressions: []v1.NodeSelectorRequirement{
"operator": "In", {
"values": ["%s"] Key: "color",
}] Operator: v1.NodeSelectorOpIn,
}] Values: []string{simpleNodeLabel["color"]},
}}}`, simpleNodeLabel["color"]), },
},
},
},
},
},
} }
daemon.Spec.Template.ObjectMeta.Annotations = affinity
manager.dsStore.Add(daemon) manager.dsStore.Add(daemon)
syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0) syncAndValidateDaemonSets(t, manager, daemon, podControl, 3, 0)
} }
......
...@@ -5373,12 +5373,18 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{ ...@@ -5373,12 +5373,18 @@ var OpenAPIDefinitions *common.OpenAPIDefinitions = &common.OpenAPIDefinitions{
Format: "", Format: "",
}, },
}, },
"affinity": {
SchemaProps: spec.SchemaProps{
Description: "If specified, the pod's scheduling constraints",
Ref: spec.MustCreateRef("#/definitions/v1.Affinity"),
},
},
}, },
Required: []string{"containers"}, Required: []string{"containers"},
}, },
}, },
Dependencies: []string{ Dependencies: []string{
"v1.Container", "v1.LocalObjectReference", "v1.PodSecurityContext", "v1.Volume"}, "v1.Affinity", "v1.Container", "v1.LocalObjectReference", "v1.PodSecurityContext", "v1.Volume"},
}, },
"v1.PodStatus": { "v1.PodStatus": {
Schema: spec.Schema{ Schema: spec.Schema{
......
...@@ -132,6 +132,7 @@ func TestMerge(t *testing.T) { ...@@ -132,6 +132,7 @@ func TestMerge(t *testing.T) {
DNSPolicy: api.DNSClusterFirst, DNSPolicy: api.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
}, },
}, },
......
...@@ -54,6 +54,7 @@ func TestDecodeSinglePod(t *testing.T) { ...@@ -54,6 +54,7 @@ func TestDecodeSinglePod(t *testing.T) {
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
}}, }},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
} }
json, err := runtime.Encode(testapi.Default.Codec(), pod) json, err := runtime.Encode(testapi.Default.Codec(), pod)
...@@ -114,6 +115,7 @@ func TestDecodePodList(t *testing.T) { ...@@ -114,6 +115,7 @@ func TestDecodePodList(t *testing.T) {
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(), SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
}}, }},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
} }
podList := &v1.PodList{ podList := &v1.PodList{
......
...@@ -188,6 +188,7 @@ func getTestCases(hostname types.NodeName) []*testCase { ...@@ -188,6 +188,7 @@ func getTestCases(hostname types.NodeName) []*testCase {
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, Containers: []v1.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: v1.PodPending, Phase: v1.PodPending,
...@@ -213,6 +214,7 @@ func getTestCases(hostname types.NodeName) []*testCase { ...@@ -213,6 +214,7 @@ func getTestCases(hostname types.NodeName) []*testCase {
ImagePullPolicy: "Always", ImagePullPolicy: "Always",
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}}, SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: v1.PodPending, Phase: v1.PodPending,
......
...@@ -148,6 +148,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -148,6 +148,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: string(nodeName), NodeName: string(nodeName),
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}}, Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: v1.PodPending, Phase: v1.PodPending,
...@@ -169,6 +170,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -169,6 +170,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst, DNSPolicy: v1.DNSClusterFirst,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
Affinity: &v1.Affinity{},
Containers: []v1.Container{{ Containers: []v1.Container{{
Name: "1", Name: "1",
...@@ -199,6 +201,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -199,6 +201,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: nodeName, NodeName: nodeName,
Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}}, Containers: []v1.Container{{Name: "1", Image: "foo", ImagePullPolicy: v1.PullAlways}},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: v1.PodPending, Phase: v1.PodPending,
...@@ -213,6 +216,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -213,6 +216,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
NodeName: nodeName, NodeName: nodeName,
Containers: []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: ""}}, Containers: []v1.Container{{Name: "2", Image: "bar:bartag", ImagePullPolicy: ""}},
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
}, },
Status: v1.PodStatus{ Status: v1.PodStatus{
Phase: v1.PodPending, Phase: v1.PodPending,
...@@ -236,6 +240,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -236,6 +240,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst, DNSPolicy: v1.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
Containers: []v1.Container{{ Containers: []v1.Container{{
Name: "1", Name: "1",
...@@ -262,6 +267,7 @@ func TestExtractPodsFromHTTP(t *testing.T) { ...@@ -262,6 +267,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
DNSPolicy: v1.DNSClusterFirst, DNSPolicy: v1.DNSClusterFirst,
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &v1.PodSecurityContext{}, SecurityContext: &v1.PodSecurityContext{},
Affinity: &v1.Affinity{},
Containers: []v1.Container{{ Containers: []v1.Container{{
Name: "2", Name: "2",
......
...@@ -72,6 +72,7 @@ func validNewPod() *api.Pod { ...@@ -72,6 +72,7 @@ func validNewPod() *api.Pod {
}, },
}, },
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
} }
} }
...@@ -658,6 +659,7 @@ func TestEtcdUpdateScheduled(t *testing.T) { ...@@ -658,6 +659,7 @@ func TestEtcdUpdateScheduled(t *testing.T) {
}, },
}, },
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
}, nil, 1) }, nil, 1)
if err != nil { if err != nil {
...@@ -686,6 +688,7 @@ func TestEtcdUpdateScheduled(t *testing.T) { ...@@ -686,6 +688,7 @@ func TestEtcdUpdateScheduled(t *testing.T) {
TerminationGracePeriodSeconds: &grace, TerminationGracePeriodSeconds: &grace,
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
} }
_, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn, api.Scheme)) _, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn, api.Scheme))
...@@ -726,6 +729,7 @@ func TestEtcdUpdateStatus(t *testing.T) { ...@@ -726,6 +729,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
}, },
}, },
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
} }
err := storage.Storage.Create(ctx, key, &podStart, nil, 0) err := storage.Storage.Create(ctx, key, &podStart, nil, 0)
...@@ -750,6 +754,7 @@ func TestEtcdUpdateStatus(t *testing.T) { ...@@ -750,6 +754,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
}, },
}, },
SecurityContext: &api.PodSecurityContext{}, SecurityContext: &api.PodSecurityContext{},
Affinity: &api.Affinity{},
}, },
Status: api.PodStatus{ Status: api.PodStatus{
Phase: api.PodRunning, Phase: api.PodRunning,
......
...@@ -582,14 +582,6 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool { ...@@ -582,14 +582,6 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool {
} }
} }
// Parse required node affinity scheduling requirements
// and check if the current node match the requirements.
affinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
glog.V(10).Infof("Failed to get Affinity from Pod %+v, err: %+v", podName(pod), err)
return false
}
// 1. nil NodeSelector matches all nodes (i.e. does not filter out any nodes) // 1. nil NodeSelector matches all nodes (i.e. does not filter out any nodes)
// 2. nil []NodeSelectorTerm (equivalent to non-nil empty NodeSelector) matches no nodes // 2. nil []NodeSelectorTerm (equivalent to non-nil empty NodeSelector) matches no nodes
// 3. zero-length non-nil []NodeSelectorTerm matches no nodes also, just for simplicity // 3. zero-length non-nil []NodeSelectorTerm matches no nodes also, just for simplicity
...@@ -597,6 +589,7 @@ func podMatchesNodeLabels(pod *v1.Pod, node *v1.Node) bool { ...@@ -597,6 +589,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 := 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.
......
...@@ -38,10 +38,15 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod ...@@ -38,10 +38,15 @@ func PriorityMetadata(pod *v1.Pod, nodeNameToInfo map[string]*schedulercache.Nod
if err != nil { if err != nil {
return nil return nil
} }
affinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations) affinity := pod.Spec.Affinity
annotationAffinity, err := v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil { if err != nil {
return nil return nil
} }
if annotationAffinity != nil {
affinity.PodAffinity = annotationAffinity.PodAffinity
affinity.PodAntiAffinity = annotationAffinity.PodAntiAffinity
}
return &priorityMetadata{ return &priorityMetadata{
nonZeroRequest: getNonZeroRequests(pod), nonZeroRequest: getNonZeroRequests(pod),
podTolerations: tolerations, podTolerations: tolerations,
......
...@@ -41,12 +41,8 @@ func CalculateNodeAffinityPriorityMap(pod *v1.Pod, meta interface{}, nodeInfo *s ...@@ -41,12 +41,8 @@ func CalculateNodeAffinityPriorityMap(pod *v1.Pod, meta interface{}, nodeInfo *s
if priorityMeta, ok := meta.(*priorityMetadata); ok { if priorityMeta, ok := meta.(*priorityMetadata); ok {
affinity = priorityMeta.affinity affinity = priorityMeta.affinity
} else { } else {
// We couldn't parse metadata - fallback to computing it. // We couldn't parse metadata - fallback to the podspec.
var err error affinity = pod.Spec.Affinity
affinity, err = v1.GetAffinityFromPodAnnotations(pod.Annotations)
if err != nil {
return schedulerapi.HostPriority{}, err
}
} }
var count int32 var count int32
......
...@@ -32,62 +32,72 @@ func TestNodeAffinityPriority(t *testing.T) { ...@@ -32,62 +32,72 @@ func TestNodeAffinityPriority(t *testing.T) {
label4 := map[string]string{"abc": "az11", "def": "az22"} label4 := map[string]string{"abc": "az11", "def": "az22"}
label5 := map[string]string{"foo": "bar", "key": "value", "az": "az1"} label5 := map[string]string{"foo": "bar", "key": "value", "az": "az1"}
affinity1 := map[string]string{ affinity1 := &v1.Affinity{
v1.AffinityAnnotationKey: ` NodeAffinity: &v1.NodeAffinity{
{"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{{
{ Weight: 2,
"weight": 2, Preference: v1.NodeSelectorTerm{
"preference": { MatchExpressions: []v1.NodeSelectorRequirement{{
"matchExpressions": [ Key: "foo",
{ Operator: v1.NodeSelectorOpIn,
"key": "foo", Values: []string{"bar"},
"operator": "In", "values": ["bar"] }},
} },
] }},
} },
}
]}}`,
} }
affinity2 := map[string]string{ affinity2 := &v1.Affinity{
v1.AffinityAnnotationKey: ` NodeAffinity: &v1.NodeAffinity{
{"nodeAffinity": {"preferredDuringSchedulingIgnoredDuringExecution": [ PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
{ {
"weight": 2, Weight: 2,
"preference": {"matchExpressions": [ Preference: v1.NodeSelectorTerm{
{ MatchExpressions: []v1.NodeSelectorRequirement{
"key": "foo", {
"operator": "In", "values": ["bar"] Key: "foo",
} Operator: v1.NodeSelectorOpIn,
]} Values: []string{"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"] Weight: 4,
Preference: v1.NodeSelectorTerm{
MatchExpressions: []v1.NodeSelectorRequirement{
{
Key: "key",
Operator: v1.NodeSelectorOpIn,
Values: []string{"value"},
},
},
},
},
{
Weight: 5,
Preference: v1.NodeSelectorTerm{
MatchExpressions: []v1.NodeSelectorRequirement{
{
Key: "foo",
Operator: v1.NodeSelectorOpIn,
Values: []string{"bar"},
},
{
Key: "key",
Operator: v1.NodeSelectorOpIn,
Values: []string{"value"},
},
{
Key: "az",
Operator: v1.NodeSelectorOpIn,
Values: []string{"az1"},
},
},
}, },
{ },
"key": "az", },
"operator": "In", "values": ["az1"] },
}
]}
}
]}}`,
} }
tests := []struct { tests := []struct {
...@@ -112,8 +122,8 @@ func TestNodeAffinityPriority(t *testing.T) { ...@@ -112,8 +122,8 @@ func TestNodeAffinityPriority(t *testing.T) {
}, },
{ {
pod: &v1.Pod{ pod: &v1.Pod{
ObjectMeta: v1.ObjectMeta{ Spec: v1.PodSpec{
Annotations: affinity1, Affinity: affinity1,
}, },
}, },
nodes: []*v1.Node{ nodes: []*v1.Node{
...@@ -126,8 +136,8 @@ func TestNodeAffinityPriority(t *testing.T) { ...@@ -126,8 +136,8 @@ func TestNodeAffinityPriority(t *testing.T) {
}, },
{ {
pod: &v1.Pod{ pod: &v1.Pod{
ObjectMeta: v1.ObjectMeta{ Spec: v1.PodSpec{
Annotations: affinity1, Affinity: affinity1,
}, },
}, },
nodes: []*v1.Node{ nodes: []*v1.Node{
...@@ -140,8 +150,8 @@ func TestNodeAffinityPriority(t *testing.T) { ...@@ -140,8 +150,8 @@ func TestNodeAffinityPriority(t *testing.T) {
}, },
{ {
pod: &v1.Pod{ pod: &v1.Pod{
ObjectMeta: v1.ObjectMeta{ Spec: v1.PodSpec{
Annotations: affinity2, Affinity: affinity2,
}, },
}, },
nodes: []*v1.Node{ nodes: []*v1.Node{
......
...@@ -1824,6 +1824,8 @@ type PodSpec struct { ...@@ -1824,6 +1824,8 @@ type PodSpec struct {
// If not specified, the pod will not have a domainname at all. // If not specified, the pod will not have a domainname at all.
// +optional // +optional
Subdomain string `json:"subdomain,omitempty"` Subdomain string `json:"subdomain,omitempty"`
// If specified, the pod's scheduling constraints
Affinity *Affinity `json:"affinity,omitempty"`
} }
// Sysctl defines a kernel parameter to be set // Sysctl defines a kernel parameter to be set
......
...@@ -175,6 +175,9 @@ func SetDefaults_PodSpec(obj *PodSpec) { ...@@ -175,6 +175,9 @@ func SetDefaults_PodSpec(obj *PodSpec) {
period := int64(DefaultTerminationGracePeriodSeconds) period := int64(DefaultTerminationGracePeriodSeconds)
obj.TerminationGracePeriodSeconds = &period obj.TerminationGracePeriodSeconds = &period
} }
if obj.Affinity == nil {
obj.Affinity = &Affinity{}
}
} }
func SetDefaults_Probe(obj *Probe) { func SetDefaults_Probe(obj *Probe) {
if obj.TimeoutSeconds == 0 { if obj.TimeoutSeconds == 0 {
......
...@@ -2107,6 +2107,8 @@ type PodSpec struct { ...@@ -2107,6 +2107,8 @@ type PodSpec struct {
// If not specified, the pod will not have a domainname at all. // If not specified, the pod will not have a domainname at all.
// +optional // +optional
Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"` Subdomain string `json:"subdomain,omitempty" protobuf:"bytes,17,opt,name=subdomain"`
// If specified, the pod's scheduling constraints
Affinity *Affinity `json:"affinity,omitempty" protobuf:"bytes,18,opt,name=affinity"`
} }
// PodSecurityContext holds pod-level security attributes and common container settings. // PodSecurityContext holds pod-level security attributes and common container settings.
......
...@@ -1547,7 +1547,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco ...@@ -1547,7 +1547,7 @@ func (x codecSelfer1234) decSliceStatefulSet(v *[]StatefulSet, d *codec1978.Deco
yyrg127 := len(yyv127) > 0 yyrg127 := len(yyv127) > 0
yyv2127 := yyv127 yyv2127 := yyv127
yyrl127, yyrt127 = z.DecInferLen(yyl127, z.DecBasicHandle().MaxInitLen, 776) yyrl127, yyrt127 = z.DecInferLen(yyl127, z.DecBasicHandle().MaxInitLen, 784)
if yyrt127 { if yyrt127 {
if yyrl127 <= cap(yyv127) { if yyrl127 <= cap(yyv127) {
yyv127 = yyv127[:yyrl127] yyv127 = yyv127[:yyrl127]
......
...@@ -4247,7 +4247,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) { ...@@ -4247,7 +4247,7 @@ func (x codecSelfer1234) decSliceJob(v *[]Job, d *codec1978.Decoder) {
yyrg370 := len(yyv370) > 0 yyrg370 := len(yyv370) > 0
yyv2370 := yyv370 yyv2370 := yyv370
yyrl370, yyrt370 = z.DecInferLen(yyl370, z.DecBasicHandle().MaxInitLen, 800) yyrl370, yyrt370 = z.DecInferLen(yyl370, z.DecBasicHandle().MaxInitLen, 808)
if yyrt370 { if yyrt370 {
if yyrl370 <= cap(yyv370) { if yyrl370 <= cap(yyv370) {
yyv370 = yyv370[:yyrl370] yyv370 = yyv370[:yyrl370]
...@@ -4479,7 +4479,7 @@ func (x codecSelfer1234) decSliceCronJob(v *[]CronJob, d *codec1978.Decoder) { ...@@ -4479,7 +4479,7 @@ func (x codecSelfer1234) decSliceCronJob(v *[]CronJob, d *codec1978.Decoder) {
yyrg382 := len(yyv382) > 0 yyrg382 := len(yyv382) > 0
yyv2382 := yyv382 yyv2382 := yyv382
yyrl382, yyrt382 = z.DecInferLen(yyl382, z.DecBasicHandle().MaxInitLen, 1048) yyrl382, yyrt382 = z.DecInferLen(yyl382, z.DecBasicHandle().MaxInitLen, 1056)
if yyrt382 { if yyrt382 {
if yyrl382 <= cap(yyv382) { if yyrl382 <= cap(yyv382) {
yyv382 = yyv382[:yyrl382] yyv382 = yyv382[:yyrl382]
......
...@@ -17413,7 +17413,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode ...@@ -17413,7 +17413,7 @@ func (x codecSelfer1234) decSliceDeployment(v *[]Deployment, d *codec1978.Decode
yyrg1441 := len(yyv1441) > 0 yyrg1441 := len(yyv1441) > 0
yyv21441 := yyv1441 yyv21441 := yyv1441
yyrl1441, yyrt1441 = z.DecInferLen(yyl1441, z.DecBasicHandle().MaxInitLen, 832) yyrl1441, yyrt1441 = z.DecInferLen(yyl1441, z.DecBasicHandle().MaxInitLen, 840)
if yyrt1441 { if yyrt1441 {
if yyrl1441 <= cap(yyv1441) { if yyrl1441 <= cap(yyv1441) {
yyv1441 = yyv1441[:yyrl1441] yyv1441 = yyv1441[:yyrl1441]
...@@ -17529,7 +17529,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder) ...@@ -17529,7 +17529,7 @@ func (x codecSelfer1234) decSliceDaemonSet(v *[]DaemonSet, d *codec1978.Decoder)
yyrg1447 := len(yyv1447) > 0 yyrg1447 := len(yyv1447) > 0
yyv21447 := yyv1447 yyv21447 := yyv1447
yyrl1447, yyrt1447 = z.DecInferLen(yyl1447, z.DecBasicHandle().MaxInitLen, 728) yyrl1447, yyrt1447 = z.DecInferLen(yyl1447, z.DecBasicHandle().MaxInitLen, 736)
if yyrt1447 { if yyrt1447 {
if yyrl1447 <= cap(yyv1447) { if yyrl1447 <= cap(yyv1447) {
yyv1447 = yyv1447[:yyrl1447] yyv1447 = yyv1447[:yyrl1447]
...@@ -18225,7 +18225,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode ...@@ -18225,7 +18225,7 @@ func (x codecSelfer1234) decSliceReplicaSet(v *[]ReplicaSet, d *codec1978.Decode
yyrg1483 := len(yyv1483) > 0 yyrg1483 := len(yyv1483) > 0
yyv21483 := yyv1483 yyv21483 := yyv1483
yyrl1483, yyrt1483 = z.DecInferLen(yyl1483, z.DecBasicHandle().MaxInitLen, 768) yyrl1483, yyrt1483 = z.DecInferLen(yyl1483, z.DecBasicHandle().MaxInitLen, 776)
if yyrt1483 { if yyrt1483 {
if yyrl1483 <= cap(yyv1483) { if yyrl1483 <= cap(yyv1483) {
yyv1483 = yyv1483[:yyrl1483] yyv1483 = yyv1483[:yyrl1483]
......
...@@ -209,17 +209,22 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { ...@@ -209,17 +209,22 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
complexLabel := map[string]string{daemonsetNameLabel: dsName} complexLabel := map[string]string{daemonsetNameLabel: dsName}
nodeSelector := map[string]string{daemonsetColorLabel: "blue"} nodeSelector := map[string]string{daemonsetColorLabel: "blue"}
framework.Logf("Creating daemon with a node affinity %s", dsName) framework.Logf("Creating daemon with a node affinity %s", dsName)
affinity := map[string]string{ affinity := &v1.Affinity{
v1.AffinityAnnotationKey: fmt.Sprintf(` NodeAffinity: &v1.NodeAffinity{
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [{ {
"key": "%s", MatchExpressions: []v1.NodeSelectorRequirement{
"operator": "In", {
"values": ["%s"] Key: daemonsetColorLabel,
}] Operator: v1.NodeSelectorOpIn,
}] Values: []string{nodeSelector[daemonsetColorLabel]},
}}}`, daemonsetColorLabel, nodeSelector[daemonsetColorLabel]), },
},
},
},
},
},
} }
_, err := c.Extensions().DaemonSets(ns).Create(&extensions.DaemonSet{ _, err := c.Extensions().DaemonSets(ns).Create(&extensions.DaemonSet{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
...@@ -229,10 +234,10 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() { ...@@ -229,10 +234,10 @@ var _ = framework.KubeDescribe("Daemon set [Serial]", func() {
Selector: &metav1.LabelSelector{MatchLabels: complexLabel}, Selector: &metav1.LabelSelector{MatchLabels: complexLabel},
Template: v1.PodTemplateSpec{ Template: v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
Labels: complexLabel, Labels: complexLabel,
Annotations: affinity,
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Affinity: affinity,
Containers: []v1.Container{ Containers: []v1.Container{
{ {
Name: dsName, Name: dsName,
......
...@@ -314,17 +314,22 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume ...@@ -314,17 +314,22 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume
targetNode := nodeList.Items[0] targetNode := nodeList.Items[0]
By("Creating RC which spawns configmap-volume pods") By("Creating RC which spawns configmap-volume pods")
affinity := map[string]string{ affinity := &v1.Affinity{
v1.AffinityAnnotationKey: fmt.Sprintf(` NodeAffinity: &v1.NodeAffinity{
{"nodeAffinity": { "requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [{ {
"key": "kubernetes.io/hostname", MatchExpressions: []v1.NodeSelectorRequirement{
"operator": "In", {
"values": ["%s"] Key: "kubernetes.io/hostname",
}] Operator: v1.NodeSelectorOpIn,
}] Values: []string{targetNode.Name},
}}}`, targetNode.Name), },
},
},
},
},
},
} }
rc := &v1.ReplicationController{ rc := &v1.ReplicationController{
...@@ -338,8 +343,7 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume ...@@ -338,8 +343,7 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume
}, },
Template: &v1.PodTemplateSpec{ Template: &v1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: v1.ObjectMeta{
Annotations: affinity, Labels: map[string]string{"name": rcName},
Labels: map[string]string{"name": rcName},
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
Containers: []v1.Container{ Containers: []v1.Container{
...@@ -355,6 +359,7 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume ...@@ -355,6 +359,7 @@ func testNoWrappedVolumeRace(f *framework.Framework, volumes []v1.Volume, volume
VolumeMounts: volumeMounts, VolumeMounts: volumeMounts,
}, },
}, },
Affinity: affinity,
DNSPolicy: v1.DNSDefault, DNSPolicy: v1.DNSDefault,
Volumes: volumes, Volumes: volumes,
}, },
......
...@@ -45,6 +45,7 @@ var masterNodes sets.String ...@@ -45,6 +45,7 @@ var masterNodes sets.String
type pausePodConfig struct { type pausePodConfig struct {
Name string Name string
Affinity string Affinity string
NodeAffinity *v1.Affinity
Annotations, Labels, NodeSelector map[string]string Annotations, Labels, NodeSelector map[string]string
Resources *v1.ResourceRequirements Resources *v1.ResourceRequirements
} }
...@@ -240,15 +241,17 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() { ...@@ -240,15 +241,17 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
podName := "without-label" podName := "without-label"
_, err := cs.Core().Pods(ns).Create(initPausePod(f, pausePodConfig{ _, err := cs.Core().Pods(ns).Create(initPausePod(f, pausePodConfig{
Name: podName, Name: podName,
Affinity: `{ NodeAffinity: &v1.Affinity{
"nodeAffinity": { NodeAffinity: &v1.NodeAffinity{
"requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [] {
}] MatchExpressions: []v1.NodeSelectorRequirement{},
},
},
}, },
} },
}`, },
})) }))
if err == nil || !errors.IsInvalid(err) { if err == nil || !errors.IsInvalid(err) {
...@@ -300,28 +303,31 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() { ...@@ -300,28 +303,31 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
createPausePod(f, pausePodConfig{ createPausePod(f, pausePodConfig{
Name: podName, Name: podName,
Affinity: `{ NodeAffinity: &v1.Affinity{
"nodeAffinity": { NodeAffinity: &v1.NodeAffinity{
"requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [ NodeSelectorTerms: []v1.NodeSelectorTerm{
{ {
"matchExpressions": [{ MatchExpressions: []v1.NodeSelectorRequirement{
"key": "foo", {
"operator": "In", Key: "foo",
"values": ["bar", "value2"] Operator: v1.NodeSelectorOpIn,
}] Values: []string{"bar", "value2"},
},
},
}, {
MatchExpressions: []v1.NodeSelectorRequirement{
{
Key: "diffkey",
Operator: v1.NodeSelectorOpIn,
Values: []string{"wrong", "value2"},
},
},
}, },
{ },
"matchExpressions": [{ },
"key": "diffkey", },
"operator": "In", },
"values": ["wrong", "value2"]
}]
}
]
}
}
}`,
Labels: map[string]string{"name": "restricted"}, Labels: map[string]string{"name": "restricted"},
}) })
waitForScheduler() waitForScheduler()
...@@ -344,23 +350,27 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() { ...@@ -344,23 +350,27 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
labelPodName := "with-labels" labelPodName := "with-labels"
pod := createPausePod(f, pausePodConfig{ pod := createPausePod(f, pausePodConfig{
Name: labelPodName, Name: labelPodName,
Affinity: `{ NodeAffinity: &v1.Affinity{
"nodeAffinity": { NodeAffinity: &v1.NodeAffinity{
"requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [{ {
"key": "kubernetes.io/hostname", MatchExpressions: []v1.NodeSelectorRequirement{
"operator": "In", {
"values": ["` + nodeName + `"] Key: "kubernetes.io/hostname",
},{ Operator: v1.NodeSelectorOpIn,
"key": "` + k + `", Values: []string{nodeName},
"operator": "In", }, {
"values": ["` + v + `"] Key: k,
}] Operator: v1.NodeSelectorOpIn,
}] Values: []string{v},
} },
} },
}`, },
},
},
},
},
}) })
// check that pod got scheduled. We intentionally DO NOT check that the // check that pod got scheduled. We intentionally DO NOT check that the
...@@ -374,31 +384,6 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() { ...@@ -374,31 +384,6 @@ var _ = framework.KubeDescribe("SchedulerPredicates [Serial]", func() {
Expect(labelPod.Spec.NodeName).To(Equal(nodeName)) Expect(labelPod.Spec.NodeName).To(Equal(nodeName))
}) })
// Verify that an escaped JSON string of NodeAffinity in a YAML PodSpec works.
It("validates that embedding the JSON NodeAffinity setting as a string in the annotation value work", func() {
nodeName := getNodeThatCanRunPod(f)
By("Trying to apply a label with fake az info on the found node.")
k := "kubernetes.io/e2e-az-name"
v := "e2e-az1"
framework.AddOrUpdateLabelOnNode(cs, nodeName, k, v)
framework.ExpectNodeHasLabel(cs, nodeName, k, v)
defer framework.RemoveLabelOffNode(cs, nodeName, k)
By("Trying to launch a pod that with NodeAffinity setting as embedded JSON string in the annotation value.")
pod := createPodWithNodeAffinity(f)
// check that pod got scheduled. We intentionally DO NOT check that the
// pod is running because this will create a race condition with the
// kubelet and the scheduler: the scheduler might have scheduled a pod
// already when the kubelet does not know about its new label yet. The
// kubelet will then refuse to launch the pod.
framework.ExpectNoError(framework.WaitForPodNotPending(cs, ns, pod.Name, ""))
labelPod, err := cs.Core().Pods(ns).Get(pod.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
Expect(labelPod.Spec.NodeName).To(Equal(nodeName))
})
// labelSelector Operator is DoesNotExist but values are there in requiredDuringSchedulingIgnoredDuringExecution // labelSelector Operator is DoesNotExist but values are there in requiredDuringSchedulingIgnoredDuringExecution
// part of podAffinity,so validation fails. // part of podAffinity,so validation fails.
It("validates that a pod with an invalid podAffinity is rejected because of the LabelSelectorRequirement is invalid", func() { It("validates that a pod with an invalid podAffinity is rejected because of the LabelSelectorRequirement is invalid", func() {
...@@ -777,6 +762,7 @@ func initPausePod(f *framework.Framework, conf pausePodConfig) *v1.Pod { ...@@ -777,6 +762,7 @@ func initPausePod(f *framework.Framework, conf pausePodConfig) *v1.Pod {
}, },
Spec: v1.PodSpec{ Spec: v1.PodSpec{
NodeSelector: conf.NodeSelector, NodeSelector: conf.NodeSelector,
Affinity: conf.NodeAffinity,
Containers: []v1.Container{ Containers: []v1.Container{
{ {
Name: podName, Name: podName,
...@@ -822,19 +808,23 @@ func runPodAndGetNodeName(f *framework.Framework, conf pausePodConfig) string { ...@@ -822,19 +808,23 @@ func runPodAndGetNodeName(f *framework.Framework, conf pausePodConfig) string {
func createPodWithNodeAffinity(f *framework.Framework) *v1.Pod { func createPodWithNodeAffinity(f *framework.Framework) *v1.Pod {
return createPausePod(f, pausePodConfig{ return createPausePod(f, pausePodConfig{
Name: "with-nodeaffinity-" + string(uuid.NewUUID()), Name: "with-nodeaffinity-" + string(uuid.NewUUID()),
Affinity: `{ NodeAffinity: &v1.Affinity{
"nodeAffinity": { NodeAffinity: &v1.NodeAffinity{
"requiredDuringSchedulingIgnoredDuringExecution": { RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
"nodeSelectorTerms": [{ NodeSelectorTerms: []v1.NodeSelectorTerm{
"matchExpressions": [{ {
"key": "kubernetes.io/e2e-az-name", MatchExpressions: []v1.NodeSelectorRequirement{
"operator": "In", {
"values": ["e2e-az1", "e2e-az2"] Key: "kubernetes.io/e2e-az-name",
}] Operator: v1.NodeSelectorOpIn,
}] Values: []string{"e2e-az1", "e2e-az2"},
} },
} },
}`, },
},
},
},
},
}) })
} }
......
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