allocationfilter_test.go 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132
  1. package kubecost
  2. import (
  3. "reflect"
  4. "testing"
  5. )
  6. func Test_AllocationFilterCondition_Matches(t *testing.T) {
  7. cases := []struct {
  8. name string
  9. a *Allocation
  10. filter AllocationFilter
  11. expected bool
  12. }{
  13. {
  14. name: "ClusterID Equals -> true",
  15. a: &Allocation{
  16. Properties: &AllocationProperties{
  17. Cluster: "cluster-one",
  18. },
  19. },
  20. filter: AllocationFilterCondition{
  21. Field: FilterClusterID,
  22. Op: FilterEquals,
  23. Value: "cluster-one",
  24. },
  25. expected: true,
  26. },
  27. {
  28. name: "ClusterID StartsWith -> true",
  29. a: &Allocation{
  30. Properties: &AllocationProperties{
  31. Cluster: "cluster-one",
  32. },
  33. },
  34. filter: AllocationFilterCondition{
  35. Field: FilterClusterID,
  36. Op: FilterStartsWith,
  37. Value: "cluster",
  38. },
  39. expected: true,
  40. },
  41. {
  42. name: "ClusterID StartsWith -> false",
  43. a: &Allocation{
  44. Properties: &AllocationProperties{
  45. Cluster: "k8s-one",
  46. },
  47. },
  48. filter: AllocationFilterCondition{
  49. Field: FilterClusterID,
  50. Op: FilterStartsWith,
  51. Value: "cluster",
  52. },
  53. expected: false,
  54. },
  55. {
  56. name: "ClusterID empty StartsWith '' -> true",
  57. a: &Allocation{
  58. Properties: &AllocationProperties{
  59. Cluster: "",
  60. },
  61. },
  62. filter: AllocationFilterCondition{
  63. Field: FilterClusterID,
  64. Op: FilterStartsWith,
  65. Value: "",
  66. },
  67. expected: true,
  68. },
  69. {
  70. name: "ClusterID nonempty StartsWith '' -> true",
  71. a: &Allocation{
  72. Properties: &AllocationProperties{
  73. Cluster: "abc",
  74. },
  75. },
  76. filter: AllocationFilterCondition{
  77. Field: FilterClusterID,
  78. Op: FilterStartsWith,
  79. Value: "",
  80. },
  81. expected: true,
  82. },
  83. {
  84. name: "Node Equals -> true",
  85. a: &Allocation{
  86. Properties: &AllocationProperties{
  87. Node: "node123",
  88. },
  89. },
  90. filter: AllocationFilterCondition{
  91. Field: FilterNode,
  92. Op: FilterEquals,
  93. Value: "node123",
  94. },
  95. expected: true,
  96. },
  97. {
  98. name: "Namespace NotEquals -> false",
  99. a: &Allocation{
  100. Properties: &AllocationProperties{
  101. Namespace: "kube-system",
  102. },
  103. },
  104. filter: AllocationFilterCondition{
  105. Field: FilterNamespace,
  106. Op: FilterNotEquals,
  107. Value: "kube-system",
  108. },
  109. expected: false,
  110. },
  111. {
  112. name: "Namespace NotEquals Unallocated -> true",
  113. a: &Allocation{
  114. Properties: &AllocationProperties{
  115. Namespace: "kube-system",
  116. },
  117. },
  118. filter: AllocationFilterCondition{
  119. Field: FilterNamespace,
  120. Op: FilterNotEquals,
  121. Value: UnallocatedSuffix,
  122. },
  123. expected: true,
  124. },
  125. {
  126. name: "Namespace NotEquals Unallocated -> false",
  127. a: &Allocation{
  128. Properties: &AllocationProperties{
  129. Namespace: "",
  130. },
  131. },
  132. filter: AllocationFilterCondition{
  133. Field: FilterNamespace,
  134. Op: FilterNotEquals,
  135. Value: UnallocatedSuffix,
  136. },
  137. expected: false,
  138. },
  139. {
  140. name: "Namespace Equals Unallocated -> true",
  141. a: &Allocation{
  142. Properties: &AllocationProperties{
  143. Namespace: "",
  144. },
  145. },
  146. filter: AllocationFilterCondition{
  147. Field: FilterNamespace,
  148. Op: FilterEquals,
  149. Value: UnallocatedSuffix,
  150. },
  151. expected: true,
  152. },
  153. {
  154. name: "ControllerKind Equals -> true",
  155. a: &Allocation{
  156. Properties: &AllocationProperties{
  157. ControllerKind: "deployment", // We generally store controller kinds as all lowercase
  158. },
  159. },
  160. filter: AllocationFilterCondition{
  161. Field: FilterControllerKind,
  162. Op: FilterEquals,
  163. Value: "deployment",
  164. },
  165. expected: true,
  166. },
  167. {
  168. name: "ControllerName Equals -> true",
  169. a: &Allocation{
  170. Properties: &AllocationProperties{
  171. Controller: "kc-cost-analyzer",
  172. },
  173. },
  174. filter: AllocationFilterCondition{
  175. Field: FilterControllerName,
  176. Op: FilterEquals,
  177. Value: "kc-cost-analyzer",
  178. },
  179. expected: true,
  180. },
  181. {
  182. name: "Pod (with UID) Equals -> true",
  183. a: &Allocation{
  184. Properties: &AllocationProperties{
  185. Pod: "pod-123 UID-ABC",
  186. },
  187. },
  188. filter: AllocationFilterCondition{
  189. Field: FilterPod,
  190. Op: FilterEquals,
  191. Value: "pod-123 UID-ABC",
  192. },
  193. expected: true,
  194. },
  195. {
  196. name: "Container Equals -> true",
  197. a: &Allocation{
  198. Properties: &AllocationProperties{
  199. Container: "cost-model",
  200. },
  201. },
  202. filter: AllocationFilterCondition{
  203. Field: FilterContainer,
  204. Op: FilterEquals,
  205. Value: "cost-model",
  206. },
  207. expected: true,
  208. },
  209. {
  210. name: `label[app]="foo" -> true`,
  211. a: &Allocation{
  212. Properties: &AllocationProperties{
  213. Labels: map[string]string{
  214. "app": "foo",
  215. },
  216. },
  217. },
  218. filter: AllocationFilterCondition{
  219. Field: FilterLabel,
  220. Op: FilterEquals,
  221. Key: "app",
  222. Value: "foo",
  223. },
  224. expected: true,
  225. },
  226. {
  227. name: `label[app]="foo" -> different value -> false`,
  228. a: &Allocation{
  229. Properties: &AllocationProperties{
  230. Labels: map[string]string{
  231. "app": "bar",
  232. },
  233. },
  234. },
  235. filter: AllocationFilterCondition{
  236. Field: FilterLabel,
  237. Op: FilterEquals,
  238. Key: "app",
  239. Value: "foo",
  240. },
  241. expected: false,
  242. },
  243. {
  244. name: `label[app]="foo" -> label missing -> false`,
  245. a: &Allocation{
  246. Properties: &AllocationProperties{
  247. Labels: map[string]string{
  248. "someotherlabel": "someothervalue",
  249. },
  250. },
  251. },
  252. filter: AllocationFilterCondition{
  253. Field: FilterLabel,
  254. Op: FilterEquals,
  255. Key: "app",
  256. Value: "foo",
  257. },
  258. expected: false,
  259. },
  260. {
  261. name: `label[app]=Unallocated -> label missing -> true`,
  262. a: &Allocation{
  263. Properties: &AllocationProperties{
  264. Labels: map[string]string{
  265. "someotherlabel": "someothervalue",
  266. },
  267. },
  268. },
  269. filter: AllocationFilterCondition{
  270. Field: FilterLabel,
  271. Op: FilterEquals,
  272. Key: "app",
  273. Value: UnallocatedSuffix,
  274. },
  275. expected: true,
  276. },
  277. {
  278. name: `label[app]=Unallocated -> label present -> false`,
  279. a: &Allocation{
  280. Properties: &AllocationProperties{
  281. Labels: map[string]string{
  282. "app": "test",
  283. },
  284. },
  285. },
  286. filter: AllocationFilterCondition{
  287. Field: FilterLabel,
  288. Op: FilterEquals,
  289. Key: "app",
  290. Value: UnallocatedSuffix,
  291. },
  292. expected: false,
  293. },
  294. {
  295. name: `label[app]!=Unallocated -> label missing -> false`,
  296. a: &Allocation{
  297. Properties: &AllocationProperties{
  298. Labels: map[string]string{
  299. "someotherlabel": "someothervalue",
  300. },
  301. },
  302. },
  303. filter: AllocationFilterCondition{
  304. Field: FilterLabel,
  305. Op: FilterNotEquals,
  306. Key: "app",
  307. Value: UnallocatedSuffix,
  308. },
  309. expected: false,
  310. },
  311. {
  312. name: `label[app]!=Unallocated -> label present -> true`,
  313. a: &Allocation{
  314. Properties: &AllocationProperties{
  315. Labels: map[string]string{
  316. "app": "test",
  317. },
  318. },
  319. },
  320. filter: AllocationFilterCondition{
  321. Field: FilterLabel,
  322. Op: FilterNotEquals,
  323. Key: "app",
  324. Value: UnallocatedSuffix,
  325. },
  326. expected: true,
  327. },
  328. {
  329. name: `label[app]!="foo" -> label missing -> true`,
  330. a: &Allocation{
  331. Properties: &AllocationProperties{
  332. Labels: map[string]string{
  333. "someotherlabel": "someothervalue",
  334. },
  335. },
  336. },
  337. filter: AllocationFilterCondition{
  338. Field: FilterLabel,
  339. Op: FilterNotEquals,
  340. Key: "app",
  341. Value: "foo",
  342. },
  343. expected: true,
  344. },
  345. {
  346. name: `annotation[prom_modified_name]="testing123" -> true`,
  347. a: &Allocation{
  348. Properties: &AllocationProperties{
  349. Annotations: map[string]string{
  350. "prom_modified_name": "testing123",
  351. },
  352. },
  353. },
  354. filter: AllocationFilterCondition{
  355. Field: FilterAnnotation,
  356. Op: FilterEquals,
  357. Key: "prom_modified_name",
  358. Value: "testing123",
  359. },
  360. expected: true,
  361. },
  362. {
  363. name: `annotation[app]="foo" -> different value -> false`,
  364. a: &Allocation{
  365. Properties: &AllocationProperties{
  366. Annotations: map[string]string{
  367. "app": "bar",
  368. },
  369. },
  370. },
  371. filter: AllocationFilterCondition{
  372. Field: FilterAnnotation,
  373. Op: FilterEquals,
  374. Key: "app",
  375. Value: "foo",
  376. },
  377. expected: false,
  378. },
  379. {
  380. name: `annotation[app]="foo" -> annotation missing -> false`,
  381. a: &Allocation{
  382. Properties: &AllocationProperties{
  383. Annotations: map[string]string{
  384. "someotherannotation": "someothervalue",
  385. },
  386. },
  387. },
  388. filter: AllocationFilterCondition{
  389. Field: FilterAnnotation,
  390. Op: FilterEquals,
  391. Key: "app",
  392. Value: "foo",
  393. },
  394. expected: false,
  395. },
  396. {
  397. name: `annotation[app]!="foo" -> annotation missing -> true`,
  398. a: &Allocation{
  399. Properties: &AllocationProperties{
  400. Annotations: map[string]string{
  401. "someotherannotation": "someothervalue",
  402. },
  403. },
  404. },
  405. filter: AllocationFilterCondition{
  406. Field: FilterAnnotation,
  407. Op: FilterNotEquals,
  408. Key: "app",
  409. Value: "foo",
  410. },
  411. expected: true,
  412. },
  413. {
  414. name: `namespace unallocated -> true`,
  415. a: &Allocation{
  416. Properties: &AllocationProperties{
  417. Namespace: "",
  418. },
  419. },
  420. filter: AllocationFilterCondition{
  421. Field: FilterNamespace,
  422. Op: FilterEquals,
  423. Value: UnallocatedSuffix,
  424. },
  425. expected: true,
  426. },
  427. {
  428. name: `services contains -> true`,
  429. a: &Allocation{
  430. Properties: &AllocationProperties{
  431. Services: []string{"serv1", "serv2"},
  432. },
  433. },
  434. filter: AllocationFilterCondition{
  435. Field: FilterServices,
  436. Op: FilterContains,
  437. Value: "serv2",
  438. },
  439. expected: true,
  440. },
  441. {
  442. name: `services contains -> false`,
  443. a: &Allocation{
  444. Properties: &AllocationProperties{
  445. Services: []string{"serv1", "serv2"},
  446. },
  447. },
  448. filter: AllocationFilterCondition{
  449. Field: FilterServices,
  450. Op: FilterContains,
  451. Value: "serv3",
  452. },
  453. expected: false,
  454. },
  455. {
  456. name: `services notcontains -> true`,
  457. a: &Allocation{
  458. Properties: &AllocationProperties{
  459. Services: []string{"serv1", "serv2"},
  460. },
  461. },
  462. filter: AllocationFilterCondition{
  463. Field: FilterServices,
  464. Op: FilterNotContains,
  465. Value: "serv3",
  466. },
  467. expected: true,
  468. },
  469. {
  470. name: `services notcontains -> false`,
  471. a: &Allocation{
  472. Properties: &AllocationProperties{
  473. Services: []string{"serv1", "serv2"},
  474. },
  475. },
  476. filter: AllocationFilterCondition{
  477. Field: FilterServices,
  478. Op: FilterNotContains,
  479. Value: "serv2",
  480. },
  481. expected: false,
  482. },
  483. {
  484. name: `services notcontains unallocated -> true`,
  485. a: &Allocation{
  486. Properties: &AllocationProperties{
  487. Services: []string{"serv1", "serv2"},
  488. },
  489. },
  490. filter: AllocationFilterCondition{
  491. Field: FilterServices,
  492. Op: FilterNotContains,
  493. Value: UnallocatedSuffix,
  494. },
  495. expected: true,
  496. },
  497. {
  498. name: `services notcontains unallocated -> false`,
  499. a: &Allocation{
  500. Properties: &AllocationProperties{
  501. Services: []string{},
  502. },
  503. },
  504. filter: AllocationFilterCondition{
  505. Field: FilterServices,
  506. Op: FilterNotContains,
  507. Value: UnallocatedSuffix,
  508. },
  509. expected: false,
  510. },
  511. {
  512. name: `services containsprefix -> true`,
  513. a: &Allocation{
  514. Properties: &AllocationProperties{
  515. Services: []string{"serv1", "serv2"},
  516. },
  517. },
  518. filter: AllocationFilterCondition{
  519. Field: FilterServices,
  520. Op: FilterContainsPrefix,
  521. Value: "serv",
  522. },
  523. expected: true,
  524. },
  525. {
  526. name: `services containsprefix -> false`,
  527. a: &Allocation{
  528. Properties: &AllocationProperties{
  529. Services: []string{"foo", "bar"},
  530. },
  531. },
  532. filter: AllocationFilterCondition{
  533. Field: FilterServices,
  534. Op: FilterContainsPrefix,
  535. Value: "serv",
  536. },
  537. expected: false,
  538. },
  539. {
  540. name: `services contains unallocated -> false`,
  541. a: &Allocation{
  542. Properties: &AllocationProperties{
  543. Services: []string{"serv1", "serv2"},
  544. },
  545. },
  546. filter: AllocationFilterCondition{
  547. Field: FilterServices,
  548. Op: FilterContains,
  549. Value: UnallocatedSuffix,
  550. },
  551. expected: false,
  552. },
  553. {
  554. name: `services contains unallocated -> true`,
  555. a: &Allocation{
  556. Properties: &AllocationProperties{
  557. Services: []string{},
  558. },
  559. },
  560. filter: AllocationFilterCondition{
  561. Field: FilterServices,
  562. Op: FilterContains,
  563. Value: UnallocatedSuffix,
  564. },
  565. expected: true,
  566. },
  567. }
  568. for _, c := range cases {
  569. result := c.filter.Matches(c.a)
  570. if result != c.expected {
  571. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  572. }
  573. }
  574. }
  575. func Test_AllocationFilterNone_Matches(t *testing.T) {
  576. cases := []struct {
  577. name string
  578. a *Allocation
  579. }{
  580. {
  581. name: "nil",
  582. a: nil,
  583. },
  584. {
  585. name: "nil properties",
  586. a: &Allocation{
  587. Properties: nil,
  588. },
  589. },
  590. {
  591. name: "empty properties",
  592. a: &Allocation{
  593. Properties: &AllocationProperties{},
  594. },
  595. },
  596. {
  597. name: "ClusterID",
  598. a: &Allocation{
  599. Properties: &AllocationProperties{
  600. Cluster: "cluster-one",
  601. },
  602. },
  603. },
  604. {
  605. name: "Node",
  606. a: &Allocation{
  607. Properties: &AllocationProperties{
  608. Node: "node123",
  609. },
  610. },
  611. },
  612. {
  613. name: "Namespace",
  614. a: &Allocation{
  615. Properties: &AllocationProperties{
  616. Namespace: "kube-system",
  617. },
  618. },
  619. },
  620. {
  621. name: "ControllerKind",
  622. a: &Allocation{
  623. Properties: &AllocationProperties{
  624. ControllerKind: "deployment", // We generally store controller kinds as all lowercase
  625. },
  626. },
  627. },
  628. {
  629. name: "ControllerName",
  630. a: &Allocation{
  631. Properties: &AllocationProperties{
  632. Controller: "kc-cost-analyzer",
  633. },
  634. },
  635. },
  636. {
  637. name: "Pod",
  638. a: &Allocation{
  639. Properties: &AllocationProperties{
  640. Pod: "pod-123 UID-ABC",
  641. },
  642. },
  643. },
  644. {
  645. name: "Container",
  646. a: &Allocation{
  647. Properties: &AllocationProperties{
  648. Container: "cost-model",
  649. },
  650. },
  651. },
  652. {
  653. name: `label`,
  654. a: &Allocation{
  655. Properties: &AllocationProperties{
  656. Labels: map[string]string{
  657. "app": "foo",
  658. },
  659. },
  660. },
  661. },
  662. {
  663. name: `annotation`,
  664. a: &Allocation{
  665. Properties: &AllocationProperties{
  666. Annotations: map[string]string{
  667. "prom_modified_name": "testing123",
  668. },
  669. },
  670. },
  671. },
  672. {
  673. name: `services`,
  674. a: &Allocation{
  675. Properties: &AllocationProperties{
  676. Services: []string{"serv1", "serv2"},
  677. },
  678. },
  679. },
  680. }
  681. for _, c := range cases {
  682. result := AllocationFilterNone{}.Matches(c.a)
  683. if result {
  684. t.Errorf("%s: should have been rejected", c.name)
  685. }
  686. }
  687. }
  688. func Test_AllocationFilterAnd_Matches(t *testing.T) {
  689. cases := []struct {
  690. name string
  691. a *Allocation
  692. filter AllocationFilter
  693. expected bool
  694. }{
  695. {
  696. name: `label[app]="foo" and namespace="kubecost" -> both true`,
  697. a: &Allocation{
  698. Properties: &AllocationProperties{
  699. Namespace: "kubecost",
  700. Labels: map[string]string{
  701. "app": "foo",
  702. },
  703. },
  704. },
  705. filter: AllocationFilterAnd{[]AllocationFilter{
  706. AllocationFilterCondition{
  707. Field: FilterLabel,
  708. Op: FilterEquals,
  709. Key: "app",
  710. Value: "foo",
  711. },
  712. AllocationFilterCondition{
  713. Field: FilterNamespace,
  714. Op: FilterEquals,
  715. Value: "kubecost",
  716. },
  717. }},
  718. expected: true,
  719. },
  720. {
  721. name: `label[app]="foo" and namespace="kubecost" -> first true`,
  722. a: &Allocation{
  723. Properties: &AllocationProperties{
  724. Namespace: "kube-system",
  725. Labels: map[string]string{
  726. "app": "foo",
  727. },
  728. },
  729. },
  730. filter: AllocationFilterAnd{[]AllocationFilter{
  731. AllocationFilterCondition{
  732. Field: FilterLabel,
  733. Op: FilterEquals,
  734. Key: "app",
  735. Value: "foo",
  736. },
  737. AllocationFilterCondition{
  738. Field: FilterNamespace,
  739. Op: FilterEquals,
  740. Value: "kubecost",
  741. },
  742. }},
  743. expected: false,
  744. },
  745. {
  746. name: `label[app]="foo" and namespace="kubecost" -> second true`,
  747. a: &Allocation{
  748. Properties: &AllocationProperties{
  749. Namespace: "kubecost",
  750. Labels: map[string]string{
  751. "app": "bar",
  752. },
  753. },
  754. },
  755. filter: AllocationFilterAnd{[]AllocationFilter{
  756. AllocationFilterCondition{
  757. Field: FilterLabel,
  758. Op: FilterEquals,
  759. Key: "app",
  760. Value: "foo",
  761. },
  762. AllocationFilterCondition{
  763. Field: FilterNamespace,
  764. Op: FilterEquals,
  765. Value: "kubecost",
  766. },
  767. }},
  768. expected: false,
  769. },
  770. {
  771. name: `label[app]="foo" and namespace="kubecost" -> both false`,
  772. a: &Allocation{
  773. Properties: &AllocationProperties{
  774. Namespace: "kube-system",
  775. Labels: map[string]string{
  776. "app": "bar",
  777. },
  778. },
  779. },
  780. filter: AllocationFilterAnd{[]AllocationFilter{
  781. AllocationFilterCondition{
  782. Field: FilterLabel,
  783. Op: FilterEquals,
  784. Key: "app",
  785. Value: "foo",
  786. },
  787. AllocationFilterCondition{
  788. Field: FilterNamespace,
  789. Op: FilterEquals,
  790. Value: "kubecost",
  791. },
  792. }},
  793. expected: false,
  794. },
  795. {
  796. name: `(and none) matches nothing`,
  797. a: &Allocation{
  798. Properties: &AllocationProperties{
  799. Namespace: "kube-system",
  800. Labels: map[string]string{
  801. "app": "bar",
  802. },
  803. },
  804. },
  805. filter: AllocationFilterAnd{[]AllocationFilter{
  806. AllocationFilterNone{},
  807. }},
  808. expected: false,
  809. },
  810. }
  811. for _, c := range cases {
  812. result := c.filter.Matches(c.a)
  813. if result != c.expected {
  814. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  815. }
  816. }
  817. }
  818. func Test_AllocationFilterOr_Matches(t *testing.T) {
  819. cases := []struct {
  820. name string
  821. a *Allocation
  822. filter AllocationFilter
  823. expected bool
  824. }{
  825. {
  826. name: `label[app]="foo" or namespace="kubecost" -> both true`,
  827. a: &Allocation{
  828. Properties: &AllocationProperties{
  829. Namespace: "kubecost",
  830. Labels: map[string]string{
  831. "app": "foo",
  832. },
  833. },
  834. },
  835. filter: AllocationFilterOr{[]AllocationFilter{
  836. AllocationFilterCondition{
  837. Field: FilterLabel,
  838. Op: FilterEquals,
  839. Key: "app",
  840. Value: "foo",
  841. },
  842. AllocationFilterCondition{
  843. Field: FilterNamespace,
  844. Op: FilterEquals,
  845. Value: "kubecost",
  846. },
  847. }},
  848. expected: true,
  849. },
  850. {
  851. name: `label[app]="foo" or namespace="kubecost" -> first true`,
  852. a: &Allocation{
  853. Properties: &AllocationProperties{
  854. Namespace: "kube-system",
  855. Labels: map[string]string{
  856. "app": "foo",
  857. },
  858. },
  859. },
  860. filter: AllocationFilterOr{[]AllocationFilter{
  861. AllocationFilterCondition{
  862. Field: FilterLabel,
  863. Op: FilterEquals,
  864. Key: "app",
  865. Value: "foo",
  866. },
  867. AllocationFilterCondition{
  868. Field: FilterNamespace,
  869. Op: FilterEquals,
  870. Value: "kubecost",
  871. },
  872. }},
  873. expected: true,
  874. },
  875. {
  876. name: `label[app]="foo" or namespace="kubecost" -> second true`,
  877. a: &Allocation{
  878. Properties: &AllocationProperties{
  879. Namespace: "kubecost",
  880. Labels: map[string]string{
  881. "app": "bar",
  882. },
  883. },
  884. },
  885. filter: AllocationFilterOr{[]AllocationFilter{
  886. AllocationFilterCondition{
  887. Field: FilterLabel,
  888. Op: FilterEquals,
  889. Key: "app",
  890. Value: "foo",
  891. },
  892. AllocationFilterCondition{
  893. Field: FilterNamespace,
  894. Op: FilterEquals,
  895. Value: "kubecost",
  896. },
  897. }},
  898. expected: true,
  899. },
  900. {
  901. name: `label[app]="foo" or namespace="kubecost" -> both false`,
  902. a: &Allocation{
  903. Properties: &AllocationProperties{
  904. Namespace: "kube-system",
  905. Labels: map[string]string{
  906. "app": "bar",
  907. },
  908. },
  909. },
  910. filter: AllocationFilterOr{[]AllocationFilter{
  911. AllocationFilterCondition{
  912. Field: FilterLabel,
  913. Op: FilterEquals,
  914. Key: "app",
  915. Value: "foo",
  916. },
  917. AllocationFilterCondition{
  918. Field: FilterNamespace,
  919. Op: FilterEquals,
  920. Value: "kubecost",
  921. },
  922. }},
  923. expected: false,
  924. },
  925. }
  926. for _, c := range cases {
  927. result := c.filter.Matches(c.a)
  928. if result != c.expected {
  929. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  930. }
  931. }
  932. }
  933. func Test_AllocationFilter_Flattened(t *testing.T) {
  934. cases := []struct {
  935. name string
  936. input AllocationFilter
  937. expected AllocationFilter
  938. }{
  939. {
  940. name: "AllocationFilterCondition",
  941. input: AllocationFilterCondition{
  942. Field: FilterNamespace,
  943. Op: FilterEquals,
  944. },
  945. expected: AllocationFilterCondition{
  946. Field: FilterNamespace,
  947. Op: FilterEquals,
  948. },
  949. },
  950. {
  951. name: "empty AllocationFilterAnd (nil)",
  952. input: AllocationFilterAnd{},
  953. expected: nil,
  954. },
  955. {
  956. name: "empty AllocationFilterAnd (len 0)",
  957. input: AllocationFilterAnd{Filters: []AllocationFilter{}},
  958. expected: nil,
  959. },
  960. {
  961. name: "empty AllocationFilterOr (nil)",
  962. input: AllocationFilterOr{},
  963. expected: nil,
  964. },
  965. {
  966. name: "empty AllocationFilterOr (len 0)",
  967. input: AllocationFilterOr{Filters: []AllocationFilter{}},
  968. expected: nil,
  969. },
  970. {
  971. name: "single-element AllocationFilterAnd",
  972. input: AllocationFilterAnd{Filters: []AllocationFilter{
  973. AllocationFilterCondition{
  974. Field: FilterNamespace,
  975. Op: FilterEquals,
  976. },
  977. }},
  978. expected: AllocationFilterCondition{
  979. Field: FilterNamespace,
  980. Op: FilterEquals,
  981. },
  982. },
  983. {
  984. name: "single-element AllocationFilterOr",
  985. input: AllocationFilterOr{Filters: []AllocationFilter{
  986. AllocationFilterCondition{
  987. Field: FilterNamespace,
  988. Op: FilterEquals,
  989. },
  990. }},
  991. expected: AllocationFilterCondition{
  992. Field: FilterNamespace,
  993. Op: FilterEquals,
  994. },
  995. },
  996. {
  997. name: "multi-element AllocationFilterAnd",
  998. input: AllocationFilterAnd{Filters: []AllocationFilter{
  999. AllocationFilterCondition{
  1000. Field: FilterNamespace,
  1001. Op: FilterEquals,
  1002. },
  1003. AllocationFilterCondition{
  1004. Field: FilterClusterID,
  1005. Op: FilterNotEquals,
  1006. },
  1007. AllocationFilterCondition{
  1008. Field: FilterServices,
  1009. Op: FilterContains,
  1010. },
  1011. }},
  1012. expected: AllocationFilterAnd{Filters: []AllocationFilter{
  1013. AllocationFilterCondition{
  1014. Field: FilterNamespace,
  1015. Op: FilterEquals,
  1016. },
  1017. AllocationFilterCondition{
  1018. Field: FilterClusterID,
  1019. Op: FilterNotEquals,
  1020. },
  1021. AllocationFilterCondition{
  1022. Field: FilterServices,
  1023. Op: FilterContains,
  1024. },
  1025. }},
  1026. },
  1027. {
  1028. name: "multi-element AllocationFilterOr",
  1029. input: AllocationFilterOr{Filters: []AllocationFilter{
  1030. AllocationFilterCondition{
  1031. Field: FilterNamespace,
  1032. Op: FilterEquals,
  1033. },
  1034. AllocationFilterCondition{
  1035. Field: FilterClusterID,
  1036. Op: FilterNotEquals,
  1037. },
  1038. AllocationFilterCondition{
  1039. Field: FilterServices,
  1040. Op: FilterContains,
  1041. },
  1042. }},
  1043. expected: AllocationFilterOr{Filters: []AllocationFilter{
  1044. AllocationFilterCondition{
  1045. Field: FilterNamespace,
  1046. Op: FilterEquals,
  1047. },
  1048. AllocationFilterCondition{
  1049. Field: FilterClusterID,
  1050. Op: FilterNotEquals,
  1051. },
  1052. AllocationFilterCondition{
  1053. Field: FilterServices,
  1054. Op: FilterContains,
  1055. },
  1056. }},
  1057. },
  1058. {
  1059. name: "AllocationFilterNone",
  1060. input: AllocationFilterNone{},
  1061. expected: AllocationFilterNone{},
  1062. },
  1063. }
  1064. for _, c := range cases {
  1065. t.Run(c.name, func(t *testing.T) {
  1066. result := c.input.Flattened()
  1067. if !reflect.DeepEqual(result, c.expected) {
  1068. t.Errorf("Expected: '%s'. Got '%s'.", c.expected, result)
  1069. }
  1070. })
  1071. }
  1072. }