clustercache_test.go 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422
  1. package scrape
  2. import (
  3. "reflect"
  4. "testing"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/clustercache"
  7. "github.com/opencost/opencost/core/pkg/source"
  8. "github.com/opencost/opencost/modules/collector-source/pkg/metric"
  9. "github.com/opencost/opencost/modules/collector-source/pkg/util"
  10. v1 "k8s.io/api/core/v1"
  11. "k8s.io/apimachinery/pkg/api/resource"
  12. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  13. "k8s.io/apimachinery/pkg/types"
  14. )
  15. var Start1Str = "2025-01-01T00:00:00Z00:00"
  16. func Test_kubernetesScraper_scrapeNodes(t *testing.T) {
  17. start1, _ := time.Parse(time.RFC3339, Start1Str)
  18. type scrape struct {
  19. Nodes []*clustercache.Node
  20. Timestamp time.Time
  21. }
  22. tests := []struct {
  23. name string
  24. scrapes []scrape
  25. expected []metric.Update
  26. }{
  27. {
  28. name: "simple",
  29. scrapes: []scrape{
  30. {
  31. Nodes: []*clustercache.Node{
  32. {
  33. Name: "node1",
  34. UID: "uuid1",
  35. SpecProviderID: "i-1",
  36. Status: v1.NodeStatus{
  37. Capacity: v1.ResourceList{
  38. v1.ResourceCPU: resource.MustParse("2"),
  39. v1.ResourceMemory: resource.MustParse("2048"),
  40. },
  41. Allocatable: v1.ResourceList{
  42. v1.ResourceCPU: resource.MustParse("1"),
  43. v1.ResourceMemory: resource.MustParse("1024"),
  44. },
  45. },
  46. Labels: map[string]string{
  47. "test1": "blah",
  48. "test2": "blah2",
  49. },
  50. },
  51. },
  52. Timestamp: start1,
  53. },
  54. },
  55. expected: []metric.Update{
  56. {
  57. Name: metric.NodeInfo,
  58. Labels: map[string]string{
  59. source.NodeLabel: "node1",
  60. source.ProviderIDLabel: "i-1",
  61. source.UIDLabel: "uuid1",
  62. },
  63. Value: 0,
  64. AdditionalInfo: map[string]string{
  65. source.NodeLabel: "node1",
  66. source.ProviderIDLabel: "i-1",
  67. source.UIDLabel: "uuid1",
  68. },
  69. },
  70. {
  71. Name: metric.NodeResourceCapacities,
  72. Labels: map[string]string{
  73. source.NodeLabel: "node1",
  74. source.ProviderIDLabel: "i-1",
  75. source.UIDLabel: "uuid1",
  76. source.ResourceLabel: "cpu",
  77. source.UnitLabel: "core",
  78. },
  79. Value: 2.0,
  80. AdditionalInfo: nil,
  81. },
  82. {
  83. Name: metric.NodeResourceCapacities,
  84. Labels: map[string]string{
  85. source.NodeLabel: "node1",
  86. source.ProviderIDLabel: "i-1",
  87. source.UIDLabel: "uuid1",
  88. source.ResourceLabel: "memory",
  89. source.UnitLabel: "byte",
  90. },
  91. Value: 2048.0,
  92. AdditionalInfo: nil,
  93. },
  94. {
  95. Name: metric.KubeNodeStatusCapacityCPUCores,
  96. Labels: map[string]string{
  97. source.NodeLabel: "node1",
  98. source.ProviderIDLabel: "i-1",
  99. source.UIDLabel: "uuid1",
  100. },
  101. Value: 2.0,
  102. AdditionalInfo: nil,
  103. },
  104. {
  105. Name: metric.KubeNodeStatusCapacityMemoryBytes,
  106. Labels: map[string]string{
  107. source.NodeLabel: "node1",
  108. source.ProviderIDLabel: "i-1",
  109. source.UIDLabel: "uuid1",
  110. },
  111. Value: 2048.0,
  112. AdditionalInfo: nil,
  113. },
  114. {
  115. Name: metric.NodeResourcesAllocatable,
  116. Labels: map[string]string{
  117. source.NodeLabel: "node1",
  118. source.ProviderIDLabel: "i-1",
  119. source.UIDLabel: "uuid1",
  120. source.ResourceLabel: "cpu",
  121. source.UnitLabel: "core",
  122. },
  123. Value: 1.0,
  124. AdditionalInfo: nil,
  125. },
  126. {
  127. Name: metric.NodeResourcesAllocatable,
  128. Labels: map[string]string{
  129. source.NodeLabel: "node1",
  130. source.ProviderIDLabel: "i-1",
  131. source.UIDLabel: "uuid1",
  132. source.ResourceLabel: "memory",
  133. source.UnitLabel: "byte",
  134. },
  135. Value: 1024.0,
  136. AdditionalInfo: nil,
  137. },
  138. {
  139. Name: metric.KubeNodeStatusAllocatableCPUCores,
  140. Labels: map[string]string{
  141. source.NodeLabel: "node1",
  142. source.ProviderIDLabel: "i-1",
  143. source.UIDLabel: "uuid1",
  144. },
  145. Value: 1.0,
  146. AdditionalInfo: nil,
  147. },
  148. {
  149. Name: metric.KubeNodeStatusAllocatableMemoryBytes,
  150. Labels: map[string]string{
  151. source.NodeLabel: "node1",
  152. source.ProviderIDLabel: "i-1",
  153. source.UIDLabel: "uuid1",
  154. },
  155. Value: 1024.0,
  156. AdditionalInfo: nil,
  157. },
  158. {
  159. Name: metric.KubeNodeLabels,
  160. Labels: map[string]string{
  161. source.NodeLabel: "node1",
  162. source.ProviderIDLabel: "i-1",
  163. source.UIDLabel: "uuid1",
  164. },
  165. Value: 0,
  166. AdditionalInfo: map[string]string{
  167. "label_test1": "blah",
  168. "label_test2": "blah2",
  169. },
  170. },
  171. },
  172. },
  173. }
  174. for _, tt := range tests {
  175. t.Run(tt.name, func(t *testing.T) {
  176. ks := &ClusterCacheScraper{}
  177. var scrapeResults []metric.Update
  178. for _, s := range tt.scrapes {
  179. res := ks.scrapeNodes(s.Nodes)
  180. scrapeResults = append(scrapeResults, res...)
  181. }
  182. if len(scrapeResults) != len(tt.expected) {
  183. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  184. }
  185. for i, expected := range tt.expected {
  186. got := scrapeResults[i]
  187. if !reflect.DeepEqual(expected, got) {
  188. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  189. }
  190. }
  191. })
  192. }
  193. }
  194. func Test_kubernetesScraper_scrapeDeployments(t *testing.T) {
  195. start1, _ := time.Parse(time.RFC3339, Start1Str)
  196. type scrape struct {
  197. Deployments []*clustercache.Deployment
  198. Timestamp time.Time
  199. }
  200. tests := []struct {
  201. name string
  202. scrapes []scrape
  203. expected []metric.Update
  204. }{
  205. {
  206. name: "simple",
  207. scrapes: []scrape{
  208. {
  209. Deployments: []*clustercache.Deployment{
  210. {
  211. Name: "deployment1",
  212. Namespace: "namespace1",
  213. UID: "uuid1",
  214. MatchLabels: map[string]string{
  215. "test1": "blah",
  216. "test2": "blah2",
  217. },
  218. },
  219. },
  220. Timestamp: start1,
  221. },
  222. },
  223. // deploymentInfo map is shared across all 4 metrics and has namespace
  224. // added to it before DeploymentMatchLabels is appended, so all 4 metrics
  225. // reflect the final state: {uid, namespace_uid, deployment, namespace}.
  226. expected: []metric.Update{
  227. {
  228. Name: metric.DeploymentLabels,
  229. Labels: map[string]string{
  230. source.UIDLabel: "uuid1",
  231. source.NamespaceUIDLabel: "",
  232. source.DeploymentLabel: "deployment1",
  233. source.NamespaceLabel: "namespace1",
  234. },
  235. Value: 0,
  236. AdditionalInfo: map[string]string{
  237. source.UIDLabel: "uuid1",
  238. source.NamespaceUIDLabel: "",
  239. source.DeploymentLabel: "deployment1",
  240. source.NamespaceLabel: "namespace1",
  241. },
  242. },
  243. {
  244. Name: metric.DeploymentLabels,
  245. Labels: map[string]string{
  246. source.UIDLabel: "uuid1",
  247. source.NamespaceUIDLabel: "",
  248. source.DeploymentLabel: "deployment1",
  249. source.NamespaceLabel: "namespace1",
  250. },
  251. Value: 0,
  252. AdditionalInfo: map[string]string{},
  253. },
  254. {
  255. Name: metric.DeploymentAnnotations,
  256. Labels: map[string]string{
  257. source.UIDLabel: "uuid1",
  258. source.NamespaceUIDLabel: "",
  259. source.DeploymentLabel: "deployment1",
  260. source.NamespaceLabel: "namespace1",
  261. },
  262. Value: 0,
  263. AdditionalInfo: map[string]string{},
  264. },
  265. {
  266. Name: metric.DeploymentMatchLabels,
  267. Labels: map[string]string{
  268. source.UIDLabel: "uuid1",
  269. source.NamespaceUIDLabel: "",
  270. source.DeploymentLabel: "deployment1",
  271. source.NamespaceLabel: "namespace1",
  272. },
  273. Value: 0,
  274. AdditionalInfo: map[string]string{
  275. "label_test1": "blah",
  276. "label_test2": "blah2",
  277. },
  278. },
  279. },
  280. },
  281. }
  282. for _, tt := range tests {
  283. t.Run(tt.name, func(t *testing.T) {
  284. ks := &ClusterCacheScraper{}
  285. nsIndex := newSyncMap[string, types.UID](0)
  286. var scrapeResults []metric.Update
  287. for _, s := range tt.scrapes {
  288. res := ks.scrapeDeployments(s.Deployments, nsIndex)
  289. scrapeResults = append(scrapeResults, res...)
  290. }
  291. if len(scrapeResults) != len(tt.expected) {
  292. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  293. }
  294. for i, expected := range tt.expected {
  295. got := scrapeResults[i]
  296. if !reflect.DeepEqual(expected, got) {
  297. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  298. }
  299. }
  300. })
  301. }
  302. }
  303. func Test_kubernetesScraper_scrapeNamespaces(t *testing.T) {
  304. start1, _ := time.Parse(time.RFC3339, Start1Str)
  305. type scrape struct {
  306. Namespaces []*clustercache.Namespace
  307. Timestamp time.Time
  308. }
  309. tests := []struct {
  310. name string
  311. scrapes []scrape
  312. expected []metric.Update
  313. }{
  314. {
  315. name: "simple",
  316. scrapes: []scrape{
  317. {
  318. Namespaces: []*clustercache.Namespace{
  319. {
  320. Name: "namespace1",
  321. UID: "uuid1",
  322. Labels: map[string]string{
  323. "test1": "blah",
  324. "test2": "blah2",
  325. },
  326. Annotations: map[string]string{
  327. "test3": "blah3",
  328. "test4": "blah4",
  329. },
  330. },
  331. },
  332. Timestamp: start1,
  333. },
  334. },
  335. expected: []metric.Update{
  336. {
  337. Name: metric.NamespaceInfo,
  338. Labels: map[string]string{
  339. source.NamespaceLabel: "namespace1",
  340. source.UIDLabel: "uuid1",
  341. },
  342. Value: 0,
  343. AdditionalInfo: map[string]string{
  344. source.NamespaceLabel: "namespace1",
  345. source.UIDLabel: "uuid1",
  346. },
  347. },
  348. {
  349. Name: metric.KubeNamespaceLabels,
  350. Labels: map[string]string{
  351. source.NamespaceLabel: "namespace1",
  352. source.UIDLabel: "uuid1",
  353. },
  354. Value: 0,
  355. AdditionalInfo: map[string]string{
  356. "label_test1": "blah",
  357. "label_test2": "blah2",
  358. },
  359. },
  360. {
  361. Name: metric.KubeNamespaceAnnotations,
  362. Labels: map[string]string{
  363. source.NamespaceLabel: "namespace1",
  364. source.UIDLabel: "uuid1",
  365. },
  366. Value: 0,
  367. AdditionalInfo: map[string]string{
  368. "annotation_test3": "blah3",
  369. "annotation_test4": "blah4",
  370. },
  371. },
  372. },
  373. },
  374. }
  375. for _, tt := range tests {
  376. t.Run(tt.name, func(t *testing.T) {
  377. ks := &ClusterCacheScraper{}
  378. var scrapeResults []metric.Update
  379. for _, s := range tt.scrapes {
  380. res := ks.scrapeNamespaces(s.Namespaces)
  381. scrapeResults = append(scrapeResults, res...)
  382. }
  383. if len(scrapeResults) != len(tt.expected) {
  384. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  385. }
  386. for i, expected := range tt.expected {
  387. got := scrapeResults[i]
  388. if !reflect.DeepEqual(expected, got) {
  389. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  390. }
  391. }
  392. })
  393. }
  394. }
  395. func Test_kubernetesScraper_scrapePods(t *testing.T) {
  396. start1, _ := time.Parse(time.RFC3339, Start1Str)
  397. type scrape struct {
  398. Pods []*clustercache.Pod
  399. Timestamp time.Time
  400. }
  401. tests := []struct {
  402. name string
  403. scrapes []scrape
  404. expected []metric.Update
  405. }{
  406. {
  407. name: "simple",
  408. scrapes: []scrape{
  409. {
  410. Pods: []*clustercache.Pod{
  411. {
  412. Name: "pod1",
  413. Namespace: "namespace1",
  414. UID: "uuid1",
  415. Spec: clustercache.PodSpec{
  416. NodeName: "node1",
  417. Containers: []clustercache.Container{
  418. {
  419. Name: "container1",
  420. Resources: v1.ResourceRequirements{
  421. Requests: map[v1.ResourceName]resource.Quantity{
  422. v1.ResourceCPU: resource.MustParse("500m"),
  423. v1.ResourceMemory: resource.MustParse("512"),
  424. },
  425. Limits: map[v1.ResourceName]resource.Quantity{
  426. v1.ResourceCPU: resource.MustParse("1"),
  427. v1.ResourceMemory: resource.MustParse("1024"),
  428. },
  429. },
  430. },
  431. },
  432. },
  433. Labels: map[string]string{
  434. "test1": "blah",
  435. "test2": "blah2",
  436. },
  437. Annotations: map[string]string{
  438. "test3": "blah3",
  439. "test4": "blah4",
  440. },
  441. OwnerReferences: []metav1.OwnerReference{
  442. {
  443. Kind: source.DeploymentLabel,
  444. Name: "deployment1",
  445. Controller: nil,
  446. },
  447. },
  448. Status: clustercache.PodStatus{
  449. ContainerStatuses: []v1.ContainerStatus{
  450. {
  451. Name: "container1",
  452. State: v1.ContainerState{
  453. Running: &v1.ContainerStateRunning{},
  454. },
  455. },
  456. },
  457. },
  458. },
  459. },
  460. Timestamp: start1,
  461. },
  462. },
  463. // podInfo is mutated after PodInfo is appended (namespace/node/instance added),
  464. // so PodInfo.Labels also reflects those fields at assertion time.
  465. expected: []metric.Update{
  466. {
  467. Name: metric.PodInfo,
  468. Labels: map[string]string{
  469. source.UIDLabel: "uuid1",
  470. source.PodLabel: "pod1",
  471. source.NamespaceUIDLabel: "",
  472. source.NodeUIDLabel: "",
  473. source.NamespaceLabel: "namespace1",
  474. source.NodeLabel: "node1",
  475. source.InstanceLabel: "node1",
  476. },
  477. Value: 0,
  478. AdditionalInfo: map[string]string{
  479. source.UIDLabel: "uuid1",
  480. source.PodLabel: "pod1",
  481. source.NamespaceUIDLabel: "",
  482. source.NodeUIDLabel: "",
  483. source.NamespaceLabel: "namespace1",
  484. source.NodeLabel: "node1",
  485. source.InstanceLabel: "node1",
  486. },
  487. },
  488. {
  489. Name: metric.KubePodLabels,
  490. Labels: map[string]string{
  491. source.UIDLabel: "uuid1",
  492. source.PodLabel: "pod1",
  493. source.NamespaceUIDLabel: "",
  494. source.NodeUIDLabel: "",
  495. source.NamespaceLabel: "namespace1",
  496. source.NodeLabel: "node1",
  497. source.InstanceLabel: "node1",
  498. },
  499. Value: 0,
  500. AdditionalInfo: map[string]string{
  501. "label_test1": "blah",
  502. "label_test2": "blah2",
  503. },
  504. },
  505. {
  506. Name: metric.KubePodAnnotations,
  507. Labels: map[string]string{
  508. source.UIDLabel: "uuid1",
  509. source.PodLabel: "pod1",
  510. source.NamespaceUIDLabel: "",
  511. source.NodeUIDLabel: "",
  512. source.NamespaceLabel: "namespace1",
  513. source.NodeLabel: "node1",
  514. source.InstanceLabel: "node1",
  515. },
  516. Value: 0,
  517. AdditionalInfo: map[string]string{
  518. "annotation_test3": "blah3",
  519. "annotation_test4": "blah4",
  520. },
  521. },
  522. {
  523. Name: metric.KubePodOwner,
  524. Labels: map[string]string{
  525. source.UIDLabel: "uuid1",
  526. source.PodLabel: "pod1",
  527. source.NamespaceUIDLabel: "",
  528. source.NodeUIDLabel: "",
  529. source.NamespaceLabel: "namespace1",
  530. source.NodeLabel: "node1",
  531. source.InstanceLabel: "node1",
  532. source.OwnerKindLabel: "deployment",
  533. source.OwnerNameLabel: "deployment1",
  534. source.OwnerUIDLabel: "",
  535. source.ContainerLabel: "false",
  536. },
  537. Value: 0,
  538. AdditionalInfo: nil,
  539. },
  540. {
  541. Name: metric.KubePodContainerStatusRunning,
  542. Labels: map[string]string{
  543. source.UIDLabel: "uuid1",
  544. source.PodLabel: "pod1",
  545. source.NamespaceUIDLabel: "",
  546. source.NodeUIDLabel: "",
  547. source.NamespaceLabel: "namespace1",
  548. source.NodeLabel: "node1",
  549. source.InstanceLabel: "node1",
  550. source.ContainerLabel: "container1",
  551. },
  552. Value: 0,
  553. AdditionalInfo: map[string]string{
  554. source.UIDLabel: "uuid1",
  555. source.PodLabel: "pod1",
  556. source.NamespaceUIDLabel: "",
  557. source.NodeUIDLabel: "",
  558. source.NamespaceLabel: "namespace1",
  559. source.NodeLabel: "node1",
  560. source.InstanceLabel: "node1",
  561. source.ContainerLabel: "container1",
  562. },
  563. },
  564. {
  565. Name: metric.KubePodContainerResourceRequests,
  566. Labels: map[string]string{
  567. source.UIDLabel: "uuid1",
  568. source.PodLabel: "pod1",
  569. source.NamespaceUIDLabel: "",
  570. source.NodeUIDLabel: "",
  571. source.NamespaceLabel: "namespace1",
  572. source.NodeLabel: "node1",
  573. source.InstanceLabel: "node1",
  574. source.ContainerLabel: "container1",
  575. source.ResourceLabel: "cpu",
  576. source.UnitLabel: "core",
  577. },
  578. Value: 0.5,
  579. AdditionalInfo: nil,
  580. },
  581. {
  582. Name: metric.KubePodContainerResourceRequests,
  583. Labels: map[string]string{
  584. source.UIDLabel: "uuid1",
  585. source.PodLabel: "pod1",
  586. source.NamespaceUIDLabel: "",
  587. source.NodeUIDLabel: "",
  588. source.NamespaceLabel: "namespace1",
  589. source.NodeLabel: "node1",
  590. source.InstanceLabel: "node1",
  591. source.ContainerLabel: "container1",
  592. source.ResourceLabel: "memory",
  593. source.UnitLabel: "byte",
  594. },
  595. Value: 512,
  596. AdditionalInfo: nil,
  597. },
  598. {
  599. Name: metric.KubePodContainerResourceLimits,
  600. Labels: map[string]string{
  601. source.UIDLabel: "uuid1",
  602. source.PodLabel: "pod1",
  603. source.NamespaceUIDLabel: "",
  604. source.NodeUIDLabel: "",
  605. source.NamespaceLabel: "namespace1",
  606. source.NodeLabel: "node1",
  607. source.InstanceLabel: "node1",
  608. source.ContainerLabel: "container1",
  609. source.ResourceLabel: "cpu",
  610. source.UnitLabel: "core",
  611. },
  612. Value: 1,
  613. AdditionalInfo: nil,
  614. },
  615. {
  616. Name: metric.KubePodContainerResourceLimits,
  617. Labels: map[string]string{
  618. source.UIDLabel: "uuid1",
  619. source.PodLabel: "pod1",
  620. source.NamespaceUIDLabel: "",
  621. source.NodeUIDLabel: "",
  622. source.NamespaceLabel: "namespace1",
  623. source.NodeLabel: "node1",
  624. source.InstanceLabel: "node1",
  625. source.ContainerLabel: "container1",
  626. source.ResourceLabel: "memory",
  627. source.UnitLabel: "byte",
  628. },
  629. Value: 1024,
  630. AdditionalInfo: nil,
  631. },
  632. },
  633. },
  634. }
  635. for _, tt := range tests {
  636. t.Run(tt.name, func(t *testing.T) {
  637. ks := &ClusterCacheScraper{}
  638. nodeIndex := newSyncMap[string, types.UID](0)
  639. nsIndex := newSyncMap[string, types.UID](0)
  640. pvcIndex := newSyncMap[pvcKey, types.UID](0)
  641. var scrapeResults []metric.Update
  642. for _, s := range tt.scrapes {
  643. res := ks.scrapePods(s.Pods, nodeIndex, nsIndex, pvcIndex)
  644. scrapeResults = append(scrapeResults, res...)
  645. }
  646. if len(scrapeResults) != len(tt.expected) {
  647. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  648. }
  649. for i, expected := range tt.expected {
  650. got := scrapeResults[i]
  651. if !reflect.DeepEqual(expected, got) {
  652. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  653. }
  654. }
  655. })
  656. }
  657. }
  658. func Test_kubernetesScraper_scrapePVCs(t *testing.T) {
  659. start1, _ := time.Parse(time.RFC3339, Start1Str)
  660. type scrape struct {
  661. PVCs []*clustercache.PersistentVolumeClaim
  662. Timestamp time.Time
  663. }
  664. tests := []struct {
  665. name string
  666. scrapes []scrape
  667. expected []metric.Update
  668. }{
  669. {
  670. name: "simple",
  671. scrapes: []scrape{
  672. {
  673. PVCs: []*clustercache.PersistentVolumeClaim{
  674. {
  675. Name: "pvc1",
  676. Namespace: "namespace1",
  677. UID: "uuid1",
  678. Spec: v1.PersistentVolumeClaimSpec{
  679. VolumeName: "vol1",
  680. StorageClassName: util.Ptr("storageClass1"),
  681. Resources: v1.VolumeResourceRequirements{
  682. Requests: v1.ResourceList{
  683. v1.ResourceStorage: resource.MustParse("4096"),
  684. },
  685. },
  686. },
  687. },
  688. },
  689. Timestamp: start1,
  690. },
  691. },
  692. expected: []metric.Update{
  693. {
  694. Name: metric.KubePersistentVolumeClaimInfo,
  695. Labels: map[string]string{
  696. source.UIDLabel: "uuid1",
  697. source.PVCLabel: "pvc1",
  698. source.NamespaceUIDLabel: "",
  699. source.NamespaceLabel: "namespace1",
  700. source.VolumeNameLabel: "vol1",
  701. source.PVUIDLabel: "",
  702. source.StorageClassLabel: "storageClass1",
  703. },
  704. Value: 0,
  705. AdditionalInfo: map[string]string{
  706. source.UIDLabel: "uuid1",
  707. source.PVCLabel: "pvc1",
  708. source.NamespaceUIDLabel: "",
  709. source.NamespaceLabel: "namespace1",
  710. source.VolumeNameLabel: "vol1",
  711. source.PVUIDLabel: "",
  712. source.StorageClassLabel: "storageClass1",
  713. },
  714. },
  715. {
  716. Name: metric.KubePersistentVolumeClaimResourceRequestsStorageBytes,
  717. Labels: map[string]string{
  718. source.UIDLabel: "uuid1",
  719. source.PVCLabel: "pvc1",
  720. source.NamespaceUIDLabel: "",
  721. source.NamespaceLabel: "namespace1",
  722. source.VolumeNameLabel: "vol1",
  723. source.PVUIDLabel: "",
  724. source.StorageClassLabel: "storageClass1",
  725. },
  726. Value: 4096,
  727. AdditionalInfo: nil,
  728. },
  729. },
  730. },
  731. }
  732. for _, tt := range tests {
  733. t.Run(tt.name, func(t *testing.T) {
  734. ks := &ClusterCacheScraper{}
  735. nsIndex := newSyncMap[string, types.UID](0)
  736. pvIndex := newSyncMap[string, types.UID](0)
  737. var scrapeResults []metric.Update
  738. for _, s := range tt.scrapes {
  739. res := ks.scrapePVCs(s.PVCs, nsIndex, pvIndex)
  740. scrapeResults = append(scrapeResults, res...)
  741. }
  742. if len(scrapeResults) != len(tt.expected) {
  743. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  744. }
  745. for i, expected := range tt.expected {
  746. got := scrapeResults[i]
  747. if !reflect.DeepEqual(expected, got) {
  748. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  749. }
  750. }
  751. })
  752. }
  753. }
  754. func Test_kubernetesScraper_scrapePVs(t *testing.T) {
  755. start1, _ := time.Parse(time.RFC3339, Start1Str)
  756. type scrape struct {
  757. PVs []*clustercache.PersistentVolume
  758. Timestamp time.Time
  759. }
  760. tests := []struct {
  761. name string
  762. scrapes []scrape
  763. expected []metric.Update
  764. }{
  765. {
  766. name: "simple",
  767. scrapes: []scrape{
  768. {
  769. PVs: []*clustercache.PersistentVolume{
  770. {
  771. Name: "pv1",
  772. UID: "uuid1",
  773. Spec: v1.PersistentVolumeSpec{
  774. StorageClassName: "storageClass1",
  775. PersistentVolumeSource: v1.PersistentVolumeSource{
  776. CSI: &v1.CSIPersistentVolumeSource{
  777. VolumeHandle: "vol-1",
  778. },
  779. },
  780. Capacity: v1.ResourceList{
  781. v1.ResourceStorage: resource.MustParse("4096"),
  782. },
  783. },
  784. },
  785. },
  786. Timestamp: start1,
  787. },
  788. },
  789. expected: []metric.Update{
  790. {
  791. Name: metric.KubecostPVInfo,
  792. Labels: map[string]string{
  793. source.UIDLabel: "uuid1",
  794. source.PVLabel: "pv1",
  795. source.StorageClassLabel: "storageClass1",
  796. source.ProviderIDLabel: "vol-1",
  797. source.CSIVolumeHandleLabel: "vol-1",
  798. },
  799. Value: 0,
  800. AdditionalInfo: map[string]string{
  801. source.UIDLabel: "uuid1",
  802. source.PVLabel: "pv1",
  803. source.StorageClassLabel: "storageClass1",
  804. source.ProviderIDLabel: "vol-1",
  805. source.CSIVolumeHandleLabel: "vol-1",
  806. },
  807. },
  808. {
  809. Name: metric.KubePersistentVolumeCapacityBytes,
  810. Labels: map[string]string{
  811. source.UIDLabel: "uuid1",
  812. source.PVLabel: "pv1",
  813. source.StorageClassLabel: "storageClass1",
  814. source.ProviderIDLabel: "vol-1",
  815. source.CSIVolumeHandleLabel: "vol-1",
  816. },
  817. Value: 4096,
  818. AdditionalInfo: nil,
  819. },
  820. },
  821. },
  822. }
  823. for _, tt := range tests {
  824. t.Run(tt.name, func(t *testing.T) {
  825. ks := &ClusterCacheScraper{}
  826. var scrapeResults []metric.Update
  827. for _, s := range tt.scrapes {
  828. res := ks.scrapePVs(s.PVs)
  829. scrapeResults = append(scrapeResults, res...)
  830. }
  831. if len(scrapeResults) != len(tt.expected) {
  832. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  833. }
  834. for i, expected := range tt.expected {
  835. got := scrapeResults[i]
  836. if !reflect.DeepEqual(expected, got) {
  837. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  838. }
  839. }
  840. })
  841. }
  842. }
  843. func Test_kubernetesScraper_scrapeServices(t *testing.T) {
  844. start1, _ := time.Parse(time.RFC3339, Start1Str)
  845. type scrape struct {
  846. Services []*clustercache.Service
  847. Timestamp time.Time
  848. }
  849. tests := []struct {
  850. name string
  851. scrapes []scrape
  852. expected []metric.Update
  853. }{
  854. {
  855. name: "simple",
  856. scrapes: []scrape{
  857. {
  858. Services: []*clustercache.Service{
  859. {
  860. Name: "service1",
  861. Namespace: "namespace1",
  862. UID: "uuid1",
  863. SpecSelector: map[string]string{
  864. "test1": "blah",
  865. "test2": "blah2",
  866. },
  867. },
  868. },
  869. Timestamp: start1,
  870. },
  871. },
  872. expected: []metric.Update{
  873. {
  874. Name: metric.ServiceInfo,
  875. Labels: map[string]string{
  876. source.UIDLabel: "uuid1",
  877. source.ServiceLabel: "service1",
  878. source.NamespaceLabel: "namespace1",
  879. source.ServiceTypeLabel: "",
  880. },
  881. Value: 0,
  882. AdditionalInfo: map[string]string{
  883. source.UIDLabel: "uuid1",
  884. source.ServiceLabel: "service1",
  885. source.NamespaceLabel: "namespace1",
  886. source.ServiceTypeLabel: "",
  887. },
  888. },
  889. {
  890. Name: metric.ServiceSelectorLabels,
  891. Labels: map[string]string{
  892. source.UIDLabel: "uuid1",
  893. source.ServiceLabel: "service1",
  894. source.NamespaceLabel: "namespace1",
  895. source.ServiceTypeLabel: "",
  896. },
  897. Value: 0,
  898. AdditionalInfo: map[string]string{
  899. "label_test1": "blah",
  900. "label_test2": "blah2",
  901. },
  902. },
  903. },
  904. },
  905. }
  906. for _, tt := range tests {
  907. t.Run(tt.name, func(t *testing.T) {
  908. ks := &ClusterCacheScraper{}
  909. var scrapeResults []metric.Update
  910. for _, s := range tt.scrapes {
  911. res := ks.scrapeServices(s.Services)
  912. scrapeResults = append(scrapeResults, res...)
  913. }
  914. if len(scrapeResults) != len(tt.expected) {
  915. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  916. }
  917. for i, expected := range tt.expected {
  918. got := scrapeResults[i]
  919. if !reflect.DeepEqual(expected, got) {
  920. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  921. }
  922. }
  923. })
  924. }
  925. }
  926. func Test_kubernetesScraper_scrapeStatefulSets(t *testing.T) {
  927. start1, _ := time.Parse(time.RFC3339, Start1Str)
  928. type scrape struct {
  929. StatefulSets []*clustercache.StatefulSet
  930. Timestamp time.Time
  931. }
  932. tests := []struct {
  933. name string
  934. scrapes []scrape
  935. expected []metric.Update
  936. }{
  937. {
  938. name: "simple",
  939. scrapes: []scrape{
  940. {
  941. StatefulSets: []*clustercache.StatefulSet{
  942. {
  943. Name: "statefulSet1",
  944. Namespace: "namespace1",
  945. UID: "uuid1",
  946. SpecSelector: &metav1.LabelSelector{
  947. MatchLabels: map[string]string{
  948. "test1": "blah",
  949. "test2": "blah2",
  950. },
  951. },
  952. },
  953. },
  954. Timestamp: start1,
  955. },
  956. },
  957. // statefulSetInfo map is shared across all 4 metrics; namespace is added
  958. // before StatefulSetMatchLabels is appended, so all 4 reflect the final state.
  959. expected: []metric.Update{
  960. {
  961. Name: metric.StatefulSetInfo,
  962. Labels: map[string]string{
  963. source.UIDLabel: "uuid1",
  964. source.NamespaceUIDLabel: "",
  965. source.StatefulSetLabel: "statefulSet1",
  966. source.NamespaceLabel: "namespace1",
  967. },
  968. Value: 0,
  969. AdditionalInfo: map[string]string{
  970. source.UIDLabel: "uuid1",
  971. source.NamespaceUIDLabel: "",
  972. source.StatefulSetLabel: "statefulSet1",
  973. source.NamespaceLabel: "namespace1",
  974. },
  975. },
  976. {
  977. Name: metric.StatefulSetLabels,
  978. Labels: map[string]string{
  979. source.UIDLabel: "uuid1",
  980. source.NamespaceUIDLabel: "",
  981. source.StatefulSetLabel: "statefulSet1",
  982. source.NamespaceLabel: "namespace1",
  983. },
  984. Value: 0,
  985. AdditionalInfo: map[string]string{},
  986. },
  987. {
  988. Name: metric.StatefulSetAnnotations,
  989. Labels: map[string]string{
  990. source.UIDLabel: "uuid1",
  991. source.NamespaceUIDLabel: "",
  992. source.StatefulSetLabel: "statefulSet1",
  993. source.NamespaceLabel: "namespace1",
  994. },
  995. Value: 0,
  996. AdditionalInfo: map[string]string{},
  997. },
  998. {
  999. Name: metric.StatefulSetMatchLabels,
  1000. Labels: map[string]string{
  1001. source.UIDLabel: "uuid1",
  1002. source.NamespaceUIDLabel: "",
  1003. source.StatefulSetLabel: "statefulSet1",
  1004. source.NamespaceLabel: "namespace1",
  1005. },
  1006. Value: 0,
  1007. AdditionalInfo: map[string]string{
  1008. "label_test1": "blah",
  1009. "label_test2": "blah2",
  1010. },
  1011. },
  1012. },
  1013. },
  1014. }
  1015. for _, tt := range tests {
  1016. t.Run(tt.name, func(t *testing.T) {
  1017. ks := &ClusterCacheScraper{}
  1018. nsIndex := newSyncMap[string, types.UID](0)
  1019. var scrapeResults []metric.Update
  1020. for _, s := range tt.scrapes {
  1021. res := ks.scrapeStatefulSets(s.StatefulSets, nsIndex)
  1022. scrapeResults = append(scrapeResults, res...)
  1023. }
  1024. if len(scrapeResults) != len(tt.expected) {
  1025. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1026. }
  1027. for i, expected := range tt.expected {
  1028. got := scrapeResults[i]
  1029. if !reflect.DeepEqual(expected, got) {
  1030. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1031. }
  1032. }
  1033. })
  1034. }
  1035. }
  1036. func Test_kubernetesScraper_scrapeReplicaSets(t *testing.T) {
  1037. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1038. type scrape struct {
  1039. ReplicaSets []*clustercache.ReplicaSet
  1040. Timestamp time.Time
  1041. }
  1042. tests := []struct {
  1043. name string
  1044. scrapes []scrape
  1045. expected []metric.Update
  1046. }{
  1047. {
  1048. name: "simple",
  1049. scrapes: []scrape{
  1050. {
  1051. ReplicaSets: []*clustercache.ReplicaSet{
  1052. {
  1053. Name: "replicaSet1",
  1054. Namespace: "namespace1",
  1055. UID: "uuid1",
  1056. OwnerReferences: []metav1.OwnerReference{
  1057. {
  1058. Name: "rollout1",
  1059. Kind: "Rollout",
  1060. },
  1061. },
  1062. },
  1063. {
  1064. Name: "pureReplicaSet",
  1065. Namespace: "namespace1",
  1066. UID: "uuid2",
  1067. OwnerReferences: []metav1.OwnerReference{},
  1068. },
  1069. },
  1070. Timestamp: start1,
  1071. },
  1072. },
  1073. expected: []metric.Update{
  1074. // replicaSet1: info/labels/annotations use replicaSetInfo (uid, namespace_uid, replicaset)
  1075. {
  1076. Name: metric.ReplicaSetInfo,
  1077. Labels: map[string]string{
  1078. source.UIDLabel: "uuid1",
  1079. source.NamespaceUIDLabel: "",
  1080. source.ReplicaSetLabel: "replicaSet1",
  1081. },
  1082. Value: 0,
  1083. AdditionalInfo: map[string]string{
  1084. source.UIDLabel: "uuid1",
  1085. source.NamespaceUIDLabel: "",
  1086. source.ReplicaSetLabel: "replicaSet1",
  1087. },
  1088. },
  1089. {
  1090. Name: metric.ReplicaSetLabels,
  1091. Labels: map[string]string{
  1092. source.UIDLabel: "uuid1",
  1093. source.NamespaceUIDLabel: "",
  1094. source.ReplicaSetLabel: "replicaSet1",
  1095. },
  1096. Value: 0,
  1097. AdditionalInfo: map[string]string{},
  1098. },
  1099. {
  1100. Name: metric.ReplicaSetAnnotations,
  1101. Labels: map[string]string{
  1102. source.UIDLabel: "uuid1",
  1103. source.NamespaceUIDLabel: "",
  1104. source.ReplicaSetLabel: "replicaSet1",
  1105. },
  1106. Value: 0,
  1107. AdditionalInfo: map[string]string{},
  1108. },
  1109. // replicaSet1 owner: uses replicaSetOwnerInfo (replicaset, namespace, uid) + owner fields
  1110. {
  1111. Name: metric.KubeReplicasetOwner,
  1112. Labels: map[string]string{
  1113. source.ReplicaSetLabel: "replicaSet1",
  1114. source.NamespaceLabel: "namespace1",
  1115. source.UIDLabel: "uuid1",
  1116. source.OwnerNameLabel: "rollout1",
  1117. source.OwnerKindLabel: "Rollout",
  1118. source.OwnerUIDLabel: "",
  1119. source.ControllerLabel: "false",
  1120. },
  1121. Value: 0,
  1122. },
  1123. // pureReplicaSet: info/labels/annotations
  1124. {
  1125. Name: metric.ReplicaSetInfo,
  1126. Labels: map[string]string{
  1127. source.UIDLabel: "uuid2",
  1128. source.NamespaceUIDLabel: "",
  1129. source.ReplicaSetLabel: "pureReplicaSet",
  1130. },
  1131. Value: 0,
  1132. AdditionalInfo: map[string]string{
  1133. source.UIDLabel: "uuid2",
  1134. source.NamespaceUIDLabel: "",
  1135. source.ReplicaSetLabel: "pureReplicaSet",
  1136. },
  1137. },
  1138. {
  1139. Name: metric.ReplicaSetLabels,
  1140. Labels: map[string]string{
  1141. source.UIDLabel: "uuid2",
  1142. source.NamespaceUIDLabel: "",
  1143. source.ReplicaSetLabel: "pureReplicaSet",
  1144. },
  1145. Value: 0,
  1146. AdditionalInfo: map[string]string{},
  1147. },
  1148. {
  1149. Name: metric.ReplicaSetAnnotations,
  1150. Labels: map[string]string{
  1151. source.UIDLabel: "uuid2",
  1152. source.NamespaceUIDLabel: "",
  1153. source.ReplicaSetLabel: "pureReplicaSet",
  1154. },
  1155. Value: 0,
  1156. AdditionalInfo: map[string]string{},
  1157. },
  1158. // pureReplicaSet owner: no owners path uses <none> sentinel, no owner_uid/controller
  1159. {
  1160. Name: metric.KubeReplicasetOwner,
  1161. Labels: map[string]string{
  1162. source.ReplicaSetLabel: "pureReplicaSet",
  1163. source.NamespaceLabel: "namespace1",
  1164. source.UIDLabel: "uuid2",
  1165. source.OwnerNameLabel: source.NoneLabelValue,
  1166. source.OwnerKindLabel: source.NoneLabelValue,
  1167. },
  1168. Value: 0,
  1169. },
  1170. },
  1171. },
  1172. }
  1173. for _, tt := range tests {
  1174. t.Run(tt.name, func(t *testing.T) {
  1175. ks := &ClusterCacheScraper{}
  1176. nsIndex := newSyncMap[string, types.UID](0)
  1177. var scrapeResults []metric.Update
  1178. for _, s := range tt.scrapes {
  1179. res := ks.scrapeReplicaSets(s.ReplicaSets, nsIndex)
  1180. scrapeResults = append(scrapeResults, res...)
  1181. }
  1182. if len(scrapeResults) != len(tt.expected) {
  1183. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1184. }
  1185. for i, expected := range tt.expected {
  1186. got := scrapeResults[i]
  1187. if !reflect.DeepEqual(expected, got) {
  1188. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1189. }
  1190. }
  1191. })
  1192. }
  1193. }
  1194. func Test_kubernetesScraper_scrapeResourceQuotas(t *testing.T) {
  1195. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1196. type scrape struct {
  1197. ResourceQuotas []*clustercache.ResourceQuota
  1198. Timestamp time.Time
  1199. }
  1200. tests := []struct {
  1201. name string
  1202. scrapes []scrape
  1203. expected []metric.Update
  1204. }{
  1205. {
  1206. name: "simple",
  1207. scrapes: []scrape{
  1208. {
  1209. ResourceQuotas: []*clustercache.ResourceQuota{
  1210. {
  1211. Name: "resourceQuota1",
  1212. Namespace: "namespace1",
  1213. UID: "uuid1",
  1214. Spec: v1.ResourceQuotaSpec{
  1215. Hard: v1.ResourceList{
  1216. v1.ResourceRequestsCPU: resource.MustParse("1"),
  1217. v1.ResourceRequestsMemory: resource.MustParse("1024"),
  1218. v1.ResourceLimitsCPU: resource.MustParse("2"),
  1219. v1.ResourceLimitsMemory: resource.MustParse("2048"),
  1220. },
  1221. },
  1222. Status: v1.ResourceQuotaStatus{
  1223. Used: v1.ResourceList{
  1224. v1.ResourceRequestsCPU: resource.MustParse("0.5"),
  1225. v1.ResourceRequestsMemory: resource.MustParse("512"),
  1226. v1.ResourceLimitsCPU: resource.MustParse("1"),
  1227. v1.ResourceLimitsMemory: resource.MustParse("1024"),
  1228. },
  1229. },
  1230. },
  1231. },
  1232. Timestamp: start1,
  1233. },
  1234. },
  1235. expected: []metric.Update{
  1236. {
  1237. Name: metric.ResourceQuotaInfo,
  1238. Labels: map[string]string{
  1239. source.UIDLabel: "uuid1",
  1240. source.NamespaceUIDLabel: "",
  1241. source.ResourceQuotaLabel: "resourceQuota1",
  1242. },
  1243. Value: 0,
  1244. AdditionalInfo: map[string]string{
  1245. source.UIDLabel: "uuid1",
  1246. source.NamespaceUIDLabel: "",
  1247. source.ResourceQuotaLabel: "resourceQuota1",
  1248. },
  1249. },
  1250. {
  1251. Name: metric.KubeResourceQuotaSpecResourceRequests,
  1252. Labels: map[string]string{
  1253. source.UIDLabel: "uuid1",
  1254. source.NamespaceUIDLabel: "",
  1255. source.ResourceQuotaLabel: "resourceQuota1",
  1256. source.ResourceLabel: "cpu",
  1257. source.UnitLabel: "core",
  1258. },
  1259. Value: 1,
  1260. AdditionalInfo: nil,
  1261. },
  1262. {
  1263. Name: metric.KubeResourceQuotaSpecResourceRequests,
  1264. Labels: map[string]string{
  1265. source.UIDLabel: "uuid1",
  1266. source.NamespaceUIDLabel: "",
  1267. source.ResourceQuotaLabel: "resourceQuota1",
  1268. source.ResourceLabel: "memory",
  1269. source.UnitLabel: "byte",
  1270. },
  1271. Value: 1024,
  1272. AdditionalInfo: nil,
  1273. },
  1274. {
  1275. Name: metric.KubeResourceQuotaSpecResourceLimits,
  1276. Labels: map[string]string{
  1277. source.UIDLabel: "uuid1",
  1278. source.NamespaceUIDLabel: "",
  1279. source.ResourceQuotaLabel: "resourceQuota1",
  1280. source.ResourceLabel: "cpu",
  1281. source.UnitLabel: "core",
  1282. },
  1283. Value: 2,
  1284. AdditionalInfo: nil,
  1285. },
  1286. {
  1287. Name: metric.KubeResourceQuotaSpecResourceLimits,
  1288. Labels: map[string]string{
  1289. source.UIDLabel: "uuid1",
  1290. source.NamespaceUIDLabel: "",
  1291. source.ResourceQuotaLabel: "resourceQuota1",
  1292. source.ResourceLabel: "memory",
  1293. source.UnitLabel: "byte",
  1294. },
  1295. Value: 2048,
  1296. AdditionalInfo: nil,
  1297. },
  1298. {
  1299. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1300. Labels: map[string]string{
  1301. source.UIDLabel: "uuid1",
  1302. source.NamespaceUIDLabel: "",
  1303. source.ResourceQuotaLabel: "resourceQuota1",
  1304. source.ResourceLabel: "cpu",
  1305. source.UnitLabel: "core",
  1306. },
  1307. Value: 0.5,
  1308. AdditionalInfo: nil,
  1309. },
  1310. {
  1311. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1312. Labels: map[string]string{
  1313. source.UIDLabel: "uuid1",
  1314. source.NamespaceUIDLabel: "",
  1315. source.ResourceQuotaLabel: "resourceQuota1",
  1316. source.ResourceLabel: "memory",
  1317. source.UnitLabel: "byte",
  1318. },
  1319. Value: 512,
  1320. AdditionalInfo: nil,
  1321. },
  1322. {
  1323. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1324. Labels: map[string]string{
  1325. source.UIDLabel: "uuid1",
  1326. source.NamespaceUIDLabel: "",
  1327. source.ResourceQuotaLabel: "resourceQuota1",
  1328. source.ResourceLabel: "cpu",
  1329. source.UnitLabel: "core",
  1330. },
  1331. Value: 1,
  1332. AdditionalInfo: nil,
  1333. },
  1334. {
  1335. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1336. Labels: map[string]string{
  1337. source.UIDLabel: "uuid1",
  1338. source.NamespaceUIDLabel: "",
  1339. source.ResourceQuotaLabel: "resourceQuota1",
  1340. source.ResourceLabel: "memory",
  1341. source.UnitLabel: "byte",
  1342. },
  1343. Value: 1024,
  1344. AdditionalInfo: nil,
  1345. },
  1346. },
  1347. },
  1348. }
  1349. for _, tt := range tests {
  1350. t.Run(tt.name, func(t *testing.T) {
  1351. ks := &ClusterCacheScraper{}
  1352. nsIndex := newSyncMap[string, types.UID](0)
  1353. var scrapeResults []metric.Update
  1354. for _, s := range tt.scrapes {
  1355. res := ks.scrapeResourceQuotas(s.ResourceQuotas, nsIndex)
  1356. scrapeResults = append(scrapeResults, res...)
  1357. }
  1358. if len(scrapeResults) != len(tt.expected) {
  1359. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1360. }
  1361. for i, expected := range tt.expected {
  1362. got := scrapeResults[i]
  1363. if !reflect.DeepEqual(expected, got) {
  1364. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1365. }
  1366. }
  1367. })
  1368. }
  1369. }