k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/scheduler/framework/plugins/interpodaffinity/plugin_test.go (about) 1 /* 2 Copyright 2024 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package interpodaffinity 18 19 import ( 20 "context" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 v1 "k8s.io/api/core/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/klog/v2/ktesting" 27 "k8s.io/kubernetes/pkg/scheduler/apis/config" 28 "k8s.io/kubernetes/pkg/scheduler/framework" 29 plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing" 30 "k8s.io/kubernetes/pkg/scheduler/internal/cache" 31 st "k8s.io/kubernetes/pkg/scheduler/testing" 32 ) 33 34 func Test_isSchedulableAfterPodChange(t *testing.T) { 35 tests := []struct { 36 name string 37 pod *v1.Pod 38 oldPod, newPod *v1.Pod 39 expectedHint framework.QueueingHint 40 }{ 41 { 42 name: "add a pod which matches the pod affinity", 43 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 44 newPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 45 expectedHint: framework.Queue, 46 }, 47 { 48 name: "add an un-scheduled pod", 49 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 50 newPod: st.MakePod().Label("service", "securityscan").Obj(), 51 expectedHint: framework.QueueSkip, 52 }, 53 { 54 name: "add a pod which doesn't match the pod affinity", 55 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 56 newPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 57 expectedHint: framework.QueueSkip, 58 }, 59 { 60 name: "update a pod from non-match to match pod affinity", 61 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 62 newPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 63 oldPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 64 expectedHint: framework.Queue, 65 }, 66 { 67 name: "the updating pod matches target pod's affinity both before and after label changes", 68 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 69 newPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 70 oldPod: st.MakePod().Node("fake-node").Label("service", "value2").Obj(), 71 expectedHint: framework.QueueSkip, 72 }, 73 { 74 name: "update an un-scheduled pod", 75 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 76 newPod: st.MakePod().Label("service", "securityscan").Obj(), 77 oldPod: st.MakePod().Label("service", "securityscan").Obj(), 78 expectedHint: framework.QueueSkip, 79 }, 80 { 81 name: "update a pod from match to non-match the pod affinity", 82 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 83 newPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 84 oldPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 85 expectedHint: framework.QueueSkip, 86 }, 87 { 88 name: "update a pod from match to non-match pod's affinity - multiple terms case", 89 pod: st.MakePod().Name("p").PodAffinityExists("aaa", "hostname", st.PodAffinityWithRequiredReq). 90 PodAffinityExists("bbb", "hostname", st.PodAffinityWithRequiredReq).Obj(), 91 newPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 92 oldPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 93 expectedHint: framework.QueueSkip, 94 }, 95 { 96 name: "update a pod from non-match to match pod's affinity - multiple terms case", 97 pod: st.MakePod().Name("p").PodAffinityExists("aaa", "hostname", st.PodAffinityWithRequiredReq). 98 PodAffinityExists("bbb", "hostname", st.PodAffinityWithRequiredReq).Obj(), 99 newPod: st.MakePod().Node("fake-node").Label("aaa", "").Label("bbb", "").Obj(), 100 oldPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 101 expectedHint: framework.Queue, 102 }, 103 { 104 name: "modify pod label to change it from satisfying pod anti-affinity to not satisfying anti-affinity", 105 pod: st.MakePod().Name("p").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), 106 newPod: st.MakePod().Node("fake-node").Label("service", "aaaa").Obj(), 107 oldPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 108 expectedHint: framework.Queue, 109 }, 110 { 111 name: "modify pod label to change it from not satisfying pod anti-affinity to satisfying anti-affinity", 112 pod: st.MakePod().Name("p").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), 113 newPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 114 oldPod: st.MakePod().Node("fake-node").Label("service", "bbb").Obj(), 115 expectedHint: framework.QueueSkip, 116 }, 117 { 118 name: "delete a pod which doesn't match pod's anti-affinity", 119 pod: st.MakePod().Name("p").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), 120 oldPod: st.MakePod().Node("fake-node").Label("aaa", "a").Obj(), 121 expectedHint: framework.QueueSkip, 122 }, 123 { 124 name: "delete a pod which matches pod's anti-affinity", 125 pod: st.MakePod().Name("p").PodAntiAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAntiAffinityWithRequiredReq).Obj(), 126 oldPod: st.MakePod().Node("fake-node").Label("service", "securityscan").Obj(), 127 expectedHint: framework.Queue, 128 }, 129 } 130 for _, tc := range tests { 131 t.Run(tc.name, func(t *testing.T) { 132 logger, ctx := ktesting.NewTestContext(t) 133 ctx, cancel := context.WithCancel(ctx) 134 defer cancel() 135 136 snapshot := cache.NewSnapshot(nil, nil) 137 pl := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{}, snapshot, namespaces) 138 p := pl.(*InterPodAffinity) 139 actualHint, err := p.isSchedulableAfterPodChange(logger, tc.pod, tc.oldPod, tc.newPod) 140 if err != nil { 141 t.Errorf("unexpected error: %v", err) 142 } 143 if diff := cmp.Diff(tc.expectedHint, actualHint); diff != "" { 144 t.Errorf("expected QueuingHint doesn't match (-want,+got): \n %s", diff) 145 } 146 }) 147 } 148 } 149 150 func Test_isSchedulableAfterNodeChange(t *testing.T) { 151 testcases := []struct { 152 name string 153 pod *v1.Pod 154 oldNode, newNode *v1.Node 155 expectedHint framework.QueueingHint 156 }{ 157 { 158 name: "add a new node with matched pod affinity topologyKey", 159 pod: st.MakePod().Name("p").PodAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 160 newNode: st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(), 161 expectedHint: framework.Queue, 162 }, 163 { 164 name: "add a new node with matched pod anti-affinity topologyKey", 165 pod: st.MakePod().Name("p").PodAntiAffinity("zone", &metav1.LabelSelector{MatchLabels: map[string]string{"another": "label"}}, st.PodAntiAffinityWithRequiredReq).Obj(), 166 newNode: st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(), 167 expectedHint: framework.Queue, 168 }, 169 { 170 name: "add a new node without matched topologyKey", 171 pod: st.MakePod().Name("p").PodAffinityIn("service", "region", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 172 newNode: st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(), 173 expectedHint: framework.QueueSkip, 174 }, 175 { 176 name: "update node topologyKey", 177 pod: st.MakePod().Name("p").PodAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 178 oldNode: st.MakeNode().Name("node-a").Label("zone", "zone1").Obj(), 179 newNode: st.MakeNode().Name("node-a").Label("zone", "zone2").Obj(), 180 expectedHint: framework.Queue, 181 }, 182 { 183 name: "update node lable but not topologyKey", 184 pod: st.MakePod().Name("p").PodAffinityIn("service", "zone", []string{"securityscan", "value2"}, st.PodAffinityWithRequiredReq).Obj(), 185 oldNode: st.MakeNode().Name("node-a").Label("aaa", "a").Obj(), 186 newNode: st.MakeNode().Name("node-a").Label("aaa", "b").Obj(), 187 expectedHint: framework.QueueSkip, 188 }, 189 } 190 for _, tc := range testcases { 191 t.Run(tc.name, func(t *testing.T) { 192 logger, ctx := ktesting.NewTestContext(t) 193 ctx, cancel := context.WithCancel(ctx) 194 defer cancel() 195 196 snapshot := cache.NewSnapshot(nil, nil) 197 pl := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{}, snapshot, namespaces) 198 p := pl.(*InterPodAffinity) 199 actualHint, err := p.isSchedulableAfterNodeChange(logger, tc.pod, tc.oldNode, tc.newNode) 200 if err != nil { 201 t.Errorf("unexpected error: %v", err) 202 } 203 if diff := cmp.Diff(tc.expectedHint, actualHint); diff != "" { 204 t.Errorf("expected QueuingHint doesn't match (-want,+got): \n %s", diff) 205 } 206 }) 207 } 208 }