clustercache_test.go 28 KB

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