Updating the nodeAffinity of gated pods having nil affinity should be allowed

This commit is contained in:
aleskandro 2023-05-16 09:04:59 +02:00
parent 54d2ced4d6
commit 4c9887e3eb
3 changed files with 175 additions and 0 deletions

View File

@ -4753,7 +4753,14 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod, opts PodValidationOptions) fiel
// already effectively nil, no change needed
case mungedPodSpec.Affinity == nil && oldNodeAffinity != nil:
mungedPodSpec.Affinity = &core.Affinity{NodeAffinity: oldNodeAffinity} // +k8s:verify-mutation:reason=clone
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
default:
// 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.
mungedPodSpec.Affinity.NodeAffinity = oldNodeAffinity // +k8s:verify-mutation:reason=clone
}
}

View File

@ -12836,6 +12836,117 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "empty NodeSelectorTerm (selects nothing) cannot become populated (selects something)",
}, {
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",
},
}
for _, test := range tests {

View File

@ -844,6 +844,63 @@ func TestMutablePodSchedulingDirectives(t *testing.T) {
},
enableSchedulingGates: true,
},
{
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,
},
}
for _, tc := range cases {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, tc.enableSchedulingGates)()