1
16
17 package topologymanager
18
19 import (
20 "fmt"
21 "strings"
22 "testing"
23
24 "k8s.io/api/core/v1"
25
26 cadvisorapi "github.com/google/cadvisor/info/v1"
27
28 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask"
29 "k8s.io/kubernetes/pkg/kubelet/lifecycle"
30 )
31
32 func NewTestBitMask(sockets ...int) bitmask.BitMask {
33 s, _ := bitmask.NewBitMask(sockets...)
34 return s
35 }
36
37 func TestNewManager(t *testing.T) {
38 tcases := []struct {
39 description string
40 policyName string
41 expectedPolicy string
42 expectedError error
43 topologyError error
44 policyOptions map[string]string
45 topology []cadvisorapi.Node
46 }{
47 {
48 description: "Policy is set to none",
49 policyName: "none",
50 expectedPolicy: "none",
51 },
52 {
53 description: "Policy is set to best-effort",
54 policyName: "best-effort",
55 expectedPolicy: "best-effort",
56 },
57 {
58 description: "Policy is set to restricted",
59 policyName: "restricted",
60 expectedPolicy: "restricted",
61 },
62 {
63 description: "Policy is set to single-numa-node",
64 policyName: "single-numa-node",
65 expectedPolicy: "single-numa-node",
66 },
67 {
68 description: "Policy is set to unknown",
69 policyName: "unknown",
70 expectedError: fmt.Errorf("unknown policy: \"unknown\""),
71 },
72 {
73 description: "Unknown policy name best-effort policy",
74 policyName: "best-effort",
75 expectedPolicy: "best-effort",
76 expectedError: fmt.Errorf("unknown Topology Manager Policy option:"),
77 policyOptions: map[string]string{
78 "unknown-option": "true",
79 },
80 },
81 {
82 description: "Unknown policy name restricted policy",
83 policyName: "restricted",
84 expectedPolicy: "restricted",
85 expectedError: fmt.Errorf("unknown Topology Manager Policy option:"),
86 policyOptions: map[string]string{
87 "unknown-option": "true",
88 },
89 },
90 {
91 description: "can't get NUMA distances",
92 policyName: "best-effort",
93 expectedPolicy: "best-effort",
94 policyOptions: map[string]string{
95 PreferClosestNUMANodes: "true",
96 },
97 expectedError: fmt.Errorf("error getting NUMA distances from cadvisor"),
98 topology: []cadvisorapi.Node{
99 {
100 Id: 0,
101 },
102 },
103 },
104 {
105 description: "more than 8 NUMA nodes",
106 policyName: "best-effort",
107 expectedPolicy: "best-effort",
108 expectedError: fmt.Errorf("unsupported on machines with more than %v NUMA Nodes", maxAllowableNUMANodes),
109 topology: []cadvisorapi.Node{
110 {
111 Id: 0,
112 },
113 {
114 Id: 1,
115 },
116 {
117 Id: 2,
118 },
119 {
120 Id: 3,
121 },
122 {
123 Id: 4,
124 },
125 {
126 Id: 5,
127 },
128 {
129 Id: 6,
130 },
131 {
132 Id: 7,
133 },
134 {
135 Id: 8,
136 },
137 },
138 },
139 }
140
141 for _, tc := range tcases {
142 topology := tc.topology
143
144 mngr, err := NewManager(topology, tc.policyName, "container", tc.policyOptions)
145 if tc.expectedError != nil {
146 if !strings.Contains(err.Error(), tc.expectedError.Error()) {
147 t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tc.expectedError.Error())
148 }
149 } else {
150 rawMgr := mngr.(*manager)
151 var policyName string
152 if rawScope, ok := rawMgr.scope.(*containerScope); ok {
153 policyName = rawScope.policy.Name()
154 } else if rawScope, ok := rawMgr.scope.(*noneScope); ok {
155 policyName = rawScope.policy.Name()
156 }
157 if policyName != tc.expectedPolicy {
158 t.Errorf("Unexpected policy name. Have: %q wants %q", policyName, tc.expectedPolicy)
159 }
160 }
161 }
162 }
163
164 func TestManagerScope(t *testing.T) {
165 tcases := []struct {
166 description string
167 scopeName string
168 expectedScope string
169 expectedError error
170 }{
171 {
172 description: "Topology Manager Scope is set to container",
173 scopeName: "container",
174 expectedScope: "container",
175 },
176 {
177 description: "Topology Manager Scope is set to pod",
178 scopeName: "pod",
179 expectedScope: "pod",
180 },
181 {
182 description: "Topology Manager Scope is set to unknown",
183 scopeName: "unknown",
184 expectedError: fmt.Errorf("unknown scope: \"unknown\""),
185 },
186 }
187
188 for _, tc := range tcases {
189 mngr, err := NewManager(nil, "best-effort", tc.scopeName, nil)
190
191 if tc.expectedError != nil {
192 if !strings.Contains(err.Error(), tc.expectedError.Error()) {
193 t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), tc.expectedError.Error())
194 }
195 } else {
196 rawMgr := mngr.(*manager)
197 if rawMgr.scope.Name() != tc.expectedScope {
198 t.Errorf("Unexpected scope name. Have: %q wants %q", rawMgr.scope, tc.expectedScope)
199 }
200 }
201 }
202 }
203
204 type mockHintProvider struct {
205 th map[string][]TopologyHint
206
207
208
209 }
210
211 func (m *mockHintProvider) GetTopologyHints(pod *v1.Pod, container *v1.Container) map[string][]TopologyHint {
212 return m.th
213 }
214
215 func (m *mockHintProvider) GetPodTopologyHints(pod *v1.Pod) map[string][]TopologyHint {
216 return m.th
217 }
218
219 func (m *mockHintProvider) Allocate(pod *v1.Pod, container *v1.Container) error {
220
221 return nil
222 }
223
224 type mockPolicy struct {
225 nonePolicy
226 ph []map[string][]TopologyHint
227 }
228
229 func (p *mockPolicy) Merge(providersHints []map[string][]TopologyHint) (TopologyHint, bool) {
230 p.ph = providersHints
231 return TopologyHint{}, true
232 }
233
234 func TestAddHintProvider(t *testing.T) {
235 tcases := []struct {
236 name string
237 hp []HintProvider
238 }{
239 {
240 name: "Add HintProvider",
241 hp: []HintProvider{
242 &mockHintProvider{},
243 &mockHintProvider{},
244 &mockHintProvider{},
245 },
246 },
247 }
248 mngr := manager{}
249 mngr.scope = NewContainerScope(NewNonePolicy())
250 for _, tc := range tcases {
251 for _, hp := range tc.hp {
252 mngr.AddHintProvider(hp)
253 }
254 if len(tc.hp) != len(mngr.scope.(*containerScope).hintProviders) {
255 t.Errorf("error")
256 }
257 }
258 }
259
260 func TestAdmit(t *testing.T) {
261 numaInfo := &NUMAInfo{
262 Nodes: []int{0, 1},
263 NUMADistances: NUMADistances{
264 0: {10, 11},
265 1: {11, 10},
266 },
267 }
268
269 opts := PolicyOptions{}
270 bePolicy := NewBestEffortPolicy(numaInfo, opts)
271 restrictedPolicy := NewRestrictedPolicy(numaInfo, opts)
272 singleNumaPolicy := NewSingleNumaNodePolicy(numaInfo, opts)
273
274 tcases := []struct {
275 name string
276 result lifecycle.PodAdmitResult
277 qosClass v1.PodQOSClass
278 policy Policy
279 hp []HintProvider
280 expected bool
281 }{
282 {
283 name: "QOSClass set as BestEffort. None Policy. No Hints.",
284 qosClass: v1.PodQOSBestEffort,
285 policy: NewNonePolicy(),
286 hp: []HintProvider{},
287 expected: true,
288 },
289 {
290 name: "QOSClass set as Guaranteed. None Policy. No Hints.",
291 qosClass: v1.PodQOSGuaranteed,
292 policy: NewNonePolicy(),
293 hp: []HintProvider{},
294 expected: true,
295 },
296 {
297 name: "QOSClass set as BestEffort. single-numa-node Policy. No Hints.",
298 qosClass: v1.PodQOSBestEffort,
299 policy: singleNumaPolicy,
300 hp: []HintProvider{
301 &mockHintProvider{},
302 },
303 expected: true,
304 },
305 {
306 name: "QOSClass set as BestEffort. Restricted Policy. No Hints.",
307 qosClass: v1.PodQOSBestEffort,
308 policy: restrictedPolicy,
309 hp: []HintProvider{
310 &mockHintProvider{},
311 },
312 expected: true,
313 },
314 {
315 name: "QOSClass set as Guaranteed. BestEffort Policy. Preferred Affinity.",
316 qosClass: v1.PodQOSGuaranteed,
317 policy: bePolicy,
318 hp: []HintProvider{
319 &mockHintProvider{
320 map[string][]TopologyHint{
321 "resource": {
322 {
323 NUMANodeAffinity: NewTestBitMask(0),
324 Preferred: true,
325 },
326 {
327 NUMANodeAffinity: NewTestBitMask(0, 1),
328 Preferred: false,
329 },
330 },
331 },
332 },
333 },
334 expected: true,
335 },
336 {
337 name: "QOSClass set as Guaranteed. BestEffort Policy. More than one Preferred Affinity.",
338 qosClass: v1.PodQOSGuaranteed,
339 policy: bePolicy,
340 hp: []HintProvider{
341 &mockHintProvider{
342 map[string][]TopologyHint{
343 "resource": {
344 {
345 NUMANodeAffinity: NewTestBitMask(0),
346 Preferred: true,
347 },
348 {
349 NUMANodeAffinity: NewTestBitMask(1),
350 Preferred: true,
351 },
352 {
353 NUMANodeAffinity: NewTestBitMask(0, 1),
354 Preferred: false,
355 },
356 },
357 },
358 },
359 },
360 expected: true,
361 },
362 {
363 name: "QOSClass set as Burstable. BestEffort Policy. More than one Preferred Affinity.",
364 qosClass: v1.PodQOSBurstable,
365 policy: bePolicy,
366 hp: []HintProvider{
367 &mockHintProvider{
368 map[string][]TopologyHint{
369 "resource": {
370 {
371 NUMANodeAffinity: NewTestBitMask(0),
372 Preferred: true,
373 },
374 {
375 NUMANodeAffinity: NewTestBitMask(1),
376 Preferred: true,
377 },
378 {
379 NUMANodeAffinity: NewTestBitMask(0, 1),
380 Preferred: false,
381 },
382 },
383 },
384 },
385 },
386 expected: true,
387 },
388 {
389 name: "QOSClass set as Guaranteed. BestEffort Policy. No Preferred Affinity.",
390 qosClass: v1.PodQOSGuaranteed,
391 policy: bePolicy,
392 hp: []HintProvider{
393 &mockHintProvider{
394 map[string][]TopologyHint{
395 "resource": {
396 {
397 NUMANodeAffinity: NewTestBitMask(0, 1),
398 Preferred: false,
399 },
400 },
401 },
402 },
403 },
404 expected: true,
405 },
406 {
407 name: "QOSClass set as Guaranteed. Restricted Policy. Preferred Affinity.",
408 qosClass: v1.PodQOSGuaranteed,
409 policy: restrictedPolicy,
410 hp: []HintProvider{
411 &mockHintProvider{
412 map[string][]TopologyHint{
413 "resource": {
414 {
415 NUMANodeAffinity: NewTestBitMask(0),
416 Preferred: true,
417 },
418 {
419 NUMANodeAffinity: NewTestBitMask(0, 1),
420 Preferred: false,
421 },
422 },
423 },
424 },
425 },
426 expected: true,
427 },
428 {
429 name: "QOSClass set as Burstable. Restricted Policy. Preferred Affinity.",
430 qosClass: v1.PodQOSBurstable,
431 policy: restrictedPolicy,
432 hp: []HintProvider{
433 &mockHintProvider{
434 map[string][]TopologyHint{
435 "resource": {
436 {
437 NUMANodeAffinity: NewTestBitMask(0),
438 Preferred: true,
439 },
440 {
441 NUMANodeAffinity: NewTestBitMask(0, 1),
442 Preferred: false,
443 },
444 },
445 },
446 },
447 },
448 expected: true,
449 },
450 {
451 name: "QOSClass set as Guaranteed. Restricted Policy. More than one Preferred affinity.",
452 qosClass: v1.PodQOSGuaranteed,
453 policy: restrictedPolicy,
454 hp: []HintProvider{
455 &mockHintProvider{
456 map[string][]TopologyHint{
457 "resource": {
458 {
459 NUMANodeAffinity: NewTestBitMask(0),
460 Preferred: true,
461 },
462 {
463 NUMANodeAffinity: NewTestBitMask(1),
464 Preferred: true,
465 },
466 {
467 NUMANodeAffinity: NewTestBitMask(0, 1),
468 Preferred: false,
469 },
470 },
471 },
472 },
473 },
474 expected: true,
475 },
476 {
477 name: "QOSClass set as Burstable. Restricted Policy. More than one Preferred affinity.",
478 qosClass: v1.PodQOSBurstable,
479 policy: restrictedPolicy,
480 hp: []HintProvider{
481 &mockHintProvider{
482 map[string][]TopologyHint{
483 "resource": {
484 {
485 NUMANodeAffinity: NewTestBitMask(0),
486 Preferred: true,
487 },
488 {
489 NUMANodeAffinity: NewTestBitMask(1),
490 Preferred: true,
491 },
492 {
493 NUMANodeAffinity: NewTestBitMask(0, 1),
494 Preferred: false,
495 },
496 },
497 },
498 },
499 },
500 expected: true,
501 },
502 {
503 name: "QOSClass set as Guaranteed. Restricted Policy. No Preferred affinity.",
504 qosClass: v1.PodQOSGuaranteed,
505 policy: restrictedPolicy,
506 hp: []HintProvider{
507 &mockHintProvider{
508 map[string][]TopologyHint{
509 "resource": {
510 {
511 NUMANodeAffinity: NewTestBitMask(0, 1),
512 Preferred: false,
513 },
514 },
515 },
516 },
517 },
518 expected: false,
519 },
520 {
521 name: "QOSClass set as Burstable. Restricted Policy. No Preferred affinity.",
522 qosClass: v1.PodQOSBurstable,
523 policy: restrictedPolicy,
524 hp: []HintProvider{
525 &mockHintProvider{
526 map[string][]TopologyHint{
527 "resource": {
528 {
529 NUMANodeAffinity: NewTestBitMask(0, 1),
530 Preferred: false,
531 },
532 },
533 },
534 },
535 },
536 expected: false,
537 },
538 }
539 for _, tc := range tcases {
540 ctnScopeManager := manager{}
541 ctnScopeManager.scope = NewContainerScope(tc.policy)
542 ctnScopeManager.scope.(*containerScope).hintProviders = tc.hp
543
544 podScopeManager := manager{}
545 podScopeManager.scope = NewPodScope(tc.policy)
546 podScopeManager.scope.(*podScope).hintProviders = tc.hp
547
548 pod := &v1.Pod{
549 Spec: v1.PodSpec{
550 Containers: []v1.Container{
551 {
552 Resources: v1.ResourceRequirements{},
553 },
554 },
555 },
556 Status: v1.PodStatus{
557 QOSClass: tc.qosClass,
558 },
559 }
560
561 podAttr := lifecycle.PodAdmitAttributes{
562 Pod: pod,
563 }
564
565
566 ctnActual := ctnScopeManager.Admit(&podAttr)
567 if ctnActual.Admit != tc.expected {
568 t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, ctnActual.Admit)
569 }
570 if !ctnActual.Admit && ctnActual.Reason != ErrorTopologyAffinity {
571 t.Errorf("Error occurred, expected Reason in result to be %v got %v", ErrorTopologyAffinity, ctnActual.Reason)
572 }
573
574
575 podActual := podScopeManager.Admit(&podAttr)
576 if podActual.Admit != tc.expected {
577 t.Errorf("Error occurred, expected Admit in result to be %v got %v", tc.expected, podActual.Admit)
578 }
579 if !ctnActual.Admit && ctnActual.Reason != ErrorTopologyAffinity {
580 t.Errorf("Error occurred, expected Reason in result to be %v got %v", ErrorTopologyAffinity, ctnActual.Reason)
581 }
582 }
583 }
584
View as plain text