2
0

cluster_helpers_test.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  1. package costmodel
  2. import (
  3. "github.com/kubecost/cost-model/pkg/prom"
  4. "github.com/kubecost/cost-model/pkg/util"
  5. "reflect"
  6. "testing"
  7. "time"
  8. "github.com/davecgh/go-spew/spew"
  9. )
  10. func TestMergeTypeMaps(t *testing.T) {
  11. cases := []struct {
  12. name string
  13. map1 map[nodeIdentifierNoProviderID]string
  14. map2 map[nodeIdentifierNoProviderID]string
  15. expected map[nodeIdentifierNoProviderID]string
  16. }{
  17. {
  18. name: "both empty",
  19. map1: map[nodeIdentifierNoProviderID]string{},
  20. map2: map[nodeIdentifierNoProviderID]string{},
  21. expected: map[nodeIdentifierNoProviderID]string{},
  22. },
  23. {
  24. name: "map2 empty",
  25. map1: map[nodeIdentifierNoProviderID]string{
  26. nodeIdentifierNoProviderID{
  27. Cluster: "cluster1",
  28. Name: "node1",
  29. }: "type1",
  30. },
  31. map2: map[nodeIdentifierNoProviderID]string{},
  32. expected: map[nodeIdentifierNoProviderID]string{
  33. nodeIdentifierNoProviderID{
  34. Cluster: "cluster1",
  35. Name: "node1",
  36. }: "type1",
  37. },
  38. },
  39. {
  40. name: "map1 empty",
  41. map1: map[nodeIdentifierNoProviderID]string{},
  42. map2: map[nodeIdentifierNoProviderID]string{
  43. nodeIdentifierNoProviderID{
  44. Cluster: "cluster1",
  45. Name: "node1",
  46. }: "type1",
  47. },
  48. expected: map[nodeIdentifierNoProviderID]string{
  49. nodeIdentifierNoProviderID{
  50. Cluster: "cluster1",
  51. Name: "node1",
  52. }: "type1",
  53. },
  54. },
  55. {
  56. name: "no overlap",
  57. map1: map[nodeIdentifierNoProviderID]string{
  58. nodeIdentifierNoProviderID{
  59. Cluster: "cluster1",
  60. Name: "node1",
  61. }: "type1",
  62. },
  63. map2: map[nodeIdentifierNoProviderID]string{
  64. nodeIdentifierNoProviderID{
  65. Cluster: "cluster1",
  66. Name: "node2",
  67. }: "type2",
  68. nodeIdentifierNoProviderID{
  69. Cluster: "cluster1",
  70. Name: "node4",
  71. }: "type4",
  72. },
  73. expected: map[nodeIdentifierNoProviderID]string{
  74. nodeIdentifierNoProviderID{
  75. Cluster: "cluster1",
  76. Name: "node1",
  77. }: "type1",
  78. nodeIdentifierNoProviderID{
  79. Cluster: "cluster1",
  80. Name: "node2",
  81. }: "type2",
  82. nodeIdentifierNoProviderID{
  83. Cluster: "cluster1",
  84. Name: "node4",
  85. }: "type4",
  86. },
  87. },
  88. {
  89. name: "with overlap",
  90. map1: map[nodeIdentifierNoProviderID]string{
  91. nodeIdentifierNoProviderID{
  92. Cluster: "cluster1",
  93. Name: "node1",
  94. }: "type1",
  95. },
  96. map2: map[nodeIdentifierNoProviderID]string{
  97. nodeIdentifierNoProviderID{
  98. Cluster: "cluster1",
  99. Name: "node2",
  100. }: "type2",
  101. nodeIdentifierNoProviderID{
  102. Cluster: "cluster1",
  103. Name: "node1",
  104. }: "type4",
  105. },
  106. expected: map[nodeIdentifierNoProviderID]string{
  107. nodeIdentifierNoProviderID{
  108. Cluster: "cluster1",
  109. Name: "node1",
  110. }: "type1",
  111. nodeIdentifierNoProviderID{
  112. Cluster: "cluster1",
  113. Name: "node2",
  114. }: "type2",
  115. },
  116. },
  117. }
  118. for _, testCase := range cases {
  119. t.Run(testCase.name, func(t *testing.T) {
  120. result := mergeTypeMaps(testCase.map1, testCase.map2)
  121. if !reflect.DeepEqual(result, testCase.expected) {
  122. t.Errorf("mergeTypeMaps case %s failed. Got %+v but expected %+v", testCase.name, result, testCase.expected)
  123. }
  124. })
  125. }
  126. }
  127. func TestBuildNodeMap(t *testing.T) {
  128. cases := []struct {
  129. name string
  130. cpuCostMap map[NodeIdentifier]float64
  131. ramCostMap map[NodeIdentifier]float64
  132. gpuCostMap map[NodeIdentifier]float64
  133. gpuCountMap map[NodeIdentifier]float64
  134. cpuCoresMap map[nodeIdentifierNoProviderID]float64
  135. ramBytesMap map[nodeIdentifierNoProviderID]float64
  136. ramUserPctMap map[nodeIdentifierNoProviderID]float64
  137. ramSystemPctMap map[nodeIdentifierNoProviderID]float64
  138. cpuBreakdownMap map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown
  139. activeDataMap map[NodeIdentifier]activeData
  140. preemptibleMap map[NodeIdentifier]bool
  141. labelsMap map[nodeIdentifierNoProviderID]map[string]string
  142. clusterAndNameToType map[nodeIdentifierNoProviderID]string
  143. expected map[NodeIdentifier]*Node
  144. }{
  145. {
  146. name: "empty",
  147. expected: map[NodeIdentifier]*Node{},
  148. },
  149. {
  150. name: "just cpu cost",
  151. cpuCostMap: map[NodeIdentifier]float64{
  152. NodeIdentifier{
  153. Cluster: "cluster1",
  154. Name: "node1",
  155. ProviderID: "prov_node1",
  156. }: 0.048,
  157. },
  158. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  159. nodeIdentifierNoProviderID{
  160. Cluster: "cluster1",
  161. Name: "node1",
  162. }: "type1",
  163. },
  164. expected: map[NodeIdentifier]*Node{
  165. NodeIdentifier{
  166. Cluster: "cluster1",
  167. Name: "node1",
  168. ProviderID: "prov_node1",
  169. }: &Node{
  170. Cluster: "cluster1",
  171. Name: "node1",
  172. ProviderID: "prov_node1",
  173. NodeType: "type1",
  174. CPUCost: 0.048,
  175. CPUBreakdown: &ClusterCostsBreakdown{},
  176. RAMBreakdown: &ClusterCostsBreakdown{},
  177. },
  178. },
  179. },
  180. {
  181. name: "just cpu cost with empty provider ID",
  182. cpuCostMap: map[NodeIdentifier]float64{
  183. NodeIdentifier{
  184. Cluster: "cluster1",
  185. Name: "node1",
  186. }: 0.048,
  187. },
  188. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  189. nodeIdentifierNoProviderID{
  190. Cluster: "cluster1",
  191. Name: "node1",
  192. }: "type1",
  193. },
  194. expected: map[NodeIdentifier]*Node{
  195. NodeIdentifier{
  196. Cluster: "cluster1",
  197. Name: "node1",
  198. }: &Node{
  199. Cluster: "cluster1",
  200. Name: "node1",
  201. NodeType: "type1",
  202. CPUCost: 0.048,
  203. CPUBreakdown: &ClusterCostsBreakdown{},
  204. RAMBreakdown: &ClusterCostsBreakdown{},
  205. },
  206. },
  207. },
  208. {
  209. name: "cpu cost with overlapping node names",
  210. cpuCostMap: map[NodeIdentifier]float64{
  211. NodeIdentifier{
  212. Cluster: "cluster1",
  213. Name: "node1",
  214. ProviderID: "prov_node1_A",
  215. }: 0.048,
  216. NodeIdentifier{
  217. Cluster: "cluster1",
  218. Name: "node1",
  219. ProviderID: "prov_node1_B",
  220. }: 0.087,
  221. },
  222. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  223. nodeIdentifierNoProviderID{
  224. Cluster: "cluster1",
  225. Name: "node1",
  226. }: "type1",
  227. },
  228. expected: map[NodeIdentifier]*Node{
  229. NodeIdentifier{
  230. Cluster: "cluster1",
  231. Name: "node1",
  232. ProviderID: "prov_node1_A",
  233. }: &Node{
  234. Cluster: "cluster1",
  235. Name: "node1",
  236. ProviderID: "prov_node1_A",
  237. NodeType: "type1",
  238. CPUCost: 0.048,
  239. CPUBreakdown: &ClusterCostsBreakdown{},
  240. RAMBreakdown: &ClusterCostsBreakdown{},
  241. },
  242. NodeIdentifier{
  243. Cluster: "cluster1",
  244. Name: "node1",
  245. ProviderID: "prov_node1_B",
  246. }: &Node{
  247. Cluster: "cluster1",
  248. Name: "node1",
  249. ProviderID: "prov_node1_B",
  250. NodeType: "type1",
  251. CPUCost: 0.087,
  252. CPUBreakdown: &ClusterCostsBreakdown{},
  253. RAMBreakdown: &ClusterCostsBreakdown{},
  254. },
  255. },
  256. },
  257. {
  258. name: "all fields + overlapping node names",
  259. cpuCostMap: map[NodeIdentifier]float64{
  260. NodeIdentifier{
  261. Cluster: "cluster1",
  262. Name: "node1",
  263. ProviderID: "prov_node1_A",
  264. }: 0.048,
  265. NodeIdentifier{
  266. Cluster: "cluster1",
  267. Name: "node1",
  268. ProviderID: "prov_node1_B",
  269. }: 0.087,
  270. NodeIdentifier{
  271. Cluster: "cluster1",
  272. Name: "node2",
  273. ProviderID: "prov_node2_A",
  274. }: 0.033,
  275. },
  276. ramCostMap: map[NodeIdentifier]float64{
  277. NodeIdentifier{
  278. Cluster: "cluster1",
  279. Name: "node1",
  280. ProviderID: "prov_node1_A",
  281. }: 0.09,
  282. NodeIdentifier{
  283. Cluster: "cluster1",
  284. Name: "node1",
  285. ProviderID: "prov_node1_B",
  286. }: 0.3,
  287. NodeIdentifier{
  288. Cluster: "cluster1",
  289. Name: "node2",
  290. ProviderID: "prov_node2_A",
  291. }: 0.024,
  292. },
  293. gpuCostMap: map[NodeIdentifier]float64{
  294. NodeIdentifier{
  295. Cluster: "cluster1",
  296. Name: "node1",
  297. ProviderID: "prov_node1_A",
  298. }: 0.8,
  299. NodeIdentifier{
  300. Cluster: "cluster1",
  301. Name: "node1",
  302. ProviderID: "prov_node1_B",
  303. }: 1.4,
  304. NodeIdentifier{
  305. Cluster: "cluster1",
  306. Name: "node2",
  307. ProviderID: "prov_node2_A",
  308. }: 3.1,
  309. },
  310. gpuCountMap: map[NodeIdentifier]float64{
  311. NodeIdentifier{
  312. Cluster: "cluster1",
  313. Name: "node1",
  314. ProviderID: "prov_node1_A",
  315. }: 1.0,
  316. NodeIdentifier{
  317. Cluster: "cluster1",
  318. Name: "node1",
  319. ProviderID: "prov_node1_B",
  320. }: 1.0,
  321. NodeIdentifier{
  322. Cluster: "cluster1",
  323. Name: "node2",
  324. ProviderID: "prov_node2_A",
  325. }: 2.0,
  326. },
  327. cpuCoresMap: map[nodeIdentifierNoProviderID]float64{
  328. nodeIdentifierNoProviderID{
  329. Cluster: "cluster1",
  330. Name: "node1",
  331. }: 2.0,
  332. nodeIdentifierNoProviderID{
  333. Cluster: "cluster1",
  334. Name: "node2",
  335. }: 5.0,
  336. },
  337. ramBytesMap: map[nodeIdentifierNoProviderID]float64{
  338. nodeIdentifierNoProviderID{
  339. Cluster: "cluster1",
  340. Name: "node1",
  341. }: 2048.0,
  342. nodeIdentifierNoProviderID{
  343. Cluster: "cluster1",
  344. Name: "node2",
  345. }: 6303.0,
  346. },
  347. ramUserPctMap: map[nodeIdentifierNoProviderID]float64{
  348. nodeIdentifierNoProviderID{
  349. Cluster: "cluster1",
  350. Name: "node1",
  351. }: 30.0,
  352. nodeIdentifierNoProviderID{
  353. Cluster: "cluster1",
  354. Name: "node2",
  355. }: 42.6,
  356. },
  357. ramSystemPctMap: map[nodeIdentifierNoProviderID]float64{
  358. nodeIdentifierNoProviderID{
  359. Cluster: "cluster1",
  360. Name: "node1",
  361. }: 15.0,
  362. nodeIdentifierNoProviderID{
  363. Cluster: "cluster1",
  364. Name: "node2",
  365. }: 20.1,
  366. },
  367. cpuBreakdownMap: map[nodeIdentifierNoProviderID]*ClusterCostsBreakdown{
  368. nodeIdentifierNoProviderID{
  369. Cluster: "cluster1",
  370. Name: "node1",
  371. }: &ClusterCostsBreakdown{
  372. System: 20.2,
  373. User: 68.0,
  374. },
  375. nodeIdentifierNoProviderID{
  376. Cluster: "cluster1",
  377. Name: "node2",
  378. }: &ClusterCostsBreakdown{
  379. System: 28.9,
  380. User: 34.0,
  381. },
  382. },
  383. activeDataMap: map[NodeIdentifier]activeData{
  384. NodeIdentifier{
  385. Cluster: "cluster1",
  386. Name: "node1",
  387. ProviderID: "prov_node1_A",
  388. }: activeData{
  389. start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  390. end: time.Date(2020, 6, 16, 9, 20, 39, 0, time.UTC),
  391. minutes: 5*60 + 35 + (11.0 / 60.0),
  392. },
  393. NodeIdentifier{
  394. Cluster: "cluster1",
  395. Name: "node1",
  396. ProviderID: "prov_node1_B",
  397. }: activeData{
  398. start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  399. end: time.Date(2020, 6, 16, 9, 21, 39, 0, time.UTC),
  400. minutes: 5*60 + 36 + (11.0 / 60.0),
  401. },
  402. NodeIdentifier{
  403. Cluster: "cluster1",
  404. Name: "node2",
  405. ProviderID: "prov_node2_A",
  406. }: activeData{
  407. start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  408. end: time.Date(2020, 6, 16, 9, 10, 39, 0, time.UTC),
  409. minutes: 5*60 + 25 + (11.0 / 60.0),
  410. },
  411. },
  412. preemptibleMap: map[NodeIdentifier]bool{
  413. NodeIdentifier{
  414. Cluster: "cluster1",
  415. Name: "node1",
  416. ProviderID: "prov_node1_A",
  417. }: true,
  418. NodeIdentifier{
  419. Cluster: "cluster1",
  420. Name: "node1",
  421. ProviderID: "prov_node1_B",
  422. }: false,
  423. NodeIdentifier{
  424. Cluster: "cluster1",
  425. Name: "node2",
  426. ProviderID: "prov_node2_A",
  427. }: false,
  428. },
  429. labelsMap: map[nodeIdentifierNoProviderID]map[string]string{
  430. nodeIdentifierNoProviderID{
  431. Cluster: "cluster1",
  432. Name: "node1",
  433. }: map[string]string{
  434. "labelname1_A": "labelvalue1_A",
  435. "labelname1_B": "labelvalue1_B",
  436. },
  437. nodeIdentifierNoProviderID{
  438. Cluster: "cluster1",
  439. Name: "node2",
  440. }: map[string]string{
  441. "labelname2_A": "labelvalue2_A",
  442. "labelname2_B": "labelvalue2_B",
  443. },
  444. },
  445. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  446. nodeIdentifierNoProviderID{
  447. Cluster: "cluster1",
  448. Name: "node1",
  449. }: "type1",
  450. nodeIdentifierNoProviderID{
  451. Cluster: "cluster1",
  452. Name: "node2",
  453. }: "type2",
  454. },
  455. expected: map[NodeIdentifier]*Node{
  456. NodeIdentifier{
  457. Cluster: "cluster1",
  458. Name: "node1",
  459. ProviderID: "prov_node1_A",
  460. }: &Node{
  461. Cluster: "cluster1",
  462. Name: "node1",
  463. ProviderID: "prov_node1_A",
  464. NodeType: "type1",
  465. CPUCost: 0.048,
  466. RAMCost: 0.09,
  467. GPUCost: 0.8,
  468. CPUCores: 2.0,
  469. GPUCount: 1.0,
  470. RAMBytes: 2048.0,
  471. RAMBreakdown: &ClusterCostsBreakdown{
  472. User: 30.0,
  473. System: 15.0,
  474. },
  475. CPUBreakdown: &ClusterCostsBreakdown{
  476. System: 20.2,
  477. User: 68.0,
  478. },
  479. Start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  480. End: time.Date(2020, 6, 16, 9, 20, 39, 0, time.UTC),
  481. Minutes: 5*60 + 35 + (11.0 / 60.0),
  482. Preemptible: true,
  483. Labels: map[string]string{
  484. "labelname1_A": "labelvalue1_A",
  485. "labelname1_B": "labelvalue1_B",
  486. },
  487. },
  488. NodeIdentifier{
  489. Cluster: "cluster1",
  490. Name: "node1",
  491. ProviderID: "prov_node1_B",
  492. }: &Node{
  493. Cluster: "cluster1",
  494. Name: "node1",
  495. ProviderID: "prov_node1_B",
  496. NodeType: "type1",
  497. CPUCost: 0.087,
  498. RAMCost: 0.3,
  499. GPUCost: 1.4,
  500. CPUCores: 2.0,
  501. GPUCount: 1.0,
  502. RAMBytes: 2048.0,
  503. RAMBreakdown: &ClusterCostsBreakdown{
  504. User: 30.0,
  505. System: 15.0,
  506. },
  507. CPUBreakdown: &ClusterCostsBreakdown{
  508. System: 20.2,
  509. User: 68.0,
  510. },
  511. Start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  512. End: time.Date(2020, 6, 16, 9, 21, 39, 0, time.UTC),
  513. Minutes: 5*60 + 36 + (11.0 / 60.0),
  514. Preemptible: false,
  515. Labels: map[string]string{
  516. "labelname1_A": "labelvalue1_A",
  517. "labelname1_B": "labelvalue1_B",
  518. },
  519. },
  520. NodeIdentifier{
  521. Cluster: "cluster1",
  522. Name: "node2",
  523. ProviderID: "prov_node2_A",
  524. }: &Node{
  525. Cluster: "cluster1",
  526. Name: "node2",
  527. ProviderID: "prov_node2_A",
  528. NodeType: "type2",
  529. CPUCost: 0.033,
  530. RAMCost: 0.024,
  531. GPUCost: 3.1,
  532. CPUCores: 5.0,
  533. GPUCount: 2.0,
  534. RAMBytes: 6303.0,
  535. RAMBreakdown: &ClusterCostsBreakdown{
  536. User: 42.6,
  537. System: 20.1,
  538. },
  539. CPUBreakdown: &ClusterCostsBreakdown{
  540. System: 28.9,
  541. User: 34.0,
  542. },
  543. Start: time.Date(2020, 6, 16, 3, 45, 28, 0, time.UTC),
  544. End: time.Date(2020, 6, 16, 9, 10, 39, 0, time.UTC),
  545. Minutes: 5*60 + 25 + (11.0 / 60.0),
  546. Preemptible: false,
  547. Labels: map[string]string{
  548. "labelname2_A": "labelvalue2_A",
  549. "labelname2_B": "labelvalue2_B",
  550. },
  551. },
  552. },
  553. },
  554. {
  555. name: "e2-micro cpu cost adjustment",
  556. cpuCostMap: map[NodeIdentifier]float64{
  557. NodeIdentifier{
  558. Cluster: "cluster1",
  559. Name: "node1",
  560. ProviderID: "prov_node1",
  561. }: 0.048,
  562. },
  563. cpuCoresMap: map[nodeIdentifierNoProviderID]float64{
  564. nodeIdentifierNoProviderID{
  565. Cluster: "cluster1",
  566. Name: "node1",
  567. }: 6.0, // GKE lies about number of cores
  568. },
  569. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  570. nodeIdentifierNoProviderID{
  571. Cluster: "cluster1",
  572. Name: "node1",
  573. }: "e2-micro", // for this node type
  574. },
  575. expected: map[NodeIdentifier]*Node{
  576. NodeIdentifier{
  577. Cluster: "cluster1",
  578. Name: "node1",
  579. ProviderID: "prov_node1",
  580. }: &Node{
  581. Cluster: "cluster1",
  582. Name: "node1",
  583. ProviderID: "prov_node1",
  584. NodeType: "e2-micro",
  585. CPUCost: 0.048 * (partialCPUMap["e2-micro"] / 6.0), // adjustmentFactor is (v / GKE cores)
  586. CPUCores: partialCPUMap["e2-micro"],
  587. CPUBreakdown: &ClusterCostsBreakdown{},
  588. RAMBreakdown: &ClusterCostsBreakdown{},
  589. },
  590. },
  591. },
  592. {
  593. name: "e2-small cpu cost adjustment",
  594. cpuCostMap: map[NodeIdentifier]float64{
  595. NodeIdentifier{
  596. Cluster: "cluster1",
  597. Name: "node1",
  598. ProviderID: "prov_node1",
  599. }: 0.048,
  600. },
  601. cpuCoresMap: map[nodeIdentifierNoProviderID]float64{
  602. nodeIdentifierNoProviderID{
  603. Cluster: "cluster1",
  604. Name: "node1",
  605. }: 6.0, // GKE lies about number of cores
  606. },
  607. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  608. nodeIdentifierNoProviderID{
  609. Cluster: "cluster1",
  610. Name: "node1",
  611. }: "e2-small", // for this node type
  612. },
  613. expected: map[NodeIdentifier]*Node{
  614. NodeIdentifier{
  615. Cluster: "cluster1",
  616. Name: "node1",
  617. ProviderID: "prov_node1",
  618. }: &Node{
  619. Cluster: "cluster1",
  620. Name: "node1",
  621. ProviderID: "prov_node1",
  622. NodeType: "e2-small",
  623. CPUCost: 0.048 * (partialCPUMap["e2-small"] / 6.0), // adjustmentFactor is (v / GKE cores)
  624. CPUCores: partialCPUMap["e2-small"],
  625. CPUBreakdown: &ClusterCostsBreakdown{},
  626. RAMBreakdown: &ClusterCostsBreakdown{},
  627. },
  628. },
  629. },
  630. {
  631. name: "e2-medium cpu cost adjustment",
  632. cpuCostMap: map[NodeIdentifier]float64{
  633. NodeIdentifier{
  634. Cluster: "cluster1",
  635. Name: "node1",
  636. ProviderID: "prov_node1",
  637. }: 0.048,
  638. },
  639. cpuCoresMap: map[nodeIdentifierNoProviderID]float64{
  640. nodeIdentifierNoProviderID{
  641. Cluster: "cluster1",
  642. Name: "node1",
  643. }: 6.0, // GKE lies about number of cores
  644. },
  645. clusterAndNameToType: map[nodeIdentifierNoProviderID]string{
  646. nodeIdentifierNoProviderID{
  647. Cluster: "cluster1",
  648. Name: "node1",
  649. }: "e2-medium", // for this node type
  650. },
  651. expected: map[NodeIdentifier]*Node{
  652. NodeIdentifier{
  653. Cluster: "cluster1",
  654. Name: "node1",
  655. ProviderID: "prov_node1",
  656. }: &Node{
  657. Cluster: "cluster1",
  658. Name: "node1",
  659. ProviderID: "prov_node1",
  660. NodeType: "e2-medium",
  661. CPUCost: 0.048 * (partialCPUMap["e2-medium"] / 6.0), // adjustmentFactor is (v / GKE cores)
  662. CPUCores: partialCPUMap["e2-medium"],
  663. CPUBreakdown: &ClusterCostsBreakdown{},
  664. RAMBreakdown: &ClusterCostsBreakdown{},
  665. },
  666. },
  667. },
  668. }
  669. for _, testCase := range cases {
  670. t.Run(testCase.name, func(t *testing.T) {
  671. result := buildNodeMap(
  672. testCase.cpuCostMap, testCase.ramCostMap, testCase.gpuCostMap, testCase.gpuCountMap,
  673. testCase.cpuCoresMap, testCase.ramBytesMap, testCase.ramUserPctMap,
  674. testCase.ramSystemPctMap,
  675. testCase.cpuBreakdownMap,
  676. testCase.activeDataMap,
  677. testCase.preemptibleMap,
  678. testCase.labelsMap,
  679. testCase.clusterAndNameToType,
  680. )
  681. if !reflect.DeepEqual(result, testCase.expected) {
  682. t.Errorf("buildNodeMap case %s failed. Got %+v but expected %+v", testCase.name, result, testCase.expected)
  683. // Use spew because we have to follow pointers to figure out
  684. // what isn't matching up
  685. t.Logf("Got: %s", spew.Sdump(result))
  686. t.Logf("Expected: %s", spew.Sdump(testCase.expected))
  687. }
  688. })
  689. }
  690. }
  691. func TestBuildGPUCostMap(t *testing.T) {
  692. cases := []struct {
  693. name string
  694. promResult []*prom.QueryResult
  695. countMap map[NodeIdentifier]float64
  696. expected map[NodeIdentifier]float64
  697. }{
  698. {
  699. name: "All Zeros",
  700. promResult: []*prom.QueryResult{
  701. {
  702. Metric: map[string]interface{}{
  703. "cluster_id": "cluster1",
  704. "node": "node1",
  705. "instance_type": "type1",
  706. "provider_id": "provider1",
  707. },
  708. Values: []*util.Vector{
  709. &util.Vector{
  710. Timestamp: 0,
  711. Value: 0,
  712. },
  713. },
  714. },
  715. },
  716. countMap: map[NodeIdentifier]float64{
  717. NodeIdentifier{
  718. Cluster: "cluster1",
  719. Name: "node1",
  720. ProviderID: "provider1",
  721. }: 0,
  722. },
  723. expected: map[NodeIdentifier]float64{
  724. NodeIdentifier{
  725. Cluster: "cluster1",
  726. Name: "node1",
  727. ProviderID: "provider1",
  728. }: 0,
  729. },
  730. },
  731. {
  732. name: "Zero Node Count",
  733. promResult: []*prom.QueryResult{
  734. {
  735. Metric: map[string]interface{}{
  736. "cluster_id": "cluster1",
  737. "node": "node1",
  738. "instance_type": "type1",
  739. "provider_id": "provider1",
  740. },
  741. Values: []*util.Vector{
  742. &util.Vector{
  743. Timestamp: 0,
  744. Value: 2,
  745. },
  746. },
  747. },
  748. },
  749. countMap: map[NodeIdentifier]float64{
  750. NodeIdentifier{
  751. Cluster: "cluster1",
  752. Name: "node1",
  753. ProviderID: "provider1",
  754. }: 0,
  755. },
  756. expected: map[NodeIdentifier]float64{
  757. NodeIdentifier{
  758. Cluster: "cluster1",
  759. Name: "node1",
  760. ProviderID: "provider1",
  761. }: 2,
  762. },
  763. },
  764. {
  765. name: "Missing Node Count",
  766. promResult: []*prom.QueryResult{
  767. {
  768. Metric: map[string]interface{}{
  769. "cluster_id": "cluster1",
  770. "node": "node1",
  771. "instance_type": "type1",
  772. "provider_id": "provider1",
  773. },
  774. Values: []*util.Vector{
  775. &util.Vector{
  776. Timestamp: 0,
  777. Value: 2,
  778. },
  779. },
  780. },
  781. },
  782. countMap: map[NodeIdentifier]float64{},
  783. expected: map[NodeIdentifier]float64{
  784. NodeIdentifier{
  785. Cluster: "cluster1",
  786. Name: "node1",
  787. ProviderID: "provider1",
  788. }: 2,
  789. },
  790. },
  791. {
  792. name: "missing cost data",
  793. promResult: []*prom.QueryResult{
  794. {},
  795. },
  796. countMap: map[NodeIdentifier]float64{
  797. NodeIdentifier{
  798. Cluster: "cluster1",
  799. Name: "node1",
  800. ProviderID: "provider1",
  801. }: 0,
  802. },
  803. expected: map[NodeIdentifier]float64{},
  804. },
  805. {
  806. name: "All values present",
  807. promResult: []*prom.QueryResult{
  808. {
  809. Metric: map[string]interface{}{
  810. "cluster_id": "cluster1",
  811. "node": "node1",
  812. "instance_type": "type1",
  813. "provider_id": "provider1",
  814. },
  815. Values: []*util.Vector{
  816. &util.Vector{
  817. Timestamp: 0,
  818. Value: 2,
  819. },
  820. },
  821. },
  822. },
  823. countMap: map[NodeIdentifier]float64{
  824. NodeIdentifier{
  825. Cluster: "cluster1",
  826. Name: "node1",
  827. ProviderID: "provider1",
  828. }: 2,
  829. },
  830. expected: map[NodeIdentifier]float64{
  831. NodeIdentifier{
  832. Cluster: "cluster1",
  833. Name: "node1",
  834. ProviderID: "provider1",
  835. }: 4,
  836. },
  837. },
  838. }
  839. for _, testCase := range cases {
  840. t.Run(testCase.name, func(t *testing.T) {
  841. result, _ := buildGPUCostMap(testCase.promResult, testCase.countMap)
  842. if !reflect.DeepEqual(result, testCase.expected) {
  843. t.Errorf("buildGPUCostMap case %s failed. Got %+v but expected %+v", testCase.name, result, testCase.expected)
  844. }
  845. })
  846. }
  847. }