allocationfilter_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. package kubecost
  2. import (
  3. "testing"
  4. )
  5. func Test_AllocationFilterCondition_Matches(t *testing.T) {
  6. cases := []struct {
  7. name string
  8. a *Allocation
  9. filter AllocationFilter
  10. expected bool
  11. }{
  12. {
  13. name: "ClusterID Equals -> true",
  14. a: &Allocation{
  15. Properties: &AllocationProperties{
  16. Cluster: "cluster-one",
  17. },
  18. },
  19. filter: AllocationFilterCondition{
  20. Field: FilterClusterID,
  21. Op: FilterEquals,
  22. Value: "cluster-one",
  23. },
  24. expected: true,
  25. },
  26. {
  27. name: "Node Equals -> true",
  28. a: &Allocation{
  29. Properties: &AllocationProperties{
  30. Node: "node123",
  31. },
  32. },
  33. filter: AllocationFilterCondition{
  34. Field: FilterNode,
  35. Op: FilterEquals,
  36. Value: "node123",
  37. },
  38. expected: true,
  39. },
  40. {
  41. name: "Namespace NotEquals -> false",
  42. a: &Allocation{
  43. Properties: &AllocationProperties{
  44. Namespace: "kube-system",
  45. },
  46. },
  47. filter: AllocationFilterCondition{
  48. Field: FilterNamespace,
  49. Op: FilterNotEquals,
  50. Value: "kube-system",
  51. },
  52. expected: false,
  53. },
  54. {
  55. name: "Namespace NotEquals Unallocated -> true",
  56. a: &Allocation{
  57. Properties: &AllocationProperties{
  58. Namespace: "kube-system",
  59. },
  60. },
  61. filter: AllocationFilterCondition{
  62. Field: FilterNamespace,
  63. Op: FilterNotEquals,
  64. Value: UnallocatedSuffix,
  65. },
  66. expected: true,
  67. },
  68. {
  69. name: "Namespace NotEquals Unallocated -> false",
  70. a: &Allocation{
  71. Properties: &AllocationProperties{
  72. Namespace: "",
  73. },
  74. },
  75. filter: AllocationFilterCondition{
  76. Field: FilterNamespace,
  77. Op: FilterNotEquals,
  78. Value: UnallocatedSuffix,
  79. },
  80. expected: false,
  81. },
  82. {
  83. name: "Namespace Equals Unallocated -> true",
  84. a: &Allocation{
  85. Properties: &AllocationProperties{
  86. Namespace: "",
  87. },
  88. },
  89. filter: AllocationFilterCondition{
  90. Field: FilterNamespace,
  91. Op: FilterEquals,
  92. Value: UnallocatedSuffix,
  93. },
  94. expected: true,
  95. },
  96. {
  97. name: "ControllerKind Equals -> true",
  98. a: &Allocation{
  99. Properties: &AllocationProperties{
  100. ControllerKind: "deployment", // We generally store controller kinds as all lowercase
  101. },
  102. },
  103. filter: AllocationFilterCondition{
  104. Field: FilterControllerKind,
  105. Op: FilterEquals,
  106. Value: "deployment",
  107. },
  108. expected: true,
  109. },
  110. {
  111. name: "ControllerName Equals -> true",
  112. a: &Allocation{
  113. Properties: &AllocationProperties{
  114. Controller: "kc-cost-analyzer",
  115. },
  116. },
  117. filter: AllocationFilterCondition{
  118. Field: FilterControllerName,
  119. Op: FilterEquals,
  120. Value: "kc-cost-analyzer",
  121. },
  122. expected: true,
  123. },
  124. {
  125. name: "Pod (with UID) Equals -> true",
  126. a: &Allocation{
  127. Properties: &AllocationProperties{
  128. Pod: "pod-123 UID-ABC",
  129. },
  130. },
  131. filter: AllocationFilterCondition{
  132. Field: FilterPod,
  133. Op: FilterEquals,
  134. Value: "pod-123 UID-ABC",
  135. },
  136. expected: true,
  137. },
  138. {
  139. name: "Container Equals -> true",
  140. a: &Allocation{
  141. Properties: &AllocationProperties{
  142. Container: "cost-model",
  143. },
  144. },
  145. filter: AllocationFilterCondition{
  146. Field: FilterContainer,
  147. Op: FilterEquals,
  148. Value: "cost-model",
  149. },
  150. expected: true,
  151. },
  152. {
  153. name: `label[app]="foo" -> true`,
  154. a: &Allocation{
  155. Properties: &AllocationProperties{
  156. Labels: map[string]string{
  157. "app": "foo",
  158. },
  159. },
  160. },
  161. filter: AllocationFilterCondition{
  162. Field: FilterLabel,
  163. Op: FilterEquals,
  164. Key: "app",
  165. Value: "foo",
  166. },
  167. expected: true,
  168. },
  169. {
  170. name: `label[app]="foo" -> different value -> false`,
  171. a: &Allocation{
  172. Properties: &AllocationProperties{
  173. Labels: map[string]string{
  174. "app": "bar",
  175. },
  176. },
  177. },
  178. filter: AllocationFilterCondition{
  179. Field: FilterLabel,
  180. Op: FilterEquals,
  181. Key: "app",
  182. Value: "foo",
  183. },
  184. expected: false,
  185. },
  186. {
  187. name: `label[app]="foo" -> label missing -> false`,
  188. a: &Allocation{
  189. Properties: &AllocationProperties{
  190. Labels: map[string]string{
  191. "someotherlabel": "someothervalue",
  192. },
  193. },
  194. },
  195. filter: AllocationFilterCondition{
  196. Field: FilterLabel,
  197. Op: FilterEquals,
  198. Key: "app",
  199. Value: "foo",
  200. },
  201. expected: false,
  202. },
  203. {
  204. name: `label[app]!="foo" -> label missing -> true`,
  205. a: &Allocation{
  206. Properties: &AllocationProperties{
  207. Labels: map[string]string{
  208. "someotherlabel": "someothervalue",
  209. },
  210. },
  211. },
  212. filter: AllocationFilterCondition{
  213. Field: FilterLabel,
  214. Op: FilterNotEquals,
  215. Key: "app",
  216. Value: "foo",
  217. },
  218. expected: true,
  219. },
  220. {
  221. name: `annotation[prom_modified_name]="testing123" -> true`,
  222. a: &Allocation{
  223. Properties: &AllocationProperties{
  224. Annotations: map[string]string{
  225. "prom_modified_name": "testing123",
  226. },
  227. },
  228. },
  229. filter: AllocationFilterCondition{
  230. Field: FilterAnnotation,
  231. Op: FilterEquals,
  232. Key: "prom_modified_name",
  233. Value: "testing123",
  234. },
  235. expected: true,
  236. },
  237. {
  238. name: `annotation[app]="foo" -> different value -> false`,
  239. a: &Allocation{
  240. Properties: &AllocationProperties{
  241. Annotations: map[string]string{
  242. "app": "bar",
  243. },
  244. },
  245. },
  246. filter: AllocationFilterCondition{
  247. Field: FilterAnnotation,
  248. Op: FilterEquals,
  249. Key: "app",
  250. Value: "foo",
  251. },
  252. expected: false,
  253. },
  254. {
  255. name: `annotation[app]="foo" -> annotation missing -> false`,
  256. a: &Allocation{
  257. Properties: &AllocationProperties{
  258. Annotations: map[string]string{
  259. "someotherannotation": "someothervalue",
  260. },
  261. },
  262. },
  263. filter: AllocationFilterCondition{
  264. Field: FilterAnnotation,
  265. Op: FilterEquals,
  266. Key: "app",
  267. Value: "foo",
  268. },
  269. expected: false,
  270. },
  271. {
  272. name: `annotation[app]!="foo" -> annotation missing -> true`,
  273. a: &Allocation{
  274. Properties: &AllocationProperties{
  275. Annotations: map[string]string{
  276. "someotherannotation": "someothervalue",
  277. },
  278. },
  279. },
  280. filter: AllocationFilterCondition{
  281. Field: FilterAnnotation,
  282. Op: FilterNotEquals,
  283. Key: "app",
  284. Value: "foo",
  285. },
  286. expected: true,
  287. },
  288. {
  289. name: `namespace unallocated -> true`,
  290. a: &Allocation{
  291. Properties: &AllocationProperties{
  292. Namespace: "",
  293. },
  294. },
  295. filter: AllocationFilterCondition{
  296. Field: FilterNamespace,
  297. Op: FilterEquals,
  298. Value: UnallocatedSuffix,
  299. },
  300. expected: true,
  301. },
  302. {
  303. name: `services contains -> true`,
  304. a: &Allocation{
  305. Properties: &AllocationProperties{
  306. Services: []string{"serv1", "serv2"},
  307. },
  308. },
  309. filter: AllocationFilterCondition{
  310. Field: FilterServices,
  311. Op: FilterContains,
  312. Value: "serv2",
  313. },
  314. expected: true,
  315. },
  316. {
  317. name: `services contains -> false`,
  318. a: &Allocation{
  319. Properties: &AllocationProperties{
  320. Services: []string{"serv1", "serv2"},
  321. },
  322. },
  323. filter: AllocationFilterCondition{
  324. Field: FilterServices,
  325. Op: FilterContains,
  326. Value: "serv3",
  327. },
  328. expected: false,
  329. },
  330. {
  331. name: `services contains unallocated -> false`,
  332. a: &Allocation{
  333. Properties: &AllocationProperties{
  334. Services: []string{"serv1", "serv2"},
  335. },
  336. },
  337. filter: AllocationFilterCondition{
  338. Field: FilterServices,
  339. Op: FilterContains,
  340. Value: UnallocatedSuffix,
  341. },
  342. expected: false,
  343. },
  344. {
  345. name: `services contains unallocated -> true`,
  346. a: &Allocation{
  347. Properties: &AllocationProperties{
  348. Services: []string{},
  349. },
  350. },
  351. filter: AllocationFilterCondition{
  352. Field: FilterServices,
  353. Op: FilterContains,
  354. Value: UnallocatedSuffix,
  355. },
  356. expected: true,
  357. },
  358. }
  359. for _, c := range cases {
  360. result := c.filter.Matches(c.a)
  361. if result != c.expected {
  362. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  363. }
  364. }
  365. }
  366. func Test_AllocationFilterAnd_Matches(t *testing.T) {
  367. cases := []struct {
  368. name string
  369. a *Allocation
  370. filter AllocationFilter
  371. expected bool
  372. }{
  373. {
  374. name: `label[app]="foo" and namespace="kubecost" -> both true`,
  375. a: &Allocation{
  376. Properties: &AllocationProperties{
  377. Namespace: "kubecost",
  378. Labels: map[string]string{
  379. "app": "foo",
  380. },
  381. },
  382. },
  383. filter: AllocationFilterAnd{[]AllocationFilter{
  384. AllocationFilterCondition{
  385. Field: FilterLabel,
  386. Op: FilterEquals,
  387. Key: "app",
  388. Value: "foo",
  389. },
  390. AllocationFilterCondition{
  391. Field: FilterNamespace,
  392. Op: FilterEquals,
  393. Value: "kubecost",
  394. },
  395. }},
  396. expected: true,
  397. },
  398. {
  399. name: `label[app]="foo" and namespace="kubecost" -> first true`,
  400. a: &Allocation{
  401. Properties: &AllocationProperties{
  402. Namespace: "kube-system",
  403. Labels: map[string]string{
  404. "app": "foo",
  405. },
  406. },
  407. },
  408. filter: AllocationFilterAnd{[]AllocationFilter{
  409. AllocationFilterCondition{
  410. Field: FilterLabel,
  411. Op: FilterEquals,
  412. Key: "app",
  413. Value: "foo",
  414. },
  415. AllocationFilterCondition{
  416. Field: FilterNamespace,
  417. Op: FilterEquals,
  418. Value: "kubecost",
  419. },
  420. }},
  421. expected: false,
  422. },
  423. {
  424. name: `label[app]="foo" and namespace="kubecost" -> second true`,
  425. a: &Allocation{
  426. Properties: &AllocationProperties{
  427. Namespace: "kubecost",
  428. Labels: map[string]string{
  429. "app": "bar",
  430. },
  431. },
  432. },
  433. filter: AllocationFilterAnd{[]AllocationFilter{
  434. AllocationFilterCondition{
  435. Field: FilterLabel,
  436. Op: FilterEquals,
  437. Key: "app",
  438. Value: "foo",
  439. },
  440. AllocationFilterCondition{
  441. Field: FilterNamespace,
  442. Op: FilterEquals,
  443. Value: "kubecost",
  444. },
  445. }},
  446. expected: false,
  447. },
  448. {
  449. name: `label[app]="foo" and namespace="kubecost" -> both false`,
  450. a: &Allocation{
  451. Properties: &AllocationProperties{
  452. Namespace: "kube-system",
  453. Labels: map[string]string{
  454. "app": "bar",
  455. },
  456. },
  457. },
  458. filter: AllocationFilterAnd{[]AllocationFilter{
  459. AllocationFilterCondition{
  460. Field: FilterLabel,
  461. Op: FilterEquals,
  462. Key: "app",
  463. Value: "foo",
  464. },
  465. AllocationFilterCondition{
  466. Field: FilterNamespace,
  467. Op: FilterEquals,
  468. Value: "kubecost",
  469. },
  470. }},
  471. expected: false,
  472. },
  473. }
  474. for _, c := range cases {
  475. result := c.filter.Matches(c.a)
  476. if result != c.expected {
  477. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  478. }
  479. }
  480. }
  481. func Test_AllocationFilterOr_Matches(t *testing.T) {
  482. cases := []struct {
  483. name string
  484. a *Allocation
  485. filter AllocationFilter
  486. expected bool
  487. }{
  488. {
  489. name: `label[app]="foo" or namespace="kubecost" -> both true`,
  490. a: &Allocation{
  491. Properties: &AllocationProperties{
  492. Namespace: "kubecost",
  493. Labels: map[string]string{
  494. "app": "foo",
  495. },
  496. },
  497. },
  498. filter: AllocationFilterOr{[]AllocationFilter{
  499. AllocationFilterCondition{
  500. Field: FilterLabel,
  501. Op: FilterEquals,
  502. Key: "app",
  503. Value: "foo",
  504. },
  505. AllocationFilterCondition{
  506. Field: FilterNamespace,
  507. Op: FilterEquals,
  508. Value: "kubecost",
  509. },
  510. }},
  511. expected: true,
  512. },
  513. {
  514. name: `label[app]="foo" or namespace="kubecost" -> first true`,
  515. a: &Allocation{
  516. Properties: &AllocationProperties{
  517. Namespace: "kube-system",
  518. Labels: map[string]string{
  519. "app": "foo",
  520. },
  521. },
  522. },
  523. filter: AllocationFilterOr{[]AllocationFilter{
  524. AllocationFilterCondition{
  525. Field: FilterLabel,
  526. Op: FilterEquals,
  527. Key: "app",
  528. Value: "foo",
  529. },
  530. AllocationFilterCondition{
  531. Field: FilterNamespace,
  532. Op: FilterEquals,
  533. Value: "kubecost",
  534. },
  535. }},
  536. expected: true,
  537. },
  538. {
  539. name: `label[app]="foo" or namespace="kubecost" -> second true`,
  540. a: &Allocation{
  541. Properties: &AllocationProperties{
  542. Namespace: "kubecost",
  543. Labels: map[string]string{
  544. "app": "bar",
  545. },
  546. },
  547. },
  548. filter: AllocationFilterOr{[]AllocationFilter{
  549. AllocationFilterCondition{
  550. Field: FilterLabel,
  551. Op: FilterEquals,
  552. Key: "app",
  553. Value: "foo",
  554. },
  555. AllocationFilterCondition{
  556. Field: FilterNamespace,
  557. Op: FilterEquals,
  558. Value: "kubecost",
  559. },
  560. }},
  561. expected: true,
  562. },
  563. {
  564. name: `label[app]="foo" or namespace="kubecost" -> both false`,
  565. a: &Allocation{
  566. Properties: &AllocationProperties{
  567. Namespace: "kube-system",
  568. Labels: map[string]string{
  569. "app": "bar",
  570. },
  571. },
  572. },
  573. filter: AllocationFilterOr{[]AllocationFilter{
  574. AllocationFilterCondition{
  575. Field: FilterLabel,
  576. Op: FilterEquals,
  577. Key: "app",
  578. Value: "foo",
  579. },
  580. AllocationFilterCondition{
  581. Field: FilterNamespace,
  582. Op: FilterEquals,
  583. Value: "kubecost",
  584. },
  585. }},
  586. expected: false,
  587. },
  588. }
  589. for _, c := range cases {
  590. result := c.filter.Matches(c.a)
  591. if result != c.expected {
  592. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  593. }
  594. }
  595. }