clustercache_test.go 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481
  1. package scrape
  2. import (
  3. "reflect"
  4. "testing"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/clustercache"
  7. "github.com/opencost/opencost/core/pkg/source"
  8. "github.com/opencost/opencost/modules/collector-source/pkg/metric"
  9. "github.com/opencost/opencost/modules/collector-source/pkg/util"
  10. v1 "k8s.io/api/core/v1"
  11. "k8s.io/apimachinery/pkg/api/resource"
  12. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  13. "k8s.io/apimachinery/pkg/types"
  14. )
  15. var Start1Str = "2025-01-01T00:00:00Z00:00"
  16. func Test_kubernetesScraper_scrapeNodes(t *testing.T) {
  17. start1, _ := time.Parse(time.RFC3339, Start1Str)
  18. type scrape struct {
  19. Nodes []*clustercache.Node
  20. Timestamp time.Time
  21. }
  22. tests := []struct {
  23. name string
  24. scrapes []scrape
  25. expected []metric.Update
  26. }{
  27. {
  28. name: "simple",
  29. scrapes: []scrape{
  30. {
  31. Nodes: []*clustercache.Node{
  32. {
  33. Name: "node1",
  34. UID: "uuid1",
  35. SpecProviderID: "i-1",
  36. Status: v1.NodeStatus{
  37. Capacity: v1.ResourceList{
  38. v1.ResourceCPU: resource.MustParse("2"),
  39. v1.ResourceMemory: resource.MustParse("2048"),
  40. },
  41. Allocatable: v1.ResourceList{
  42. v1.ResourceCPU: resource.MustParse("1"),
  43. v1.ResourceMemory: resource.MustParse("1024"),
  44. },
  45. },
  46. Labels: map[string]string{
  47. "test1": "blah",
  48. "test2": "blah2",
  49. },
  50. },
  51. },
  52. Timestamp: start1,
  53. },
  54. },
  55. expected: []metric.Update{
  56. {
  57. Name: metric.NodeInfo,
  58. Labels: map[string]string{
  59. source.NodeLabel: "node1",
  60. source.ProviderIDLabel: "i-1",
  61. source.UIDLabel: "uuid1",
  62. },
  63. Value: 0,
  64. AdditionalInfo: map[string]string{
  65. source.NodeLabel: "node1",
  66. source.ProviderIDLabel: "i-1",
  67. source.UIDLabel: "uuid1",
  68. },
  69. },
  70. {
  71. Name: metric.NodeResourceCapacities,
  72. Labels: map[string]string{
  73. source.NodeLabel: "node1",
  74. source.ProviderIDLabel: "i-1",
  75. source.UIDLabel: "uuid1",
  76. source.ResourceLabel: "cpu",
  77. source.UnitLabel: "core",
  78. },
  79. Value: 2.0,
  80. AdditionalInfo: nil,
  81. },
  82. {
  83. Name: metric.NodeResourceCapacities,
  84. Labels: map[string]string{
  85. source.NodeLabel: "node1",
  86. source.ProviderIDLabel: "i-1",
  87. source.UIDLabel: "uuid1",
  88. source.ResourceLabel: "memory",
  89. source.UnitLabel: "byte",
  90. },
  91. Value: 2048.0,
  92. AdditionalInfo: nil,
  93. },
  94. {
  95. Name: metric.KubeNodeStatusCapacityCPUCores,
  96. Labels: map[string]string{
  97. source.NodeLabel: "node1",
  98. source.ProviderIDLabel: "i-1",
  99. source.UIDLabel: "uuid1",
  100. },
  101. Value: 2.0,
  102. AdditionalInfo: nil,
  103. },
  104. {
  105. Name: metric.KubeNodeStatusCapacityMemoryBytes,
  106. Labels: map[string]string{
  107. source.NodeLabel: "node1",
  108. source.ProviderIDLabel: "i-1",
  109. source.UIDLabel: "uuid1",
  110. },
  111. Value: 2048.0,
  112. AdditionalInfo: nil,
  113. },
  114. {
  115. Name: metric.NodeResourcesAllocatable,
  116. Labels: map[string]string{
  117. source.NodeLabel: "node1",
  118. source.ProviderIDLabel: "i-1",
  119. source.UIDLabel: "uuid1",
  120. source.ResourceLabel: "cpu",
  121. source.UnitLabel: "core",
  122. },
  123. Value: 1.0,
  124. AdditionalInfo: nil,
  125. },
  126. {
  127. Name: metric.NodeResourcesAllocatable,
  128. Labels: map[string]string{
  129. source.NodeLabel: "node1",
  130. source.ProviderIDLabel: "i-1",
  131. source.UIDLabel: "uuid1",
  132. source.ResourceLabel: "memory",
  133. source.UnitLabel: "byte",
  134. },
  135. Value: 1024.0,
  136. AdditionalInfo: nil,
  137. },
  138. {
  139. Name: metric.KubeNodeStatusAllocatableCPUCores,
  140. Labels: map[string]string{
  141. source.NodeLabel: "node1",
  142. source.ProviderIDLabel: "i-1",
  143. source.UIDLabel: "uuid1",
  144. },
  145. Value: 1.0,
  146. AdditionalInfo: nil,
  147. },
  148. {
  149. Name: metric.KubeNodeStatusAllocatableMemoryBytes,
  150. Labels: map[string]string{
  151. source.NodeLabel: "node1",
  152. source.ProviderIDLabel: "i-1",
  153. source.UIDLabel: "uuid1",
  154. },
  155. Value: 1024.0,
  156. AdditionalInfo: nil,
  157. },
  158. {
  159. Name: metric.KubeNodeLabels,
  160. Labels: map[string]string{
  161. source.NodeLabel: "node1",
  162. source.ProviderIDLabel: "i-1",
  163. source.UIDLabel: "uuid1",
  164. },
  165. Value: 0,
  166. AdditionalInfo: map[string]string{
  167. "label_test1": "blah",
  168. "label_test2": "blah2",
  169. },
  170. },
  171. },
  172. },
  173. }
  174. for _, tt := range tests {
  175. t.Run(tt.name, func(t *testing.T) {
  176. ks := &ClusterCacheScraper{}
  177. var scrapeResults []metric.Update
  178. for _, s := range tt.scrapes {
  179. res := ks.scrapeNodes(s.Nodes)
  180. scrapeResults = append(scrapeResults, res...)
  181. }
  182. if len(scrapeResults) != len(tt.expected) {
  183. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  184. }
  185. for i, expected := range tt.expected {
  186. got := scrapeResults[i]
  187. if !reflect.DeepEqual(expected, got) {
  188. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  189. }
  190. }
  191. })
  192. }
  193. }
  194. func Test_kubernetesScraper_scrapeDeployments(t *testing.T) {
  195. start1, _ := time.Parse(time.RFC3339, Start1Str)
  196. type scrape struct {
  197. Deployments []*clustercache.Deployment
  198. Timestamp time.Time
  199. }
  200. tests := []struct {
  201. name string
  202. scrapes []scrape
  203. expected []metric.Update
  204. }{
  205. {
  206. name: "simple",
  207. scrapes: []scrape{
  208. {
  209. Deployments: []*clustercache.Deployment{
  210. {
  211. Name: "deployment1",
  212. Namespace: "namespace1",
  213. UID: "uuid1",
  214. MatchLabels: map[string]string{
  215. "test1": "blah",
  216. "test2": "blah2",
  217. },
  218. },
  219. },
  220. Timestamp: start1,
  221. },
  222. },
  223. // deploymentInfo map is shared across all 4 metrics and has namespace
  224. // added to it before DeploymentMatchLabels is appended, so all 4 metrics
  225. // reflect the final state: {uid, namespace_uid, deployment, namespace}.
  226. expected: []metric.Update{
  227. {
  228. Name: metric.DeploymentInfo,
  229. Labels: map[string]string{
  230. source.UIDLabel: "uuid1",
  231. source.NamespaceUIDLabel: "ns-uuid1",
  232. source.DeploymentLabel: "deployment1",
  233. source.NamespaceLabel: "namespace1",
  234. },
  235. Value: 0,
  236. AdditionalInfo: map[string]string{
  237. source.UIDLabel: "uuid1",
  238. source.NamespaceUIDLabel: "ns-uuid1",
  239. source.DeploymentLabel: "deployment1",
  240. source.NamespaceLabel: "namespace1",
  241. },
  242. },
  243. {
  244. Name: metric.DeploymentLabels,
  245. Labels: map[string]string{
  246. source.UIDLabel: "uuid1",
  247. source.NamespaceUIDLabel: "ns-uuid1",
  248. source.DeploymentLabel: "deployment1",
  249. source.NamespaceLabel: "namespace1",
  250. },
  251. Value: 0,
  252. AdditionalInfo: map[string]string{},
  253. },
  254. {
  255. Name: metric.DeploymentAnnotations,
  256. Labels: map[string]string{
  257. source.UIDLabel: "uuid1",
  258. source.NamespaceUIDLabel: "ns-uuid1",
  259. source.DeploymentLabel: "deployment1",
  260. source.NamespaceLabel: "namespace1",
  261. },
  262. Value: 0,
  263. AdditionalInfo: map[string]string{},
  264. },
  265. {
  266. Name: metric.DeploymentMatchLabels,
  267. Labels: map[string]string{
  268. source.UIDLabel: "uuid1",
  269. source.NamespaceUIDLabel: "ns-uuid1",
  270. source.DeploymentLabel: "deployment1",
  271. source.NamespaceLabel: "namespace1",
  272. },
  273. Value: 0,
  274. AdditionalInfo: map[string]string{
  275. "label_test1": "blah",
  276. "label_test2": "blah2",
  277. },
  278. },
  279. },
  280. },
  281. }
  282. for _, tt := range tests {
  283. t.Run(tt.name, func(t *testing.T) {
  284. ks := &ClusterCacheScraper{}
  285. nsIndex := make(map[string]types.UID, 0)
  286. nsIndex["namespace1"] = "ns-uuid1"
  287. var scrapeResults []metric.Update
  288. for _, s := range tt.scrapes {
  289. res := ks.scrapeDeployments(s.Deployments, nsIndex)
  290. scrapeResults = append(scrapeResults, res...)
  291. }
  292. if len(scrapeResults) != len(tt.expected) {
  293. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  294. }
  295. for i, expected := range tt.expected {
  296. got := scrapeResults[i]
  297. if !reflect.DeepEqual(expected, got) {
  298. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  299. }
  300. }
  301. })
  302. }
  303. }
  304. func Test_kubernetesScraper_scrapeNamespaces(t *testing.T) {
  305. start1, _ := time.Parse(time.RFC3339, Start1Str)
  306. type scrape struct {
  307. Namespaces []*clustercache.Namespace
  308. Timestamp time.Time
  309. }
  310. tests := []struct {
  311. name string
  312. scrapes []scrape
  313. expected []metric.Update
  314. }{
  315. {
  316. name: "simple",
  317. scrapes: []scrape{
  318. {
  319. Namespaces: []*clustercache.Namespace{
  320. {
  321. Name: "namespace1",
  322. UID: "uuid1",
  323. Labels: map[string]string{
  324. "test1": "blah",
  325. "test2": "blah2",
  326. },
  327. Annotations: map[string]string{
  328. "test3": "blah3",
  329. "test4": "blah4",
  330. },
  331. },
  332. },
  333. Timestamp: start1,
  334. },
  335. },
  336. expected: []metric.Update{
  337. {
  338. Name: metric.NamespaceInfo,
  339. Labels: map[string]string{
  340. source.NamespaceLabel: "namespace1",
  341. source.UIDLabel: "uuid1",
  342. },
  343. Value: 0,
  344. AdditionalInfo: map[string]string{
  345. source.NamespaceLabel: "namespace1",
  346. source.UIDLabel: "uuid1",
  347. },
  348. },
  349. {
  350. Name: metric.KubeNamespaceLabels,
  351. Labels: map[string]string{
  352. source.NamespaceLabel: "namespace1",
  353. source.UIDLabel: "uuid1",
  354. },
  355. Value: 0,
  356. AdditionalInfo: map[string]string{
  357. "label_test1": "blah",
  358. "label_test2": "blah2",
  359. },
  360. },
  361. {
  362. Name: metric.KubeNamespaceAnnotations,
  363. Labels: map[string]string{
  364. source.NamespaceLabel: "namespace1",
  365. source.UIDLabel: "uuid1",
  366. },
  367. Value: 0,
  368. AdditionalInfo: map[string]string{
  369. "annotation_test3": "blah3",
  370. "annotation_test4": "blah4",
  371. },
  372. },
  373. },
  374. },
  375. }
  376. for _, tt := range tests {
  377. t.Run(tt.name, func(t *testing.T) {
  378. ks := &ClusterCacheScraper{}
  379. var scrapeResults []metric.Update
  380. for _, s := range tt.scrapes {
  381. res := ks.scrapeNamespaces(s.Namespaces)
  382. scrapeResults = append(scrapeResults, res...)
  383. }
  384. if len(scrapeResults) != len(tt.expected) {
  385. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  386. }
  387. for i, expected := range tt.expected {
  388. got := scrapeResults[i]
  389. if !reflect.DeepEqual(expected, got) {
  390. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  391. }
  392. }
  393. })
  394. }
  395. }
  396. func Test_kubernetesScraper_scrapePods(t *testing.T) {
  397. start1, _ := time.Parse(time.RFC3339, Start1Str)
  398. type scrape struct {
  399. PVCs []*clustercache.PersistentVolumeClaim
  400. Pods []*clustercache.Pod
  401. Timestamp time.Time
  402. }
  403. tests := []struct {
  404. name string
  405. nsSetup func(map[string]types.UID)
  406. scrapes []scrape
  407. expected []metric.Update
  408. }{
  409. {
  410. name: "simple",
  411. scrapes: []scrape{
  412. {
  413. PVCs: []*clustercache.PersistentVolumeClaim{
  414. {
  415. Name: "pvc1",
  416. Namespace: "namespace1",
  417. UID: "uuid1",
  418. Spec: v1.PersistentVolumeClaimSpec{
  419. VolumeName: "vol1",
  420. StorageClassName: util.Ptr("storageClass1"),
  421. Resources: v1.VolumeResourceRequirements{
  422. Requests: v1.ResourceList{
  423. v1.ResourceStorage: resource.MustParse("4096"),
  424. },
  425. },
  426. },
  427. },
  428. },
  429. Pods: []*clustercache.Pod{
  430. {
  431. Name: "pod1",
  432. Namespace: "namespace1",
  433. UID: "uuid1",
  434. Spec: clustercache.PodSpec{
  435. NodeName: "node1",
  436. Containers: []clustercache.Container{
  437. {
  438. Name: "container1",
  439. Resources: v1.ResourceRequirements{
  440. Requests: map[v1.ResourceName]resource.Quantity{
  441. v1.ResourceCPU: resource.MustParse("500m"),
  442. v1.ResourceMemory: resource.MustParse("512"),
  443. },
  444. Limits: map[v1.ResourceName]resource.Quantity{
  445. v1.ResourceCPU: resource.MustParse("1"),
  446. v1.ResourceMemory: resource.MustParse("1024"),
  447. },
  448. },
  449. },
  450. },
  451. },
  452. Labels: map[string]string{
  453. "test1": "blah",
  454. "test2": "blah2",
  455. },
  456. Annotations: map[string]string{
  457. "test3": "blah3",
  458. "test4": "blah4",
  459. },
  460. OwnerReferences: []metav1.OwnerReference{
  461. {
  462. Kind: source.DeploymentLabel,
  463. Name: "deployment1",
  464. Controller: nil,
  465. },
  466. },
  467. Status: clustercache.PodStatus{
  468. ContainerStatuses: []v1.ContainerStatus{
  469. {
  470. Name: "container1",
  471. State: v1.ContainerState{
  472. Running: &v1.ContainerStateRunning{},
  473. },
  474. },
  475. },
  476. },
  477. },
  478. },
  479. Timestamp: start1,
  480. },
  481. },
  482. // podInfo is mutated after PodInfo is appended (namespace/node/instance added),
  483. // so PodInfo.Labels also reflects those fields at assertion time.
  484. expected: []metric.Update{
  485. {
  486. Name: metric.PodInfo,
  487. Labels: map[string]string{
  488. source.UIDLabel: "uuid1",
  489. source.PodLabel: "pod1",
  490. source.NamespaceUIDLabel: "",
  491. source.NodeUIDLabel: "",
  492. source.NamespaceLabel: "namespace1",
  493. source.NodeLabel: "node1",
  494. source.InstanceLabel: "node1",
  495. },
  496. Value: 0,
  497. AdditionalInfo: map[string]string{
  498. source.UIDLabel: "uuid1",
  499. source.PodLabel: "pod1",
  500. source.NamespaceUIDLabel: "",
  501. source.NodeUIDLabel: "",
  502. source.NamespaceLabel: "namespace1",
  503. source.NodeLabel: "node1",
  504. source.InstanceLabel: "node1",
  505. },
  506. },
  507. {
  508. Name: metric.KubePodLabels,
  509. Labels: map[string]string{
  510. source.UIDLabel: "uuid1",
  511. source.PodLabel: "pod1",
  512. source.NamespaceUIDLabel: "",
  513. source.NodeUIDLabel: "",
  514. source.NamespaceLabel: "namespace1",
  515. source.NodeLabel: "node1",
  516. source.InstanceLabel: "node1",
  517. },
  518. Value: 0,
  519. AdditionalInfo: map[string]string{
  520. "label_test1": "blah",
  521. "label_test2": "blah2",
  522. },
  523. },
  524. {
  525. Name: metric.KubePodAnnotations,
  526. Labels: map[string]string{
  527. source.UIDLabel: "uuid1",
  528. source.PodLabel: "pod1",
  529. source.NamespaceUIDLabel: "",
  530. source.NodeUIDLabel: "",
  531. source.NamespaceLabel: "namespace1",
  532. source.NodeLabel: "node1",
  533. source.InstanceLabel: "node1",
  534. },
  535. Value: 0,
  536. AdditionalInfo: map[string]string{
  537. "annotation_test3": "blah3",
  538. "annotation_test4": "blah4",
  539. },
  540. },
  541. {
  542. Name: metric.KubePodOwner,
  543. Labels: map[string]string{
  544. source.UIDLabel: "uuid1",
  545. source.PodLabel: "pod1",
  546. source.NamespaceUIDLabel: "",
  547. source.NodeUIDLabel: "",
  548. source.NamespaceLabel: "namespace1",
  549. source.NodeLabel: "node1",
  550. source.InstanceLabel: "node1",
  551. source.OwnerKindLabel: "deployment",
  552. source.OwnerNameLabel: "deployment1",
  553. source.OwnerUIDLabel: "",
  554. source.ContainerLabel: "false",
  555. },
  556. Value: 0,
  557. AdditionalInfo: nil,
  558. },
  559. {
  560. Name: metric.KubePodContainerStatusRunning,
  561. Labels: map[string]string{
  562. source.UIDLabel: "uuid1",
  563. source.PodLabel: "pod1",
  564. source.NamespaceUIDLabel: "",
  565. source.NodeUIDLabel: "",
  566. source.NamespaceLabel: "namespace1",
  567. source.NodeLabel: "node1",
  568. source.InstanceLabel: "node1",
  569. source.ContainerLabel: "container1",
  570. },
  571. Value: 0,
  572. AdditionalInfo: map[string]string{
  573. source.UIDLabel: "uuid1",
  574. source.PodLabel: "pod1",
  575. source.NamespaceUIDLabel: "",
  576. source.NodeUIDLabel: "",
  577. source.NamespaceLabel: "namespace1",
  578. source.NodeLabel: "node1",
  579. source.InstanceLabel: "node1",
  580. source.ContainerLabel: "container1",
  581. },
  582. },
  583. {
  584. Name: metric.KubePodContainerResourceRequests,
  585. Labels: map[string]string{
  586. source.UIDLabel: "uuid1",
  587. source.PodLabel: "pod1",
  588. source.NamespaceUIDLabel: "",
  589. source.NodeUIDLabel: "",
  590. source.NamespaceLabel: "namespace1",
  591. source.NodeLabel: "node1",
  592. source.InstanceLabel: "node1",
  593. source.ContainerLabel: "container1",
  594. source.ResourceLabel: "cpu",
  595. source.UnitLabel: "core",
  596. },
  597. Value: 0.5,
  598. AdditionalInfo: nil,
  599. },
  600. {
  601. Name: metric.KubePodContainerResourceRequests,
  602. Labels: map[string]string{
  603. source.UIDLabel: "uuid1",
  604. source.PodLabel: "pod1",
  605. source.NamespaceUIDLabel: "",
  606. source.NodeUIDLabel: "",
  607. source.NamespaceLabel: "namespace1",
  608. source.NodeLabel: "node1",
  609. source.InstanceLabel: "node1",
  610. source.ContainerLabel: "container1",
  611. source.ResourceLabel: "memory",
  612. source.UnitLabel: "byte",
  613. },
  614. Value: 512,
  615. AdditionalInfo: nil,
  616. },
  617. {
  618. Name: metric.KubePodContainerResourceLimits,
  619. Labels: map[string]string{
  620. source.UIDLabel: "uuid1",
  621. source.PodLabel: "pod1",
  622. source.NamespaceUIDLabel: "",
  623. source.NodeUIDLabel: "",
  624. source.NamespaceLabel: "namespace1",
  625. source.NodeLabel: "node1",
  626. source.InstanceLabel: "node1",
  627. source.ContainerLabel: "container1",
  628. source.ResourceLabel: "cpu",
  629. source.UnitLabel: "core",
  630. },
  631. Value: 1,
  632. AdditionalInfo: nil,
  633. },
  634. {
  635. Name: metric.KubePodContainerResourceLimits,
  636. Labels: map[string]string{
  637. source.UIDLabel: "uuid1",
  638. source.PodLabel: "pod1",
  639. source.NamespaceUIDLabel: "",
  640. source.NodeUIDLabel: "",
  641. source.NamespaceLabel: "namespace1",
  642. source.NodeLabel: "node1",
  643. source.InstanceLabel: "node1",
  644. source.ContainerLabel: "container1",
  645. source.ResourceLabel: "memory",
  646. source.UnitLabel: "byte",
  647. },
  648. Value: 1024,
  649. AdditionalInfo: nil,
  650. },
  651. {
  652. Name: metric.PodPVCAllocation,
  653. Labels: map[string]string{
  654. source.PodLabel: unmountedPVsContainer,
  655. source.NamespaceLabel: "namespace1",
  656. source.PVCLabel: "pvc1",
  657. source.PVLabel: "vol1",
  658. },
  659. Value: 4096,
  660. AdditionalInfo: nil,
  661. },
  662. },
  663. },
  664. {
  665. name: "with namespace index",
  666. nsSetup: func(nsIndex map[string]types.UID) {
  667. nsIndex["namespace1"] = "ns-uuid1"
  668. },
  669. scrapes: []scrape{
  670. {
  671. Pods: []*clustercache.Pod{
  672. {
  673. Name: "pod1",
  674. Namespace: "namespace1",
  675. UID: "uuid1",
  676. Spec: clustercache.PodSpec{
  677. NodeName: "node1",
  678. Containers: []clustercache.Container{
  679. {
  680. Name: "container1",
  681. Resources: v1.ResourceRequirements{
  682. Requests: map[v1.ResourceName]resource.Quantity{
  683. v1.ResourceCPU: resource.MustParse("500m"),
  684. v1.ResourceMemory: resource.MustParse("512"),
  685. },
  686. Limits: map[v1.ResourceName]resource.Quantity{
  687. v1.ResourceCPU: resource.MustParse("1"),
  688. v1.ResourceMemory: resource.MustParse("1024"),
  689. },
  690. },
  691. },
  692. },
  693. },
  694. Status: clustercache.PodStatus{
  695. ContainerStatuses: []v1.ContainerStatus{
  696. {
  697. Name: "container1",
  698. State: v1.ContainerState{
  699. Running: &v1.ContainerStateRunning{},
  700. },
  701. },
  702. },
  703. },
  704. },
  705. },
  706. Timestamp: start1,
  707. },
  708. },
  709. expected: []metric.Update{
  710. {
  711. Name: metric.PodInfo,
  712. Labels: map[string]string{
  713. source.UIDLabel: "uuid1",
  714. source.PodLabel: "pod1",
  715. source.NamespaceUIDLabel: "ns-uuid1",
  716. source.NodeUIDLabel: "",
  717. source.NamespaceLabel: "namespace1",
  718. source.NodeLabel: "node1",
  719. source.InstanceLabel: "node1",
  720. },
  721. Value: 0,
  722. AdditionalInfo: map[string]string{
  723. source.UIDLabel: "uuid1",
  724. source.PodLabel: "pod1",
  725. source.NamespaceUIDLabel: "ns-uuid1",
  726. source.NodeUIDLabel: "",
  727. source.NamespaceLabel: "namespace1",
  728. source.NodeLabel: "node1",
  729. source.InstanceLabel: "node1",
  730. },
  731. },
  732. {
  733. Name: metric.KubePodLabels,
  734. Labels: map[string]string{
  735. source.UIDLabel: "uuid1",
  736. source.PodLabel: "pod1",
  737. source.NamespaceUIDLabel: "ns-uuid1",
  738. source.NodeUIDLabel: "",
  739. source.NamespaceLabel: "namespace1",
  740. source.NodeLabel: "node1",
  741. source.InstanceLabel: "node1",
  742. },
  743. Value: 0,
  744. AdditionalInfo: map[string]string{},
  745. },
  746. {
  747. Name: metric.KubePodAnnotations,
  748. Labels: map[string]string{
  749. source.UIDLabel: "uuid1",
  750. source.PodLabel: "pod1",
  751. source.NamespaceUIDLabel: "ns-uuid1",
  752. source.NodeUIDLabel: "",
  753. source.NamespaceLabel: "namespace1",
  754. source.NodeLabel: "node1",
  755. source.InstanceLabel: "node1",
  756. },
  757. Value: 0,
  758. AdditionalInfo: map[string]string{},
  759. },
  760. {
  761. Name: metric.KubePodContainerStatusRunning,
  762. Labels: map[string]string{
  763. source.UIDLabel: "uuid1",
  764. source.PodLabel: "pod1",
  765. source.NamespaceUIDLabel: "ns-uuid1",
  766. source.NodeUIDLabel: "",
  767. source.NamespaceLabel: "namespace1",
  768. source.NodeLabel: "node1",
  769. source.InstanceLabel: "node1",
  770. source.ContainerLabel: "container1",
  771. },
  772. Value: 0,
  773. AdditionalInfo: map[string]string{
  774. source.UIDLabel: "uuid1",
  775. source.PodLabel: "pod1",
  776. source.NamespaceUIDLabel: "ns-uuid1",
  777. source.NodeUIDLabel: "",
  778. source.NamespaceLabel: "namespace1",
  779. source.NodeLabel: "node1",
  780. source.InstanceLabel: "node1",
  781. source.ContainerLabel: "container1",
  782. },
  783. },
  784. {
  785. Name: metric.KubePodContainerResourceRequests,
  786. Labels: map[string]string{
  787. source.UIDLabel: "uuid1",
  788. source.PodLabel: "pod1",
  789. source.NamespaceUIDLabel: "ns-uuid1",
  790. source.NodeUIDLabel: "",
  791. source.NamespaceLabel: "namespace1",
  792. source.NodeLabel: "node1",
  793. source.InstanceLabel: "node1",
  794. source.ContainerLabel: "container1",
  795. source.ResourceLabel: "cpu",
  796. source.UnitLabel: "core",
  797. },
  798. Value: 0.5,
  799. AdditionalInfo: nil,
  800. },
  801. {
  802. Name: metric.KubePodContainerResourceRequests,
  803. Labels: map[string]string{
  804. source.UIDLabel: "uuid1",
  805. source.PodLabel: "pod1",
  806. source.NamespaceUIDLabel: "ns-uuid1",
  807. source.NodeUIDLabel: "",
  808. source.NamespaceLabel: "namespace1",
  809. source.NodeLabel: "node1",
  810. source.InstanceLabel: "node1",
  811. source.ContainerLabel: "container1",
  812. source.ResourceLabel: "memory",
  813. source.UnitLabel: "byte",
  814. },
  815. Value: 512,
  816. AdditionalInfo: nil,
  817. },
  818. {
  819. Name: metric.KubePodContainerResourceLimits,
  820. Labels: map[string]string{
  821. source.UIDLabel: "uuid1",
  822. source.PodLabel: "pod1",
  823. source.NamespaceUIDLabel: "ns-uuid1",
  824. source.NodeUIDLabel: "",
  825. source.NamespaceLabel: "namespace1",
  826. source.NodeLabel: "node1",
  827. source.InstanceLabel: "node1",
  828. source.ContainerLabel: "container1",
  829. source.ResourceLabel: "cpu",
  830. source.UnitLabel: "core",
  831. },
  832. Value: 1,
  833. AdditionalInfo: nil,
  834. },
  835. {
  836. Name: metric.KubePodContainerResourceLimits,
  837. Labels: map[string]string{
  838. source.UIDLabel: "uuid1",
  839. source.PodLabel: "pod1",
  840. source.NamespaceUIDLabel: "ns-uuid1",
  841. source.NodeUIDLabel: "",
  842. source.NamespaceLabel: "namespace1",
  843. source.NodeLabel: "node1",
  844. source.InstanceLabel: "node1",
  845. source.ContainerLabel: "container1",
  846. source.ResourceLabel: "memory",
  847. source.UnitLabel: "byte",
  848. },
  849. Value: 1024,
  850. AdditionalInfo: nil,
  851. },
  852. },
  853. },
  854. }
  855. for _, tt := range tests {
  856. t.Run(tt.name, func(t *testing.T) {
  857. ks := &ClusterCacheScraper{}
  858. nodeIndex := make(map[string]types.UID, 0)
  859. nsIndex := make(map[string]types.UID, 0)
  860. if tt.nsSetup != nil {
  861. tt.nsSetup(nsIndex)
  862. }
  863. pvcIndex := make(map[pvcKey]types.UID, 0)
  864. var scrapeResults []metric.Update
  865. for _, s := range tt.scrapes {
  866. res := ks.scrapePods(s.Pods, s.PVCs, nodeIndex, nsIndex, pvcIndex)
  867. scrapeResults = append(scrapeResults, res...)
  868. }
  869. if len(scrapeResults) != len(tt.expected) {
  870. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  871. }
  872. for i, expected := range tt.expected {
  873. got := scrapeResults[i]
  874. if !reflect.DeepEqual(expected, got) {
  875. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  876. }
  877. }
  878. })
  879. }
  880. }
  881. func Test_kubernetesScraper_scrapePVCs(t *testing.T) {
  882. start1, _ := time.Parse(time.RFC3339, Start1Str)
  883. type scrape struct {
  884. PVCs []*clustercache.PersistentVolumeClaim
  885. Timestamp time.Time
  886. }
  887. tests := []struct {
  888. name string
  889. nsSetup func(map[string]types.UID)
  890. scrapes []scrape
  891. expected []metric.Update
  892. }{
  893. {
  894. name: "simple",
  895. scrapes: []scrape{
  896. {
  897. PVCs: []*clustercache.PersistentVolumeClaim{
  898. {
  899. Name: "pvc1",
  900. Namespace: "namespace1",
  901. UID: "uuid1",
  902. Spec: v1.PersistentVolumeClaimSpec{
  903. VolumeName: "vol1",
  904. StorageClassName: util.Ptr("storageClass1"),
  905. Resources: v1.VolumeResourceRequirements{
  906. Requests: v1.ResourceList{
  907. v1.ResourceStorage: resource.MustParse("4096"),
  908. },
  909. },
  910. },
  911. },
  912. },
  913. Timestamp: start1,
  914. },
  915. },
  916. expected: []metric.Update{
  917. {
  918. Name: metric.KubePersistentVolumeClaimInfo,
  919. Labels: map[string]string{
  920. source.UIDLabel: "uuid1",
  921. source.PVCLabel: "pvc1",
  922. source.NamespaceUIDLabel: "",
  923. source.NamespaceLabel: "namespace1",
  924. source.VolumeNameLabel: "vol1",
  925. source.PVUIDLabel: "",
  926. source.StorageClassLabel: "storageClass1",
  927. },
  928. Value: 0,
  929. AdditionalInfo: map[string]string{
  930. source.UIDLabel: "uuid1",
  931. source.PVCLabel: "pvc1",
  932. source.NamespaceUIDLabel: "",
  933. source.NamespaceLabel: "namespace1",
  934. source.VolumeNameLabel: "vol1",
  935. source.PVUIDLabel: "",
  936. source.StorageClassLabel: "storageClass1",
  937. },
  938. },
  939. {
  940. Name: metric.KubePersistentVolumeClaimResourceRequestsStorageBytes,
  941. Labels: map[string]string{
  942. source.UIDLabel: "uuid1",
  943. source.PVCLabel: "pvc1",
  944. source.NamespaceUIDLabel: "",
  945. source.NamespaceLabel: "namespace1",
  946. source.VolumeNameLabel: "vol1",
  947. source.PVUIDLabel: "",
  948. source.StorageClassLabel: "storageClass1",
  949. },
  950. Value: 4096,
  951. AdditionalInfo: nil,
  952. },
  953. },
  954. },
  955. {
  956. name: "with namespace index",
  957. nsSetup: func(nsIndex map[string]types.UID) {
  958. nsIndex["namespace1"] = "ns-uuid1"
  959. },
  960. scrapes: []scrape{
  961. {
  962. PVCs: []*clustercache.PersistentVolumeClaim{
  963. {
  964. Name: "pvc1",
  965. Namespace: "namespace1",
  966. UID: "uuid1",
  967. Spec: v1.PersistentVolumeClaimSpec{
  968. VolumeName: "vol1",
  969. StorageClassName: util.Ptr("storageClass1"),
  970. Resources: v1.VolumeResourceRequirements{
  971. Requests: v1.ResourceList{
  972. v1.ResourceStorage: resource.MustParse("4096"),
  973. },
  974. },
  975. },
  976. },
  977. },
  978. Timestamp: start1,
  979. },
  980. },
  981. expected: []metric.Update{
  982. {
  983. Name: metric.KubePersistentVolumeClaimInfo,
  984. Labels: map[string]string{
  985. source.UIDLabel: "uuid1",
  986. source.PVCLabel: "pvc1",
  987. source.NamespaceUIDLabel: "ns-uuid1",
  988. source.NamespaceLabel: "namespace1",
  989. source.VolumeNameLabel: "vol1",
  990. source.PVUIDLabel: "",
  991. source.StorageClassLabel: "storageClass1",
  992. },
  993. Value: 0,
  994. AdditionalInfo: map[string]string{
  995. source.UIDLabel: "uuid1",
  996. source.PVCLabel: "pvc1",
  997. source.NamespaceUIDLabel: "ns-uuid1",
  998. source.NamespaceLabel: "namespace1",
  999. source.VolumeNameLabel: "vol1",
  1000. source.PVUIDLabel: "",
  1001. source.StorageClassLabel: "storageClass1",
  1002. },
  1003. },
  1004. {
  1005. Name: metric.KubePersistentVolumeClaimResourceRequestsStorageBytes,
  1006. Labels: map[string]string{
  1007. source.UIDLabel: "uuid1",
  1008. source.PVCLabel: "pvc1",
  1009. source.NamespaceUIDLabel: "ns-uuid1",
  1010. source.NamespaceLabel: "namespace1",
  1011. source.VolumeNameLabel: "vol1",
  1012. source.PVUIDLabel: "",
  1013. source.StorageClassLabel: "storageClass1",
  1014. },
  1015. Value: 4096,
  1016. AdditionalInfo: nil,
  1017. },
  1018. },
  1019. },
  1020. }
  1021. for _, tt := range tests {
  1022. t.Run(tt.name, func(t *testing.T) {
  1023. ks := &ClusterCacheScraper{}
  1024. nsIndex := make(map[string]types.UID, 0)
  1025. if tt.nsSetup != nil {
  1026. tt.nsSetup(nsIndex)
  1027. }
  1028. pvIndex := make(map[string]types.UID, 0)
  1029. var scrapeResults []metric.Update
  1030. for _, s := range tt.scrapes {
  1031. res := ks.scrapePVCs(s.PVCs, nsIndex, pvIndex)
  1032. scrapeResults = append(scrapeResults, res...)
  1033. }
  1034. if len(scrapeResults) != len(tt.expected) {
  1035. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1036. }
  1037. for i, expected := range tt.expected {
  1038. got := scrapeResults[i]
  1039. if !reflect.DeepEqual(expected, got) {
  1040. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1041. }
  1042. }
  1043. })
  1044. }
  1045. }
  1046. func Test_kubernetesScraper_scrapePVs(t *testing.T) {
  1047. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1048. type scrape struct {
  1049. PVs []*clustercache.PersistentVolume
  1050. Timestamp time.Time
  1051. }
  1052. tests := []struct {
  1053. name string
  1054. scrapes []scrape
  1055. expected []metric.Update
  1056. }{
  1057. {
  1058. name: "simple",
  1059. scrapes: []scrape{
  1060. {
  1061. PVs: []*clustercache.PersistentVolume{
  1062. {
  1063. Name: "pv1",
  1064. UID: "uuid1",
  1065. Spec: v1.PersistentVolumeSpec{
  1066. StorageClassName: "storageClass1",
  1067. PersistentVolumeSource: v1.PersistentVolumeSource{
  1068. CSI: &v1.CSIPersistentVolumeSource{
  1069. VolumeHandle: "vol-1",
  1070. },
  1071. },
  1072. Capacity: v1.ResourceList{
  1073. v1.ResourceStorage: resource.MustParse("4096"),
  1074. },
  1075. },
  1076. },
  1077. },
  1078. Timestamp: start1,
  1079. },
  1080. },
  1081. expected: []metric.Update{
  1082. {
  1083. Name: metric.KubecostPVInfo,
  1084. Labels: map[string]string{
  1085. source.UIDLabel: "uuid1",
  1086. source.PVLabel: "pv1",
  1087. source.StorageClassLabel: "storageClass1",
  1088. source.ProviderIDLabel: "vol-1",
  1089. source.CSIVolumeHandleLabel: "vol-1",
  1090. },
  1091. Value: 0,
  1092. AdditionalInfo: map[string]string{
  1093. source.UIDLabel: "uuid1",
  1094. source.PVLabel: "pv1",
  1095. source.StorageClassLabel: "storageClass1",
  1096. source.ProviderIDLabel: "vol-1",
  1097. source.CSIVolumeHandleLabel: "vol-1",
  1098. },
  1099. },
  1100. {
  1101. Name: metric.KubePersistentVolumeCapacityBytes,
  1102. Labels: map[string]string{
  1103. source.UIDLabel: "uuid1",
  1104. source.PVLabel: "pv1",
  1105. source.StorageClassLabel: "storageClass1",
  1106. source.ProviderIDLabel: "vol-1",
  1107. source.CSIVolumeHandleLabel: "vol-1",
  1108. },
  1109. Value: 4096,
  1110. AdditionalInfo: nil,
  1111. },
  1112. },
  1113. },
  1114. }
  1115. for _, tt := range tests {
  1116. t.Run(tt.name, func(t *testing.T) {
  1117. ks := &ClusterCacheScraper{}
  1118. var scrapeResults []metric.Update
  1119. for _, s := range tt.scrapes {
  1120. res := ks.scrapePVs(s.PVs)
  1121. scrapeResults = append(scrapeResults, res...)
  1122. }
  1123. if len(scrapeResults) != len(tt.expected) {
  1124. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1125. }
  1126. for i, expected := range tt.expected {
  1127. got := scrapeResults[i]
  1128. if !reflect.DeepEqual(expected, got) {
  1129. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1130. }
  1131. }
  1132. })
  1133. }
  1134. }
  1135. func Test_kubernetesScraper_scrapeServices(t *testing.T) {
  1136. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1137. type scrape struct {
  1138. Services []*clustercache.Service
  1139. Timestamp time.Time
  1140. }
  1141. tests := []struct {
  1142. name string
  1143. nsSetup func(map[string]types.UID)
  1144. scrapes []scrape
  1145. expected []metric.Update
  1146. }{
  1147. {
  1148. name: "simple",
  1149. scrapes: []scrape{
  1150. {
  1151. Services: []*clustercache.Service{
  1152. {
  1153. Name: "service1",
  1154. Namespace: "namespace1",
  1155. UID: "uuid1",
  1156. SpecSelector: map[string]string{
  1157. "test1": "blah",
  1158. "test2": "blah2",
  1159. },
  1160. },
  1161. },
  1162. Timestamp: start1,
  1163. },
  1164. },
  1165. expected: []metric.Update{
  1166. {
  1167. Name: metric.ServiceInfo,
  1168. Labels: map[string]string{
  1169. source.UIDLabel: "uuid1",
  1170. source.ServiceLabel: "service1",
  1171. source.NamespaceLabel: "namespace1",
  1172. source.NamespaceUIDLabel: "",
  1173. source.ServiceTypeLabel: "",
  1174. source.LBIngressAddress: "",
  1175. },
  1176. Value: 0,
  1177. AdditionalInfo: map[string]string{
  1178. source.UIDLabel: "uuid1",
  1179. source.ServiceLabel: "service1",
  1180. source.NamespaceLabel: "namespace1",
  1181. source.NamespaceUIDLabel: "",
  1182. source.ServiceTypeLabel: "",
  1183. source.LBIngressAddress: "",
  1184. },
  1185. },
  1186. {
  1187. Name: metric.ServiceSelectorLabels,
  1188. Labels: map[string]string{
  1189. source.UIDLabel: "uuid1",
  1190. source.ServiceLabel: "service1",
  1191. source.NamespaceLabel: "namespace1",
  1192. source.NamespaceUIDLabel: "",
  1193. source.ServiceTypeLabel: "",
  1194. source.LBIngressAddress: "",
  1195. },
  1196. Value: 0,
  1197. AdditionalInfo: map[string]string{
  1198. "label_test1": "blah",
  1199. "label_test2": "blah2",
  1200. },
  1201. },
  1202. },
  1203. },
  1204. {
  1205. name: "with namespace index",
  1206. nsSetup: func(nsIndex map[string]types.UID) {
  1207. nsIndex["namespace1"] = "ns-uuid1"
  1208. },
  1209. scrapes: []scrape{
  1210. {
  1211. Services: []*clustercache.Service{
  1212. {
  1213. Name: "service1",
  1214. Namespace: "namespace1",
  1215. UID: "uuid1",
  1216. },
  1217. },
  1218. Timestamp: start1,
  1219. },
  1220. },
  1221. expected: []metric.Update{
  1222. {
  1223. Name: metric.ServiceInfo,
  1224. Labels: map[string]string{
  1225. source.UIDLabel: "uuid1",
  1226. source.ServiceLabel: "service1",
  1227. source.NamespaceLabel: "namespace1",
  1228. source.NamespaceUIDLabel: "ns-uuid1",
  1229. source.ServiceTypeLabel: "",
  1230. source.LBIngressAddress: "",
  1231. },
  1232. Value: 0,
  1233. AdditionalInfo: map[string]string{
  1234. source.UIDLabel: "uuid1",
  1235. source.ServiceLabel: "service1",
  1236. source.NamespaceLabel: "namespace1",
  1237. source.NamespaceUIDLabel: "ns-uuid1",
  1238. source.ServiceTypeLabel: "",
  1239. source.LBIngressAddress: "",
  1240. },
  1241. },
  1242. {
  1243. Name: metric.ServiceSelectorLabels,
  1244. Labels: map[string]string{
  1245. source.UIDLabel: "uuid1",
  1246. source.ServiceLabel: "service1",
  1247. source.NamespaceLabel: "namespace1",
  1248. source.NamespaceUIDLabel: "ns-uuid1",
  1249. source.ServiceTypeLabel: "",
  1250. source.LBIngressAddress: "",
  1251. },
  1252. Value: 0,
  1253. AdditionalInfo: map[string]string{},
  1254. },
  1255. },
  1256. },
  1257. {
  1258. name: "with LB ingress IP",
  1259. nsSetup: func(nsIndex map[string]types.UID) {
  1260. nsIndex["namespace1"] = "ns-uuid1"
  1261. },
  1262. scrapes: []scrape{
  1263. {
  1264. Services: []*clustercache.Service{
  1265. {
  1266. Name: "service1",
  1267. Namespace: "namespace1",
  1268. UID: "uuid1",
  1269. Type: v1.ServiceTypeLoadBalancer,
  1270. Status: v1.ServiceStatus{
  1271. LoadBalancer: v1.LoadBalancerStatus{
  1272. Ingress: []v1.LoadBalancerIngress{
  1273. {IP: "1.2.3.4"},
  1274. },
  1275. },
  1276. },
  1277. },
  1278. },
  1279. Timestamp: start1,
  1280. },
  1281. },
  1282. expected: []metric.Update{
  1283. {
  1284. Name: metric.ServiceInfo,
  1285. Labels: map[string]string{
  1286. source.UIDLabel: "uuid1",
  1287. source.ServiceLabel: "service1",
  1288. source.NamespaceLabel: "namespace1",
  1289. source.NamespaceUIDLabel: "ns-uuid1",
  1290. source.ServiceTypeLabel: "LoadBalancer",
  1291. source.LBIngressAddress: "1.2.3.4",
  1292. },
  1293. Value: 0,
  1294. AdditionalInfo: map[string]string{
  1295. source.UIDLabel: "uuid1",
  1296. source.ServiceLabel: "service1",
  1297. source.NamespaceLabel: "namespace1",
  1298. source.NamespaceUIDLabel: "ns-uuid1",
  1299. source.ServiceTypeLabel: "LoadBalancer",
  1300. source.LBIngressAddress: "1.2.3.4",
  1301. },
  1302. },
  1303. {
  1304. Name: metric.ServiceSelectorLabels,
  1305. Labels: map[string]string{
  1306. source.UIDLabel: "uuid1",
  1307. source.ServiceLabel: "service1",
  1308. source.NamespaceLabel: "namespace1",
  1309. source.NamespaceUIDLabel: "ns-uuid1",
  1310. source.ServiceTypeLabel: "LoadBalancer",
  1311. source.LBIngressAddress: "1.2.3.4",
  1312. },
  1313. Value: 0,
  1314. AdditionalInfo: map[string]string{},
  1315. },
  1316. },
  1317. },
  1318. }
  1319. for _, tt := range tests {
  1320. t.Run(tt.name, func(t *testing.T) {
  1321. ks := &ClusterCacheScraper{}
  1322. nsIndex := make(map[string]types.UID)
  1323. if tt.nsSetup != nil {
  1324. tt.nsSetup(nsIndex)
  1325. }
  1326. var scrapeResults []metric.Update
  1327. for _, s := range tt.scrapes {
  1328. res := ks.scrapeServices(s.Services, nsIndex)
  1329. scrapeResults = append(scrapeResults, res...)
  1330. }
  1331. if len(scrapeResults) != len(tt.expected) {
  1332. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1333. }
  1334. for i, expected := range tt.expected {
  1335. got := scrapeResults[i]
  1336. if !reflect.DeepEqual(expected, got) {
  1337. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1338. }
  1339. }
  1340. })
  1341. }
  1342. }
  1343. func Test_kubernetesScraper_scrapeStatefulSets(t *testing.T) {
  1344. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1345. type scrape struct {
  1346. StatefulSets []*clustercache.StatefulSet
  1347. Timestamp time.Time
  1348. }
  1349. tests := []struct {
  1350. name string
  1351. nsSetup func(map[string]types.UID)
  1352. scrapes []scrape
  1353. expected []metric.Update
  1354. }{
  1355. {
  1356. name: "simple",
  1357. scrapes: []scrape{
  1358. {
  1359. StatefulSets: []*clustercache.StatefulSet{
  1360. {
  1361. Name: "statefulSet1",
  1362. Namespace: "namespace1",
  1363. UID: "uuid1",
  1364. SpecSelector: &metav1.LabelSelector{
  1365. MatchLabels: map[string]string{
  1366. "test1": "blah",
  1367. "test2": "blah2",
  1368. },
  1369. },
  1370. },
  1371. },
  1372. Timestamp: start1,
  1373. },
  1374. },
  1375. // statefulSetInfo map is shared across all 4 metrics; namespace is added
  1376. // before StatefulSetMatchLabels is appended, so all 4 reflect the final state.
  1377. expected: []metric.Update{
  1378. {
  1379. Name: metric.StatefulSetInfo,
  1380. Labels: map[string]string{
  1381. source.UIDLabel: "uuid1",
  1382. source.NamespaceUIDLabel: "",
  1383. source.StatefulSetLabel: "statefulSet1",
  1384. source.NamespaceLabel: "namespace1",
  1385. },
  1386. Value: 0,
  1387. AdditionalInfo: map[string]string{
  1388. source.UIDLabel: "uuid1",
  1389. source.NamespaceUIDLabel: "",
  1390. source.StatefulSetLabel: "statefulSet1",
  1391. source.NamespaceLabel: "namespace1",
  1392. },
  1393. },
  1394. {
  1395. Name: metric.StatefulSetLabels,
  1396. Labels: map[string]string{
  1397. source.UIDLabel: "uuid1",
  1398. source.NamespaceUIDLabel: "",
  1399. source.StatefulSetLabel: "statefulSet1",
  1400. source.NamespaceLabel: "namespace1",
  1401. },
  1402. Value: 0,
  1403. AdditionalInfo: map[string]string{},
  1404. },
  1405. {
  1406. Name: metric.StatefulSetAnnotations,
  1407. Labels: map[string]string{
  1408. source.UIDLabel: "uuid1",
  1409. source.NamespaceUIDLabel: "",
  1410. source.StatefulSetLabel: "statefulSet1",
  1411. source.NamespaceLabel: "namespace1",
  1412. },
  1413. Value: 0,
  1414. AdditionalInfo: map[string]string{},
  1415. },
  1416. {
  1417. Name: metric.StatefulSetMatchLabels,
  1418. Labels: map[string]string{
  1419. source.UIDLabel: "uuid1",
  1420. source.NamespaceUIDLabel: "",
  1421. source.StatefulSetLabel: "statefulSet1",
  1422. source.NamespaceLabel: "namespace1",
  1423. },
  1424. Value: 0,
  1425. AdditionalInfo: map[string]string{
  1426. "label_test1": "blah",
  1427. "label_test2": "blah2",
  1428. },
  1429. },
  1430. },
  1431. },
  1432. {
  1433. // statefulSetInfo map is shared; NamespaceLabel is added before MatchLabels,
  1434. // so all 4 metrics reflect the final state including namespace_uid.
  1435. name: "with namespace index",
  1436. nsSetup: func(nsIndex map[string]types.UID) {
  1437. nsIndex["namespace1"] = "ns-uuid1"
  1438. },
  1439. scrapes: []scrape{
  1440. {
  1441. StatefulSets: []*clustercache.StatefulSet{
  1442. {
  1443. Name: "statefulSet1",
  1444. Namespace: "namespace1",
  1445. UID: "uuid1",
  1446. SpecSelector: &metav1.LabelSelector{},
  1447. },
  1448. },
  1449. Timestamp: start1,
  1450. },
  1451. },
  1452. expected: []metric.Update{
  1453. {
  1454. Name: metric.StatefulSetInfo,
  1455. Labels: map[string]string{
  1456. source.UIDLabel: "uuid1",
  1457. source.NamespaceUIDLabel: "ns-uuid1",
  1458. source.StatefulSetLabel: "statefulSet1",
  1459. source.NamespaceLabel: "namespace1",
  1460. },
  1461. Value: 0,
  1462. AdditionalInfo: map[string]string{
  1463. source.UIDLabel: "uuid1",
  1464. source.NamespaceUIDLabel: "ns-uuid1",
  1465. source.StatefulSetLabel: "statefulSet1",
  1466. source.NamespaceLabel: "namespace1",
  1467. },
  1468. },
  1469. {
  1470. Name: metric.StatefulSetLabels,
  1471. Labels: map[string]string{
  1472. source.UIDLabel: "uuid1",
  1473. source.NamespaceUIDLabel: "ns-uuid1",
  1474. source.StatefulSetLabel: "statefulSet1",
  1475. source.NamespaceLabel: "namespace1",
  1476. },
  1477. Value: 0,
  1478. AdditionalInfo: map[string]string{},
  1479. },
  1480. {
  1481. Name: metric.StatefulSetAnnotations,
  1482. Labels: map[string]string{
  1483. source.UIDLabel: "uuid1",
  1484. source.NamespaceUIDLabel: "ns-uuid1",
  1485. source.StatefulSetLabel: "statefulSet1",
  1486. source.NamespaceLabel: "namespace1",
  1487. },
  1488. Value: 0,
  1489. AdditionalInfo: map[string]string{},
  1490. },
  1491. {
  1492. Name: metric.StatefulSetMatchLabels,
  1493. Labels: map[string]string{
  1494. source.UIDLabel: "uuid1",
  1495. source.NamespaceUIDLabel: "ns-uuid1",
  1496. source.StatefulSetLabel: "statefulSet1",
  1497. source.NamespaceLabel: "namespace1",
  1498. },
  1499. Value: 0,
  1500. AdditionalInfo: map[string]string{},
  1501. },
  1502. },
  1503. },
  1504. }
  1505. for _, tt := range tests {
  1506. t.Run(tt.name, func(t *testing.T) {
  1507. ks := &ClusterCacheScraper{}
  1508. nsIndex := make(map[string]types.UID, 0)
  1509. if tt.nsSetup != nil {
  1510. tt.nsSetup(nsIndex)
  1511. }
  1512. var scrapeResults []metric.Update
  1513. for _, s := range tt.scrapes {
  1514. res := ks.scrapeStatefulSets(s.StatefulSets, nsIndex)
  1515. scrapeResults = append(scrapeResults, res...)
  1516. }
  1517. if len(scrapeResults) != len(tt.expected) {
  1518. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1519. }
  1520. for i, expected := range tt.expected {
  1521. got := scrapeResults[i]
  1522. if !reflect.DeepEqual(expected, got) {
  1523. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1524. }
  1525. }
  1526. })
  1527. }
  1528. }
  1529. func Test_kubernetesScraper_scrapeReplicaSets(t *testing.T) {
  1530. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1531. type scrape struct {
  1532. ReplicaSets []*clustercache.ReplicaSet
  1533. Timestamp time.Time
  1534. }
  1535. tests := []struct {
  1536. name string
  1537. nsSetup func(map[string]types.UID)
  1538. scrapes []scrape
  1539. expected []metric.Update
  1540. }{
  1541. {
  1542. name: "simple",
  1543. scrapes: []scrape{
  1544. {
  1545. ReplicaSets: []*clustercache.ReplicaSet{
  1546. {
  1547. Name: "replicaSet1",
  1548. Namespace: "namespace1",
  1549. UID: "uuid1",
  1550. OwnerReferences: []metav1.OwnerReference{
  1551. {
  1552. Name: "rollout1",
  1553. Kind: "Rollout",
  1554. },
  1555. },
  1556. },
  1557. {
  1558. Name: "pureReplicaSet",
  1559. Namespace: "namespace1",
  1560. UID: "uuid2",
  1561. OwnerReferences: []metav1.OwnerReference{},
  1562. },
  1563. },
  1564. Timestamp: start1,
  1565. },
  1566. },
  1567. expected: []metric.Update{
  1568. // replicaSet1: info/labels/annotations use replicaSetInfo (uid, namespace_uid, replicaset)
  1569. {
  1570. Name: metric.ReplicaSetInfo,
  1571. Labels: map[string]string{
  1572. source.UIDLabel: "uuid1",
  1573. source.NamespaceUIDLabel: "",
  1574. source.ReplicaSetLabel: "replicaSet1",
  1575. },
  1576. Value: 0,
  1577. AdditionalInfo: map[string]string{
  1578. source.UIDLabel: "uuid1",
  1579. source.NamespaceUIDLabel: "",
  1580. source.ReplicaSetLabel: "replicaSet1",
  1581. },
  1582. },
  1583. {
  1584. Name: metric.ReplicaSetLabels,
  1585. Labels: map[string]string{
  1586. source.UIDLabel: "uuid1",
  1587. source.NamespaceUIDLabel: "",
  1588. source.ReplicaSetLabel: "replicaSet1",
  1589. },
  1590. Value: 0,
  1591. AdditionalInfo: map[string]string{},
  1592. },
  1593. {
  1594. Name: metric.ReplicaSetAnnotations,
  1595. Labels: map[string]string{
  1596. source.UIDLabel: "uuid1",
  1597. source.NamespaceUIDLabel: "",
  1598. source.ReplicaSetLabel: "replicaSet1",
  1599. },
  1600. Value: 0,
  1601. AdditionalInfo: map[string]string{},
  1602. },
  1603. // replicaSet1 owner: uses replicaSetOwnerInfo (replicaset, namespace, uid) + owner fields
  1604. {
  1605. Name: metric.KubeReplicasetOwner,
  1606. Labels: map[string]string{
  1607. source.ReplicaSetLabel: "replicaSet1",
  1608. source.NamespaceLabel: "namespace1",
  1609. source.UIDLabel: "uuid1",
  1610. source.OwnerNameLabel: "rollout1",
  1611. source.OwnerKindLabel: "Rollout",
  1612. source.OwnerUIDLabel: "",
  1613. source.ControllerLabel: "false",
  1614. },
  1615. Value: 0,
  1616. },
  1617. // pureReplicaSet: info/labels/annotations
  1618. {
  1619. Name: metric.ReplicaSetInfo,
  1620. Labels: map[string]string{
  1621. source.UIDLabel: "uuid2",
  1622. source.NamespaceUIDLabel: "",
  1623. source.ReplicaSetLabel: "pureReplicaSet",
  1624. },
  1625. Value: 0,
  1626. AdditionalInfo: map[string]string{
  1627. source.UIDLabel: "uuid2",
  1628. source.NamespaceUIDLabel: "",
  1629. source.ReplicaSetLabel: "pureReplicaSet",
  1630. },
  1631. },
  1632. {
  1633. Name: metric.ReplicaSetLabels,
  1634. Labels: map[string]string{
  1635. source.UIDLabel: "uuid2",
  1636. source.NamespaceUIDLabel: "",
  1637. source.ReplicaSetLabel: "pureReplicaSet",
  1638. },
  1639. Value: 0,
  1640. AdditionalInfo: map[string]string{},
  1641. },
  1642. {
  1643. Name: metric.ReplicaSetAnnotations,
  1644. Labels: map[string]string{
  1645. source.UIDLabel: "uuid2",
  1646. source.NamespaceUIDLabel: "",
  1647. source.ReplicaSetLabel: "pureReplicaSet",
  1648. },
  1649. Value: 0,
  1650. AdditionalInfo: map[string]string{},
  1651. },
  1652. // pureReplicaSet owner: no owners path uses <none> sentinel, no owner_uid/controller
  1653. {
  1654. Name: metric.KubeReplicasetOwner,
  1655. Labels: map[string]string{
  1656. source.ReplicaSetLabel: "pureReplicaSet",
  1657. source.NamespaceLabel: "namespace1",
  1658. source.UIDLabel: "uuid2",
  1659. source.OwnerNameLabel: source.NoneLabelValue,
  1660. source.OwnerKindLabel: source.NoneLabelValue,
  1661. },
  1662. Value: 0,
  1663. },
  1664. },
  1665. },
  1666. {
  1667. name: "with namespace index",
  1668. nsSetup: func(nsIndex map[string]types.UID) {
  1669. nsIndex["namespace1"] = "ns-uuid1"
  1670. },
  1671. scrapes: []scrape{
  1672. {
  1673. ReplicaSets: []*clustercache.ReplicaSet{
  1674. {
  1675. Name: "replicaSet1",
  1676. Namespace: "namespace1",
  1677. UID: "uuid1",
  1678. OwnerReferences: []metav1.OwnerReference{},
  1679. },
  1680. },
  1681. Timestamp: start1,
  1682. },
  1683. },
  1684. expected: []metric.Update{
  1685. {
  1686. Name: metric.ReplicaSetInfo,
  1687. Labels: map[string]string{
  1688. source.UIDLabel: "uuid1",
  1689. source.NamespaceUIDLabel: "ns-uuid1",
  1690. source.ReplicaSetLabel: "replicaSet1",
  1691. },
  1692. Value: 0,
  1693. AdditionalInfo: map[string]string{
  1694. source.UIDLabel: "uuid1",
  1695. source.NamespaceUIDLabel: "ns-uuid1",
  1696. source.ReplicaSetLabel: "replicaSet1",
  1697. },
  1698. },
  1699. {
  1700. Name: metric.ReplicaSetLabels,
  1701. Labels: map[string]string{
  1702. source.UIDLabel: "uuid1",
  1703. source.NamespaceUIDLabel: "ns-uuid1",
  1704. source.ReplicaSetLabel: "replicaSet1",
  1705. },
  1706. Value: 0,
  1707. AdditionalInfo: map[string]string{},
  1708. },
  1709. {
  1710. Name: metric.ReplicaSetAnnotations,
  1711. Labels: map[string]string{
  1712. source.UIDLabel: "uuid1",
  1713. source.NamespaceUIDLabel: "ns-uuid1",
  1714. source.ReplicaSetLabel: "replicaSet1",
  1715. },
  1716. Value: 0,
  1717. AdditionalInfo: map[string]string{},
  1718. },
  1719. {
  1720. Name: metric.KubeReplicasetOwner,
  1721. Labels: map[string]string{
  1722. source.ReplicaSetLabel: "replicaSet1",
  1723. source.NamespaceLabel: "namespace1",
  1724. source.UIDLabel: "uuid1",
  1725. source.OwnerNameLabel: source.NoneLabelValue,
  1726. source.OwnerKindLabel: source.NoneLabelValue,
  1727. },
  1728. Value: 0,
  1729. },
  1730. },
  1731. },
  1732. }
  1733. for _, tt := range tests {
  1734. t.Run(tt.name, func(t *testing.T) {
  1735. ks := &ClusterCacheScraper{}
  1736. nsIndex := make(map[string]types.UID, 0)
  1737. if tt.nsSetup != nil {
  1738. tt.nsSetup(nsIndex)
  1739. }
  1740. var scrapeResults []metric.Update
  1741. for _, s := range tt.scrapes {
  1742. res := ks.scrapeReplicaSets(s.ReplicaSets, nsIndex)
  1743. scrapeResults = append(scrapeResults, res...)
  1744. }
  1745. if len(scrapeResults) != len(tt.expected) {
  1746. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1747. }
  1748. for i, expected := range tt.expected {
  1749. got := scrapeResults[i]
  1750. if !reflect.DeepEqual(expected, got) {
  1751. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1752. }
  1753. }
  1754. })
  1755. }
  1756. }
  1757. func Test_kubernetesScraper_scrapeResourceQuotas(t *testing.T) {
  1758. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1759. type scrape struct {
  1760. ResourceQuotas []*clustercache.ResourceQuota
  1761. Timestamp time.Time
  1762. }
  1763. tests := []struct {
  1764. name string
  1765. nsSetup func(map[string]types.UID)
  1766. scrapes []scrape
  1767. expected []metric.Update
  1768. }{
  1769. {
  1770. name: "simple",
  1771. scrapes: []scrape{
  1772. {
  1773. ResourceQuotas: []*clustercache.ResourceQuota{
  1774. {
  1775. Name: "resourceQuota1",
  1776. Namespace: "namespace1",
  1777. UID: "uuid1",
  1778. Spec: v1.ResourceQuotaSpec{
  1779. Hard: v1.ResourceList{
  1780. v1.ResourceRequestsCPU: resource.MustParse("1"),
  1781. v1.ResourceRequestsMemory: resource.MustParse("1024"),
  1782. v1.ResourceLimitsCPU: resource.MustParse("2"),
  1783. v1.ResourceLimitsMemory: resource.MustParse("2048"),
  1784. },
  1785. },
  1786. Status: v1.ResourceQuotaStatus{
  1787. Used: v1.ResourceList{
  1788. v1.ResourceRequestsCPU: resource.MustParse("0.5"),
  1789. v1.ResourceRequestsMemory: resource.MustParse("512"),
  1790. v1.ResourceLimitsCPU: resource.MustParse("1"),
  1791. v1.ResourceLimitsMemory: resource.MustParse("1024"),
  1792. },
  1793. },
  1794. },
  1795. },
  1796. Timestamp: start1,
  1797. },
  1798. },
  1799. expected: []metric.Update{
  1800. {
  1801. Name: metric.ResourceQuotaInfo,
  1802. Labels: map[string]string{
  1803. source.UIDLabel: "uuid1",
  1804. source.NamespaceUIDLabel: "",
  1805. source.ResourceQuotaLabel: "resourceQuota1",
  1806. },
  1807. Value: 0,
  1808. AdditionalInfo: map[string]string{
  1809. source.UIDLabel: "uuid1",
  1810. source.NamespaceUIDLabel: "",
  1811. source.ResourceQuotaLabel: "resourceQuota1",
  1812. },
  1813. },
  1814. {
  1815. Name: metric.KubeResourceQuotaSpecResourceRequests,
  1816. Labels: map[string]string{
  1817. source.UIDLabel: "uuid1",
  1818. source.NamespaceUIDLabel: "",
  1819. source.ResourceQuotaLabel: "resourceQuota1",
  1820. source.ResourceLabel: "cpu",
  1821. source.UnitLabel: "core",
  1822. },
  1823. Value: 1,
  1824. AdditionalInfo: nil,
  1825. },
  1826. {
  1827. Name: metric.KubeResourceQuotaSpecResourceRequests,
  1828. Labels: map[string]string{
  1829. source.UIDLabel: "uuid1",
  1830. source.NamespaceUIDLabel: "",
  1831. source.ResourceQuotaLabel: "resourceQuota1",
  1832. source.ResourceLabel: "memory",
  1833. source.UnitLabel: "byte",
  1834. },
  1835. Value: 1024,
  1836. AdditionalInfo: nil,
  1837. },
  1838. {
  1839. Name: metric.KubeResourceQuotaSpecResourceLimits,
  1840. Labels: map[string]string{
  1841. source.UIDLabel: "uuid1",
  1842. source.NamespaceUIDLabel: "",
  1843. source.ResourceQuotaLabel: "resourceQuota1",
  1844. source.ResourceLabel: "cpu",
  1845. source.UnitLabel: "core",
  1846. },
  1847. Value: 2,
  1848. AdditionalInfo: nil,
  1849. },
  1850. {
  1851. Name: metric.KubeResourceQuotaSpecResourceLimits,
  1852. Labels: map[string]string{
  1853. source.UIDLabel: "uuid1",
  1854. source.NamespaceUIDLabel: "",
  1855. source.ResourceQuotaLabel: "resourceQuota1",
  1856. source.ResourceLabel: "memory",
  1857. source.UnitLabel: "byte",
  1858. },
  1859. Value: 2048,
  1860. AdditionalInfo: nil,
  1861. },
  1862. {
  1863. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1864. Labels: map[string]string{
  1865. source.UIDLabel: "uuid1",
  1866. source.NamespaceUIDLabel: "",
  1867. source.ResourceQuotaLabel: "resourceQuota1",
  1868. source.ResourceLabel: "cpu",
  1869. source.UnitLabel: "core",
  1870. },
  1871. Value: 0.5,
  1872. AdditionalInfo: nil,
  1873. },
  1874. {
  1875. Name: metric.KubeResourceQuotaStatusUsedResourceRequests,
  1876. Labels: map[string]string{
  1877. source.UIDLabel: "uuid1",
  1878. source.NamespaceUIDLabel: "",
  1879. source.ResourceQuotaLabel: "resourceQuota1",
  1880. source.ResourceLabel: "memory",
  1881. source.UnitLabel: "byte",
  1882. },
  1883. Value: 512,
  1884. AdditionalInfo: nil,
  1885. },
  1886. {
  1887. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1888. Labels: map[string]string{
  1889. source.UIDLabel: "uuid1",
  1890. source.NamespaceUIDLabel: "",
  1891. source.ResourceQuotaLabel: "resourceQuota1",
  1892. source.ResourceLabel: "cpu",
  1893. source.UnitLabel: "core",
  1894. },
  1895. Value: 1,
  1896. AdditionalInfo: nil,
  1897. },
  1898. {
  1899. Name: metric.KubeResourceQuotaStatusUsedResourceLimits,
  1900. Labels: map[string]string{
  1901. source.UIDLabel: "uuid1",
  1902. source.NamespaceUIDLabel: "",
  1903. source.ResourceQuotaLabel: "resourceQuota1",
  1904. source.ResourceLabel: "memory",
  1905. source.UnitLabel: "byte",
  1906. },
  1907. Value: 1024,
  1908. AdditionalInfo: nil,
  1909. },
  1910. },
  1911. },
  1912. {
  1913. name: "with namespace index",
  1914. nsSetup: func(nsIndex map[string]types.UID) {
  1915. nsIndex["namespace1"] = "ns-uuid1"
  1916. },
  1917. scrapes: []scrape{
  1918. {
  1919. ResourceQuotas: []*clustercache.ResourceQuota{
  1920. {
  1921. Name: "resourceQuota1",
  1922. Namespace: "namespace1",
  1923. UID: "uuid1",
  1924. Spec: v1.ResourceQuotaSpec{
  1925. Hard: v1.ResourceList{
  1926. v1.ResourceRequestsCPU: resource.MustParse("1"),
  1927. },
  1928. },
  1929. },
  1930. },
  1931. Timestamp: start1,
  1932. },
  1933. },
  1934. expected: []metric.Update{
  1935. {
  1936. Name: metric.ResourceQuotaInfo,
  1937. Labels: map[string]string{
  1938. source.UIDLabel: "uuid1",
  1939. source.NamespaceUIDLabel: "ns-uuid1",
  1940. source.ResourceQuotaLabel: "resourceQuota1",
  1941. },
  1942. Value: 0,
  1943. AdditionalInfo: map[string]string{
  1944. source.UIDLabel: "uuid1",
  1945. source.NamespaceUIDLabel: "ns-uuid1",
  1946. source.ResourceQuotaLabel: "resourceQuota1",
  1947. },
  1948. },
  1949. {
  1950. Name: metric.KubeResourceQuotaSpecResourceRequests,
  1951. Labels: map[string]string{
  1952. source.UIDLabel: "uuid1",
  1953. source.NamespaceUIDLabel: "ns-uuid1",
  1954. source.ResourceQuotaLabel: "resourceQuota1",
  1955. source.ResourceLabel: "cpu",
  1956. source.UnitLabel: "core",
  1957. },
  1958. Value: 1,
  1959. AdditionalInfo: nil,
  1960. },
  1961. },
  1962. },
  1963. }
  1964. for _, tt := range tests {
  1965. t.Run(tt.name, func(t *testing.T) {
  1966. ks := &ClusterCacheScraper{}
  1967. nsIndex := make(map[string]types.UID, 0)
  1968. if tt.nsSetup != nil {
  1969. tt.nsSetup(nsIndex)
  1970. }
  1971. var scrapeResults []metric.Update
  1972. for _, s := range tt.scrapes {
  1973. res := ks.scrapeResourceQuotas(s.ResourceQuotas, nsIndex)
  1974. scrapeResults = append(scrapeResults, res...)
  1975. }
  1976. if len(scrapeResults) != len(tt.expected) {
  1977. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  1978. }
  1979. for i, expected := range tt.expected {
  1980. got := scrapeResults[i]
  1981. if !reflect.DeepEqual(expected, got) {
  1982. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  1983. }
  1984. }
  1985. })
  1986. }
  1987. }
  1988. func Test_kubernetesScraper_scrapeDaemonSets(t *testing.T) {
  1989. start1, _ := time.Parse(time.RFC3339, Start1Str)
  1990. type scrape struct {
  1991. DaemonSets []*clustercache.DaemonSet
  1992. Timestamp time.Time
  1993. }
  1994. tests := []struct {
  1995. name string
  1996. nsSetup func(map[string]types.UID)
  1997. scrapes []scrape
  1998. expected []metric.Update
  1999. }{
  2000. {
  2001. name: "simple",
  2002. scrapes: []scrape{
  2003. {
  2004. DaemonSets: []*clustercache.DaemonSet{
  2005. {
  2006. Name: "daemonSet1",
  2007. Namespace: "namespace1",
  2008. UID: "uuid1",
  2009. },
  2010. },
  2011. Timestamp: start1,
  2012. },
  2013. },
  2014. expected: []metric.Update{
  2015. {
  2016. Name: metric.DaemonSetInfo,
  2017. Labels: map[string]string{
  2018. source.UIDLabel: "uuid1",
  2019. source.NamespaceUIDLabel: "",
  2020. source.DaemonSetLabel: "daemonSet1",
  2021. },
  2022. Value: 0,
  2023. AdditionalInfo: map[string]string{
  2024. source.UIDLabel: "uuid1",
  2025. source.NamespaceUIDLabel: "",
  2026. source.DaemonSetLabel: "daemonSet1",
  2027. },
  2028. },
  2029. {
  2030. Name: metric.DaemonSetLabels,
  2031. Labels: map[string]string{
  2032. source.UIDLabel: "uuid1",
  2033. source.NamespaceUIDLabel: "",
  2034. source.DaemonSetLabel: "daemonSet1",
  2035. },
  2036. Value: 0,
  2037. AdditionalInfo: map[string]string{},
  2038. },
  2039. {
  2040. Name: metric.DaemonSetAnnotations,
  2041. Labels: map[string]string{
  2042. source.UIDLabel: "uuid1",
  2043. source.NamespaceUIDLabel: "",
  2044. source.DaemonSetLabel: "daemonSet1",
  2045. },
  2046. Value: 0,
  2047. AdditionalInfo: map[string]string{},
  2048. },
  2049. },
  2050. },
  2051. {
  2052. name: "with namespace index",
  2053. nsSetup: func(nsIndex map[string]types.UID) {
  2054. nsIndex["namespace1"] = "ns-uuid1"
  2055. },
  2056. scrapes: []scrape{
  2057. {
  2058. DaemonSets: []*clustercache.DaemonSet{
  2059. {
  2060. Name: "daemonSet1",
  2061. Namespace: "namespace1",
  2062. UID: "uuid1",
  2063. },
  2064. },
  2065. Timestamp: start1,
  2066. },
  2067. },
  2068. expected: []metric.Update{
  2069. {
  2070. Name: metric.DaemonSetInfo,
  2071. Labels: map[string]string{
  2072. source.UIDLabel: "uuid1",
  2073. source.NamespaceUIDLabel: "ns-uuid1",
  2074. source.DaemonSetLabel: "daemonSet1",
  2075. },
  2076. Value: 0,
  2077. AdditionalInfo: map[string]string{
  2078. source.UIDLabel: "uuid1",
  2079. source.NamespaceUIDLabel: "ns-uuid1",
  2080. source.DaemonSetLabel: "daemonSet1",
  2081. },
  2082. },
  2083. {
  2084. Name: metric.DaemonSetLabels,
  2085. Labels: map[string]string{
  2086. source.UIDLabel: "uuid1",
  2087. source.NamespaceUIDLabel: "ns-uuid1",
  2088. source.DaemonSetLabel: "daemonSet1",
  2089. },
  2090. Value: 0,
  2091. AdditionalInfo: map[string]string{},
  2092. },
  2093. {
  2094. Name: metric.DaemonSetAnnotations,
  2095. Labels: map[string]string{
  2096. source.UIDLabel: "uuid1",
  2097. source.NamespaceUIDLabel: "ns-uuid1",
  2098. source.DaemonSetLabel: "daemonSet1",
  2099. },
  2100. Value: 0,
  2101. AdditionalInfo: map[string]string{},
  2102. },
  2103. },
  2104. },
  2105. }
  2106. for _, tt := range tests {
  2107. t.Run(tt.name, func(t *testing.T) {
  2108. ks := &ClusterCacheScraper{}
  2109. nsIndex := make(map[string]types.UID, 0)
  2110. if tt.nsSetup != nil {
  2111. tt.nsSetup(nsIndex)
  2112. }
  2113. var scrapeResults []metric.Update
  2114. for _, s := range tt.scrapes {
  2115. res := ks.scrapeDaemonSets(s.DaemonSets, nsIndex)
  2116. scrapeResults = append(scrapeResults, res...)
  2117. }
  2118. if len(scrapeResults) != len(tt.expected) {
  2119. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  2120. }
  2121. for i, expected := range tt.expected {
  2122. got := scrapeResults[i]
  2123. if !reflect.DeepEqual(expected, got) {
  2124. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  2125. }
  2126. }
  2127. })
  2128. }
  2129. }
  2130. func Test_kubernetesScraper_scrapeJobs(t *testing.T) {
  2131. start1, _ := time.Parse(time.RFC3339, Start1Str)
  2132. type scrape struct {
  2133. Jobs []*clustercache.Job
  2134. Timestamp time.Time
  2135. }
  2136. tests := []struct {
  2137. name string
  2138. nsSetup func(map[string]types.UID)
  2139. scrapes []scrape
  2140. expected []metric.Update
  2141. }{
  2142. {
  2143. name: "simple",
  2144. scrapes: []scrape{
  2145. {
  2146. Jobs: []*clustercache.Job{
  2147. {
  2148. Name: "job1",
  2149. Namespace: "namespace1",
  2150. UID: "uuid1",
  2151. },
  2152. },
  2153. Timestamp: start1,
  2154. },
  2155. },
  2156. expected: []metric.Update{
  2157. {
  2158. Name: metric.JobInfo,
  2159. Labels: map[string]string{
  2160. source.UIDLabel: "uuid1",
  2161. source.NamespaceUIDLabel: "",
  2162. source.JobLabel: "job1",
  2163. },
  2164. Value: 0,
  2165. AdditionalInfo: map[string]string{
  2166. source.UIDLabel: "uuid1",
  2167. source.NamespaceUIDLabel: "",
  2168. source.JobLabel: "job1",
  2169. },
  2170. },
  2171. {
  2172. Name: metric.JobLabels,
  2173. Labels: map[string]string{
  2174. source.UIDLabel: "uuid1",
  2175. source.NamespaceUIDLabel: "",
  2176. source.JobLabel: "job1",
  2177. },
  2178. Value: 0,
  2179. AdditionalInfo: map[string]string{},
  2180. },
  2181. {
  2182. Name: metric.JobAnnotations,
  2183. Labels: map[string]string{
  2184. source.UIDLabel: "uuid1",
  2185. source.NamespaceUIDLabel: "",
  2186. source.JobLabel: "job1",
  2187. },
  2188. Value: 0,
  2189. AdditionalInfo: map[string]string{},
  2190. },
  2191. },
  2192. },
  2193. {
  2194. name: "with namespace index",
  2195. nsSetup: func(nsIndex map[string]types.UID) {
  2196. nsIndex["namespace1"] = "ns-uuid1"
  2197. },
  2198. scrapes: []scrape{
  2199. {
  2200. Jobs: []*clustercache.Job{
  2201. {
  2202. Name: "job1",
  2203. Namespace: "namespace1",
  2204. UID: "uuid1",
  2205. },
  2206. },
  2207. Timestamp: start1,
  2208. },
  2209. },
  2210. expected: []metric.Update{
  2211. {
  2212. Name: metric.JobInfo,
  2213. Labels: map[string]string{
  2214. source.UIDLabel: "uuid1",
  2215. source.NamespaceUIDLabel: "ns-uuid1",
  2216. source.JobLabel: "job1",
  2217. },
  2218. Value: 0,
  2219. AdditionalInfo: map[string]string{
  2220. source.UIDLabel: "uuid1",
  2221. source.NamespaceUIDLabel: "ns-uuid1",
  2222. source.JobLabel: "job1",
  2223. },
  2224. },
  2225. {
  2226. Name: metric.JobLabels,
  2227. Labels: map[string]string{
  2228. source.UIDLabel: "uuid1",
  2229. source.NamespaceUIDLabel: "ns-uuid1",
  2230. source.JobLabel: "job1",
  2231. },
  2232. Value: 0,
  2233. AdditionalInfo: map[string]string{},
  2234. },
  2235. {
  2236. Name: metric.JobAnnotations,
  2237. Labels: map[string]string{
  2238. source.UIDLabel: "uuid1",
  2239. source.NamespaceUIDLabel: "ns-uuid1",
  2240. source.JobLabel: "job1",
  2241. },
  2242. Value: 0,
  2243. AdditionalInfo: map[string]string{},
  2244. },
  2245. },
  2246. },
  2247. }
  2248. for _, tt := range tests {
  2249. t.Run(tt.name, func(t *testing.T) {
  2250. ks := &ClusterCacheScraper{}
  2251. nsIndex := make(map[string]types.UID, 0)
  2252. if tt.nsSetup != nil {
  2253. tt.nsSetup(nsIndex)
  2254. }
  2255. var scrapeResults []metric.Update
  2256. for _, s := range tt.scrapes {
  2257. res := ks.scrapeJobs(s.Jobs, nsIndex)
  2258. scrapeResults = append(scrapeResults, res...)
  2259. }
  2260. if len(scrapeResults) != len(tt.expected) {
  2261. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  2262. }
  2263. for i, expected := range tt.expected {
  2264. got := scrapeResults[i]
  2265. if !reflect.DeepEqual(expected, got) {
  2266. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  2267. }
  2268. }
  2269. })
  2270. }
  2271. }
  2272. func Test_kubernetesScraper_scrapeCronJobs(t *testing.T) {
  2273. start1, _ := time.Parse(time.RFC3339, Start1Str)
  2274. type scrape struct {
  2275. CronJobs []*clustercache.CronJob
  2276. Timestamp time.Time
  2277. }
  2278. tests := []struct {
  2279. name string
  2280. nsSetup func(map[string]types.UID)
  2281. scrapes []scrape
  2282. expected []metric.Update
  2283. }{
  2284. {
  2285. name: "simple",
  2286. scrapes: []scrape{
  2287. {
  2288. CronJobs: []*clustercache.CronJob{
  2289. {
  2290. Name: "cronJob1",
  2291. Namespace: "namespace1",
  2292. UID: "uuid1",
  2293. },
  2294. },
  2295. Timestamp: start1,
  2296. },
  2297. },
  2298. expected: []metric.Update{
  2299. {
  2300. Name: metric.CronJobInfo,
  2301. Labels: map[string]string{
  2302. source.UIDLabel: "uuid1",
  2303. source.NamespaceUIDLabel: "",
  2304. source.CronJobLabel: "cronJob1",
  2305. },
  2306. Value: 0,
  2307. AdditionalInfo: map[string]string{
  2308. source.UIDLabel: "uuid1",
  2309. source.NamespaceUIDLabel: "",
  2310. source.CronJobLabel: "cronJob1",
  2311. },
  2312. },
  2313. {
  2314. Name: metric.CronJobLabels,
  2315. Labels: map[string]string{
  2316. source.UIDLabel: "uuid1",
  2317. source.NamespaceUIDLabel: "",
  2318. source.CronJobLabel: "cronJob1",
  2319. },
  2320. Value: 0,
  2321. AdditionalInfo: map[string]string{},
  2322. },
  2323. {
  2324. Name: metric.CronJobAnnotations,
  2325. Labels: map[string]string{
  2326. source.UIDLabel: "uuid1",
  2327. source.NamespaceUIDLabel: "",
  2328. source.CronJobLabel: "cronJob1",
  2329. },
  2330. Value: 0,
  2331. AdditionalInfo: map[string]string{},
  2332. },
  2333. },
  2334. },
  2335. {
  2336. name: "with namespace index",
  2337. nsSetup: func(nsIndex map[string]types.UID) {
  2338. nsIndex["namespace1"] = "ns-uuid1"
  2339. },
  2340. scrapes: []scrape{
  2341. {
  2342. CronJobs: []*clustercache.CronJob{
  2343. {
  2344. Name: "cronJob1",
  2345. Namespace: "namespace1",
  2346. UID: "uuid1",
  2347. },
  2348. },
  2349. Timestamp: start1,
  2350. },
  2351. },
  2352. expected: []metric.Update{
  2353. {
  2354. Name: metric.CronJobInfo,
  2355. Labels: map[string]string{
  2356. source.UIDLabel: "uuid1",
  2357. source.NamespaceUIDLabel: "ns-uuid1",
  2358. source.CronJobLabel: "cronJob1",
  2359. },
  2360. Value: 0,
  2361. AdditionalInfo: map[string]string{
  2362. source.UIDLabel: "uuid1",
  2363. source.NamespaceUIDLabel: "ns-uuid1",
  2364. source.CronJobLabel: "cronJob1",
  2365. },
  2366. },
  2367. {
  2368. Name: metric.CronJobLabels,
  2369. Labels: map[string]string{
  2370. source.UIDLabel: "uuid1",
  2371. source.NamespaceUIDLabel: "ns-uuid1",
  2372. source.CronJobLabel: "cronJob1",
  2373. },
  2374. Value: 0,
  2375. AdditionalInfo: map[string]string{},
  2376. },
  2377. {
  2378. Name: metric.CronJobAnnotations,
  2379. Labels: map[string]string{
  2380. source.UIDLabel: "uuid1",
  2381. source.NamespaceUIDLabel: "ns-uuid1",
  2382. source.CronJobLabel: "cronJob1",
  2383. },
  2384. Value: 0,
  2385. AdditionalInfo: map[string]string{},
  2386. },
  2387. },
  2388. },
  2389. }
  2390. for _, tt := range tests {
  2391. t.Run(tt.name, func(t *testing.T) {
  2392. ks := &ClusterCacheScraper{}
  2393. nsIndex := make(map[string]types.UID, 0)
  2394. if tt.nsSetup != nil {
  2395. tt.nsSetup(nsIndex)
  2396. }
  2397. var scrapeResults []metric.Update
  2398. for _, s := range tt.scrapes {
  2399. res := ks.scrapeCronJobs(s.CronJobs, nsIndex)
  2400. scrapeResults = append(scrapeResults, res...)
  2401. }
  2402. if len(scrapeResults) != len(tt.expected) {
  2403. t.Errorf("Expected result length of %d, got %d", len(tt.expected), len(scrapeResults))
  2404. }
  2405. for i, expected := range tt.expected {
  2406. got := scrapeResults[i]
  2407. if !reflect.DeepEqual(expected, got) {
  2408. t.Errorf("Result did not match expected at index %d: got %v, want %v", i, got, expected)
  2409. }
  2410. }
  2411. })
  2412. }
  2413. }