Unverified Commit 17fe91cc authored by k8s-ci-robot's avatar k8s-ci-robot Committed by GitHub

Merge pull request #68173 from Huang-Wei/antiaffinity-symmetry-issue

Fix PodAntiAffinity issues in case of multiple affinityTerms
parents aa61b661 74905421
...@@ -525,3 +525,269 @@ func TestPredicateMetadata_ShallowCopy(t *testing.T) { ...@@ -525,3 +525,269 @@ func TestPredicateMetadata_ShallowCopy(t *testing.T) {
t.Errorf("Copy is not equal to source!") t.Errorf("Copy is not equal to source!")
} }
} }
// TestGetTPMapMatchingIncomingAffinityAntiAffinity tests against method getTPMapMatchingIncomingAffinityAntiAffinity
// on Anti Affinity cases
func TestGetTPMapMatchingIncomingAffinityAntiAffinity(t *testing.T) {
newPodAffinityTerms := func(keys ...string) []v1.PodAffinityTerm {
var terms []v1.PodAffinityTerm
for _, key := range keys {
terms = append(terms, v1.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: key,
Operator: metav1.LabelSelectorOpExists,
},
},
},
TopologyKey: "hostname",
})
}
return terms
}
newPod := func(labels ...string) *v1.Pod {
labelMap := make(map[string]string)
for _, l := range labels {
labelMap[l] = ""
}
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "normal", Labels: labelMap},
Spec: v1.PodSpec{NodeName: "nodeA"},
}
}
normalPodA := newPod("aaa")
normalPodB := newPod("bbb")
normalPodAB := newPod("aaa", "bbb")
nodeA := &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"hostname": "nodeA"}}}
tests := []struct {
name string
existingPods []*v1.Pod
nodes []*v1.Node
pod *v1.Pod
wantAffinityPodsMaps *topologyPairsMaps
wantAntiAffinityPodsMaps *topologyPairsMaps
wantErr bool
}{
{
name: "nil test",
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "aaa-normal"},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
},
{
name: "incoming pod without affinity/anti-affinity causes a no-op",
existingPods: []*v1.Pod{normalPodA},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "aaa-normal"},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
},
{
name: "no pod has label that violates incoming pod's affinity and anti-affinity",
existingPods: []*v1.Pod{normalPodB},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "aaa-anti"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
},
},
},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: newTopologyPairsMaps(),
},
{
name: "existing pod matches incoming pod's affinity and anti-affinity - single term case",
existingPods: []*v1.Pod{normalPodA},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
},
},
},
},
wantAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
wantAntiAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
},
{
name: "existing pod matches incoming pod's affinity and anti-affinity - mutiple terms case",
existingPods: []*v1.Pod{normalPodAB},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa"),
},
},
},
},
wantAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
wantAntiAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
},
{
name: "existing pod not match incoming pod's affinity but matches anti-affinity",
existingPods: []*v1.Pod{normalPodA},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
},
},
},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodA: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
},
{
name: "incoming pod's anti-affinity has more than one term - existing pod violates partial term - case 1",
existingPods: []*v1.Pod{normalPodAB},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "anaffi-antiaffiti"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "ccc"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "ccc"),
},
},
},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodAB: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
},
{
name: "incoming pod's anti-affinity has more than one term - existing pod violates partial term - case 2",
existingPods: []*v1.Pod{normalPodB},
nodes: []*v1.Node{nodeA},
pod: &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "affi-antiaffi"},
Spec: v1.PodSpec{
Affinity: &v1.Affinity{
PodAffinity: &v1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
},
PodAntiAffinity: &v1.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: newPodAffinityTerms("aaa", "bbb"),
},
},
},
},
wantAffinityPodsMaps: newTopologyPairsMaps(),
wantAntiAffinityPodsMaps: &topologyPairsMaps{
topologyPairToPods: map[topologyPair]podSet{
{key: "hostname", value: "nodeA"}: {normalPodB: struct{}{}},
},
podToTopologyPairs: map[string]topologyPairSet{
"normal_": {
topologyPair{key: "hostname", value: "nodeA"}: struct{}{},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
nodeInfoMap := schedulercache.CreateNodeNameToInfoMap(tt.existingPods, tt.nodes)
gotAffinityPodsMaps, gotAntiAffinityPodsMaps, err := getTPMapMatchingIncomingAffinityAntiAffinity(tt.pod, nodeInfoMap)
if (err != nil) != tt.wantErr {
t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotAffinityPodsMaps, tt.wantAffinityPodsMaps) {
t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() gotAffinityPodsMaps = %#v, want %#v", gotAffinityPodsMaps, tt.wantAffinityPodsMaps)
}
if !reflect.DeepEqual(gotAntiAffinityPodsMaps, tt.wantAntiAffinityPodsMaps) {
t.Errorf("getTPMapMatchingIncomingAffinityAntiAffinity() gotAntiAffinityPodsMaps = %#v, want %#v", gotAntiAffinityPodsMaps, tt.wantAntiAffinityPodsMaps)
}
})
}
}
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