Change:
When a gated pod is created with nil affinity and a user/controller tries to update its nodeAffinity, the validation fails. The pod gets updated with the new affinity as per rules in a pod as in the following using : to update the pod as with: No response <details</details<detailsTested on kind and OpenShift </details<details</details<details</details<details</details<details</details
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig scheduling

Discussion:
// already effectively nil, no change needed case mungedPodSpec.Affinity == nil && oldNodeAffinity != nil: mungedPodSpec.Affinity = &core.Affinity{NodeAffinity: oldNodeAffinity} // +k8s:verify-mutation:reason=clone <ins> case mungedPodSpec.Affinity != nil && oldPod.Spec.Affinity == nil && mungedPodSpec.Affinity.PodAntiAffinity == nil && mungedPodSpec.Affinity.PodAffinity == nil: // We ensure no other fields are being changed, but the NodeAffinity. If that's the case, and the // old pod's affinity is nil, we set the mungedPodSpec's affinity to nil. mungedPodSpec.Affinity = nil // +k8s:verify-mutation:reason=clone </ins> default: <ins> // The node affinity is being updated and the old pod Affinity is not nil. // We set the mungedPodSpec's node affinity to the old pod's node affinity. </ins> mungedPodSpec.Affinity.NodeAffinity = oldNodeAffinity // +k8s:verify-mutation:reason=clone } }

}, err:  "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:", test: "empty NodeSelectorTerm (selects nothing) cannot become populated (selects something)", <ins> }, { old: core.Pod{ Spec: core.PodSpec{ Affinity:        nil, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, new: core.Pod{ Spec: core.PodSpec{ Affinity: &core.Affinity{ NodeAffinity: &core.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{ NodeSelectorTerms: []core.NodeSelectorTerm{{ MatchExpressions: []core.NodeSelectorRequirement{{ Key:      "expr", Operator: core.NodeSelectorOpIn, Values:   []string{"foo"}, }}, }}, }, }, }, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, opts: PodValidationOptions{ AllowMutableNodeSelectorAndNodeAffinity: true, }, test: "nil affinity can be mutated for gated pods", }, { old: core.Pod{ Spec: core.PodSpec{ Affinity:        nil, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, new: core.Pod{ Spec: core.PodSpec{ Affinity: &core.Affinity{ NodeAffinity: &core.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{ NodeSelectorTerms: []core.NodeSelectorTerm{{ MatchExpressions: []core.NodeSelectorRequirement{{ Key:      "expr", Operator: core.NodeSelectorOpIn, Values:   []string{"foo"}, }}, }}, }, }, PodAffinity: &core.PodAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{ { TopologyKey: "foo", LabelSelector: &metav1.LabelSelector{ MatchLabels: map[string]string{"foo": "bar"}, }, }, }, }, }, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, opts: PodValidationOptions{ AllowMutableNodeSelectorAndNodeAffinity: true, }, err:  "pod updates may not change fields other than", test: "the podAffinity cannot be updated on gated pods", }, { old: core.Pod{ Spec: core.PodSpec{ Affinity:        nil, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, new: core.Pod{ Spec: core.PodSpec{ Affinity: &core.Affinity{ NodeAffinity: &core.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{ NodeSelectorTerms: []core.NodeSelectorTerm{{ MatchExpressions: []core.NodeSelectorRequirement{{ Key:      "expr", Operator: core.NodeSelectorOpIn, Values:   []string{"foo"}, }}, }}, }, }, PodAntiAffinity: &core.PodAntiAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{ { TopologyKey: "foo", LabelSelector: &metav1.LabelSelector{ MatchLabels: map[string]string{"foo": "bar"}, }, }, }, }, }, SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}}, }, }, opts: PodValidationOptions{ AllowMutableNodeSelectorAndNodeAffinity: true, }, err:  "pod updates may not change fields other than", test: "the podAntiAffinity cannot be updated on gated pods", </ins> }, } for _, test := range tests {

}, enableSchedulingGates: true, }, <ins> { name: "addition to nodeAffinity is allowed for gated pods with nil affinity", create: &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name:  "fake-name", Image: "fakeimage", }, }, SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}}, }, }, update: &v1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", }, Spec: v1.PodSpec{ Containers: []v1.Container{ { Name:  "fake-name", Image: "fakeimage", }, }, Affinity: &v1.Affinity{ NodeAffinity: &v1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ // Add 1 MatchExpression and 1 MatchField. NodeSelectorTerms: []v1.NodeSelectorTerm{ { MatchExpressions: []v1.NodeSelectorRequirement{ { Key:      "expr", Operator: v1.NodeSelectorOpIn, Values:   []string{"foo"}, }, }, MatchFields: []v1.NodeSelectorRequirement{ { Key:      "metadata.name", Operator: v1.NodeSelectorOpIn, Values:   []string{"foo"}, }, }, }, }, }, }, }, SchedulingGates: []v1.PodSchedulingGate{{Name: "baz"}}, }, }, enableSchedulingGates: true, }, </ins> } for _, tc := range cases { defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tc.enableSchedulingGates)()