feature(InterPodAffinity): return Skip in PreFilter

This commit is contained in:
Kensei Nakada 2023-01-07 07:35:23 +00:00
parent 643353abfe
commit 3b20a007ed
2 changed files with 85 additions and 92 deletions

View File

@ -151,7 +151,7 @@ func podMatchesAllAffinityTerms(terms []framework.AffinityTerm, pod *v1.Pod) boo
// calculates the following for each existing pod on each node: // calculates the following for each existing pod on each node:
// 1. Whether it has PodAntiAffinity // 1. Whether it has PodAntiAffinity
// 2. Whether any AffinityTerm matches the incoming pod // 2. Whether any AntiAffinityTerm matches the incoming pod
func (pl *InterPodAffinity) getExistingAntiAffinityCounts(ctx context.Context, pod *v1.Pod, nsLabels labels.Set, nodes []*framework.NodeInfo) topologyToMatchedTermCount { func (pl *InterPodAffinity) getExistingAntiAffinityCounts(ctx context.Context, pod *v1.Pod, nsLabels labels.Set, nodes []*framework.NodeInfo) topologyToMatchedTermCount {
topoMaps := make([]topologyToMatchedTermCount, len(nodes)) topoMaps := make([]topologyToMatchedTermCount, len(nodes))
index := int32(-1) index := int32(-1)
@ -259,6 +259,10 @@ func (pl *InterPodAffinity) PreFilter(ctx context.Context, cycleState *framework
s.existingAntiAffinityCounts = pl.getExistingAntiAffinityCounts(ctx, pod, s.namespaceLabels, nodesWithRequiredAntiAffinityPods) s.existingAntiAffinityCounts = pl.getExistingAntiAffinityCounts(ctx, pod, s.namespaceLabels, nodesWithRequiredAntiAffinityPods)
s.affinityCounts, s.antiAffinityCounts = pl.getIncomingAffinityAntiAffinityCounts(ctx, s.podInfo, allNodes) s.affinityCounts, s.antiAffinityCounts = pl.getIncomingAffinityAntiAffinityCounts(ctx, s.podInfo, allNodes)
if len(s.existingAntiAffinityCounts) == 0 && len(s.podInfo.RequiredAffinityTerms) == 0 && len(s.podInfo.RequiredAntiAffinityTerms) == 0 {
return nil, framework.NewStatus(framework.Skip)
}
cycleState.Write(preFilterStateKey, s) cycleState.Write(preFilterStateKey, s)
return nil, nil return nil, nil
} }

View File

@ -20,9 +20,9 @@ import (
"context" "context"
"fmt" "fmt"
"reflect" "reflect"
"strings"
"testing" "testing"
"github.com/google/go-cmp/cmp"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -69,16 +69,18 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
node1 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labels1}} node1 := v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labels1}}
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
pods []*v1.Pod pods []*v1.Pod
node *v1.Node node *v1.Node
name string name string
wantStatus *framework.Status wantPreFilterStatus *framework.Status
wantFilterStatus *framework.Status
}{ }{
{ {
name: "A pod that has no required pod affinity scheduling rules can schedule onto a node with no existing pods", name: "A pod that has no required pod affinity scheduling rules can schedule onto a node with no existing pods",
pod: new(v1.Pod), pod: new(v1.Pod),
node: &node1, node: &node1,
wantPreFilterStatus: framework.NewStatus(framework.Skip),
}, },
{ {
name: "satisfies with requiredDuringSchedulingIgnoredDuringExecution in PodAffinity using In operator that matches the existing pod", name: "satisfies with requiredDuringSchedulingIgnoredDuringExecution in PodAffinity using In operator that matches the existing pod",
@ -111,7 +113,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
}, nil), }, nil),
pods: []*v1.Pod{st.MakePod().Namespace("ns").Label("service", "securityscan").Node("node1").Obj()}, pods: []*v1.Pod{st.MakePod().Namespace("ns").Label("service", "securityscan").Node("node1").Obj()},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
), ),
@ -121,7 +123,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
pod: st.MakePod().Namespace(defaultNamespace).Labels(podLabel).PodAffinityIn("service", "", []string{"antivirusscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), pod: st.MakePod().Namespace(defaultNamespace).Labels(podLabel).PodAffinityIn("service", "", []string{"antivirusscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(),
pods: []*v1.Pod{pod}, pods: []*v1.Pod{pod},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
), ),
@ -199,7 +201,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
}, nil), }, nil),
pods: []*v1.Pod{pod}, pods: []*v1.Pod{pod},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
), ),
@ -230,7 +232,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
pods: []*v1.Pod{pod}, pods: []*v1.Pod{pod},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -244,7 +246,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
st.MakePod().Namespace(defaultNamespace).Labels(podLabel).Node("node1").PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), st.MakePod().Namespace(defaultNamespace).Labels(podLabel).Node("node1").PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
), ),
@ -255,7 +257,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAffinityNotIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), PodAffinityNotIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(),
pods: []*v1.Pod{st.MakePod().Label("service", "securityscan").Node("node2").Obj()}, pods: []*v1.Pod{st.MakePod().Label("service", "securityscan").Node("node2").Obj()},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
), ),
@ -268,7 +270,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
), ),
@ -280,7 +282,8 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
st.MakePod().Namespace(defaultNamespace).Node("node1").Labels(podLabel). st.MakePod().Namespace(defaultNamespace).Node("node1").Labels(podLabel).
PodAntiAffinityNotIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityNotIn("service", "zone", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantPreFilterStatus: framework.NewStatus(framework.Skip),
}, },
{ {
name: "satisfies the PodAntiAffinity with existing pod but doesn't satisfy PodAntiAffinity symmetry with incoming pod", name: "satisfies the PodAntiAffinity with existing pod but doesn't satisfy PodAntiAffinity symmetry with incoming pod",
@ -292,7 +295,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -307,7 +310,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -322,7 +325,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityExists("security", "zone", st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
), ),
@ -338,7 +341,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityExists("def", "zone", st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityExists("def", "zone", st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -354,7 +357,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAntiAffinityExists("def", "zone", st.PodAntiAffinityWithRequiredReq).Obj(), PodAntiAffinityExists("def", "zone", st.PodAntiAffinityWithRequiredReq).Obj(),
}, },
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -365,9 +368,9 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAffinityIn("service", "region", []string{"{{.bad-value.}}"}, st.PodAffinityWithRequiredReq). PodAffinityIn("service", "region", []string{"{{.bad-value.}}"}, st.PodAffinityWithRequiredReq).
PodAffinityIn("service", "node", []string{"antivirusscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), PodAffinityIn("service", "node", []string{"antivirusscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(),
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantPreFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
`Invalid value: "{{.bad-value.}}"`, `parsing pod: requiredAffinityTerms: values[0][service]: Invalid value: "{{.bad-value.}}": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')`,
), ),
}, },
{ {
@ -376,9 +379,9 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
PodAffinityIn("service", "region", []string{"foo"}, st.PodAffinityWithRequiredReq). PodAffinityIn("service", "region", []string{"foo"}, st.PodAffinityWithRequiredReq).
PodAffinityIn("service", "node", []string{"{{.bad-value.}}"}, st.PodAffinityWithRequiredReq).Obj(), PodAffinityIn("service", "node", []string{"{{.bad-value.}}"}, st.PodAffinityWithRequiredReq).Obj(),
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantPreFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
`Invalid value: "{{.bad-value.}}"`, `parsing pod: requiredAffinityTerms: values[0][service]: Invalid value: "{{.bad-value.}}": a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')`,
), ),
}, },
{ {
@ -438,7 +441,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
}, nil), }, nil),
pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabel}}}, pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabel}}},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
), ),
@ -471,7 +474,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
}), }),
pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabel}}}, pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabel}}},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -496,7 +499,7 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
}), }),
pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabel}}}, pods: []*v1.Pod{{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabel}}},
node: &node1, node: &node1,
wantStatus: framework.NewStatus( wantFilterStatus: framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
), ),
@ -540,16 +543,17 @@ func TestRequiredAffinitySingleNode(t *testing.T) {
p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{}, snapshot, namespaces) p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{}, snapshot, namespaces)
state := framework.NewCycleState() state := framework.NewCycleState()
_, preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, state, test.pod) _, preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, state, test.pod)
if diff := cmp.Diff(preFilterStatus, test.wantPreFilterStatus); diff != "" {
t.Errorf("PreFilter: status does not match (-want,+got):\n%s", diff)
}
if !preFilterStatus.IsSuccess() { if !preFilterStatus.IsSuccess() {
if !strings.Contains(preFilterStatus.Message(), test.wantStatus.Message()) { return
t.Errorf("prefilter failed with status: %v", preFilterStatus) }
}
} else { nodeInfo := mustGetNodeInfo(t, snapshot, test.node.Name)
nodeInfo := mustGetNodeInfo(t, snapshot, test.node.Name) gotStatus := p.(framework.FilterPlugin).Filter(ctx, state, test.pod, nodeInfo)
gotStatus := p.(framework.FilterPlugin).Filter(ctx, state, test.pod, nodeInfo) if diff := cmp.Diff(gotStatus, test.wantFilterStatus); diff != "" {
if !reflect.DeepEqual(gotStatus, test.wantStatus) { t.Errorf("Filter: status does not match (-want,+got):\n%s", diff)
t.Errorf("status does not match: %v, want: %v", gotStatus, test.wantStatus)
}
} }
}) })
} }
@ -571,11 +575,12 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
} }
tests := []struct { tests := []struct {
pod *v1.Pod pod *v1.Pod
pods []*v1.Pod pods []*v1.Pod
nodes []*v1.Node nodes []*v1.Node
wantStatuses []*framework.Status wantFilterStatuses []*framework.Status
name string wantPreFilterStatus *framework.Status
name string
}{ }{
{ {
pod: st.MakePod().Namespace(defaultNamespace).PodAffinityIn("foo", "region", []string{"bar"}, st.PodAffinityWithRequiredReq).Obj(), pod: st.MakePod().Namespace(defaultNamespace).PodAffinityIn("foo", "region", []string{"bar"}, st.PodAffinityWithRequiredReq).Obj(),
@ -587,7 +592,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChinaAzAz1}}, {ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChinaAzAz1}},
{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}}, {ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
nil, nil,
nil, nil,
framework.NewStatus( framework.NewStatus(
@ -608,7 +613,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"zone": "az1", "hostname": "h1"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"zone": "az1", "hostname": "h1"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"zone": "az2", "hostname": "h2"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"zone": "az2", "hostname": "h2"}}},
}, },
wantStatuses: []*framework.Status{nil, nil}, wantFilterStatuses: []*framework.Status{nil, nil},
name: "The affinity rule is to schedule all of the pods of this collection to the same zone. The first pod of the collection " + name: "The affinity rule is to schedule all of the pods of this collection to the same zone. The first pod of the collection " +
"should not be blocked from being scheduled onto any node, even there's no existing pod that matches the rule anywhere.", "should not be blocked from being scheduled onto any node, even there's no existing pod that matches the rule anywhere.",
}, },
@ -623,7 +628,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"zoneLabel": "az1", "hostname": "h1"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"zoneLabel": "az1", "hostname": "h1"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"zoneLabel": "az2", "hostname": "h2"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"zoneLabel": "az2", "hostname": "h2"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
@ -644,7 +649,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -666,7 +671,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -688,7 +693,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: labelRgChinaAzAz1}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: labelRgChinaAzAz1}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: labelRgIndia}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: labelRgIndia}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -712,7 +717,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: labelRgChinaAzAz1}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: labelRgChinaAzAz1}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: labelRgIndia}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: labelRgIndia}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -734,8 +739,9 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{nil, nil}, wantPreFilterStatus: framework.NewStatus(framework.Skip),
name: "Test existing pod's anti-affinity: if an existing pod has a term with invalid topologyKey, labelSelector of the term is firstly checked, and then topologyKey of the term is also checked", wantFilterStatuses: []*framework.Status{nil, nil},
name: "Test existing pod's anti-affinity: if an existing pod has a term with invalid topologyKey, labelSelector of the term is firstly checked, and then topologyKey of the term is also checked",
}, },
{ {
pod: st.MakePod().Node("nodeA").Namespace(defaultNamespace).PodAntiAffinityExists("foo", "invalid-node-label", st.PodAntiAffinityWithRequiredReq).Obj(), pod: st.MakePod().Node("nodeA").Namespace(defaultNamespace).PodAntiAffinityExists("foo", "invalid-node-label", st.PodAntiAffinityWithRequiredReq).Obj(),
@ -746,8 +752,8 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{nil, nil}, wantFilterStatuses: []*framework.Status{nil, nil},
name: "Test incoming pod's anti-affinity: even if labelSelector matches, we still check if topologyKey matches", name: "Test incoming pod's anti-affinity: even if labelSelector matches, we still check if topologyKey matches",
}, },
{ {
pod: st.MakePod().Label("foo", "").Label("bar", "").Obj(), pod: st.MakePod().Label("foo", "").Label("bar", "").Obj(),
@ -759,7 +765,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
@ -782,7 +788,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -804,7 +810,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
@ -823,7 +829,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -842,7 +848,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
@ -864,7 +870,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonAntiAffinityRulesNotMatch, ErrReasonAntiAffinityRulesNotMatch,
@ -889,7 +895,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: map[string]string{"region": "r1", "zone": "z3", "hostname": "nodeC"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: map[string]string{"region": "r1", "zone": "z3", "hostname": "nodeC"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.Unschedulable, framework.Unschedulable,
ErrReasonExistingAntiAffinityRulesNotMatch, ErrReasonExistingAntiAffinityRulesNotMatch,
@ -912,8 +918,8 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{nil, nil}, wantFilterStatuses: []*framework.Status{nil, nil},
name: "Test incoming pod's affinity: firstly check if all affinityTerms match, and then check if all topologyKeys match", name: "Test incoming pod's affinity: firstly check if all affinityTerms match, and then check if all topologyKeys match",
}, },
{ {
pod: st.MakePod().Namespace(defaultNamespace).PodAffinityExists("foo", "region", st.PodAffinityWithRequiredReq). pod: st.MakePod().Namespace(defaultNamespace).PodAffinityExists("foo", "region", st.PodAffinityWithRequiredReq).
@ -926,7 +932,7 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: map[string]string{"region": "r1", "zone": "z1", "hostname": "nodeA"}}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}}, {ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: map[string]string{"region": "r1", "zone": "z2", "hostname": "nodeB"}}},
}, },
wantStatuses: []*framework.Status{ wantFilterStatuses: []*framework.Status{
framework.NewStatus( framework.NewStatus(
framework.UnschedulableAndUnresolvable, framework.UnschedulableAndUnresolvable,
ErrReasonAffinityRulesNotMatch, ErrReasonAffinityRulesNotMatch,
@ -949,16 +955,19 @@ func TestRequiredAffinityMultipleNodes(t *testing.T) {
[]runtime.Object{ []runtime.Object{
&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "NS1"}}, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "NS1"}},
}) })
state := framework.NewCycleState()
_, preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, state, test.pod)
if diff := cmp.Diff(preFilterStatus, test.wantPreFilterStatus); diff != "" {
t.Errorf("PreFilter: status does not match (-want,+got):\n%s", diff)
}
if preFilterStatus.IsSkip() {
return
}
for indexNode, node := range test.nodes { for indexNode, node := range test.nodes {
state := framework.NewCycleState()
_, preFilterStatus := p.(framework.PreFilterPlugin).PreFilter(ctx, state, test.pod)
if !preFilterStatus.IsSuccess() {
t.Errorf("prefilter failed with status: %v", preFilterStatus)
}
nodeInfo := mustGetNodeInfo(t, snapshot, node.Name) nodeInfo := mustGetNodeInfo(t, snapshot, node.Name)
gotStatus := p.(framework.FilterPlugin).Filter(ctx, state, test.pod, nodeInfo) gotStatus := p.(framework.FilterPlugin).Filter(ctx, state, test.pod, nodeInfo)
if !reflect.DeepEqual(gotStatus, test.wantStatuses[indexNode]) { if diff := cmp.Diff(gotStatus, test.wantFilterStatuses[indexNode]); diff != "" {
t.Errorf("index: %d status does not match: %v, want: %v", indexTest, gotStatus, test.wantStatuses[indexNode]) t.Errorf("index: %d: Filter: status does not match (-want,+got):\n%s", indexTest, diff)
} }
} }
}) })
@ -1077,26 +1086,6 @@ func TestPreFilterStateAddRemovePod(t *testing.T) {
expectedAntiAffinity topologyToMatchedTermCount expectedAntiAffinity topologyToMatchedTermCount
expectedAffinity topologyToMatchedTermCount expectedAffinity topologyToMatchedTermCount
}{ }{
{
name: "no affinity exist",
pendingPod: st.MakePod().Name("pending").Labels(selector1).Obj(),
existingPods: []*v1.Pod{
{ObjectMeta: metav1.ObjectMeta{Name: "p1", Labels: selector1},
Spec: v1.PodSpec{NodeName: "nodeA"},
},
{ObjectMeta: metav1.ObjectMeta{Name: "p2"},
Spec: v1.PodSpec{NodeName: "nodeC"},
},
},
addedPod: st.MakePod().Name("addedPod").Labels(selector1).Node("nodeB").Obj(),
nodes: []*v1.Node{
{ObjectMeta: metav1.ObjectMeta{Name: "nodeA", Labels: label1}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeB", Labels: label2}},
{ObjectMeta: metav1.ObjectMeta{Name: "nodeC", Labels: label3}},
},
expectedAntiAffinity: topologyToMatchedTermCount{},
expectedAffinity: topologyToMatchedTermCount{},
},
{ {
name: "preFilterState anti-affinity terms are updated correctly after adding and removing a pod", name: "preFilterState anti-affinity terms are updated correctly after adding and removing a pod",
pendingPod: &v1.Pod{ pendingPod: &v1.Pod{