clustercache_test.go 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  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. Pods []*clustercache.Pod
  293. Timestamp time.Time
  294. }
  295. tests := []struct {
  296. name string
  297. scrapes []scrape
  298. expected []metric.Update
  299. }{
  300. {
  301. name: "simple",
  302. scrapes: []scrape{
  303. {
  304. Pods: []*clustercache.Pod{
  305. {
  306. Name: "pod1",
  307. Namespace: "namespace1",
  308. UID: "uuid1",
  309. Spec: clustercache.PodSpec{
  310. NodeName: "node1",
  311. Containers: []clustercache.Container{
  312. {
  313. Name: "container1",
  314. Resources: v1.ResourceRequirements{
  315. Requests: map[v1.ResourceName]resource.Quantity{
  316. v1.ResourceCPU: resource.MustParse("500m"),
  317. v1.ResourceMemory: resource.MustParse("512"),
  318. },
  319. Limits: map[v1.ResourceName]resource.Quantity{
  320. v1.ResourceCPU: resource.MustParse("1"),
  321. v1.ResourceMemory: resource.MustParse("1024"),
  322. },
  323. },
  324. },
  325. },
  326. },
  327. Labels: map[string]string{
  328. "test1": "blah",
  329. "test2": "blah2",
  330. },
  331. Annotations: map[string]string{
  332. "test3": "blah3",
  333. "test4": "blah4",
  334. },
  335. OwnerReferences: []metav1.OwnerReference{
  336. {
  337. Kind: source.DeploymentLabel,
  338. Name: "deployment1",
  339. Controller: nil,
  340. },
  341. },
  342. Status: clustercache.PodStatus{
  343. ContainerStatuses: []v1.ContainerStatus{
  344. {
  345. Name: "container1",
  346. State: v1.ContainerState{
  347. Running: &v1.ContainerStateRunning{},
  348. },
  349. },
  350. },
  351. },
  352. },
  353. },
  354. Timestamp: start1,
  355. },
  356. },
  357. expected: []metric.Update{
  358. {
  359. Name: metric.KubePodLabels,
  360. Labels: map[string]string{
  361. source.PodLabel: "pod1",
  362. source.NamespaceLabel: "namespace1",
  363. source.UIDLabel: "uuid1",
  364. source.NodeLabel: "node1",
  365. source.InstanceLabel: "node1",
  366. },
  367. Value: 0,
  368. AdditionalInfo: map[string]string{
  369. "label_test1": "blah",
  370. "label_test2": "blah2",
  371. },
  372. },
  373. {
  374. Name: metric.KubePodAnnotations,
  375. Labels: map[string]string{
  376. source.PodLabel: "pod1",
  377. source.NamespaceLabel: "namespace1",
  378. source.UIDLabel: "uuid1",
  379. source.NodeLabel: "node1",
  380. source.InstanceLabel: "node1",
  381. },
  382. Value: 0,
  383. AdditionalInfo: map[string]string{
  384. "annotation_test3": "blah3",
  385. "annotation_test4": "blah4",
  386. },
  387. },
  388. {
  389. Name: metric.KubePodOwner,
  390. Labels: map[string]string{
  391. source.PodLabel: "pod1",
  392. source.NamespaceLabel: "namespace1",
  393. source.UIDLabel: "uuid1",
  394. source.NodeLabel: "node1",
  395. source.InstanceLabel: "node1",
  396. source.OwnerKindLabel: "deployment",
  397. source.OwnerNameLabel: "deployment1",
  398. },
  399. Value: 0,
  400. AdditionalInfo: nil,
  401. },
  402. {
  403. Name: metric.KubePodContainerStatusRunning,
  404. Labels: map[string]string{
  405. source.PodLabel: "pod1",
  406. source.NamespaceLabel: "namespace1",
  407. source.UIDLabel: "uuid1",
  408. source.NodeLabel: "node1",
  409. source.InstanceLabel: "node1",
  410. source.ContainerLabel: "container1",
  411. },
  412. Value: 0,
  413. AdditionalInfo: nil,
  414. },
  415. {
  416. Name: metric.KubePodContainerResourceRequests,
  417. Labels: map[string]string{
  418. source.PodLabel: "pod1",
  419. source.NamespaceLabel: "namespace1",
  420. source.UIDLabel: "uuid1",
  421. source.NodeLabel: "node1",
  422. source.InstanceLabel: "node1",
  423. source.ContainerLabel: "container1",
  424. source.ResourceLabel: "cpu",
  425. source.UnitLabel: "core",
  426. },
  427. Value: 0.5,
  428. AdditionalInfo: nil,
  429. },
  430. {
  431. Name: metric.KubePodContainerResourceRequests,
  432. Labels: map[string]string{
  433. source.PodLabel: "pod1",
  434. source.NamespaceLabel: "namespace1",
  435. source.UIDLabel: "uuid1",
  436. source.NodeLabel: "node1",
  437. source.InstanceLabel: "node1",
  438. source.ContainerLabel: "container1",
  439. source.ResourceLabel: "memory",
  440. source.UnitLabel: "byte",
  441. },
  442. Value: 512,
  443. AdditionalInfo: nil,
  444. },
  445. {
  446. Name: metric.KubePodContainerResourceLimits,
  447. Labels: map[string]string{
  448. source.PodLabel: "pod1",
  449. source.NamespaceLabel: "namespace1",
  450. source.UIDLabel: "uuid1",
  451. source.NodeLabel: "node1",
  452. source.InstanceLabel: "node1",
  453. source.ContainerLabel: "container1",
  454. source.ResourceLabel: "cpu",
  455. source.UnitLabel: "core",
  456. },
  457. Value: 1,
  458. AdditionalInfo: nil,
  459. },
  460. {
  461. Name: metric.KubePodContainerResourceLimits,
  462. Labels: map[string]string{
  463. source.PodLabel: "pod1",
  464. source.NamespaceLabel: "namespace1",
  465. source.UIDLabel: "uuid1",
  466. source.NodeLabel: "node1",
  467. source.InstanceLabel: "node1",
  468. source.ContainerLabel: "container1",
  469. source.ResourceLabel: "memory",
  470. source.UnitLabel: "byte",
  471. },
  472. Value: 1024,
  473. AdditionalInfo: nil,
  474. },
  475. },
  476. },
  477. }
  478. for _, tt := range tests {
  479. t.Run(tt.name, func(t *testing.T) {
  480. ks := &ClusterCacheScraper{}
  481. var scrapeResults []metric.Update
  482. for _, s := range tt.scrapes {
  483. res := ks.scrapePods(s.Pods)
  484. scrapeResults = append(scrapeResults, res...)
  485. }
  486. if len(scrapeResults) != len(tt.expected) {
  487. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  488. }
  489. for i, expected := range tt.expected {
  490. got := scrapeResults[i]
  491. if !reflect.DeepEqual(expected, got) {
  492. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  493. }
  494. }
  495. })
  496. }
  497. }
  498. func Test_kubernetesScraper_scrapePVCs(t *testing.T) {
  499. start1, _ := time.Parse(time.RFC3339, Start1Str)
  500. type scrape struct {
  501. PVCs []*clustercache.PersistentVolumeClaim
  502. Timestamp time.Time
  503. }
  504. tests := []struct {
  505. name string
  506. scrapes []scrape
  507. expected []metric.Update
  508. }{
  509. {
  510. name: "simple",
  511. scrapes: []scrape{
  512. {
  513. PVCs: []*clustercache.PersistentVolumeClaim{
  514. {
  515. Name: "pvc1",
  516. Namespace: "namespace1",
  517. UID: "uuid1",
  518. Spec: v1.PersistentVolumeClaimSpec{
  519. VolumeName: "vol1",
  520. StorageClassName: util.Ptr("storageClass1"),
  521. Resources: v1.VolumeResourceRequirements{
  522. Requests: v1.ResourceList{
  523. v1.ResourceStorage: resource.MustParse("4096"),
  524. },
  525. },
  526. },
  527. },
  528. },
  529. Timestamp: start1,
  530. },
  531. },
  532. expected: []metric.Update{
  533. {
  534. Name: metric.KubePersistentVolumeClaimInfo,
  535. Labels: map[string]string{
  536. source.PVCLabel: "pvc1",
  537. source.NamespaceLabel: "namespace1",
  538. source.UIDLabel: "uuid1",
  539. source.VolumeNameLabel: "vol1",
  540. source.StorageClassLabel: "storageClass1",
  541. },
  542. Value: 0,
  543. AdditionalInfo: nil,
  544. },
  545. {
  546. Name: metric.KubePersistentVolumeClaimResourceRequestsStorageBytes,
  547. Labels: map[string]string{
  548. source.PVCLabel: "pvc1",
  549. source.NamespaceLabel: "namespace1",
  550. source.UIDLabel: "uuid1",
  551. source.VolumeNameLabel: "vol1",
  552. source.StorageClassLabel: "storageClass1",
  553. },
  554. Value: 4096,
  555. AdditionalInfo: nil,
  556. },
  557. },
  558. },
  559. }
  560. for _, tt := range tests {
  561. t.Run(tt.name, func(t *testing.T) {
  562. ks := &ClusterCacheScraper{}
  563. var scrapeResults []metric.Update
  564. for _, s := range tt.scrapes {
  565. res := ks.scrapePVCs(s.PVCs)
  566. scrapeResults = append(scrapeResults, res...)
  567. }
  568. if len(scrapeResults) != len(tt.expected) {
  569. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  570. }
  571. for i, expected := range tt.expected {
  572. got := scrapeResults[i]
  573. if !reflect.DeepEqual(expected, got) {
  574. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  575. }
  576. }
  577. })
  578. }
  579. }
  580. func Test_kubernetesScraper_scrapePVs(t *testing.T) {
  581. start1, _ := time.Parse(time.RFC3339, Start1Str)
  582. type scrape struct {
  583. PVs []*clustercache.PersistentVolume
  584. Timestamp time.Time
  585. }
  586. tests := []struct {
  587. name string
  588. scrapes []scrape
  589. expected []metric.Update
  590. }{
  591. {
  592. name: "simple",
  593. scrapes: []scrape{
  594. {
  595. PVs: []*clustercache.PersistentVolume{
  596. {
  597. Name: "pv1",
  598. UID: "uuid1",
  599. Spec: v1.PersistentVolumeSpec{
  600. StorageClassName: "storageClass1",
  601. PersistentVolumeSource: v1.PersistentVolumeSource{
  602. CSI: &v1.CSIPersistentVolumeSource{
  603. VolumeHandle: "vol-1",
  604. },
  605. },
  606. Capacity: v1.ResourceList{
  607. v1.ResourceStorage: resource.MustParse("4096"),
  608. },
  609. },
  610. },
  611. },
  612. Timestamp: start1,
  613. },
  614. },
  615. expected: []metric.Update{
  616. {
  617. Name: metric.KubecostPVInfo,
  618. Labels: map[string]string{
  619. source.PVLabel: "pv1",
  620. source.ProviderIDLabel: "vol-1",
  621. source.StorageClassLabel: "storageClass1",
  622. source.UIDLabel: "uuid1",
  623. },
  624. Value: 0,
  625. AdditionalInfo: nil,
  626. },
  627. {
  628. Name: metric.KubePersistentVolumeCapacityBytes,
  629. Labels: map[string]string{
  630. source.PVLabel: "pv1",
  631. source.ProviderIDLabel: "vol-1",
  632. source.StorageClassLabel: "storageClass1",
  633. source.UIDLabel: "uuid1",
  634. },
  635. Value: 4096,
  636. AdditionalInfo: nil,
  637. },
  638. },
  639. },
  640. }
  641. for _, tt := range tests {
  642. t.Run(tt.name, func(t *testing.T) {
  643. ks := &ClusterCacheScraper{}
  644. var scrapeResults []metric.Update
  645. for _, s := range tt.scrapes {
  646. res := ks.scrapePVs(s.PVs)
  647. scrapeResults = append(scrapeResults, res...)
  648. }
  649. if len(scrapeResults) != len(tt.expected) {
  650. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  651. }
  652. for i, expected := range tt.expected {
  653. got := scrapeResults[i]
  654. if !reflect.DeepEqual(expected, got) {
  655. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  656. }
  657. }
  658. })
  659. }
  660. }
  661. func Test_kubernetesScraper_scrapeServices(t *testing.T) {
  662. start1, _ := time.Parse(time.RFC3339, Start1Str)
  663. type scrape struct {
  664. Services []*clustercache.Service
  665. Timestamp time.Time
  666. }
  667. tests := []struct {
  668. name string
  669. scrapes []scrape
  670. expected []metric.Update
  671. }{
  672. {
  673. name: "simple",
  674. scrapes: []scrape{
  675. {
  676. Services: []*clustercache.Service{
  677. {
  678. Name: "service1",
  679. Namespace: "namespace1",
  680. UID: "uuid1",
  681. SpecSelector: map[string]string{
  682. "test1": "blah",
  683. "test2": "blah2",
  684. },
  685. },
  686. },
  687. Timestamp: start1,
  688. },
  689. },
  690. expected: []metric.Update{
  691. {
  692. Name: metric.ServiceSelectorLabels,
  693. Labels: map[string]string{
  694. "service": "service1",
  695. source.NamespaceLabel: "namespace1",
  696. source.UIDLabel: "uuid1",
  697. },
  698. Value: 0,
  699. AdditionalInfo: map[string]string{
  700. "label_test1": "blah",
  701. "label_test2": "blah2",
  702. },
  703. },
  704. },
  705. },
  706. }
  707. for _, tt := range tests {
  708. t.Run(tt.name, func(t *testing.T) {
  709. ks := &ClusterCacheScraper{}
  710. var scrapeResults []metric.Update
  711. for _, s := range tt.scrapes {
  712. res := ks.scrapeServices(s.Services)
  713. scrapeResults = append(scrapeResults, res...)
  714. }
  715. if len(scrapeResults) != len(tt.expected) {
  716. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  717. }
  718. for i, expected := range tt.expected {
  719. got := scrapeResults[i]
  720. if !reflect.DeepEqual(expected, got) {
  721. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  722. }
  723. }
  724. })
  725. }
  726. }
  727. func Test_kubernetesScraper_scrapeStatefulSets(t *testing.T) {
  728. start1, _ := time.Parse(time.RFC3339, Start1Str)
  729. type scrape struct {
  730. StatefulSets []*clustercache.StatefulSet
  731. Timestamp time.Time
  732. }
  733. tests := []struct {
  734. name string
  735. scrapes []scrape
  736. expected []metric.Update
  737. }{
  738. {
  739. name: "simple",
  740. scrapes: []scrape{
  741. {
  742. StatefulSets: []*clustercache.StatefulSet{
  743. {
  744. Name: "statefulSet1",
  745. Namespace: "namespace1",
  746. UID: "uuid1",
  747. SpecSelector: &metav1.LabelSelector{
  748. MatchLabels: map[string]string{
  749. "test1": "blah",
  750. "test2": "blah2",
  751. },
  752. },
  753. },
  754. },
  755. Timestamp: start1,
  756. },
  757. },
  758. expected: []metric.Update{
  759. {
  760. Name: metric.StatefulSetMatchLabels,
  761. Labels: map[string]string{
  762. source.StatefulSetLabel: "statefulSet1",
  763. source.NamespaceLabel: "namespace1",
  764. source.UIDLabel: "uuid1",
  765. },
  766. Value: 0,
  767. AdditionalInfo: map[string]string{
  768. "label_test1": "blah",
  769. "label_test2": "blah2",
  770. },
  771. },
  772. },
  773. },
  774. }
  775. for _, tt := range tests {
  776. t.Run(tt.name, func(t *testing.T) {
  777. ks := &ClusterCacheScraper{}
  778. var scrapeResults []metric.Update
  779. for _, s := range tt.scrapes {
  780. res := ks.scrapeStatefulSets(s.StatefulSets)
  781. scrapeResults = append(scrapeResults, res...)
  782. }
  783. if len(scrapeResults) != len(tt.expected) {
  784. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  785. }
  786. for i, expected := range tt.expected {
  787. got := scrapeResults[i]
  788. if !reflect.DeepEqual(expected, got) {
  789. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  790. }
  791. }
  792. })
  793. }
  794. }
  795. func Test_kubernetesScraper_scrapeReplicaSets(t *testing.T) {
  796. start1, _ := time.Parse(time.RFC3339, Start1Str)
  797. type scrape struct {
  798. ReplicaSets []*clustercache.ReplicaSet
  799. Timestamp time.Time
  800. }
  801. tests := []struct {
  802. name string
  803. scrapes []scrape
  804. expected []metric.Update
  805. }{
  806. {
  807. name: "simple",
  808. scrapes: []scrape{
  809. {
  810. ReplicaSets: []*clustercache.ReplicaSet{
  811. {
  812. Name: "replicaSet1",
  813. Namespace: "namespace1",
  814. UID: "uuid1",
  815. OwnerReferences: []metav1.OwnerReference{
  816. {
  817. Name: "rollout1",
  818. Kind: "Rollout",
  819. },
  820. },
  821. },
  822. {
  823. Name: "pureReplicaSet",
  824. Namespace: "namespace1",
  825. UID: "uuid2",
  826. OwnerReferences: []metav1.OwnerReference{},
  827. },
  828. },
  829. Timestamp: start1,
  830. },
  831. },
  832. expected: []metric.Update{
  833. {
  834. Name: metric.KubeReplicasetOwner,
  835. Labels: map[string]string{
  836. "replicaset": "replicaSet1",
  837. source.NamespaceLabel: "namespace1",
  838. source.UIDLabel: "uuid1",
  839. source.OwnerNameLabel: "rollout1",
  840. source.OwnerKindLabel: "Rollout",
  841. },
  842. Value: 0,
  843. },
  844. {
  845. Name: metric.KubeReplicasetOwner,
  846. Labels: map[string]string{
  847. "replicaset": "pureReplicaSet",
  848. source.NamespaceLabel: "namespace1",
  849. source.UIDLabel: "uuid2",
  850. source.OwnerNameLabel: source.NoneLabelValue,
  851. source.OwnerKindLabel: source.NoneLabelValue,
  852. },
  853. Value: 0,
  854. },
  855. },
  856. },
  857. }
  858. for _, tt := range tests {
  859. t.Run(tt.name, func(t *testing.T) {
  860. ks := &ClusterCacheScraper{}
  861. var scrapeResults []metric.Update
  862. for _, s := range tt.scrapes {
  863. res := ks.scrapeReplicaSets(s.ReplicaSets)
  864. scrapeResults = append(scrapeResults, res...)
  865. }
  866. if len(scrapeResults) != len(tt.expected) {
  867. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  868. }
  869. for i, expected := range tt.expected {
  870. got := scrapeResults[i]
  871. if !reflect.DeepEqual(expected, got) {
  872. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  873. }
  874. }
  875. })
  876. }
  877. }
  878. func Test_kubernetesScraper_scrapeResourceQuotas(t *testing.T) {
  879. start1, _ := time.Parse(time.RFC3339, Start1Str)
  880. type scrape struct {
  881. ResourceQuotas []*clustercache.ResourceQuota
  882. Timestamp time.Time
  883. }
  884. tests := []struct {
  885. name string
  886. scrapes []scrape
  887. expected []metric.Update
  888. }{
  889. {
  890. name: "simple",
  891. scrapes: []scrape{
  892. {
  893. ResourceQuotas: []*clustercache.ResourceQuota{
  894. {
  895. Name: "resourceQuota1",
  896. Namespace: "namespace1",
  897. UID: "uuid1",
  898. Spec: v1.ResourceQuotaSpec{
  899. Hard: v1.ResourceList{
  900. v1.ResourceRequestsCPU: resource.MustParse("1"),
  901. v1.ResourceRequestsMemory: resource.MustParse("1024"),
  902. v1.ResourceLimitsCPU: resource.MustParse("2"),
  903. v1.ResourceLimitsMemory: resource.MustParse("2048"),
  904. },
  905. },
  906. Status: v1.ResourceQuotaStatus{
  907. Used: v1.ResourceList{
  908. v1.ResourceRequestsCPU: resource.MustParse("0.5"),
  909. v1.ResourceRequestsMemory: resource.MustParse("512"),
  910. v1.ResourceLimitsCPU: resource.MustParse("1"),
  911. v1.ResourceLimitsMemory: resource.MustParse("1024"),
  912. },
  913. },
  914. },
  915. },
  916. Timestamp: start1,
  917. },
  918. },
  919. expected: []metric.Update{
  920. {
  921. Name: metric.ResourceQuotaInfo,
  922. Labels: map[string]string{
  923. source.ResourceQuotaLabel: "resourceQuota1",
  924. source.NamespaceLabel: "namespace1",
  925. source.UIDLabel: "uuid1",
  926. },
  927. Value: 0,
  928. AdditionalInfo: map[string]string{
  929. source.ResourceQuotaLabel: "resourceQuota1",
  930. source.NamespaceLabel: "namespace1",
  931. source.UIDLabel: "uuid1",
  932. },
  933. },
  934. {
  935. Name: metric.KubeResourceQuotaSpecResourceRequests,
  936. Labels: map[string]string{
  937. source.ResourceQuotaLabel: "resourceQuota1",
  938. source.NamespaceLabel: "namespace1",
  939. source.UIDLabel: "uuid1",
  940. source.ResourceLabel: "cpu",
  941. source.UnitLabel: "core",
  942. },
  943. Value: 1,
  944. AdditionalInfo: nil,
  945. },
  946. {
  947. Name: metric.KubeResourceQuotaSpecResourceRequests,
  948. Labels: map[string]string{
  949. source.ResourceQuotaLabel: "resourceQuota1",
  950. source.NamespaceLabel: "namespace1",
  951. source.UIDLabel: "uuid1",
  952. source.ResourceLabel: "memory",
  953. source.UnitLabel: "byte",
  954. },
  955. Value: 1024,
  956. AdditionalInfo: nil,
  957. },
  958. {
  959. Name: metric.KubeResourceQuotaSpecResourceLimits,
  960. Labels: map[string]string{
  961. source.ResourceQuotaLabel: "resourceQuota1",
  962. source.NamespaceLabel: "namespace1",
  963. source.UIDLabel: "uuid1",
  964. source.ResourceLabel: "cpu",
  965. source.UnitLabel: "core",
  966. },
  967. Value: 2,
  968. AdditionalInfo: nil,
  969. },
  970. {
  971. Name: metric.KubeResourceQuotaSpecResourceLimits,
  972. Labels: map[string]string{
  973. source.ResourceQuotaLabel: "resourceQuota1",
  974. source.NamespaceLabel: "namespace1",
  975. source.UIDLabel: "uuid1",
  976. source.ResourceLabel: "memory",
  977. source.UnitLabel: "byte",
  978. },
  979. Value: 2048,
  980. AdditionalInfo: nil,
  981. },
  982. {
  983. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  984. Labels: map[string]string{
  985. source.ResourceQuotaLabel: "resourceQuota1",
  986. source.NamespaceLabel: "namespace1",
  987. source.UIDLabel: "uuid1",
  988. source.ResourceLabel: "cpu",
  989. source.UnitLabel: "core",
  990. },
  991. Value: 0.5,
  992. AdditionalInfo: nil,
  993. },
  994. {
  995. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  996. Labels: map[string]string{
  997. source.ResourceQuotaLabel: "resourceQuota1",
  998. source.NamespaceLabel: "namespace1",
  999. source.UIDLabel: "uuid1",
  1000. source.ResourceLabel: "memory",
  1001. source.UnitLabel: "byte",
  1002. },
  1003. Value: 512,
  1004. AdditionalInfo: nil,
  1005. },
  1006. {
  1007. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1008. Labels: map[string]string{
  1009. source.ResourceQuotaLabel: "resourceQuota1",
  1010. source.NamespaceLabel: "namespace1",
  1011. source.UIDLabel: "uuid1",
  1012. source.ResourceLabel: "cpu",
  1013. source.UnitLabel: "core",
  1014. },
  1015. Value: 1,
  1016. AdditionalInfo: nil,
  1017. },
  1018. {
  1019. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1020. Labels: map[string]string{
  1021. source.ResourceQuotaLabel: "resourceQuota1",
  1022. source.NamespaceLabel: "namespace1",
  1023. source.UIDLabel: "uuid1",
  1024. source.ResourceLabel: "memory",
  1025. source.UnitLabel: "byte",
  1026. },
  1027. Value: 1024,
  1028. AdditionalInfo: nil,
  1029. },
  1030. },
  1031. },
  1032. }
  1033. for _, tt := range tests {
  1034. t.Run(tt.name, func(t *testing.T) {
  1035. ks := &ClusterCacheScraper{}
  1036. var scrapeResults []metric.Update
  1037. for _, s := range tt.scrapes {
  1038. res := ks.scrapeResourceQuotas(s.ResourceQuotas)
  1039. scrapeResults = append(scrapeResults, res...)
  1040. }
  1041. if len(scrapeResults) != len(tt.expected) {
  1042. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1043. }
  1044. for i, expected := range tt.expected {
  1045. got := scrapeResults[i]
  1046. if !reflect.DeepEqual(expected, got) {
  1047. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1048. }
  1049. }
  1050. })
  1051. }
  1052. }