1
16
17 package profile
18
19 import (
20 "context"
21 "fmt"
22 "strings"
23 "testing"
24
25 v1 "k8s.io/api/core/v1"
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/client-go/tools/events"
28 "k8s.io/klog/v2/ktesting"
29 "k8s.io/kubernetes/pkg/scheduler/apis/config"
30 "k8s.io/kubernetes/pkg/scheduler/framework"
31 frameworkruntime "k8s.io/kubernetes/pkg/scheduler/framework/runtime"
32 )
33
34 var fakeRegistry = frameworkruntime.Registry{
35 "QueueSort": newFakePlugin("QueueSort"),
36 "Bind1": newFakePlugin("Bind1"),
37 "Bind2": newFakePlugin("Bind2"),
38 "Another": newFakePlugin("Another"),
39 }
40
41 func TestNewMap(t *testing.T) {
42 cases := []struct {
43 name string
44 cfgs []config.KubeSchedulerProfile
45 wantErr string
46 }{
47 {
48 name: "valid",
49 cfgs: []config.KubeSchedulerProfile{
50 {
51 SchedulerName: "profile-1",
52 Plugins: &config.Plugins{
53 QueueSort: config.PluginSet{
54 Enabled: []config.Plugin{
55 {Name: "QueueSort"},
56 },
57 },
58 Bind: config.PluginSet{
59 Enabled: []config.Plugin{
60 {Name: "Bind1"},
61 },
62 },
63 },
64 },
65 {
66 SchedulerName: "profile-2",
67 Plugins: &config.Plugins{
68 QueueSort: config.PluginSet{
69 Enabled: []config.Plugin{
70 {Name: "QueueSort"},
71 },
72 },
73 Bind: config.PluginSet{
74 Enabled: []config.Plugin{
75 {Name: "Bind2"},
76 },
77 },
78 },
79 PluginConfig: []config.PluginConfig{
80 {
81 Name: "Bind2",
82 Args: &runtime.Unknown{Raw: []byte("{}")},
83 },
84 },
85 },
86 },
87 },
88 {
89 name: "different queue sort",
90 cfgs: []config.KubeSchedulerProfile{
91 {
92 SchedulerName: "profile-1",
93 Plugins: &config.Plugins{
94 QueueSort: config.PluginSet{
95 Enabled: []config.Plugin{
96 {Name: "QueueSort"},
97 },
98 },
99 Bind: config.PluginSet{
100 Enabled: []config.Plugin{
101 {Name: "Bind1"},
102 },
103 },
104 },
105 },
106 {
107 SchedulerName: "profile-2",
108 Plugins: &config.Plugins{
109 QueueSort: config.PluginSet{
110 Enabled: []config.Plugin{
111 {Name: "Another"},
112 },
113 },
114 Bind: config.PluginSet{
115 Enabled: []config.Plugin{
116 {Name: "Bind2"},
117 },
118 },
119 },
120 },
121 },
122 wantErr: "different queue sort plugins",
123 },
124 {
125 name: "different queue sort args",
126 cfgs: []config.KubeSchedulerProfile{
127 {
128 SchedulerName: "profile-1",
129 Plugins: &config.Plugins{
130 QueueSort: config.PluginSet{
131 Enabled: []config.Plugin{
132 {Name: "QueueSort"},
133 },
134 },
135 Bind: config.PluginSet{
136 Enabled: []config.Plugin{
137 {Name: "Bind1"},
138 },
139 },
140 },
141 PluginConfig: []config.PluginConfig{
142 {
143 Name: "QueueSort",
144 Args: &runtime.Unknown{Raw: []byte("{}")},
145 },
146 },
147 },
148 {
149 SchedulerName: "profile-2",
150 Plugins: &config.Plugins{
151 QueueSort: config.PluginSet{
152 Enabled: []config.Plugin{
153 {Name: "QueueSort"},
154 },
155 },
156 Bind: config.PluginSet{
157 Enabled: []config.Plugin{
158 {Name: "Bind2"},
159 },
160 },
161 },
162 },
163 },
164 wantErr: "different queue sort plugin args",
165 },
166 {
167 name: "duplicate scheduler name",
168 cfgs: []config.KubeSchedulerProfile{
169 {
170 SchedulerName: "profile-1",
171 Plugins: &config.Plugins{
172 QueueSort: config.PluginSet{
173 Enabled: []config.Plugin{
174 {Name: "QueueSort"},
175 },
176 },
177 Bind: config.PluginSet{
178 Enabled: []config.Plugin{
179 {Name: "Bind1"},
180 },
181 },
182 },
183 },
184 {
185 SchedulerName: "profile-1",
186 Plugins: &config.Plugins{
187 QueueSort: config.PluginSet{
188 Enabled: []config.Plugin{
189 {Name: "QueueSort"},
190 },
191 },
192 Bind: config.PluginSet{
193 Enabled: []config.Plugin{
194 {Name: "Bind2"},
195 },
196 },
197 },
198 },
199 },
200 wantErr: "duplicate profile",
201 },
202 {
203 name: "scheduler name is needed",
204 cfgs: []config.KubeSchedulerProfile{
205 {
206 Plugins: &config.Plugins{
207 QueueSort: config.PluginSet{
208 Enabled: []config.Plugin{
209 {Name: "QueueSort"},
210 },
211 },
212 Bind: config.PluginSet{
213 Enabled: []config.Plugin{
214 {Name: "Bind1"},
215 },
216 },
217 },
218 },
219 },
220 wantErr: "scheduler name is needed",
221 },
222 {
223 name: "plugins required for profile",
224 cfgs: []config.KubeSchedulerProfile{
225 {
226 SchedulerName: "profile-1",
227 },
228 },
229 wantErr: "plugins required for profile",
230 },
231 {
232 name: "invalid framework configuration",
233 cfgs: []config.KubeSchedulerProfile{
234 {
235 SchedulerName: "invalid-profile",
236 Plugins: &config.Plugins{
237 QueueSort: config.PluginSet{
238 Enabled: []config.Plugin{
239 {Name: "QueueSort"},
240 },
241 },
242 },
243 },
244 },
245 wantErr: "at least one bind plugin is needed",
246 },
247 }
248 for _, tc := range cases {
249 t.Run(tc.name, func(t *testing.T) {
250 _, ctx := ktesting.NewTestContext(t)
251 ctx, cancel := context.WithCancel(ctx)
252 defer cancel()
253 m, err := NewMap(ctx, tc.cfgs, fakeRegistry, nilRecorderFactory)
254 if err := checkErr(err, tc.wantErr); err != nil {
255 t.Fatal(err)
256 }
257 if len(tc.wantErr) != 0 {
258 return
259 }
260 if len(m) != len(tc.cfgs) {
261 t.Errorf("got %d profiles, want %d", len(m), len(tc.cfgs))
262 }
263 })
264 }
265 }
266
267 type fakePlugin struct {
268 name string
269 }
270
271 func (p *fakePlugin) Name() string {
272 return p.name
273 }
274
275 func (p *fakePlugin) Less(*framework.QueuedPodInfo, *framework.QueuedPodInfo) bool {
276 return false
277 }
278
279 func (p *fakePlugin) Bind(context.Context, *framework.CycleState, *v1.Pod, string) *framework.Status {
280 return nil
281 }
282
283 func newFakePlugin(name string) func(ctx context.Context, object runtime.Object, handle framework.Handle) (framework.Plugin, error) {
284 return func(_ context.Context, _ runtime.Object, _ framework.Handle) (framework.Plugin, error) {
285 return &fakePlugin{name: name}, nil
286 }
287 }
288
289 func nilRecorderFactory(_ string) events.EventRecorder {
290 return nil
291 }
292
293 func checkErr(err error, wantErr string) error {
294 if len(wantErr) == 0 {
295 return err
296 }
297 if err == nil || !strings.Contains(err.Error(), wantErr) {
298 return fmt.Errorf("got error %q, want %q", err, wantErr)
299 }
300 return nil
301 }
302
View as plain text