k8s.io/kubernetes@v1.29.3/pkg/kubelet/cm/cpumanager/policy_static_test.go (about) 1 /* 2 Copyright 2017 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 cpumanager 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 24 v1 "k8s.io/api/core/v1" 25 utilfeature "k8s.io/apiserver/pkg/util/feature" 26 featuregatetesting "k8s.io/component-base/featuregate/testing" 27 pkgfeatures "k8s.io/kubernetes/pkg/features" 28 "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state" 29 "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology" 30 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager" 31 "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager/bitmask" 32 "k8s.io/utils/cpuset" 33 ) 34 35 type staticPolicyTest struct { 36 description string 37 topo *topology.CPUTopology 38 numReservedCPUs int 39 reservedCPUs *cpuset.CPUSet 40 podUID string 41 options map[string]string 42 containerName string 43 stAssignments state.ContainerCPUAssignments 44 stDefaultCPUSet cpuset.CPUSet 45 pod *v1.Pod 46 topologyHint *topologymanager.TopologyHint 47 expErr error 48 expCPUAlloc bool 49 expCSet cpuset.CPUSet 50 } 51 52 // this is not a real Clone() - hence Pseudo- - because we don't clone some 53 // objects which are accessed read-only 54 func (spt staticPolicyTest) PseudoClone() staticPolicyTest { 55 return staticPolicyTest{ 56 description: spt.description, 57 topo: spt.topo, // accessed in read-only 58 numReservedCPUs: spt.numReservedCPUs, 59 podUID: spt.podUID, 60 options: spt.options, // accessed in read-only 61 containerName: spt.containerName, 62 stAssignments: spt.stAssignments.Clone(), 63 stDefaultCPUSet: spt.stDefaultCPUSet.Clone(), 64 pod: spt.pod, // accessed in read-only 65 expErr: spt.expErr, 66 expCPUAlloc: spt.expCPUAlloc, 67 expCSet: spt.expCSet.Clone(), 68 } 69 } 70 71 func TestStaticPolicyName(t *testing.T) { 72 policy, _ := NewStaticPolicy(topoSingleSocketHT, 1, cpuset.New(), topologymanager.NewFakeManager(), nil) 73 74 policyName := policy.Name() 75 if policyName != "static" { 76 t.Errorf("StaticPolicy Name() error. expected: static, returned: %v", 77 policyName) 78 } 79 } 80 81 func TestStaticPolicyStart(t *testing.T) { 82 testCases := []staticPolicyTest{ 83 { 84 description: "non-corrupted state", 85 topo: topoDualSocketHT, 86 stAssignments: state.ContainerCPUAssignments{ 87 "fakePod": map[string]cpuset.CPUSet{ 88 "0": cpuset.New(0), 89 }, 90 }, 91 stDefaultCPUSet: cpuset.New(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 92 expCSet: cpuset.New(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 93 }, 94 { 95 description: "empty cpuset", 96 topo: topoDualSocketHT, 97 numReservedCPUs: 1, 98 stAssignments: state.ContainerCPUAssignments{}, 99 stDefaultCPUSet: cpuset.New(), 100 expCSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 101 }, 102 { 103 description: "reserved cores 0 & 6 are not present in available cpuset", 104 topo: topoDualSocketHT, 105 numReservedCPUs: 2, 106 stAssignments: state.ContainerCPUAssignments{}, 107 stDefaultCPUSet: cpuset.New(0, 1), 108 expErr: fmt.Errorf("not all reserved cpus: \"0,6\" are present in defaultCpuSet: \"0-1\""), 109 }, 110 { 111 description: "assigned core 2 is still present in available cpuset", 112 topo: topoDualSocketHT, 113 stAssignments: state.ContainerCPUAssignments{ 114 "fakePod": map[string]cpuset.CPUSet{ 115 "0": cpuset.New(0, 1, 2), 116 }, 117 }, 118 stDefaultCPUSet: cpuset.New(2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 119 expErr: fmt.Errorf("pod: fakePod, container: 0 cpuset: \"0-2\" overlaps with default cpuset \"2-11\""), 120 }, 121 { 122 description: "core 12 is not present in topology but is in state cpuset", 123 topo: topoDualSocketHT, 124 stAssignments: state.ContainerCPUAssignments{ 125 "fakePod": map[string]cpuset.CPUSet{ 126 "0": cpuset.New(0, 1, 2), 127 "1": cpuset.New(3, 4), 128 }, 129 }, 130 stDefaultCPUSet: cpuset.New(5, 6, 7, 8, 9, 10, 11, 12), 131 expErr: fmt.Errorf("current set of available CPUs \"0-11\" doesn't match with CPUs in state \"0-12\""), 132 }, 133 { 134 description: "core 11 is present in topology but is not in state cpuset", 135 topo: topoDualSocketHT, 136 stAssignments: state.ContainerCPUAssignments{ 137 "fakePod": map[string]cpuset.CPUSet{ 138 "0": cpuset.New(0, 1, 2), 139 "1": cpuset.New(3, 4), 140 }, 141 }, 142 stDefaultCPUSet: cpuset.New(5, 6, 7, 8, 9, 10), 143 expErr: fmt.Errorf("current set of available CPUs \"0-11\" doesn't match with CPUs in state \"0-10\""), 144 }, 145 } 146 for _, testCase := range testCases { 147 t.Run(testCase.description, func(t *testing.T) { 148 p, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.New(), topologymanager.NewFakeManager(), nil) 149 policy := p.(*staticPolicy) 150 st := &mockState{ 151 assignments: testCase.stAssignments, 152 defaultCPUSet: testCase.stDefaultCPUSet, 153 } 154 err := policy.Start(st) 155 if !reflect.DeepEqual(err, testCase.expErr) { 156 t.Errorf("StaticPolicy Start() error (%v). expected error: %v but got: %v", 157 testCase.description, testCase.expErr, err) 158 } 159 if err != nil { 160 return 161 } 162 163 if !testCase.stDefaultCPUSet.IsEmpty() { 164 for cpuid := 1; cpuid < policy.topology.NumCPUs; cpuid++ { 165 if !st.defaultCPUSet.Contains(cpuid) { 166 t.Errorf("StaticPolicy Start() error. expected cpuid %d to be present in defaultCPUSet", cpuid) 167 } 168 } 169 } 170 if !st.GetDefaultCPUSet().Equals(testCase.expCSet) { 171 t.Errorf("State CPUSet is different than expected. Have %q wants: %q", st.GetDefaultCPUSet(), 172 testCase.expCSet) 173 } 174 175 }) 176 } 177 } 178 179 func TestStaticPolicyAdd(t *testing.T) { 180 var largeTopoCPUids []int 181 var largeTopoSock0CPUids []int 182 var largeTopoSock1CPUids []int 183 largeTopo := *topoQuadSocketFourWayHT 184 for cpuid, val := range largeTopo.CPUDetails { 185 largeTopoCPUids = append(largeTopoCPUids, cpuid) 186 if val.SocketID == 0 { 187 largeTopoSock0CPUids = append(largeTopoSock0CPUids, cpuid) 188 } else if val.SocketID == 1 { 189 largeTopoSock1CPUids = append(largeTopoSock1CPUids, cpuid) 190 } 191 } 192 largeTopoCPUSet := cpuset.New(largeTopoCPUids...) 193 largeTopoSock0CPUSet := cpuset.New(largeTopoSock0CPUids...) 194 largeTopoSock1CPUSet := cpuset.New(largeTopoSock1CPUids...) 195 196 // these are the cases which must behave the same regardless the policy options. 197 // So we will permutate the options to ensure this holds true. 198 199 optionsInsensitiveTestCases := []staticPolicyTest{ 200 { 201 description: "GuPodMultipleCores, SingleSocketHT, ExpectAllocOneCore", 202 topo: topoSingleSocketHT, 203 numReservedCPUs: 1, 204 stAssignments: state.ContainerCPUAssignments{ 205 "fakePod": map[string]cpuset.CPUSet{ 206 "fakeContainer100": cpuset.New(2, 3, 6, 7), 207 }, 208 }, 209 stDefaultCPUSet: cpuset.New(0, 1, 4, 5), 210 pod: makePod("fakePod", "fakeContainer3", "2000m", "2000m"), 211 expErr: nil, 212 expCPUAlloc: true, 213 expCSet: cpuset.New(1, 5), 214 }, 215 { 216 description: "GuPodMultipleCores, DualSocketHT, ExpectAllocOneSocket", 217 topo: topoDualSocketHT, 218 numReservedCPUs: 1, 219 stAssignments: state.ContainerCPUAssignments{ 220 "fakePod": map[string]cpuset.CPUSet{ 221 "fakeContainer100": cpuset.New(2), 222 }, 223 }, 224 stDefaultCPUSet: cpuset.New(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11), 225 pod: makePod("fakePod", "fakeContainer3", "6000m", "6000m"), 226 expErr: nil, 227 expCPUAlloc: true, 228 expCSet: cpuset.New(1, 3, 5, 7, 9, 11), 229 }, 230 { 231 description: "GuPodMultipleCores, DualSocketHT, ExpectAllocThreeCores", 232 topo: topoDualSocketHT, 233 numReservedCPUs: 1, 234 stAssignments: state.ContainerCPUAssignments{ 235 "fakePod": map[string]cpuset.CPUSet{ 236 "fakeContainer100": cpuset.New(1, 5), 237 }, 238 }, 239 stDefaultCPUSet: cpuset.New(0, 2, 3, 4, 6, 7, 8, 9, 10, 11), 240 pod: makePod("fakePod", "fakeContainer3", "6000m", "6000m"), 241 expErr: nil, 242 expCPUAlloc: true, 243 expCSet: cpuset.New(2, 3, 4, 8, 9, 10), 244 }, 245 { 246 description: "GuPodMultipleCores, DualSocketNoHT, ExpectAllocOneSocket", 247 topo: topoDualSocketNoHT, 248 numReservedCPUs: 1, 249 stAssignments: state.ContainerCPUAssignments{ 250 "fakePod": map[string]cpuset.CPUSet{ 251 "fakeContainer100": cpuset.New(), 252 }, 253 }, 254 stDefaultCPUSet: cpuset.New(0, 1, 3, 4, 5, 6, 7), 255 pod: makePod("fakePod", "fakeContainer1", "4000m", "4000m"), 256 expErr: nil, 257 expCPUAlloc: true, 258 expCSet: cpuset.New(4, 5, 6, 7), 259 }, 260 { 261 description: "GuPodMultipleCores, DualSocketNoHT, ExpectAllocFourCores", 262 topo: topoDualSocketNoHT, 263 numReservedCPUs: 1, 264 stAssignments: state.ContainerCPUAssignments{ 265 "fakePod": map[string]cpuset.CPUSet{ 266 "fakeContainer100": cpuset.New(4, 5), 267 }, 268 }, 269 stDefaultCPUSet: cpuset.New(0, 1, 3, 6, 7), 270 pod: makePod("fakePod", "fakeContainer1", "4000m", "4000m"), 271 expErr: nil, 272 expCPUAlloc: true, 273 expCSet: cpuset.New(1, 3, 6, 7), 274 }, 275 { 276 description: "GuPodMultipleCores, DualSocketHT, ExpectAllocOneSocketOneCore", 277 topo: topoDualSocketHT, 278 numReservedCPUs: 1, 279 stAssignments: state.ContainerCPUAssignments{ 280 "fakePod": map[string]cpuset.CPUSet{ 281 "fakeContainer100": cpuset.New(2), 282 }, 283 }, 284 stDefaultCPUSet: cpuset.New(0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11), 285 pod: makePod("fakePod", "fakeContainer3", "8000m", "8000m"), 286 expErr: nil, 287 expCPUAlloc: true, 288 expCSet: cpuset.New(1, 3, 4, 5, 7, 9, 10, 11), 289 }, 290 { 291 description: "NonGuPod, SingleSocketHT, NoAlloc", 292 topo: topoSingleSocketHT, 293 numReservedCPUs: 1, 294 stAssignments: state.ContainerCPUAssignments{}, 295 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 296 pod: makePod("fakePod", "fakeContainer1", "1000m", "2000m"), 297 expErr: nil, 298 expCPUAlloc: false, 299 expCSet: cpuset.New(), 300 }, 301 { 302 description: "GuPodNonIntegerCore, SingleSocketHT, NoAlloc", 303 topo: topoSingleSocketHT, 304 numReservedCPUs: 1, 305 stAssignments: state.ContainerCPUAssignments{}, 306 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 307 pod: makePod("fakePod", "fakeContainer4", "977m", "977m"), 308 expErr: nil, 309 expCPUAlloc: false, 310 expCSet: cpuset.New(), 311 }, 312 { 313 // All the CPUs from Socket 0 are available. Some CPUs from each 314 // Socket have been already assigned. 315 // Expect all CPUs from Socket 0. 316 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocSock0", 317 topo: topoQuadSocketFourWayHT, 318 stAssignments: state.ContainerCPUAssignments{ 319 "fakePod": map[string]cpuset.CPUSet{ 320 "fakeContainer100": cpuset.New(3, 11, 4, 5, 6, 7), 321 }, 322 }, 323 stDefaultCPUSet: largeTopoCPUSet.Difference(cpuset.New(3, 11, 4, 5, 6, 7)), 324 pod: makePod("fakePod", "fakeContainer5", "72000m", "72000m"), 325 expErr: nil, 326 expCPUAlloc: true, 327 expCSet: largeTopoSock0CPUSet, 328 }, 329 { 330 // Only 2 full cores from three Sockets and some partial cores are available. 331 // Expect CPUs from the 2 full cores available from the three Sockets. 332 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllFullCoresFromThreeSockets", 333 topo: topoQuadSocketFourWayHT, 334 stAssignments: state.ContainerCPUAssignments{ 335 "fakePod": map[string]cpuset.CPUSet{ 336 "fakeContainer100": largeTopoCPUSet.Difference(cpuset.New(1, 25, 13, 38, 2, 9, 11, 35, 23, 48, 12, 51, 337 53, 173, 113, 233, 54, 61)), 338 }, 339 }, 340 stDefaultCPUSet: cpuset.New(1, 25, 13, 38, 2, 9, 11, 35, 23, 48, 12, 51, 53, 173, 113, 233, 54, 61), 341 pod: makePod("fakePod", "fakeCcontainer5", "12000m", "12000m"), 342 expErr: nil, 343 expCPUAlloc: true, 344 expCSet: cpuset.New(1, 25, 13, 38, 11, 35, 23, 48, 53, 173, 113, 233), 345 }, 346 { 347 // All CPUs from Socket 1, 1 full core and some partial cores are available. 348 // Expect all CPUs from Socket 1 and the hyper-threads from the full core. 349 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocAllSock1+FullCore", 350 topo: topoQuadSocketFourWayHT, 351 stAssignments: state.ContainerCPUAssignments{ 352 "fakePod": map[string]cpuset.CPUSet{ 353 "fakeContainer100": largeTopoCPUSet.Difference(largeTopoSock1CPUSet.Union(cpuset.New(10, 34, 22, 47, 53, 354 173, 61, 181, 108, 228, 115, 235))), 355 }, 356 }, 357 stDefaultCPUSet: largeTopoSock1CPUSet.Union(cpuset.New(10, 34, 22, 47, 53, 173, 61, 181, 108, 228, 358 115, 235)), 359 pod: makePod("fakePod", "fakeContainer5", "76000m", "76000m"), 360 expErr: nil, 361 expCPUAlloc: true, 362 expCSet: largeTopoSock1CPUSet.Union(cpuset.New(10, 34, 22, 47)), 363 }, 364 } 365 366 // testcases for the default behaviour of the policy. 367 defaultOptionsTestCases := []staticPolicyTest{ 368 { 369 description: "GuPodSingleCore, SingleSocketHT, ExpectAllocOneCPU", 370 topo: topoSingleSocketHT, 371 numReservedCPUs: 1, 372 stAssignments: state.ContainerCPUAssignments{}, 373 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 374 pod: makePod("fakePod", "fakeContainer2", "1000m", "1000m"), 375 expErr: nil, 376 expCPUAlloc: true, 377 expCSet: cpuset.New(4), // expect sibling of partial core 378 }, 379 { 380 // Only partial cores are available in the entire system. 381 // Expect allocation of all the CPUs from the partial cores. 382 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocCPUs", 383 topo: topoQuadSocketFourWayHT, 384 stAssignments: state.ContainerCPUAssignments{ 385 "fakePod": map[string]cpuset.CPUSet{ 386 "fakeContainer100": largeTopoCPUSet.Difference(cpuset.New(10, 11, 53, 37, 55, 67, 52)), 387 }, 388 }, 389 stDefaultCPUSet: cpuset.New(10, 11, 53, 67, 52), 390 pod: makePod("fakePod", "fakeContainer5", "5000m", "5000m"), 391 expErr: nil, 392 expCPUAlloc: true, 393 expCSet: cpuset.New(10, 11, 53, 67, 52), 394 }, 395 { 396 description: "GuPodSingleCore, SingleSocketHT, ExpectError", 397 topo: topoSingleSocketHT, 398 numReservedCPUs: 1, 399 stAssignments: state.ContainerCPUAssignments{}, 400 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 401 pod: makePod("fakePod", "fakeContainer2", "8000m", "8000m"), 402 expErr: fmt.Errorf("not enough cpus available to satisfy request: requested=8, available=7"), 403 expCPUAlloc: false, 404 expCSet: cpuset.New(), 405 }, 406 { 407 description: "GuPodMultipleCores, SingleSocketHT, ExpectSameAllocation", 408 topo: topoSingleSocketHT, 409 numReservedCPUs: 1, 410 stAssignments: state.ContainerCPUAssignments{ 411 "fakePod": map[string]cpuset.CPUSet{ 412 "fakeContainer3": cpuset.New(2, 3, 6, 7), 413 }, 414 }, 415 stDefaultCPUSet: cpuset.New(0, 1, 4, 5), 416 pod: makePod("fakePod", "fakeContainer3", "4000m", "4000m"), 417 expErr: nil, 418 expCPUAlloc: true, 419 expCSet: cpuset.New(2, 3, 6, 7), 420 }, 421 { 422 description: "GuPodMultipleCores, DualSocketHT, NoAllocExpectError", 423 topo: topoDualSocketHT, 424 numReservedCPUs: 1, 425 stAssignments: state.ContainerCPUAssignments{ 426 "fakePod": map[string]cpuset.CPUSet{ 427 "fakeContainer100": cpuset.New(1, 2, 3), 428 }, 429 }, 430 stDefaultCPUSet: cpuset.New(0, 4, 5, 6, 7, 8, 9, 10, 11), 431 pod: makePod("fakePod", "fakeContainer5", "10000m", "10000m"), 432 expErr: fmt.Errorf("not enough cpus available to satisfy request: requested=10, available=8"), 433 expCPUAlloc: false, 434 expCSet: cpuset.New(), 435 }, 436 { 437 description: "GuPodMultipleCores, SingleSocketHT, NoAllocExpectError", 438 topo: topoSingleSocketHT, 439 numReservedCPUs: 1, 440 stAssignments: state.ContainerCPUAssignments{ 441 "fakePod": map[string]cpuset.CPUSet{ 442 "fakeContainer100": cpuset.New(1, 2, 3, 4, 5, 6), 443 }, 444 }, 445 stDefaultCPUSet: cpuset.New(0, 7), 446 pod: makePod("fakePod", "fakeContainer5", "2000m", "2000m"), 447 expErr: fmt.Errorf("not enough cpus available to satisfy request: requested=2, available=1"), 448 expCPUAlloc: false, 449 expCSet: cpuset.New(), 450 }, 451 { 452 // Only 7 CPUs are available. 453 // Pod requests 76 cores. 454 // Error is expected since available CPUs are less than the request. 455 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, NoAlloc", 456 topo: topoQuadSocketFourWayHT, 457 stAssignments: state.ContainerCPUAssignments{ 458 "fakePod": map[string]cpuset.CPUSet{ 459 "fakeContainer100": largeTopoCPUSet.Difference(cpuset.New(10, 11, 53, 37, 55, 67, 52)), 460 }, 461 }, 462 stDefaultCPUSet: cpuset.New(10, 11, 53, 37, 55, 67, 52), 463 pod: makePod("fakePod", "fakeContainer5", "76000m", "76000m"), 464 expErr: fmt.Errorf("not enough cpus available to satisfy request: requested=76, available=7"), 465 expCPUAlloc: false, 466 expCSet: cpuset.New(), 467 }, 468 } 469 470 // testcases for the FullPCPUsOnlyOption 471 smtalignOptionTestCases := []staticPolicyTest{ 472 { 473 description: "GuPodSingleCore, SingleSocketHT, ExpectAllocOneCPU", 474 topo: topoSingleSocketHT, 475 options: map[string]string{ 476 FullPCPUsOnlyOption: "true", 477 }, 478 numReservedCPUs: 1, 479 stAssignments: state.ContainerCPUAssignments{}, 480 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 481 pod: makePod("fakePod", "fakeContainer2", "1000m", "1000m"), 482 expErr: SMTAlignmentError{RequestedCPUs: 1, CpusPerCore: 2}, 483 expCPUAlloc: false, 484 expCSet: cpuset.New(), // reject allocation of sibling of partial core 485 }, 486 { 487 // test SMT-level != 2 - which is the default on x86_64 488 description: "GuPodMultipleCores, topoQuadSocketFourWayHT, ExpectAllocOneCPUs", 489 topo: topoQuadSocketFourWayHT, 490 options: map[string]string{ 491 FullPCPUsOnlyOption: "true", 492 }, 493 numReservedCPUs: 8, 494 stAssignments: state.ContainerCPUAssignments{}, 495 stDefaultCPUSet: largeTopoCPUSet, 496 pod: makePod("fakePod", "fakeContainer15", "15000m", "15000m"), 497 expErr: SMTAlignmentError{RequestedCPUs: 15, CpusPerCore: 4}, 498 expCPUAlloc: false, 499 expCSet: cpuset.New(), 500 }, 501 { 502 description: "GuPodManyCores, topoDualSocketHT, ExpectDoNotAllocPartialCPU", 503 topo: topoDualSocketHT, 504 options: map[string]string{ 505 FullPCPUsOnlyOption: "true", 506 }, 507 numReservedCPUs: 2, 508 reservedCPUs: newCPUSetPtr(1, 6), 509 stAssignments: state.ContainerCPUAssignments{}, 510 stDefaultCPUSet: cpuset.New(0, 2, 3, 4, 5, 7, 8, 9, 10, 11), 511 pod: makePod("fakePod", "fakeContainerBug113537_1", "10000m", "10000m"), 512 expErr: SMTAlignmentError{RequestedCPUs: 10, CpusPerCore: 2, AvailablePhysicalCPUs: 8}, 513 expCPUAlloc: false, 514 expCSet: cpuset.New(), 515 }, 516 { 517 description: "GuPodManyCores, topoDualSocketHT, AutoReserve, ExpectAllocAllCPUs", 518 topo: topoDualSocketHT, 519 options: map[string]string{ 520 FullPCPUsOnlyOption: "true", 521 }, 522 numReservedCPUs: 2, 523 stAssignments: state.ContainerCPUAssignments{}, 524 stDefaultCPUSet: cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 525 pod: makePod("fakePod", "fakeContainerBug113537_2", "10000m", "10000m"), 526 expErr: nil, 527 expCPUAlloc: true, 528 expCSet: cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 529 }, 530 { 531 description: "GuPodManyCores, topoDualSocketHT, ExpectAllocAllCPUs", 532 topo: topoDualSocketHT, 533 options: map[string]string{ 534 FullPCPUsOnlyOption: "true", 535 }, 536 numReservedCPUs: 2, 537 reservedCPUs: newCPUSetPtr(0, 6), 538 stAssignments: state.ContainerCPUAssignments{}, 539 stDefaultCPUSet: cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 540 pod: makePod("fakePod", "fakeContainerBug113537_2", "10000m", "10000m"), 541 expErr: nil, 542 expCPUAlloc: true, 543 expCSet: cpuset.New(1, 2, 3, 4, 5, 7, 8, 9, 10, 11), 544 }, 545 } 546 newNUMAAffinity := func(bits ...int) bitmask.BitMask { 547 affinity, _ := bitmask.NewBitMask(bits...) 548 return affinity 549 } 550 alignBySocketOptionTestCases := []staticPolicyTest{ 551 { 552 description: "Align by socket: true, cpu's within same socket of numa in hint are part of allocation", 553 topo: topoDualSocketMultiNumaPerSocketHT, 554 options: map[string]string{ 555 AlignBySocketOption: "true", 556 }, 557 numReservedCPUs: 1, 558 stAssignments: state.ContainerCPUAssignments{}, 559 stDefaultCPUSet: cpuset.New(2, 11, 21, 22), 560 pod: makePod("fakePod", "fakeContainer2", "2000m", "2000m"), 561 topologyHint: &topologymanager.TopologyHint{NUMANodeAffinity: newNUMAAffinity(0, 2), Preferred: true}, 562 expErr: nil, 563 expCPUAlloc: true, 564 expCSet: cpuset.New(2, 11), 565 }, 566 { 567 description: "Align by socket: false, cpu's are taken strictly from NUMA nodes in hint", 568 topo: topoDualSocketMultiNumaPerSocketHT, 569 options: map[string]string{ 570 AlignBySocketOption: "false", 571 }, 572 numReservedCPUs: 1, 573 stAssignments: state.ContainerCPUAssignments{}, 574 stDefaultCPUSet: cpuset.New(2, 11, 21, 22), 575 pod: makePod("fakePod", "fakeContainer2", "2000m", "2000m"), 576 topologyHint: &topologymanager.TopologyHint{NUMANodeAffinity: newNUMAAffinity(0, 2), Preferred: true}, 577 expErr: nil, 578 expCPUAlloc: true, 579 expCSet: cpuset.New(2, 21), 580 }, 581 } 582 583 for _, testCase := range optionsInsensitiveTestCases { 584 for _, options := range []map[string]string{ 585 nil, 586 { 587 FullPCPUsOnlyOption: "true", 588 }, 589 } { 590 tCase := testCase.PseudoClone() 591 tCase.description = fmt.Sprintf("options=%v %s", options, testCase.description) 592 tCase.options = options 593 runStaticPolicyTestCase(t, tCase) 594 } 595 } 596 597 for _, testCase := range defaultOptionsTestCases { 598 runStaticPolicyTestCase(t, testCase) 599 } 600 for _, testCase := range smtalignOptionTestCases { 601 runStaticPolicyTestCase(t, testCase) 602 } 603 for _, testCase := range alignBySocketOptionTestCases { 604 runStaticPolicyTestCaseWithFeatureGate(t, testCase) 605 } 606 } 607 608 func runStaticPolicyTestCase(t *testing.T, testCase staticPolicyTest) { 609 tm := topologymanager.NewFakeManager() 610 if testCase.topologyHint != nil { 611 tm = topologymanager.NewFakeManagerWithHint(testCase.topologyHint) 612 } 613 cpus := cpuset.New() 614 if testCase.reservedCPUs != nil { 615 cpus = testCase.reservedCPUs.Clone() 616 } 617 policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpus, tm, testCase.options) 618 619 st := &mockState{ 620 assignments: testCase.stAssignments, 621 defaultCPUSet: testCase.stDefaultCPUSet, 622 } 623 624 container := &testCase.pod.Spec.Containers[0] 625 err := policy.Allocate(st, testCase.pod, container) 626 if !reflect.DeepEqual(err, testCase.expErr) { 627 t.Errorf("StaticPolicy Allocate() error (%v). expected add error: %q but got: %q", 628 testCase.description, testCase.expErr, err) 629 } 630 631 if testCase.expCPUAlloc { 632 cset, found := st.assignments[string(testCase.pod.UID)][container.Name] 633 if !found { 634 t.Errorf("StaticPolicy Allocate() error (%v). expected container %v to be present in assignments %v", 635 testCase.description, container.Name, st.assignments) 636 } 637 638 if !reflect.DeepEqual(cset, testCase.expCSet) { 639 t.Errorf("StaticPolicy Allocate() error (%v). expected cpuset %v but got %v", 640 testCase.description, testCase.expCSet, cset) 641 } 642 643 if !cset.Intersection(st.defaultCPUSet).IsEmpty() { 644 t.Errorf("StaticPolicy Allocate() error (%v). expected cpuset %v to be disoint from the shared cpuset %v", 645 testCase.description, cset, st.defaultCPUSet) 646 } 647 } 648 649 if !testCase.expCPUAlloc { 650 _, found := st.assignments[string(testCase.pod.UID)][container.Name] 651 if found { 652 t.Errorf("StaticPolicy Allocate() error (%v). Did not expect container %v to be present in assignments %v", 653 testCase.description, container.Name, st.assignments) 654 } 655 } 656 } 657 658 func runStaticPolicyTestCaseWithFeatureGate(t *testing.T, testCase staticPolicyTest) { 659 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, pkgfeatures.CPUManagerPolicyAlphaOptions, true)() 660 runStaticPolicyTestCase(t, testCase) 661 } 662 663 func TestStaticPolicyReuseCPUs(t *testing.T) { 664 testCases := []struct { 665 staticPolicyTest 666 expCSetAfterAlloc cpuset.CPUSet 667 expCSetAfterRemove cpuset.CPUSet 668 }{ 669 { 670 staticPolicyTest: staticPolicyTest{ 671 description: "SingleSocketHT, DeAllocOneInitContainer", 672 topo: topoSingleSocketHT, 673 pod: makeMultiContainerPod( 674 []struct{ request, limit string }{ 675 {"4000m", "4000m"}}, // 0, 1, 4, 5 676 []struct{ request, limit string }{ 677 {"2000m", "2000m"}}), // 0, 4 678 containerName: "initContainer-0", 679 stAssignments: state.ContainerCPUAssignments{}, 680 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 681 }, 682 expCSetAfterAlloc: cpuset.New(2, 3, 6, 7), 683 expCSetAfterRemove: cpuset.New(1, 2, 3, 5, 6, 7), 684 }, 685 } 686 687 for _, testCase := range testCases { 688 policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.New(), topologymanager.NewFakeManager(), nil) 689 690 st := &mockState{ 691 assignments: testCase.stAssignments, 692 defaultCPUSet: testCase.stDefaultCPUSet, 693 } 694 pod := testCase.pod 695 696 // allocate 697 for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) { 698 policy.Allocate(st, pod, &container) 699 } 700 if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSetAfterAlloc) { 701 t.Errorf("StaticPolicy Allocate() error (%v). expected default cpuset %v but got %v", 702 testCase.description, testCase.expCSetAfterAlloc, st.defaultCPUSet) 703 } 704 705 // remove 706 policy.RemoveContainer(st, string(pod.UID), testCase.containerName) 707 708 if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSetAfterRemove) { 709 t.Errorf("StaticPolicy RemoveContainer() error (%v). expected default cpuset %v but got %v", 710 testCase.description, testCase.expCSetAfterRemove, st.defaultCPUSet) 711 } 712 if _, found := st.assignments[string(pod.UID)][testCase.containerName]; found { 713 t.Errorf("StaticPolicy RemoveContainer() error (%v). expected (pod %v, container %v) not be in assignments %v", 714 testCase.description, testCase.podUID, testCase.containerName, st.assignments) 715 } 716 } 717 } 718 719 func TestStaticPolicyDoNotReuseCPUs(t *testing.T) { 720 testCases := []struct { 721 staticPolicyTest 722 expCSetAfterAlloc cpuset.CPUSet 723 }{ 724 { 725 staticPolicyTest: staticPolicyTest{ 726 description: "SingleSocketHT, Don't reuse CPUs of a restartable init container", 727 topo: topoSingleSocketHT, 728 pod: makeMultiContainerPodWithOptions( 729 []*containerOptions{ 730 {request: "4000m", limit: "4000m", restartPolicy: v1.ContainerRestartPolicyAlways}}, // 0, 1, 4, 5 731 []*containerOptions{ 732 {request: "2000m", limit: "2000m"}}), // 2, 6 733 stAssignments: state.ContainerCPUAssignments{}, 734 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 735 }, 736 expCSetAfterAlloc: cpuset.New(3, 7), 737 }, 738 } 739 740 for _, testCase := range testCases { 741 policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.New(), topologymanager.NewFakeManager(), nil) 742 743 st := &mockState{ 744 assignments: testCase.stAssignments, 745 defaultCPUSet: testCase.stDefaultCPUSet, 746 } 747 pod := testCase.pod 748 749 // allocate 750 for _, container := range append(pod.Spec.InitContainers, pod.Spec.Containers...) { 751 err := policy.Allocate(st, pod, &container) 752 if err != nil { 753 t.Errorf("StaticPolicy Allocate() error (%v). expected no error but got %v", 754 testCase.description, err) 755 } 756 } 757 if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSetAfterAlloc) { 758 t.Errorf("StaticPolicy Allocate() error (%v). expected default cpuset %v but got %v", 759 testCase.description, testCase.expCSetAfterAlloc, st.defaultCPUSet) 760 } 761 } 762 } 763 764 func TestStaticPolicyRemove(t *testing.T) { 765 testCases := []staticPolicyTest{ 766 { 767 description: "SingleSocketHT, DeAllocOneContainer", 768 topo: topoSingleSocketHT, 769 podUID: "fakePod", 770 containerName: "fakeContainer1", 771 stAssignments: state.ContainerCPUAssignments{ 772 "fakePod": map[string]cpuset.CPUSet{ 773 "fakeContainer1": cpuset.New(1, 2, 3), 774 }, 775 }, 776 stDefaultCPUSet: cpuset.New(4, 5, 6, 7), 777 expCSet: cpuset.New(1, 2, 3, 4, 5, 6, 7), 778 }, 779 { 780 description: "SingleSocketHT, DeAllocOneContainer, BeginEmpty", 781 topo: topoSingleSocketHT, 782 podUID: "fakePod", 783 containerName: "fakeContainer1", 784 stAssignments: state.ContainerCPUAssignments{ 785 "fakePod": map[string]cpuset.CPUSet{ 786 "fakeContainer1": cpuset.New(1, 2, 3), 787 "fakeContainer2": cpuset.New(4, 5, 6, 7), 788 }, 789 }, 790 stDefaultCPUSet: cpuset.New(), 791 expCSet: cpuset.New(1, 2, 3), 792 }, 793 { 794 description: "SingleSocketHT, DeAllocTwoContainer", 795 topo: topoSingleSocketHT, 796 podUID: "fakePod", 797 containerName: "fakeContainer1", 798 stAssignments: state.ContainerCPUAssignments{ 799 "fakePod": map[string]cpuset.CPUSet{ 800 "fakeContainer1": cpuset.New(1, 3, 5), 801 "fakeContainer2": cpuset.New(2, 4), 802 }, 803 }, 804 stDefaultCPUSet: cpuset.New(6, 7), 805 expCSet: cpuset.New(1, 3, 5, 6, 7), 806 }, 807 { 808 description: "SingleSocketHT, NoDeAlloc", 809 topo: topoSingleSocketHT, 810 podUID: "fakePod", 811 containerName: "fakeContainer2", 812 stAssignments: state.ContainerCPUAssignments{ 813 "fakePod": map[string]cpuset.CPUSet{ 814 "fakeContainer1": cpuset.New(1, 3, 5), 815 }, 816 }, 817 stDefaultCPUSet: cpuset.New(2, 4, 6, 7), 818 expCSet: cpuset.New(2, 4, 6, 7), 819 }, 820 } 821 822 for _, testCase := range testCases { 823 policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.New(), topologymanager.NewFakeManager(), nil) 824 825 st := &mockState{ 826 assignments: testCase.stAssignments, 827 defaultCPUSet: testCase.stDefaultCPUSet, 828 } 829 830 policy.RemoveContainer(st, testCase.podUID, testCase.containerName) 831 832 if !reflect.DeepEqual(st.defaultCPUSet, testCase.expCSet) { 833 t.Errorf("StaticPolicy RemoveContainer() error (%v). expected default cpuset %v but got %v", 834 testCase.description, testCase.expCSet, st.defaultCPUSet) 835 } 836 837 if _, found := st.assignments[testCase.podUID][testCase.containerName]; found { 838 t.Errorf("StaticPolicy RemoveContainer() error (%v). expected (pod %v, container %v) not be in assignments %v", 839 testCase.description, testCase.podUID, testCase.containerName, st.assignments) 840 } 841 } 842 } 843 844 func TestTopologyAwareAllocateCPUs(t *testing.T) { 845 testCases := []struct { 846 description string 847 topo *topology.CPUTopology 848 stAssignments state.ContainerCPUAssignments 849 stDefaultCPUSet cpuset.CPUSet 850 numRequested int 851 socketMask bitmask.BitMask 852 expCSet cpuset.CPUSet 853 }{ 854 { 855 description: "Request 2 CPUs, No BitMask", 856 topo: topoDualSocketHT, 857 stAssignments: state.ContainerCPUAssignments{}, 858 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 859 numRequested: 2, 860 socketMask: nil, 861 expCSet: cpuset.New(0, 6), 862 }, 863 { 864 description: "Request 2 CPUs, BitMask on Socket 0", 865 topo: topoDualSocketHT, 866 stAssignments: state.ContainerCPUAssignments{}, 867 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 868 numRequested: 2, 869 socketMask: func() bitmask.BitMask { 870 mask, _ := bitmask.NewBitMask(0) 871 return mask 872 }(), 873 expCSet: cpuset.New(0, 6), 874 }, 875 { 876 description: "Request 2 CPUs, BitMask on Socket 1", 877 topo: topoDualSocketHT, 878 stAssignments: state.ContainerCPUAssignments{}, 879 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 880 numRequested: 2, 881 socketMask: func() bitmask.BitMask { 882 mask, _ := bitmask.NewBitMask(1) 883 return mask 884 }(), 885 expCSet: cpuset.New(1, 7), 886 }, 887 { 888 description: "Request 8 CPUs, BitMask on Socket 0", 889 topo: topoDualSocketHT, 890 stAssignments: state.ContainerCPUAssignments{}, 891 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 892 numRequested: 8, 893 socketMask: func() bitmask.BitMask { 894 mask, _ := bitmask.NewBitMask(0) 895 return mask 896 }(), 897 expCSet: cpuset.New(0, 6, 2, 8, 4, 10, 1, 7), 898 }, 899 { 900 description: "Request 8 CPUs, BitMask on Socket 1", 901 topo: topoDualSocketHT, 902 stAssignments: state.ContainerCPUAssignments{}, 903 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 904 numRequested: 8, 905 socketMask: func() bitmask.BitMask { 906 mask, _ := bitmask.NewBitMask(1) 907 return mask 908 }(), 909 expCSet: cpuset.New(1, 7, 3, 9, 5, 11, 0, 6), 910 }, 911 } 912 for _, tc := range testCases { 913 p, _ := NewStaticPolicy(tc.topo, 0, cpuset.New(), topologymanager.NewFakeManager(), nil) 914 policy := p.(*staticPolicy) 915 st := &mockState{ 916 assignments: tc.stAssignments, 917 defaultCPUSet: tc.stDefaultCPUSet, 918 } 919 err := policy.Start(st) 920 if err != nil { 921 t.Errorf("StaticPolicy Start() error (%v)", err) 922 continue 923 } 924 925 cset, err := policy.allocateCPUs(st, tc.numRequested, tc.socketMask, cpuset.New()) 926 if err != nil { 927 t.Errorf("StaticPolicy allocateCPUs() error (%v). expected CPUSet %v not error %v", 928 tc.description, tc.expCSet, err) 929 continue 930 } 931 932 if !reflect.DeepEqual(tc.expCSet, cset) { 933 t.Errorf("StaticPolicy allocateCPUs() error (%v). expected CPUSet %v but got %v", 934 tc.description, tc.expCSet, cset) 935 } 936 } 937 } 938 939 // above test cases are without kubelet --reserved-cpus cmd option 940 // the following tests are with --reserved-cpus configured 941 type staticPolicyTestWithResvList struct { 942 description string 943 topo *topology.CPUTopology 944 numReservedCPUs int 945 reserved cpuset.CPUSet 946 stAssignments state.ContainerCPUAssignments 947 stDefaultCPUSet cpuset.CPUSet 948 pod *v1.Pod 949 expErr error 950 expNewErr error 951 expCPUAlloc bool 952 expCSet cpuset.CPUSet 953 } 954 955 func TestStaticPolicyStartWithResvList(t *testing.T) { 956 testCases := []staticPolicyTestWithResvList{ 957 { 958 description: "empty cpuset", 959 topo: topoDualSocketHT, 960 numReservedCPUs: 2, 961 reserved: cpuset.New(0, 1), 962 stAssignments: state.ContainerCPUAssignments{}, 963 stDefaultCPUSet: cpuset.New(), 964 expCSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 965 }, 966 { 967 description: "reserved cores 0 & 1 are not present in available cpuset", 968 topo: topoDualSocketHT, 969 numReservedCPUs: 2, 970 reserved: cpuset.New(0, 1), 971 stAssignments: state.ContainerCPUAssignments{}, 972 stDefaultCPUSet: cpuset.New(2, 3, 4, 5), 973 expErr: fmt.Errorf("not all reserved cpus: \"0-1\" are present in defaultCpuSet: \"2-5\""), 974 }, 975 { 976 description: "inconsistency between numReservedCPUs and reserved", 977 topo: topoDualSocketHT, 978 numReservedCPUs: 1, 979 reserved: cpuset.New(0, 1), 980 stAssignments: state.ContainerCPUAssignments{}, 981 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), 982 expNewErr: fmt.Errorf("[cpumanager] unable to reserve the required amount of CPUs (size of 0-1 did not equal 1)"), 983 }, 984 } 985 for _, testCase := range testCases { 986 t.Run(testCase.description, func(t *testing.T) { 987 p, err := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, testCase.reserved, topologymanager.NewFakeManager(), nil) 988 if !reflect.DeepEqual(err, testCase.expNewErr) { 989 t.Errorf("StaticPolicy Start() error (%v). expected error: %v but got: %v", 990 testCase.description, testCase.expNewErr, err) 991 } 992 if err != nil { 993 return 994 } 995 policy := p.(*staticPolicy) 996 st := &mockState{ 997 assignments: testCase.stAssignments, 998 defaultCPUSet: testCase.stDefaultCPUSet, 999 } 1000 err = policy.Start(st) 1001 if !reflect.DeepEqual(err, testCase.expErr) { 1002 t.Errorf("StaticPolicy Start() error (%v). expected error: %v but got: %v", 1003 testCase.description, testCase.expErr, err) 1004 } 1005 if err != nil { 1006 return 1007 } 1008 1009 if !st.GetDefaultCPUSet().Equals(testCase.expCSet) { 1010 t.Errorf("State CPUSet is different than expected. Have %q wants: %q", st.GetDefaultCPUSet(), 1011 testCase.expCSet) 1012 } 1013 1014 }) 1015 } 1016 } 1017 1018 func TestStaticPolicyAddWithResvList(t *testing.T) { 1019 1020 testCases := []staticPolicyTestWithResvList{ 1021 { 1022 description: "GuPodSingleCore, SingleSocketHT, ExpectError", 1023 topo: topoSingleSocketHT, 1024 numReservedCPUs: 1, 1025 reserved: cpuset.New(0), 1026 stAssignments: state.ContainerCPUAssignments{}, 1027 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 1028 pod: makePod("fakePod", "fakeContainer2", "8000m", "8000m"), 1029 expErr: fmt.Errorf("not enough cpus available to satisfy request: requested=8, available=7"), 1030 expCPUAlloc: false, 1031 expCSet: cpuset.New(), 1032 }, 1033 { 1034 description: "GuPodSingleCore, SingleSocketHT, ExpectAllocOneCPU", 1035 topo: topoSingleSocketHT, 1036 numReservedCPUs: 2, 1037 reserved: cpuset.New(0, 1), 1038 stAssignments: state.ContainerCPUAssignments{}, 1039 stDefaultCPUSet: cpuset.New(0, 1, 2, 3, 4, 5, 6, 7), 1040 pod: makePod("fakePod", "fakeContainer2", "1000m", "1000m"), 1041 expErr: nil, 1042 expCPUAlloc: true, 1043 expCSet: cpuset.New(4), // expect sibling of partial core 1044 }, 1045 { 1046 description: "GuPodMultipleCores, SingleSocketHT, ExpectAllocOneCore", 1047 topo: topoSingleSocketHT, 1048 numReservedCPUs: 2, 1049 reserved: cpuset.New(0, 1), 1050 stAssignments: state.ContainerCPUAssignments{ 1051 "fakePod": map[string]cpuset.CPUSet{ 1052 "fakeContainer100": cpuset.New(2, 3, 6, 7), 1053 }, 1054 }, 1055 stDefaultCPUSet: cpuset.New(0, 1, 4, 5), 1056 pod: makePod("fakePod", "fakeContainer3", "2000m", "2000m"), 1057 expErr: nil, 1058 expCPUAlloc: true, 1059 expCSet: cpuset.New(4, 5), 1060 }, 1061 } 1062 1063 for _, testCase := range testCases { 1064 policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, testCase.reserved, topologymanager.NewFakeManager(), nil) 1065 1066 st := &mockState{ 1067 assignments: testCase.stAssignments, 1068 defaultCPUSet: testCase.stDefaultCPUSet, 1069 } 1070 1071 container := &testCase.pod.Spec.Containers[0] 1072 err := policy.Allocate(st, testCase.pod, container) 1073 if !reflect.DeepEqual(err, testCase.expErr) { 1074 t.Errorf("StaticPolicy Allocate() error (%v). expected add error: %v but got: %v", 1075 testCase.description, testCase.expErr, err) 1076 } 1077 1078 if testCase.expCPUAlloc { 1079 cset, found := st.assignments[string(testCase.pod.UID)][container.Name] 1080 if !found { 1081 t.Errorf("StaticPolicy Allocate() error (%v). expected container %v to be present in assignments %v", 1082 testCase.description, container.Name, st.assignments) 1083 } 1084 1085 if !reflect.DeepEqual(cset, testCase.expCSet) { 1086 t.Errorf("StaticPolicy Allocate() error (%v). expected cpuset %v but got %v", 1087 testCase.description, testCase.expCSet, cset) 1088 } 1089 1090 if !cset.Intersection(st.defaultCPUSet).IsEmpty() { 1091 t.Errorf("StaticPolicy Allocate() error (%v). expected cpuset %v to be disoint from the shared cpuset %v", 1092 testCase.description, cset, st.defaultCPUSet) 1093 } 1094 } 1095 1096 if !testCase.expCPUAlloc { 1097 _, found := st.assignments[string(testCase.pod.UID)][container.Name] 1098 if found { 1099 t.Errorf("StaticPolicy Allocate() error (%v). Did not expect container %v to be present in assignments %v", 1100 testCase.description, container.Name, st.assignments) 1101 } 1102 } 1103 } 1104 } 1105 1106 type staticPolicyOptionTestCase struct { 1107 description string 1108 policyOptions map[string]string 1109 expectedError bool 1110 expectedValue StaticPolicyOptions 1111 } 1112 1113 func TestStaticPolicyOptions(t *testing.T) { 1114 testCases := []staticPolicyOptionTestCase{ 1115 { 1116 description: "nil args", 1117 policyOptions: nil, 1118 expectedError: false, 1119 expectedValue: StaticPolicyOptions{}, 1120 }, 1121 { 1122 description: "empty args", 1123 policyOptions: map[string]string{}, 1124 expectedError: false, 1125 expectedValue: StaticPolicyOptions{}, 1126 }, 1127 { 1128 description: "bad single arg", 1129 policyOptions: map[string]string{ 1130 "badValue1": "", 1131 }, 1132 expectedError: true, 1133 }, 1134 { 1135 description: "bad multiple arg", 1136 policyOptions: map[string]string{ 1137 "badValue1": "", 1138 "badvalue2": "aaaa", 1139 }, 1140 expectedError: true, 1141 }, 1142 { 1143 description: "good arg", 1144 policyOptions: map[string]string{ 1145 FullPCPUsOnlyOption: "true", 1146 }, 1147 expectedError: false, 1148 expectedValue: StaticPolicyOptions{ 1149 FullPhysicalCPUsOnly: true, 1150 }, 1151 }, 1152 { 1153 description: "good arg, bad value", 1154 policyOptions: map[string]string{ 1155 FullPCPUsOnlyOption: "enabled!", 1156 }, 1157 expectedError: true, 1158 }, 1159 1160 { 1161 description: "bad arg intermixed", 1162 policyOptions: map[string]string{ 1163 FullPCPUsOnlyOption: "1", 1164 "badvalue2": "lorem ipsum", 1165 }, 1166 expectedError: true, 1167 }, 1168 } 1169 1170 for _, testCase := range testCases { 1171 t.Run(testCase.description, func(t *testing.T) { 1172 opts, err := NewStaticPolicyOptions(testCase.policyOptions) 1173 gotError := (err != nil) 1174 if gotError != testCase.expectedError { 1175 t.Fatalf("error with args %v expected error %v got %v: %v", 1176 testCase.policyOptions, testCase.expectedError, gotError, err) 1177 } 1178 1179 if testCase.expectedError { 1180 return 1181 } 1182 1183 if !reflect.DeepEqual(opts, testCase.expectedValue) { 1184 t.Fatalf("value mismatch with args %v expected value %v got %v", 1185 testCase.policyOptions, testCase.expectedValue, opts) 1186 } 1187 }) 1188 } 1189 } 1190 1191 func newCPUSetPtr(cpus ...int) *cpuset.CPUSet { 1192 ret := cpuset.New(cpus...) 1193 return &ret 1194 }