allocationfilter_test.go 15 KB

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