clustercache_test.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  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. )
  14. var Start1Str = "2025-01-01T00:00:00Z00:00"
  15. func Test_kubernetesScraper_scrapeNodes(t *testing.T) {
  16. start1, _ := time.Parse(time.RFC3339, Start1Str)
  17. type scrape struct {
  18. Nodes []*clustercache.Node
  19. Timestamp time.Time
  20. }
  21. tests := []struct {
  22. name string
  23. scrapes []scrape
  24. expected []metric.Update
  25. }{
  26. {
  27. name: "simple",
  28. scrapes: []scrape{
  29. {
  30. Nodes: []*clustercache.Node{
  31. {
  32. Name: "node1",
  33. UID: "uuid1",
  34. SpecProviderID: "i-1",
  35. Status: v1.NodeStatus{
  36. Capacity: v1.ResourceList{
  37. v1.ResourceCPU: resource.MustParse("2"),
  38. v1.ResourceMemory: resource.MustParse("2048"),
  39. },
  40. Allocatable: v1.ResourceList{
  41. v1.ResourceCPU: resource.MustParse("1"),
  42. v1.ResourceMemory: resource.MustParse("1024"),
  43. },
  44. },
  45. Labels: map[string]string{
  46. "test1": "blah",
  47. "test2": "blah2",
  48. },
  49. },
  50. },
  51. Timestamp: start1,
  52. },
  53. },
  54. expected: []metric.Update{
  55. {
  56. Name: metric.KubeNodeStatusCapacityCPUCores,
  57. Labels: map[string]string{
  58. source.NodeLabel: "node1",
  59. source.ProviderIDLabel: "i-1",
  60. source.UIDLabel: "uuid1",
  61. },
  62. Value: 2.0,
  63. AdditionalInfo: nil,
  64. },
  65. {
  66. Name: metric.KubeNodeStatusCapacityMemoryBytes,
  67. Labels: map[string]string{
  68. source.NodeLabel: "node1",
  69. source.ProviderIDLabel: "i-1",
  70. source.UIDLabel: "uuid1",
  71. },
  72. Value: 2048.0,
  73. AdditionalInfo: nil,
  74. },
  75. {
  76. Name: metric.KubeNodeStatusAllocatableCPUCores,
  77. Labels: map[string]string{
  78. source.NodeLabel: "node1",
  79. source.ProviderIDLabel: "i-1",
  80. source.UIDLabel: "uuid1",
  81. },
  82. Value: 1.0,
  83. AdditionalInfo: nil,
  84. },
  85. {
  86. Name: metric.KubeNodeStatusAllocatableMemoryBytes,
  87. Labels: map[string]string{
  88. source.NodeLabel: "node1",
  89. source.ProviderIDLabel: "i-1",
  90. source.UIDLabel: "uuid1",
  91. },
  92. Value: 1024.0,
  93. AdditionalInfo: nil,
  94. },
  95. {
  96. Name: metric.KubeNodeLabels,
  97. Labels: map[string]string{
  98. source.NodeLabel: "node1",
  99. source.ProviderIDLabel: "i-1",
  100. source.UIDLabel: "uuid1",
  101. },
  102. Value: 0,
  103. AdditionalInfo: map[string]string{
  104. "label_test1": "blah",
  105. "label_test2": "blah2",
  106. },
  107. },
  108. },
  109. },
  110. }
  111. for _, tt := range tests {
  112. t.Run(tt.name, func(t *testing.T) {
  113. ks := &ClusterCacheScraper{}
  114. var scrapeResults []metric.Update
  115. for _, s := range tt.scrapes {
  116. res := ks.scrapeNodes(s.Nodes)
  117. scrapeResults = append(scrapeResults, res...)
  118. }
  119. if len(scrapeResults) != len(tt.expected) {
  120. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  121. }
  122. for i, expected := range tt.expected {
  123. got := scrapeResults[i]
  124. if !reflect.DeepEqual(expected, got) {
  125. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  126. }
  127. }
  128. })
  129. }
  130. }
  131. func Test_kubernetesScraper_scrapeDeployments(t *testing.T) {
  132. start1, _ := time.Parse(time.RFC3339, Start1Str)
  133. type scrape struct {
  134. Deployments []*clustercache.Deployment
  135. Timestamp time.Time
  136. }
  137. tests := []struct {
  138. name string
  139. scrapes []scrape
  140. expected []metric.Update
  141. }{
  142. {
  143. name: "simple",
  144. scrapes: []scrape{
  145. {
  146. Deployments: []*clustercache.Deployment{
  147. {
  148. Name: "deployment1",
  149. Namespace: "namespace1",
  150. UID: "uuid1",
  151. MatchLabels: map[string]string{
  152. "test1": "blah",
  153. "test2": "blah2",
  154. },
  155. },
  156. },
  157. Timestamp: start1,
  158. },
  159. },
  160. expected: []metric.Update{
  161. {
  162. Name: metric.DeploymentMatchLabels,
  163. Labels: map[string]string{
  164. source.DeploymentLabel: "deployment1",
  165. source.NamespaceLabel: "namespace1",
  166. source.UIDLabel: "uuid1",
  167. },
  168. Value: 0,
  169. AdditionalInfo: map[string]string{
  170. "label_test1": "blah",
  171. "label_test2": "blah2",
  172. },
  173. },
  174. },
  175. },
  176. }
  177. for _, tt := range tests {
  178. t.Run(tt.name, func(t *testing.T) {
  179. ks := &ClusterCacheScraper{}
  180. var scrapeResults []metric.Update
  181. for _, s := range tt.scrapes {
  182. res := ks.scrapeDeployments(s.Deployments)
  183. scrapeResults = append(scrapeResults, res...)
  184. }
  185. if len(scrapeResults) != len(tt.expected) {
  186. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  187. }
  188. for i, expected := range tt.expected {
  189. got := scrapeResults[i]
  190. if !reflect.DeepEqual(expected, got) {
  191. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  192. }
  193. }
  194. })
  195. }
  196. }
  197. func Test_kubernetesScraper_scrapeNamespaces(t *testing.T) {
  198. start1, _ := time.Parse(time.RFC3339, Start1Str)
  199. type scrape struct {
  200. Namespaces []*clustercache.Namespace
  201. Timestamp time.Time
  202. }
  203. tests := []struct {
  204. name string
  205. scrapes []scrape
  206. expected []metric.Update
  207. }{
  208. {
  209. name: "simple",
  210. scrapes: []scrape{
  211. {
  212. Namespaces: []*clustercache.Namespace{
  213. {
  214. Name: "namespace1",
  215. UID: "uuid1",
  216. Labels: map[string]string{
  217. "test1": "blah",
  218. "test2": "blah2",
  219. },
  220. Annotations: map[string]string{
  221. "test3": "blah3",
  222. "test4": "blah4",
  223. },
  224. },
  225. },
  226. Timestamp: start1,
  227. },
  228. },
  229. expected: []metric.Update{
  230. {
  231. Name: metric.NamespaceInfo,
  232. Labels: map[string]string{
  233. source.NamespaceLabel: "namespace1",
  234. source.UIDLabel: "uuid1",
  235. },
  236. Value: 0,
  237. AdditionalInfo: map[string]string{
  238. source.NamespaceLabel: "namespace1",
  239. source.UIDLabel: "uuid1",
  240. },
  241. },
  242. {
  243. Name: metric.KubeNamespaceLabels,
  244. Labels: map[string]string{
  245. source.NamespaceLabel: "namespace1",
  246. source.UIDLabel: "uuid1",
  247. },
  248. Value: 0,
  249. AdditionalInfo: map[string]string{
  250. "label_test1": "blah",
  251. "label_test2": "blah2",
  252. },
  253. },
  254. {
  255. Name: metric.KubeNamespaceAnnotations,
  256. Labels: map[string]string{
  257. source.NamespaceLabel: "namespace1",
  258. source.UIDLabel: "uuid1",
  259. },
  260. Value: 0,
  261. AdditionalInfo: map[string]string{
  262. "annotation_test3": "blah3",
  263. "annotation_test4": "blah4",
  264. },
  265. },
  266. },
  267. },
  268. }
  269. for _, tt := range tests {
  270. t.Run(tt.name, func(t *testing.T) {
  271. ks := &ClusterCacheScraper{}
  272. var scrapeResults []metric.Update
  273. for _, s := range tt.scrapes {
  274. res := ks.scrapeNamespaces(s.Namespaces)
  275. scrapeResults = append(scrapeResults, res...)
  276. }
  277. if len(scrapeResults) != len(tt.expected) {
  278. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  279. }
  280. for i, expected := range tt.expected {
  281. got := scrapeResults[i]
  282. if !reflect.DeepEqual(expected, got) {
  283. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  284. }
  285. }
  286. })
  287. }
  288. }
  289. func Test_kubernetesScraper_scrapePods(t *testing.T) {
  290. start1, _ := time.Parse(time.RFC3339, Start1Str)
  291. type scrape struct {
  292. PVCs []*clustercache.PersistentVolumeClaim
  293. Pods []*clustercache.Pod
  294. Timestamp time.Time
  295. }
  296. tests := []struct {
  297. name string
  298. scrapes []scrape
  299. expected []metric.Update
  300. }{
  301. {
  302. name: "simple",
  303. scrapes: []scrape{
  304. {
  305. PVCs: []*clustercache.PersistentVolumeClaim{
  306. {
  307. Name: "pvc1",
  308. Namespace: "namespace1",
  309. UID: "uuid1",
  310. Spec: v1.PersistentVolumeClaimSpec{
  311. VolumeName: "vol1",
  312. StorageClassName: util.Ptr("storageClass1"),
  313. Resources: v1.VolumeResourceRequirements{
  314. Requests: v1.ResourceList{
  315. v1.ResourceStorage: resource.MustParse("4096"),
  316. },
  317. },
  318. },
  319. },
  320. },
  321. Pods: []*clustercache.Pod{
  322. {
  323. Name: "pod1",
  324. Namespace: "namespace1",
  325. UID: "uuid1",
  326. Spec: clustercache.PodSpec{
  327. NodeName: "node1",
  328. Containers: []clustercache.Container{
  329. {
  330. Name: "container1",
  331. Resources: v1.ResourceRequirements{
  332. Requests: map[v1.ResourceName]resource.Quantity{
  333. v1.ResourceCPU: resource.MustParse("500m"),
  334. v1.ResourceMemory: resource.MustParse("512"),
  335. },
  336. Limits: map[v1.ResourceName]resource.Quantity{
  337. v1.ResourceCPU: resource.MustParse("1"),
  338. v1.ResourceMemory: resource.MustParse("1024"),
  339. },
  340. },
  341. },
  342. },
  343. },
  344. Labels: map[string]string{
  345. "test1": "blah",
  346. "test2": "blah2",
  347. },
  348. Annotations: map[string]string{
  349. "test3": "blah3",
  350. "test4": "blah4",
  351. },
  352. OwnerReferences: []metav1.OwnerReference{
  353. {
  354. Kind: source.DeploymentLabel,
  355. Name: "deployment1",
  356. Controller: nil,
  357. },
  358. },
  359. Status: clustercache.PodStatus{
  360. ContainerStatuses: []v1.ContainerStatus{
  361. {
  362. Name: "container1",
  363. State: v1.ContainerState{
  364. Running: &v1.ContainerStateRunning{},
  365. },
  366. },
  367. },
  368. },
  369. },
  370. },
  371. Timestamp: start1,
  372. },
  373. },
  374. expected: []metric.Update{
  375. {
  376. Name: metric.KubePodLabels,
  377. Labels: map[string]string{
  378. source.PodLabel: "pod1",
  379. source.NamespaceLabel: "namespace1",
  380. source.UIDLabel: "uuid1",
  381. source.NodeLabel: "node1",
  382. source.InstanceLabel: "node1",
  383. },
  384. Value: 0,
  385. AdditionalInfo: map[string]string{
  386. "label_test1": "blah",
  387. "label_test2": "blah2",
  388. },
  389. },
  390. {
  391. Name: metric.KubePodAnnotations,
  392. Labels: map[string]string{
  393. source.PodLabel: "pod1",
  394. source.NamespaceLabel: "namespace1",
  395. source.UIDLabel: "uuid1",
  396. source.NodeLabel: "node1",
  397. source.InstanceLabel: "node1",
  398. },
  399. Value: 0,
  400. AdditionalInfo: map[string]string{
  401. "annotation_test3": "blah3",
  402. "annotation_test4": "blah4",
  403. },
  404. },
  405. {
  406. Name: metric.KubePodOwner,
  407. Labels: map[string]string{
  408. source.PodLabel: "pod1",
  409. source.NamespaceLabel: "namespace1",
  410. source.UIDLabel: "uuid1",
  411. source.NodeLabel: "node1",
  412. source.InstanceLabel: "node1",
  413. source.OwnerKindLabel: "deployment",
  414. source.OwnerNameLabel: "deployment1",
  415. },
  416. Value: 0,
  417. AdditionalInfo: nil,
  418. },
  419. {
  420. Name: metric.KubePodContainerStatusRunning,
  421. Labels: map[string]string{
  422. source.PodLabel: "pod1",
  423. source.NamespaceLabel: "namespace1",
  424. source.UIDLabel: "uuid1",
  425. source.NodeLabel: "node1",
  426. source.InstanceLabel: "node1",
  427. source.ContainerLabel: "container1",
  428. },
  429. Value: 0,
  430. AdditionalInfo: nil,
  431. },
  432. {
  433. Name: metric.KubePodContainerResourceRequests,
  434. Labels: map[string]string{
  435. source.PodLabel: "pod1",
  436. source.NamespaceLabel: "namespace1",
  437. source.UIDLabel: "uuid1",
  438. source.NodeLabel: "node1",
  439. source.InstanceLabel: "node1",
  440. source.ContainerLabel: "container1",
  441. source.ResourceLabel: "cpu",
  442. source.UnitLabel: "core",
  443. },
  444. Value: 0.5,
  445. AdditionalInfo: nil,
  446. },
  447. {
  448. Name: metric.KubePodContainerResourceRequests,
  449. Labels: map[string]string{
  450. source.PodLabel: "pod1",
  451. source.NamespaceLabel: "namespace1",
  452. source.UIDLabel: "uuid1",
  453. source.NodeLabel: "node1",
  454. source.InstanceLabel: "node1",
  455. source.ContainerLabel: "container1",
  456. source.ResourceLabel: "memory",
  457. source.UnitLabel: "byte",
  458. },
  459. Value: 512,
  460. AdditionalInfo: nil,
  461. },
  462. {
  463. Name: metric.KubePodContainerResourceLimits,
  464. Labels: map[string]string{
  465. source.PodLabel: "pod1",
  466. source.NamespaceLabel: "namespace1",
  467. source.UIDLabel: "uuid1",
  468. source.NodeLabel: "node1",
  469. source.InstanceLabel: "node1",
  470. source.ContainerLabel: "container1",
  471. source.ResourceLabel: "cpu",
  472. source.UnitLabel: "core",
  473. },
  474. Value: 1,
  475. AdditionalInfo: nil,
  476. },
  477. {
  478. Name: metric.KubePodContainerResourceLimits,
  479. Labels: map[string]string{
  480. source.PodLabel: "pod1",
  481. source.NamespaceLabel: "namespace1",
  482. source.UIDLabel: "uuid1",
  483. source.NodeLabel: "node1",
  484. source.InstanceLabel: "node1",
  485. source.ContainerLabel: "container1",
  486. source.ResourceLabel: "memory",
  487. source.UnitLabel: "byte",
  488. },
  489. Value: 1024,
  490. AdditionalInfo: nil,
  491. },
  492. {
  493. Name: metric.PodPVCAllocation,
  494. Labels: map[string]string{
  495. source.InstanceLabel: "",
  496. source.NamespaceLabel: "namespace1",
  497. source.NodeLabel: "",
  498. source.PVLabel: "vol1",
  499. source.PVCLabel: "pvc1",
  500. source.PodLabel: "unmounted-pvs",
  501. source.UIDLabel: "",
  502. },
  503. Value: 4096,
  504. AdditionalInfo: nil,
  505. },
  506. },
  507. },
  508. }
  509. for _, tt := range tests {
  510. t.Run(tt.name, func(t *testing.T) {
  511. ks := &ClusterCacheScraper{}
  512. var scrapeResults []metric.Update
  513. for _, s := range tt.scrapes {
  514. res := ks.scrapePods(s.Pods, s.PVCs)
  515. scrapeResults = append(scrapeResults, res...)
  516. }
  517. if len(scrapeResults) != len(tt.expected) {
  518. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  519. }
  520. for i, expected := range tt.expected {
  521. got := scrapeResults[i]
  522. if !reflect.DeepEqual(expected, got) {
  523. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  524. }
  525. }
  526. })
  527. }
  528. }
  529. func Test_kubernetesScraper_scrapePVCs(t *testing.T) {
  530. start1, _ := time.Parse(time.RFC3339, Start1Str)
  531. type scrape struct {
  532. PVCs []*clustercache.PersistentVolumeClaim
  533. Timestamp time.Time
  534. }
  535. tests := []struct {
  536. name string
  537. scrapes []scrape
  538. expected []metric.Update
  539. }{
  540. {
  541. name: "simple",
  542. scrapes: []scrape{
  543. {
  544. PVCs: []*clustercache.PersistentVolumeClaim{
  545. {
  546. Name: "pvc1",
  547. Namespace: "namespace1",
  548. UID: "uuid1",
  549. Spec: v1.PersistentVolumeClaimSpec{
  550. VolumeName: "vol1",
  551. StorageClassName: util.Ptr("storageClass1"),
  552. Resources: v1.VolumeResourceRequirements{
  553. Requests: v1.ResourceList{
  554. v1.ResourceStorage: resource.MustParse("4096"),
  555. },
  556. },
  557. },
  558. },
  559. },
  560. Timestamp: start1,
  561. },
  562. },
  563. expected: []metric.Update{
  564. {
  565. Name: metric.KubePersistentVolumeClaimInfo,
  566. Labels: map[string]string{
  567. source.PVCLabel: "pvc1",
  568. source.NamespaceLabel: "namespace1",
  569. source.UIDLabel: "uuid1",
  570. source.VolumeNameLabel: "vol1",
  571. source.StorageClassLabel: "storageClass1",
  572. },
  573. Value: 0,
  574. AdditionalInfo: nil,
  575. },
  576. {
  577. Name: metric.KubePersistentVolumeClaimResourceRequestsStorageBytes,
  578. Labels: map[string]string{
  579. source.PVCLabel: "pvc1",
  580. source.NamespaceLabel: "namespace1",
  581. source.UIDLabel: "uuid1",
  582. source.VolumeNameLabel: "vol1",
  583. source.StorageClassLabel: "storageClass1",
  584. },
  585. Value: 4096,
  586. AdditionalInfo: nil,
  587. },
  588. },
  589. },
  590. }
  591. for _, tt := range tests {
  592. t.Run(tt.name, func(t *testing.T) {
  593. ks := &ClusterCacheScraper{}
  594. var scrapeResults []metric.Update
  595. for _, s := range tt.scrapes {
  596. res := ks.scrapePVCs(s.PVCs)
  597. scrapeResults = append(scrapeResults, res...)
  598. }
  599. if len(scrapeResults) != len(tt.expected) {
  600. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  601. }
  602. for i, expected := range tt.expected {
  603. got := scrapeResults[i]
  604. if !reflect.DeepEqual(expected, got) {
  605. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  606. }
  607. }
  608. })
  609. }
  610. }
  611. func Test_kubernetesScraper_scrapePVs(t *testing.T) {
  612. start1, _ := time.Parse(time.RFC3339, Start1Str)
  613. type scrape struct {
  614. PVs []*clustercache.PersistentVolume
  615. Timestamp time.Time
  616. }
  617. tests := []struct {
  618. name string
  619. scrapes []scrape
  620. expected []metric.Update
  621. }{
  622. {
  623. name: "simple",
  624. scrapes: []scrape{
  625. {
  626. PVs: []*clustercache.PersistentVolume{
  627. {
  628. Name: "pv1",
  629. UID: "uuid1",
  630. Spec: v1.PersistentVolumeSpec{
  631. StorageClassName: "storageClass1",
  632. PersistentVolumeSource: v1.PersistentVolumeSource{
  633. CSI: &v1.CSIPersistentVolumeSource{
  634. VolumeHandle: "vol-1",
  635. },
  636. },
  637. Capacity: v1.ResourceList{
  638. v1.ResourceStorage: resource.MustParse("4096"),
  639. },
  640. },
  641. },
  642. },
  643. Timestamp: start1,
  644. },
  645. },
  646. expected: []metric.Update{
  647. {
  648. Name: metric.KubecostPVInfo,
  649. Labels: map[string]string{
  650. source.PVLabel: "pv1",
  651. source.ProviderIDLabel: "vol-1",
  652. source.StorageClassLabel: "storageClass1",
  653. source.UIDLabel: "uuid1",
  654. },
  655. Value: 0,
  656. AdditionalInfo: nil,
  657. },
  658. {
  659. Name: metric.KubePersistentVolumeCapacityBytes,
  660. Labels: map[string]string{
  661. source.PVLabel: "pv1",
  662. source.ProviderIDLabel: "vol-1",
  663. source.StorageClassLabel: "storageClass1",
  664. source.UIDLabel: "uuid1",
  665. },
  666. Value: 4096,
  667. AdditionalInfo: nil,
  668. },
  669. },
  670. },
  671. }
  672. for _, tt := range tests {
  673. t.Run(tt.name, func(t *testing.T) {
  674. ks := &ClusterCacheScraper{}
  675. var scrapeResults []metric.Update
  676. for _, s := range tt.scrapes {
  677. res := ks.scrapePVs(s.PVs)
  678. scrapeResults = append(scrapeResults, res...)
  679. }
  680. if len(scrapeResults) != len(tt.expected) {
  681. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  682. }
  683. for i, expected := range tt.expected {
  684. got := scrapeResults[i]
  685. if !reflect.DeepEqual(expected, got) {
  686. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  687. }
  688. }
  689. })
  690. }
  691. }
  692. func Test_kubernetesScraper_scrapeServices(t *testing.T) {
  693. start1, _ := time.Parse(time.RFC3339, Start1Str)
  694. type scrape struct {
  695. Services []*clustercache.Service
  696. Timestamp time.Time
  697. }
  698. tests := []struct {
  699. name string
  700. scrapes []scrape
  701. expected []metric.Update
  702. }{
  703. {
  704. name: "simple",
  705. scrapes: []scrape{
  706. {
  707. Services: []*clustercache.Service{
  708. {
  709. Name: "service1",
  710. Namespace: "namespace1",
  711. UID: "uuid1",
  712. SpecSelector: map[string]string{
  713. "test1": "blah",
  714. "test2": "blah2",
  715. },
  716. },
  717. },
  718. Timestamp: start1,
  719. },
  720. },
  721. expected: []metric.Update{
  722. {
  723. Name: metric.ServiceSelectorLabels,
  724. Labels: map[string]string{
  725. "service": "service1",
  726. source.NamespaceLabel: "namespace1",
  727. source.UIDLabel: "uuid1",
  728. },
  729. Value: 0,
  730. AdditionalInfo: map[string]string{
  731. "label_test1": "blah",
  732. "label_test2": "blah2",
  733. },
  734. },
  735. },
  736. },
  737. }
  738. for _, tt := range tests {
  739. t.Run(tt.name, func(t *testing.T) {
  740. ks := &ClusterCacheScraper{}
  741. var scrapeResults []metric.Update
  742. for _, s := range tt.scrapes {
  743. res := ks.scrapeServices(s.Services)
  744. scrapeResults = append(scrapeResults, res...)
  745. }
  746. if len(scrapeResults) != len(tt.expected) {
  747. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  748. }
  749. for i, expected := range tt.expected {
  750. got := scrapeResults[i]
  751. if !reflect.DeepEqual(expected, got) {
  752. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  753. }
  754. }
  755. })
  756. }
  757. }
  758. func Test_kubernetesScraper_scrapeStatefulSets(t *testing.T) {
  759. start1, _ := time.Parse(time.RFC3339, Start1Str)
  760. type scrape struct {
  761. StatefulSets []*clustercache.StatefulSet
  762. Timestamp time.Time
  763. }
  764. tests := []struct {
  765. name string
  766. scrapes []scrape
  767. expected []metric.Update
  768. }{
  769. {
  770. name: "simple",
  771. scrapes: []scrape{
  772. {
  773. StatefulSets: []*clustercache.StatefulSet{
  774. {
  775. Name: "statefulSet1",
  776. Namespace: "namespace1",
  777. UID: "uuid1",
  778. SpecSelector: &metav1.LabelSelector{
  779. MatchLabels: map[string]string{
  780. "test1": "blah",
  781. "test2": "blah2",
  782. },
  783. },
  784. },
  785. },
  786. Timestamp: start1,
  787. },
  788. },
  789. expected: []metric.Update{
  790. {
  791. Name: metric.StatefulSetMatchLabels,
  792. Labels: map[string]string{
  793. source.StatefulSetLabel: "statefulSet1",
  794. source.NamespaceLabel: "namespace1",
  795. source.UIDLabel: "uuid1",
  796. },
  797. Value: 0,
  798. AdditionalInfo: map[string]string{
  799. "label_test1": "blah",
  800. "label_test2": "blah2",
  801. },
  802. },
  803. },
  804. },
  805. }
  806. for _, tt := range tests {
  807. t.Run(tt.name, func(t *testing.T) {
  808. ks := &ClusterCacheScraper{}
  809. var scrapeResults []metric.Update
  810. for _, s := range tt.scrapes {
  811. res := ks.scrapeStatefulSets(s.StatefulSets)
  812. scrapeResults = append(scrapeResults, res...)
  813. }
  814. if len(scrapeResults) != len(tt.expected) {
  815. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  816. }
  817. for i, expected := range tt.expected {
  818. got := scrapeResults[i]
  819. if !reflect.DeepEqual(expected, got) {
  820. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  821. }
  822. }
  823. })
  824. }
  825. }
  826. func Test_kubernetesScraper_scrapeReplicaSets(t *testing.T) {
  827. start1, _ := time.Parse(time.RFC3339, Start1Str)
  828. type scrape struct {
  829. ReplicaSets []*clustercache.ReplicaSet
  830. Timestamp time.Time
  831. }
  832. tests := []struct {
  833. name string
  834. scrapes []scrape
  835. expected []metric.Update
  836. }{
  837. {
  838. name: "simple",
  839. scrapes: []scrape{
  840. {
  841. ReplicaSets: []*clustercache.ReplicaSet{
  842. {
  843. Name: "replicaSet1",
  844. Namespace: "namespace1",
  845. UID: "uuid1",
  846. OwnerReferences: []metav1.OwnerReference{
  847. {
  848. Name: "rollout1",
  849. Kind: "Rollout",
  850. },
  851. },
  852. },
  853. {
  854. Name: "pureReplicaSet",
  855. Namespace: "namespace1",
  856. UID: "uuid2",
  857. OwnerReferences: []metav1.OwnerReference{},
  858. },
  859. },
  860. Timestamp: start1,
  861. },
  862. },
  863. expected: []metric.Update{
  864. {
  865. Name: metric.KubeReplicasetOwner,
  866. Labels: map[string]string{
  867. "replicaset": "replicaSet1",
  868. source.NamespaceLabel: "namespace1",
  869. source.UIDLabel: "uuid1",
  870. source.OwnerNameLabel: "rollout1",
  871. source.OwnerKindLabel: "Rollout",
  872. },
  873. Value: 0,
  874. },
  875. {
  876. Name: metric.KubeReplicasetOwner,
  877. Labels: map[string]string{
  878. "replicaset": "pureReplicaSet",
  879. source.NamespaceLabel: "namespace1",
  880. source.UIDLabel: "uuid2",
  881. source.OwnerNameLabel: source.NoneLabelValue,
  882. source.OwnerKindLabel: source.NoneLabelValue,
  883. },
  884. Value: 0,
  885. },
  886. },
  887. },
  888. }
  889. for _, tt := range tests {
  890. t.Run(tt.name, func(t *testing.T) {
  891. ks := &ClusterCacheScraper{}
  892. var scrapeResults []metric.Update
  893. for _, s := range tt.scrapes {
  894. res := ks.scrapeReplicaSets(s.ReplicaSets)
  895. scrapeResults = append(scrapeResults, res...)
  896. }
  897. if len(scrapeResults) != len(tt.expected) {
  898. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  899. }
  900. for i, expected := range tt.expected {
  901. got := scrapeResults[i]
  902. if !reflect.DeepEqual(expected, got) {
  903. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  904. }
  905. }
  906. })
  907. }
  908. }
  909. func Test_kubernetesScraper_scrapeResourceQuotas(t *testing.T) {
  910. start1, _ := time.Parse(time.RFC3339, Start1Str)
  911. type scrape struct {
  912. ResourceQuotas []*clustercache.ResourceQuota
  913. Timestamp time.Time
  914. }
  915. tests := []struct {
  916. name string
  917. scrapes []scrape
  918. expected []metric.Update
  919. }{
  920. {
  921. name: "simple",
  922. scrapes: []scrape{
  923. {
  924. ResourceQuotas: []*clustercache.ResourceQuota{
  925. {
  926. Name: "resourceQuota1",
  927. Namespace: "namespace1",
  928. UID: "uuid1",
  929. Spec: v1.ResourceQuotaSpec{
  930. Hard: v1.ResourceList{
  931. v1.ResourceRequestsCPU: resource.MustParse("1"),
  932. v1.ResourceRequestsMemory: resource.MustParse("1024"),
  933. v1.ResourceLimitsCPU: resource.MustParse("2"),
  934. v1.ResourceLimitsMemory: resource.MustParse("2048"),
  935. },
  936. },
  937. Status: v1.ResourceQuotaStatus{
  938. Used: v1.ResourceList{
  939. v1.ResourceRequestsCPU: resource.MustParse("0.5"),
  940. v1.ResourceRequestsMemory: resource.MustParse("512"),
  941. v1.ResourceLimitsCPU: resource.MustParse("1"),
  942. v1.ResourceLimitsMemory: resource.MustParse("1024"),
  943. },
  944. },
  945. },
  946. },
  947. Timestamp: start1,
  948. },
  949. },
  950. expected: []metric.Update{
  951. {
  952. Name: metric.ResourceQuotaInfo,
  953. Labels: map[string]string{
  954. source.ResourceQuotaLabel: "resourceQuota1",
  955. source.NamespaceLabel: "namespace1",
  956. source.UIDLabel: "uuid1",
  957. },
  958. Value: 0,
  959. AdditionalInfo: map[string]string{
  960. source.ResourceQuotaLabel: "resourceQuota1",
  961. source.NamespaceLabel: "namespace1",
  962. source.UIDLabel: "uuid1",
  963. },
  964. },
  965. {
  966. Name: metric.KubeResourceQuotaSpecResourceRequests,
  967. Labels: map[string]string{
  968. source.ResourceQuotaLabel: "resourceQuota1",
  969. source.NamespaceLabel: "namespace1",
  970. source.UIDLabel: "uuid1",
  971. source.ResourceLabel: "cpu",
  972. source.UnitLabel: "core",
  973. },
  974. Value: 1,
  975. AdditionalInfo: nil,
  976. },
  977. {
  978. Name: metric.KubeResourceQuotaSpecResourceRequests,
  979. Labels: map[string]string{
  980. source.ResourceQuotaLabel: "resourceQuota1",
  981. source.NamespaceLabel: "namespace1",
  982. source.UIDLabel: "uuid1",
  983. source.ResourceLabel: "memory",
  984. source.UnitLabel: "byte",
  985. },
  986. Value: 1024,
  987. AdditionalInfo: nil,
  988. },
  989. {
  990. Name: metric.KubeResourceQuotaSpecResourceLimits,
  991. Labels: map[string]string{
  992. source.ResourceQuotaLabel: "resourceQuota1",
  993. source.NamespaceLabel: "namespace1",
  994. source.UIDLabel: "uuid1",
  995. source.ResourceLabel: "cpu",
  996. source.UnitLabel: "core",
  997. },
  998. Value: 2,
  999. AdditionalInfo: nil,
  1000. },
  1001. {
  1002. Name: metric.KubeResourceQuotaSpecResourceLimits,
  1003. Labels: map[string]string{
  1004. source.ResourceQuotaLabel: "resourceQuota1",
  1005. source.NamespaceLabel: "namespace1",
  1006. source.UIDLabel: "uuid1",
  1007. source.ResourceLabel: "memory",
  1008. source.UnitLabel: "byte",
  1009. },
  1010. Value: 2048,
  1011. AdditionalInfo: nil,
  1012. },
  1013. {
  1014. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1015. Labels: map[string]string{
  1016. source.ResourceQuotaLabel: "resourceQuota1",
  1017. source.NamespaceLabel: "namespace1",
  1018. source.UIDLabel: "uuid1",
  1019. source.ResourceLabel: "cpu",
  1020. source.UnitLabel: "core",
  1021. },
  1022. Value: 0.5,
  1023. AdditionalInfo: nil,
  1024. },
  1025. {
  1026. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1027. Labels: map[string]string{
  1028. source.ResourceQuotaLabel: "resourceQuota1",
  1029. source.NamespaceLabel: "namespace1",
  1030. source.UIDLabel: "uuid1",
  1031. source.ResourceLabel: "memory",
  1032. source.UnitLabel: "byte",
  1033. },
  1034. Value: 512,
  1035. AdditionalInfo: nil,
  1036. },
  1037. {
  1038. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1039. Labels: map[string]string{
  1040. source.ResourceQuotaLabel: "resourceQuota1",
  1041. source.NamespaceLabel: "namespace1",
  1042. source.UIDLabel: "uuid1",
  1043. source.ResourceLabel: "cpu",
  1044. source.UnitLabel: "core",
  1045. },
  1046. Value: 1,
  1047. AdditionalInfo: nil,
  1048. },
  1049. {
  1050. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1051. Labels: map[string]string{
  1052. source.ResourceQuotaLabel: "resourceQuota1",
  1053. source.NamespaceLabel: "namespace1",
  1054. source.UIDLabel: "uuid1",
  1055. source.ResourceLabel: "memory",
  1056. source.UnitLabel: "byte",
  1057. },
  1058. Value: 1024,
  1059. AdditionalInfo: nil,
  1060. },
  1061. },
  1062. },
  1063. }
  1064. for _, tt := range tests {
  1065. t.Run(tt.name, func(t *testing.T) {
  1066. ks := &ClusterCacheScraper{}
  1067. var scrapeResults []metric.Update
  1068. for _, s := range tt.scrapes {
  1069. res := ks.scrapeResourceQuotas(s.ResourceQuotas)
  1070. scrapeResults = append(scrapeResults, res...)
  1071. }
  1072. if len(scrapeResults) != len(tt.expected) {
  1073. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1074. }
  1075. for i, expected := range tt.expected {
  1076. got := scrapeResults[i]
  1077. if !reflect.DeepEqual(expected, got) {
  1078. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1079. }
  1080. }
  1081. })
  1082. }
  1083. }