cpu_manager_test.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cpumanager
  14. import (
  15. "fmt"
  16. "reflect"
  17. "strconv"
  18. "strings"
  19. "testing"
  20. "time"
  21. "io/ioutil"
  22. "os"
  23. cadvisorapi "github.com/google/cadvisor/info/v1"
  24. v1 "k8s.io/api/core/v1"
  25. "k8s.io/apimachinery/pkg/api/resource"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
  29. "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/containermap"
  30. "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/state"
  31. "k8s.io/kubernetes/pkg/kubelet/cm/cpumanager/topology"
  32. "k8s.io/kubernetes/pkg/kubelet/cm/cpuset"
  33. "k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
  34. )
  35. type mockState struct {
  36. assignments state.ContainerCPUAssignments
  37. defaultCPUSet cpuset.CPUSet
  38. }
  39. func (s *mockState) GetCPUSet(podUID string, containerName string) (cpuset.CPUSet, bool) {
  40. res, ok := s.assignments[podUID][containerName]
  41. return res.Clone(), ok
  42. }
  43. func (s *mockState) GetDefaultCPUSet() cpuset.CPUSet {
  44. return s.defaultCPUSet.Clone()
  45. }
  46. func (s *mockState) GetCPUSetOrDefault(podUID string, containerName string) cpuset.CPUSet {
  47. if res, ok := s.GetCPUSet(podUID, containerName); ok {
  48. return res
  49. }
  50. return s.GetDefaultCPUSet()
  51. }
  52. func (s *mockState) SetCPUSet(podUID string, containerName string, cset cpuset.CPUSet) {
  53. if _, exists := s.assignments[podUID]; !exists {
  54. s.assignments[podUID] = make(map[string]cpuset.CPUSet)
  55. }
  56. s.assignments[podUID][containerName] = cset
  57. }
  58. func (s *mockState) SetDefaultCPUSet(cset cpuset.CPUSet) {
  59. s.defaultCPUSet = cset
  60. }
  61. func (s *mockState) Delete(podUID string, containerName string) {
  62. delete(s.assignments[podUID], containerName)
  63. if len(s.assignments[podUID]) == 0 {
  64. delete(s.assignments, podUID)
  65. }
  66. }
  67. func (s *mockState) ClearState() {
  68. s.defaultCPUSet = cpuset.CPUSet{}
  69. s.assignments = make(state.ContainerCPUAssignments)
  70. }
  71. func (s *mockState) SetCPUAssignments(a state.ContainerCPUAssignments) {
  72. s.assignments = a.Clone()
  73. }
  74. func (s *mockState) GetCPUAssignments() state.ContainerCPUAssignments {
  75. return s.assignments.Clone()
  76. }
  77. type mockPolicy struct {
  78. err error
  79. }
  80. func (p *mockPolicy) Name() string {
  81. return "mock"
  82. }
  83. func (p *mockPolicy) Start(s state.State) error {
  84. return p.err
  85. }
  86. func (p *mockPolicy) AddContainer(s state.State, pod *v1.Pod, container *v1.Container) error {
  87. return p.err
  88. }
  89. func (p *mockPolicy) RemoveContainer(s state.State, podUID string, containerName string) error {
  90. return p.err
  91. }
  92. func (p *mockPolicy) GetTopologyHints(s state.State, pod *v1.Pod, container *v1.Container) map[string][]topologymanager.TopologyHint {
  93. return nil
  94. }
  95. type mockRuntimeService struct {
  96. err error
  97. }
  98. func (rt mockRuntimeService) UpdateContainerResources(id string, resources *runtimeapi.LinuxContainerResources) error {
  99. return rt.err
  100. }
  101. type mockPodStatusProvider struct {
  102. podStatus v1.PodStatus
  103. found bool
  104. }
  105. func (psp mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
  106. return psp.podStatus, psp.found
  107. }
  108. func makePod(podUID, containerName, cpuRequest, cpuLimit string) *v1.Pod {
  109. pod := &v1.Pod{
  110. Spec: v1.PodSpec{
  111. Containers: []v1.Container{
  112. {
  113. Resources: v1.ResourceRequirements{
  114. Requests: v1.ResourceList{
  115. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpuRequest),
  116. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  117. },
  118. Limits: v1.ResourceList{
  119. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpuLimit),
  120. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  121. },
  122. },
  123. },
  124. },
  125. },
  126. }
  127. pod.UID = types.UID(podUID)
  128. pod.Spec.Containers[0].Name = containerName
  129. return pod
  130. }
  131. func makeMultiContainerPod(initCPUs, appCPUs []struct{ request, limit string }) *v1.Pod {
  132. pod := &v1.Pod{
  133. ObjectMeta: metav1.ObjectMeta{
  134. Name: "pod",
  135. UID: "podUID",
  136. },
  137. Spec: v1.PodSpec{
  138. InitContainers: []v1.Container{},
  139. Containers: []v1.Container{},
  140. },
  141. }
  142. for i, cpu := range initCPUs {
  143. pod.Spec.InitContainers = append(pod.Spec.InitContainers, v1.Container{
  144. Name: "initContainer-" + strconv.Itoa(i),
  145. Resources: v1.ResourceRequirements{
  146. Requests: v1.ResourceList{
  147. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu.request),
  148. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  149. },
  150. Limits: v1.ResourceList{
  151. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu.limit),
  152. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  153. },
  154. },
  155. })
  156. }
  157. for i, cpu := range appCPUs {
  158. pod.Spec.Containers = append(pod.Spec.Containers, v1.Container{
  159. Name: "appContainer-" + strconv.Itoa(i),
  160. Resources: v1.ResourceRequirements{
  161. Requests: v1.ResourceList{
  162. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu.request),
  163. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  164. },
  165. Limits: v1.ResourceList{
  166. v1.ResourceName(v1.ResourceCPU): resource.MustParse(cpu.limit),
  167. v1.ResourceName(v1.ResourceMemory): resource.MustParse("1G"),
  168. },
  169. },
  170. })
  171. }
  172. return pod
  173. }
  174. func TestCPUManagerAdd(t *testing.T) {
  175. testPolicy, _ := NewStaticPolicy(
  176. &topology.CPUTopology{
  177. NumCPUs: 4,
  178. NumSockets: 1,
  179. NumCores: 4,
  180. CPUDetails: map[int]topology.CPUInfo{
  181. 0: {CoreID: 0, SocketID: 0},
  182. 1: {CoreID: 1, SocketID: 0},
  183. 2: {CoreID: 2, SocketID: 0},
  184. 3: {CoreID: 3, SocketID: 0},
  185. },
  186. },
  187. 0,
  188. cpuset.NewCPUSet(),
  189. topologymanager.NewFakeManager())
  190. testCases := []struct {
  191. description string
  192. updateErr error
  193. policy Policy
  194. expCPUSet cpuset.CPUSet
  195. expErr error
  196. }{
  197. {
  198. description: "cpu manager add - no error",
  199. updateErr: nil,
  200. policy: testPolicy,
  201. expCPUSet: cpuset.NewCPUSet(3, 4),
  202. expErr: nil,
  203. },
  204. {
  205. description: "cpu manager add - policy add container error",
  206. updateErr: nil,
  207. policy: &mockPolicy{
  208. err: fmt.Errorf("fake reg error"),
  209. },
  210. expCPUSet: cpuset.NewCPUSet(1, 2, 3, 4),
  211. expErr: fmt.Errorf("fake reg error"),
  212. },
  213. {
  214. description: "cpu manager add - container update error",
  215. updateErr: fmt.Errorf("fake update error"),
  216. policy: testPolicy,
  217. expCPUSet: cpuset.NewCPUSet(1, 2, 3, 4),
  218. expErr: fmt.Errorf("fake update error"),
  219. },
  220. }
  221. for _, testCase := range testCases {
  222. mgr := &manager{
  223. policy: testCase.policy,
  224. state: &mockState{
  225. assignments: state.ContainerCPUAssignments{},
  226. defaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4),
  227. },
  228. containerRuntime: mockRuntimeService{
  229. err: testCase.updateErr,
  230. },
  231. containerMap: containermap.NewContainerMap(),
  232. activePods: func() []*v1.Pod { return nil },
  233. podStatusProvider: mockPodStatusProvider{},
  234. }
  235. pod := makePod("fakePod", "fakeContainer", "2", "2")
  236. container := &pod.Spec.Containers[0]
  237. err := mgr.AddContainer(pod, container, "fakeID")
  238. if !reflect.DeepEqual(err, testCase.expErr) {
  239. t.Errorf("CPU Manager AddContainer() error (%v). expected error: %v but got: %v",
  240. testCase.description, testCase.expErr, err)
  241. }
  242. if !testCase.expCPUSet.Equals(mgr.state.GetDefaultCPUSet()) {
  243. t.Errorf("CPU Manager AddContainer() error (%v). expected cpuset: %v but got: %v",
  244. testCase.description, testCase.expCPUSet, mgr.state.GetDefaultCPUSet())
  245. }
  246. }
  247. }
  248. func TestCPUManagerAddWithInitContainers(t *testing.T) {
  249. testCases := []struct {
  250. description string
  251. topo *topology.CPUTopology
  252. numReservedCPUs int
  253. initContainerIDs []string
  254. containerIDs []string
  255. stAssignments state.ContainerCPUAssignments
  256. stDefaultCPUSet cpuset.CPUSet
  257. pod *v1.Pod
  258. expInitCSets []cpuset.CPUSet
  259. expCSets []cpuset.CPUSet
  260. }{
  261. {
  262. description: "No Guaranteed Init CPUs",
  263. topo: topoSingleSocketHT,
  264. numReservedCPUs: 0,
  265. stAssignments: state.ContainerCPUAssignments{},
  266. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  267. initContainerIDs: []string{"initFakeID"},
  268. containerIDs: []string{"appFakeID"},
  269. pod: makeMultiContainerPod(
  270. []struct{ request, limit string }{{"100m", "100m"}},
  271. []struct{ request, limit string }{{"4000m", "4000m"}}),
  272. expInitCSets: []cpuset.CPUSet{
  273. cpuset.NewCPUSet()},
  274. expCSets: []cpuset.CPUSet{
  275. cpuset.NewCPUSet(0, 4, 1, 5)},
  276. },
  277. {
  278. description: "Equal Number of Guaranteed CPUs",
  279. topo: topoSingleSocketHT,
  280. numReservedCPUs: 0,
  281. stAssignments: state.ContainerCPUAssignments{},
  282. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  283. initContainerIDs: []string{"initFakeID"},
  284. containerIDs: []string{"appFakeID"},
  285. pod: makeMultiContainerPod(
  286. []struct{ request, limit string }{{"4000m", "4000m"}},
  287. []struct{ request, limit string }{{"4000m", "4000m"}}),
  288. expInitCSets: []cpuset.CPUSet{
  289. cpuset.NewCPUSet(0, 4, 1, 5)},
  290. expCSets: []cpuset.CPUSet{
  291. cpuset.NewCPUSet(0, 4, 1, 5)},
  292. },
  293. {
  294. description: "More Init Container Guaranteed CPUs",
  295. topo: topoSingleSocketHT,
  296. numReservedCPUs: 0,
  297. stAssignments: state.ContainerCPUAssignments{},
  298. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  299. initContainerIDs: []string{"initFakeID"},
  300. containerIDs: []string{"appFakeID"},
  301. pod: makeMultiContainerPod(
  302. []struct{ request, limit string }{{"6000m", "6000m"}},
  303. []struct{ request, limit string }{{"4000m", "4000m"}}),
  304. expInitCSets: []cpuset.CPUSet{
  305. cpuset.NewCPUSet(0, 4, 1, 5, 2, 6)},
  306. expCSets: []cpuset.CPUSet{
  307. cpuset.NewCPUSet(0, 4, 1, 5)},
  308. },
  309. {
  310. description: "Less Init Container Guaranteed CPUs",
  311. topo: topoSingleSocketHT,
  312. numReservedCPUs: 0,
  313. stAssignments: state.ContainerCPUAssignments{},
  314. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  315. initContainerIDs: []string{"initFakeID"},
  316. containerIDs: []string{"appFakeID"},
  317. pod: makeMultiContainerPod(
  318. []struct{ request, limit string }{{"2000m", "2000m"}},
  319. []struct{ request, limit string }{{"4000m", "4000m"}}),
  320. expInitCSets: []cpuset.CPUSet{
  321. cpuset.NewCPUSet(0, 4)},
  322. expCSets: []cpuset.CPUSet{
  323. cpuset.NewCPUSet(0, 4, 1, 5)},
  324. },
  325. {
  326. description: "Multi Init Container Equal CPUs",
  327. topo: topoSingleSocketHT,
  328. numReservedCPUs: 0,
  329. stAssignments: state.ContainerCPUAssignments{},
  330. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  331. initContainerIDs: []string{"initFakeID-1", "initFakeID-2"},
  332. containerIDs: []string{"appFakeID"},
  333. pod: makeMultiContainerPod(
  334. []struct{ request, limit string }{
  335. {"2000m", "2000m"},
  336. {"2000m", "2000m"}},
  337. []struct{ request, limit string }{
  338. {"2000m", "2000m"}}),
  339. expInitCSets: []cpuset.CPUSet{
  340. cpuset.NewCPUSet(0, 4),
  341. cpuset.NewCPUSet(0, 4)},
  342. expCSets: []cpuset.CPUSet{
  343. cpuset.NewCPUSet(0, 4)},
  344. },
  345. {
  346. description: "Multi Init Container Less CPUs",
  347. topo: topoSingleSocketHT,
  348. numReservedCPUs: 0,
  349. stAssignments: state.ContainerCPUAssignments{},
  350. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  351. initContainerIDs: []string{"initFakeID-1", "initFakeID-2"},
  352. containerIDs: []string{"appFakeID"},
  353. pod: makeMultiContainerPod(
  354. []struct{ request, limit string }{
  355. {"4000m", "4000m"},
  356. {"4000m", "4000m"}},
  357. []struct{ request, limit string }{
  358. {"2000m", "2000m"}}),
  359. expInitCSets: []cpuset.CPUSet{
  360. cpuset.NewCPUSet(0, 4, 1, 5),
  361. cpuset.NewCPUSet(0, 4, 1, 5)},
  362. expCSets: []cpuset.CPUSet{
  363. cpuset.NewCPUSet(0, 4)},
  364. },
  365. {
  366. description: "Multi Init Container More CPUs",
  367. topo: topoSingleSocketHT,
  368. numReservedCPUs: 0,
  369. stAssignments: state.ContainerCPUAssignments{},
  370. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  371. initContainerIDs: []string{"initFakeID-1", "initFakeID-2"},
  372. containerIDs: []string{"appFakeID"},
  373. pod: makeMultiContainerPod(
  374. []struct{ request, limit string }{
  375. {"2000m", "2000m"},
  376. {"2000m", "2000m"}},
  377. []struct{ request, limit string }{
  378. {"4000m", "4000m"}}),
  379. expInitCSets: []cpuset.CPUSet{
  380. cpuset.NewCPUSet(0, 4),
  381. cpuset.NewCPUSet(0, 4)},
  382. expCSets: []cpuset.CPUSet{
  383. cpuset.NewCPUSet(0, 4, 1, 5)},
  384. },
  385. {
  386. description: "Multi Init Container Increasing CPUs",
  387. topo: topoSingleSocketHT,
  388. numReservedCPUs: 0,
  389. stAssignments: state.ContainerCPUAssignments{},
  390. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  391. initContainerIDs: []string{"initFakeID-1", "initFakeID-2"},
  392. containerIDs: []string{"appFakeID"},
  393. pod: makeMultiContainerPod(
  394. []struct{ request, limit string }{
  395. {"2000m", "2000m"},
  396. {"4000m", "4000m"}},
  397. []struct{ request, limit string }{
  398. {"6000m", "6000m"}}),
  399. expInitCSets: []cpuset.CPUSet{
  400. cpuset.NewCPUSet(0, 4),
  401. cpuset.NewCPUSet(0, 4, 1, 5)},
  402. expCSets: []cpuset.CPUSet{
  403. cpuset.NewCPUSet(0, 4, 1, 5, 2, 6)},
  404. },
  405. {
  406. description: "Multi Init, Multi App Container Split CPUs",
  407. topo: topoSingleSocketHT,
  408. numReservedCPUs: 0,
  409. stAssignments: state.ContainerCPUAssignments{},
  410. stDefaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3, 4, 5, 6, 7),
  411. initContainerIDs: []string{"initFakeID-1", "initFakeID-2"},
  412. containerIDs: []string{"appFakeID-1", "appFakeID-2"},
  413. pod: makeMultiContainerPod(
  414. []struct{ request, limit string }{
  415. {"2000m", "2000m"},
  416. {"4000m", "4000m"}},
  417. []struct{ request, limit string }{
  418. {"2000m", "2000m"},
  419. {"2000m", "2000m"}}),
  420. expInitCSets: []cpuset.CPUSet{
  421. cpuset.NewCPUSet(0, 4),
  422. cpuset.NewCPUSet(0, 4, 1, 5)},
  423. expCSets: []cpuset.CPUSet{
  424. cpuset.NewCPUSet(0, 4),
  425. cpuset.NewCPUSet(1, 5)},
  426. },
  427. }
  428. for _, testCase := range testCases {
  429. policy, _ := NewStaticPolicy(testCase.topo, testCase.numReservedCPUs, cpuset.NewCPUSet(), topologymanager.NewFakeManager())
  430. state := &mockState{
  431. assignments: testCase.stAssignments,
  432. defaultCPUSet: testCase.stDefaultCPUSet,
  433. }
  434. mgr := &manager{
  435. policy: policy,
  436. state: state,
  437. containerRuntime: mockRuntimeService{},
  438. containerMap: containermap.NewContainerMap(),
  439. activePods: func() []*v1.Pod { return nil },
  440. podStatusProvider: mockPodStatusProvider{},
  441. }
  442. containers := append(
  443. testCase.pod.Spec.InitContainers,
  444. testCase.pod.Spec.Containers...)
  445. containerIDs := append(
  446. testCase.initContainerIDs,
  447. testCase.containerIDs...)
  448. expCSets := append(
  449. testCase.expInitCSets,
  450. testCase.expCSets...)
  451. for i := range containers {
  452. err := mgr.AddContainer(testCase.pod, &containers[i], containerIDs[i])
  453. if err != nil {
  454. t.Errorf("StaticPolicy AddContainer() error (%v). unexpected error for container id: %v: %v",
  455. testCase.description, containerIDs[i], err)
  456. }
  457. cset, found := state.assignments[string(testCase.pod.UID)][containers[i].Name]
  458. if !expCSets[i].IsEmpty() && !found {
  459. t.Errorf("StaticPolicy AddContainer() error (%v). expected container %v to be present in assignments %v",
  460. testCase.description, containers[i].Name, state.assignments)
  461. }
  462. if found && !cset.Equals(expCSets[i]) {
  463. t.Errorf("StaticPolicy AddContainer() error (%v). expected cpuset %v for container %v but got %v",
  464. testCase.description, expCSets[i], containers[i].Name, cset)
  465. }
  466. }
  467. }
  468. }
  469. func TestCPUManagerGenerate(t *testing.T) {
  470. testCases := []struct {
  471. description string
  472. cpuPolicyName string
  473. nodeAllocatableReservation v1.ResourceList
  474. isTopologyBroken bool
  475. expectedPolicy string
  476. expectedError error
  477. }{
  478. {
  479. description: "set none policy",
  480. cpuPolicyName: "none",
  481. nodeAllocatableReservation: nil,
  482. expectedPolicy: "none",
  483. },
  484. {
  485. description: "invalid policy name",
  486. cpuPolicyName: "invalid",
  487. nodeAllocatableReservation: nil,
  488. expectedError: fmt.Errorf("unknown policy: \"invalid\""),
  489. },
  490. {
  491. description: "static policy",
  492. cpuPolicyName: "static",
  493. nodeAllocatableReservation: v1.ResourceList{v1.ResourceCPU: *resource.NewQuantity(3, resource.DecimalSI)},
  494. expectedPolicy: "static",
  495. },
  496. {
  497. description: "static policy - broken topology",
  498. cpuPolicyName: "static",
  499. nodeAllocatableReservation: v1.ResourceList{},
  500. isTopologyBroken: true,
  501. expectedError: fmt.Errorf("could not detect number of cpus"),
  502. },
  503. {
  504. description: "static policy - broken reservation",
  505. cpuPolicyName: "static",
  506. nodeAllocatableReservation: v1.ResourceList{},
  507. expectedError: fmt.Errorf("unable to determine reserved CPU resources for static policy"),
  508. },
  509. {
  510. description: "static policy - no CPU resources",
  511. cpuPolicyName: "static",
  512. nodeAllocatableReservation: v1.ResourceList{v1.ResourceCPU: *resource.NewQuantity(0, resource.DecimalSI)},
  513. expectedError: fmt.Errorf("the static policy requires systemreserved.cpu + kubereserved.cpu to be greater than zero"),
  514. },
  515. }
  516. mockedMachineInfo := cadvisorapi.MachineInfo{
  517. NumCores: 4,
  518. Topology: []cadvisorapi.Node{
  519. {
  520. Cores: []cadvisorapi.Core{
  521. {
  522. Id: 0,
  523. Threads: []int{0},
  524. },
  525. {
  526. Id: 1,
  527. Threads: []int{1},
  528. },
  529. {
  530. Id: 2,
  531. Threads: []int{2},
  532. },
  533. {
  534. Id: 3,
  535. Threads: []int{3},
  536. },
  537. },
  538. },
  539. },
  540. }
  541. for _, testCase := range testCases {
  542. t.Run(testCase.description, func(t *testing.T) {
  543. machineInfo := &mockedMachineInfo
  544. if testCase.isTopologyBroken {
  545. machineInfo = &cadvisorapi.MachineInfo{}
  546. }
  547. sDir, err := ioutil.TempDir("/tmp/", "cpu_manager_test")
  548. if err != nil {
  549. t.Errorf("cannot create state file: %s", err.Error())
  550. }
  551. defer os.RemoveAll(sDir)
  552. mgr, err := NewManager(testCase.cpuPolicyName, 5*time.Second, machineInfo, nil, cpuset.NewCPUSet(), testCase.nodeAllocatableReservation, sDir, topologymanager.NewFakeManager())
  553. if testCase.expectedError != nil {
  554. if !strings.Contains(err.Error(), testCase.expectedError.Error()) {
  555. t.Errorf("Unexpected error message. Have: %s wants %s", err.Error(), testCase.expectedError.Error())
  556. }
  557. } else {
  558. rawMgr := mgr.(*manager)
  559. if rawMgr.policy.Name() != testCase.expectedPolicy {
  560. t.Errorf("Unexpected policy name. Have: %q wants %q", rawMgr.policy.Name(), testCase.expectedPolicy)
  561. }
  562. if rawMgr.policy.Name() == string(PolicyNone) {
  563. if rawMgr.topology != nil {
  564. t.Errorf("Expected topology to be nil for 'none' policy. Have: %q", rawMgr.topology)
  565. }
  566. }
  567. if rawMgr.policy.Name() != string(PolicyNone) {
  568. if rawMgr.topology == nil {
  569. t.Errorf("Expected topology to be non-nil for policy '%v'. Have: %q", rawMgr.policy.Name(), rawMgr.topology)
  570. }
  571. }
  572. }
  573. })
  574. }
  575. }
  576. func TestCPUManagerRemove(t *testing.T) {
  577. containerID := "fakeID"
  578. containerMap := containermap.NewContainerMap()
  579. mgr := &manager{
  580. policy: &mockPolicy{
  581. err: nil,
  582. },
  583. state: &mockState{
  584. assignments: state.ContainerCPUAssignments{},
  585. defaultCPUSet: cpuset.NewCPUSet(),
  586. },
  587. containerRuntime: mockRuntimeService{},
  588. containerMap: containerMap,
  589. activePods: func() []*v1.Pod { return nil },
  590. podStatusProvider: mockPodStatusProvider{},
  591. }
  592. containerMap.Add("", "", containerID)
  593. err := mgr.RemoveContainer(containerID)
  594. if err != nil {
  595. t.Errorf("CPU Manager RemoveContainer() error. expected error to be nil but got: %v", err)
  596. }
  597. mgr = &manager{
  598. policy: &mockPolicy{
  599. err: fmt.Errorf("fake error"),
  600. },
  601. state: state.NewMemoryState(),
  602. containerRuntime: mockRuntimeService{},
  603. containerMap: containerMap,
  604. activePods: func() []*v1.Pod { return nil },
  605. podStatusProvider: mockPodStatusProvider{},
  606. }
  607. containerMap.Add("", "", containerID)
  608. err = mgr.RemoveContainer(containerID)
  609. if !reflect.DeepEqual(err, fmt.Errorf("fake error")) {
  610. t.Errorf("CPU Manager RemoveContainer() error. expected error: fake error but got: %v", err)
  611. }
  612. }
  613. func TestReconcileState(t *testing.T) {
  614. testCases := []struct {
  615. description string
  616. activePods []*v1.Pod
  617. pspPS v1.PodStatus
  618. pspFound bool
  619. stAssignments state.ContainerCPUAssignments
  620. stDefaultCPUSet cpuset.CPUSet
  621. updateErr error
  622. expectSucceededContainerName string
  623. expectFailedContainerName string
  624. }{
  625. {
  626. description: "cpu manager reconclie - no error",
  627. activePods: []*v1.Pod{
  628. {
  629. ObjectMeta: metav1.ObjectMeta{
  630. Name: "fakePodName",
  631. UID: "fakePodUID",
  632. },
  633. Spec: v1.PodSpec{
  634. Containers: []v1.Container{
  635. {
  636. Name: "fakeContainerName",
  637. },
  638. },
  639. },
  640. },
  641. },
  642. pspPS: v1.PodStatus{
  643. ContainerStatuses: []v1.ContainerStatus{
  644. {
  645. Name: "fakeContainerName",
  646. ContainerID: "docker://fakeContainerID",
  647. State: v1.ContainerState{
  648. Running: &v1.ContainerStateRunning{},
  649. },
  650. },
  651. },
  652. },
  653. pspFound: true,
  654. stAssignments: state.ContainerCPUAssignments{
  655. "fakePodUID": map[string]cpuset.CPUSet{
  656. "fakeContainerName": cpuset.NewCPUSet(1, 2),
  657. },
  658. },
  659. stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
  660. updateErr: nil,
  661. expectSucceededContainerName: "fakeContainerName",
  662. expectFailedContainerName: "",
  663. },
  664. {
  665. description: "cpu manager reconcile init container - no error",
  666. activePods: []*v1.Pod{
  667. {
  668. ObjectMeta: metav1.ObjectMeta{
  669. Name: "fakePodName",
  670. UID: "fakePodUID",
  671. },
  672. Spec: v1.PodSpec{
  673. InitContainers: []v1.Container{
  674. {
  675. Name: "fakeContainerName",
  676. },
  677. },
  678. },
  679. },
  680. },
  681. pspPS: v1.PodStatus{
  682. InitContainerStatuses: []v1.ContainerStatus{
  683. {
  684. Name: "fakeContainerName",
  685. ContainerID: "docker://fakeContainerID",
  686. State: v1.ContainerState{
  687. Running: &v1.ContainerStateRunning{},
  688. },
  689. },
  690. },
  691. },
  692. pspFound: true,
  693. stAssignments: state.ContainerCPUAssignments{
  694. "fakePodUID": map[string]cpuset.CPUSet{
  695. "fakeContainerName": cpuset.NewCPUSet(1, 2),
  696. },
  697. },
  698. stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
  699. updateErr: nil,
  700. expectSucceededContainerName: "fakeContainerName",
  701. expectFailedContainerName: "",
  702. },
  703. {
  704. description: "cpu manager reconcile - pod status not found",
  705. activePods: []*v1.Pod{
  706. {
  707. ObjectMeta: metav1.ObjectMeta{
  708. Name: "fakePodName",
  709. UID: "fakePodUID",
  710. },
  711. Spec: v1.PodSpec{
  712. Containers: []v1.Container{
  713. {
  714. Name: "fakeContainerName",
  715. },
  716. },
  717. },
  718. },
  719. },
  720. pspPS: v1.PodStatus{},
  721. pspFound: false,
  722. stAssignments: state.ContainerCPUAssignments{},
  723. stDefaultCPUSet: cpuset.NewCPUSet(),
  724. updateErr: nil,
  725. expectSucceededContainerName: "",
  726. expectFailedContainerName: "",
  727. },
  728. {
  729. description: "cpu manager reconcile - container state not found",
  730. activePods: []*v1.Pod{
  731. {
  732. ObjectMeta: metav1.ObjectMeta{
  733. Name: "fakePodName",
  734. UID: "fakePodUID",
  735. },
  736. Spec: v1.PodSpec{
  737. Containers: []v1.Container{
  738. {
  739. Name: "fakeContainerName",
  740. },
  741. },
  742. },
  743. },
  744. },
  745. pspPS: v1.PodStatus{
  746. ContainerStatuses: []v1.ContainerStatus{
  747. {
  748. Name: "fakeContainerName1",
  749. ContainerID: "docker://fakeContainerID",
  750. },
  751. },
  752. },
  753. pspFound: true,
  754. stAssignments: state.ContainerCPUAssignments{},
  755. stDefaultCPUSet: cpuset.NewCPUSet(),
  756. updateErr: nil,
  757. expectSucceededContainerName: "",
  758. expectFailedContainerName: "fakeContainerName",
  759. },
  760. {
  761. description: "cpu manager reconclie - cpuset is empty",
  762. activePods: []*v1.Pod{
  763. {
  764. ObjectMeta: metav1.ObjectMeta{
  765. Name: "fakePodName",
  766. UID: "fakePodUID",
  767. },
  768. Spec: v1.PodSpec{
  769. Containers: []v1.Container{
  770. {
  771. Name: "fakeContainerName",
  772. },
  773. },
  774. },
  775. },
  776. },
  777. pspPS: v1.PodStatus{
  778. ContainerStatuses: []v1.ContainerStatus{
  779. {
  780. Name: "fakeContainerName",
  781. ContainerID: "docker://fakeContainerID",
  782. State: v1.ContainerState{
  783. Running: &v1.ContainerStateRunning{},
  784. },
  785. },
  786. },
  787. },
  788. pspFound: true,
  789. stAssignments: state.ContainerCPUAssignments{
  790. "fakePodUID": map[string]cpuset.CPUSet{
  791. "fakeContainerName": cpuset.NewCPUSet(),
  792. },
  793. },
  794. stDefaultCPUSet: cpuset.NewCPUSet(1, 2, 3, 4, 5, 6, 7),
  795. updateErr: nil,
  796. expectSucceededContainerName: "",
  797. expectFailedContainerName: "fakeContainerName",
  798. },
  799. {
  800. description: "cpu manager reconclie - container update error",
  801. activePods: []*v1.Pod{
  802. {
  803. ObjectMeta: metav1.ObjectMeta{
  804. Name: "fakePodName",
  805. UID: "fakePodUID",
  806. },
  807. Spec: v1.PodSpec{
  808. Containers: []v1.Container{
  809. {
  810. Name: "fakeContainerName",
  811. },
  812. },
  813. },
  814. },
  815. },
  816. pspPS: v1.PodStatus{
  817. ContainerStatuses: []v1.ContainerStatus{
  818. {
  819. Name: "fakeContainerName",
  820. ContainerID: "docker://fakeContainerID",
  821. State: v1.ContainerState{
  822. Running: &v1.ContainerStateRunning{},
  823. },
  824. },
  825. },
  826. },
  827. pspFound: true,
  828. stAssignments: state.ContainerCPUAssignments{
  829. "fakePodUID": map[string]cpuset.CPUSet{
  830. "fakeContainerName": cpuset.NewCPUSet(1, 2),
  831. },
  832. },
  833. stDefaultCPUSet: cpuset.NewCPUSet(3, 4, 5, 6, 7),
  834. updateErr: fmt.Errorf("fake container update error"),
  835. expectSucceededContainerName: "",
  836. expectFailedContainerName: "fakeContainerName",
  837. },
  838. }
  839. for _, testCase := range testCases {
  840. mgr := &manager{
  841. policy: &mockPolicy{
  842. err: nil,
  843. },
  844. state: &mockState{
  845. assignments: testCase.stAssignments,
  846. defaultCPUSet: testCase.stDefaultCPUSet,
  847. },
  848. containerRuntime: mockRuntimeService{
  849. err: testCase.updateErr,
  850. },
  851. containerMap: containermap.NewContainerMap(),
  852. activePods: func() []*v1.Pod {
  853. return testCase.activePods
  854. },
  855. podStatusProvider: mockPodStatusProvider{
  856. podStatus: testCase.pspPS,
  857. found: testCase.pspFound,
  858. },
  859. }
  860. mgr.sourcesReady = &sourcesReadyStub{}
  861. success, failure := mgr.reconcileState()
  862. if testCase.expectSucceededContainerName != "" {
  863. // Search succeeded reconciled containers for the supplied name.
  864. foundSucceededContainer := false
  865. for _, reconciled := range success {
  866. if reconciled.containerName == testCase.expectSucceededContainerName {
  867. foundSucceededContainer = true
  868. break
  869. }
  870. }
  871. if !foundSucceededContainer {
  872. t.Errorf("%v", testCase.description)
  873. t.Errorf("Expected reconciliation success for container: %s", testCase.expectSucceededContainerName)
  874. }
  875. }
  876. if testCase.expectFailedContainerName != "" {
  877. // Search failed reconciled containers for the supplied name.
  878. foundFailedContainer := false
  879. for _, reconciled := range failure {
  880. if reconciled.containerName == testCase.expectFailedContainerName {
  881. foundFailedContainer = true
  882. break
  883. }
  884. }
  885. if !foundFailedContainer {
  886. t.Errorf("%v", testCase.description)
  887. t.Errorf("Expected reconciliation failure for container: %s", testCase.expectFailedContainerName)
  888. }
  889. }
  890. }
  891. }
  892. // above test cases are without kubelet --reserved-cpus cmd option
  893. // the following tests are with --reserved-cpus configured
  894. func TestCPUManagerAddWithResvList(t *testing.T) {
  895. testPolicy, _ := NewStaticPolicy(
  896. &topology.CPUTopology{
  897. NumCPUs: 4,
  898. NumSockets: 1,
  899. NumCores: 4,
  900. CPUDetails: map[int]topology.CPUInfo{
  901. 0: {CoreID: 0, SocketID: 0},
  902. 1: {CoreID: 1, SocketID: 0},
  903. 2: {CoreID: 2, SocketID: 0},
  904. 3: {CoreID: 3, SocketID: 0},
  905. },
  906. },
  907. 1,
  908. cpuset.NewCPUSet(0),
  909. topologymanager.NewFakeManager())
  910. testCases := []struct {
  911. description string
  912. updateErr error
  913. policy Policy
  914. expCPUSet cpuset.CPUSet
  915. expErr error
  916. }{
  917. {
  918. description: "cpu manager add - no error",
  919. updateErr: nil,
  920. policy: testPolicy,
  921. expCPUSet: cpuset.NewCPUSet(0, 3),
  922. expErr: nil,
  923. },
  924. {
  925. description: "cpu manager add - container update error",
  926. updateErr: fmt.Errorf("fake update error"),
  927. policy: testPolicy,
  928. expCPUSet: cpuset.NewCPUSet(0, 1, 2, 3),
  929. expErr: fmt.Errorf("fake update error"),
  930. },
  931. }
  932. for _, testCase := range testCases {
  933. mgr := &manager{
  934. policy: testCase.policy,
  935. state: &mockState{
  936. assignments: state.ContainerCPUAssignments{},
  937. defaultCPUSet: cpuset.NewCPUSet(0, 1, 2, 3),
  938. },
  939. containerRuntime: mockRuntimeService{
  940. err: testCase.updateErr,
  941. },
  942. containerMap: containermap.NewContainerMap(),
  943. activePods: func() []*v1.Pod { return nil },
  944. podStatusProvider: mockPodStatusProvider{},
  945. }
  946. pod := makePod("fakePod", "fakeContainer", "2", "2")
  947. container := &pod.Spec.Containers[0]
  948. err := mgr.AddContainer(pod, container, "fakeID")
  949. if !reflect.DeepEqual(err, testCase.expErr) {
  950. t.Errorf("CPU Manager AddContainer() error (%v). expected error: %v but got: %v",
  951. testCase.description, testCase.expErr, err)
  952. }
  953. if !testCase.expCPUSet.Equals(mgr.state.GetDefaultCPUSet()) {
  954. t.Errorf("CPU Manager AddContainer() error (%v). expected cpuset: %v but got: %v",
  955. testCase.description, testCase.expCPUSet, mgr.state.GetDefaultCPUSet())
  956. }
  957. }
  958. }