allocationfilter_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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 containsprefix -> true`,
  360. a: &Allocation{
  361. Properties: &AllocationProperties{
  362. Services: []string{"serv1", "serv2"},
  363. },
  364. },
  365. filter: AllocationFilterCondition{
  366. Field: FilterServices,
  367. Op: FilterContainsPrefix,
  368. Value: "serv",
  369. },
  370. expected: true,
  371. },
  372. {
  373. name: `services containsprefix -> false`,
  374. a: &Allocation{
  375. Properties: &AllocationProperties{
  376. Services: []string{"foo", "bar"},
  377. },
  378. },
  379. filter: AllocationFilterCondition{
  380. Field: FilterServices,
  381. Op: FilterContainsPrefix,
  382. Value: "serv",
  383. },
  384. expected: false,
  385. },
  386. {
  387. name: `services contains unallocated -> false`,
  388. a: &Allocation{
  389. Properties: &AllocationProperties{
  390. Services: []string{"serv1", "serv2"},
  391. },
  392. },
  393. filter: AllocationFilterCondition{
  394. Field: FilterServices,
  395. Op: FilterContains,
  396. Value: UnallocatedSuffix,
  397. },
  398. expected: false,
  399. },
  400. {
  401. name: `services contains unallocated -> true`,
  402. a: &Allocation{
  403. Properties: &AllocationProperties{
  404. Services: []string{},
  405. },
  406. },
  407. filter: AllocationFilterCondition{
  408. Field: FilterServices,
  409. Op: FilterContains,
  410. Value: UnallocatedSuffix,
  411. },
  412. expected: true,
  413. },
  414. }
  415. for _, c := range cases {
  416. result := c.filter.Matches(c.a)
  417. if result != c.expected {
  418. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  419. }
  420. }
  421. }
  422. func Test_AllocationFilterAnd_Matches(t *testing.T) {
  423. cases := []struct {
  424. name string
  425. a *Allocation
  426. filter AllocationFilter
  427. expected bool
  428. }{
  429. {
  430. name: `label[app]="foo" and namespace="kubecost" -> both true`,
  431. a: &Allocation{
  432. Properties: &AllocationProperties{
  433. Namespace: "kubecost",
  434. Labels: map[string]string{
  435. "app": "foo",
  436. },
  437. },
  438. },
  439. filter: AllocationFilterAnd{[]AllocationFilter{
  440. AllocationFilterCondition{
  441. Field: FilterLabel,
  442. Op: FilterEquals,
  443. Key: "app",
  444. Value: "foo",
  445. },
  446. AllocationFilterCondition{
  447. Field: FilterNamespace,
  448. Op: FilterEquals,
  449. Value: "kubecost",
  450. },
  451. }},
  452. expected: true,
  453. },
  454. {
  455. name: `label[app]="foo" and namespace="kubecost" -> first true`,
  456. a: &Allocation{
  457. Properties: &AllocationProperties{
  458. Namespace: "kube-system",
  459. Labels: map[string]string{
  460. "app": "foo",
  461. },
  462. },
  463. },
  464. filter: AllocationFilterAnd{[]AllocationFilter{
  465. AllocationFilterCondition{
  466. Field: FilterLabel,
  467. Op: FilterEquals,
  468. Key: "app",
  469. Value: "foo",
  470. },
  471. AllocationFilterCondition{
  472. Field: FilterNamespace,
  473. Op: FilterEquals,
  474. Value: "kubecost",
  475. },
  476. }},
  477. expected: false,
  478. },
  479. {
  480. name: `label[app]="foo" and namespace="kubecost" -> second true`,
  481. a: &Allocation{
  482. Properties: &AllocationProperties{
  483. Namespace: "kubecost",
  484. Labels: map[string]string{
  485. "app": "bar",
  486. },
  487. },
  488. },
  489. filter: AllocationFilterAnd{[]AllocationFilter{
  490. AllocationFilterCondition{
  491. Field: FilterLabel,
  492. Op: FilterEquals,
  493. Key: "app",
  494. Value: "foo",
  495. },
  496. AllocationFilterCondition{
  497. Field: FilterNamespace,
  498. Op: FilterEquals,
  499. Value: "kubecost",
  500. },
  501. }},
  502. expected: false,
  503. },
  504. {
  505. name: `label[app]="foo" and namespace="kubecost" -> both false`,
  506. a: &Allocation{
  507. Properties: &AllocationProperties{
  508. Namespace: "kube-system",
  509. Labels: map[string]string{
  510. "app": "bar",
  511. },
  512. },
  513. },
  514. filter: AllocationFilterAnd{[]AllocationFilter{
  515. AllocationFilterCondition{
  516. Field: FilterLabel,
  517. Op: FilterEquals,
  518. Key: "app",
  519. Value: "foo",
  520. },
  521. AllocationFilterCondition{
  522. Field: FilterNamespace,
  523. Op: FilterEquals,
  524. Value: "kubecost",
  525. },
  526. }},
  527. expected: false,
  528. },
  529. }
  530. for _, c := range cases {
  531. result := c.filter.Matches(c.a)
  532. if result != c.expected {
  533. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  534. }
  535. }
  536. }
  537. func Test_AllocationFilterOr_Matches(t *testing.T) {
  538. cases := []struct {
  539. name string
  540. a *Allocation
  541. filter AllocationFilter
  542. expected bool
  543. }{
  544. {
  545. name: `label[app]="foo" or namespace="kubecost" -> both true`,
  546. a: &Allocation{
  547. Properties: &AllocationProperties{
  548. Namespace: "kubecost",
  549. Labels: map[string]string{
  550. "app": "foo",
  551. },
  552. },
  553. },
  554. filter: AllocationFilterOr{[]AllocationFilter{
  555. AllocationFilterCondition{
  556. Field: FilterLabel,
  557. Op: FilterEquals,
  558. Key: "app",
  559. Value: "foo",
  560. },
  561. AllocationFilterCondition{
  562. Field: FilterNamespace,
  563. Op: FilterEquals,
  564. Value: "kubecost",
  565. },
  566. }},
  567. expected: true,
  568. },
  569. {
  570. name: `label[app]="foo" or namespace="kubecost" -> first true`,
  571. a: &Allocation{
  572. Properties: &AllocationProperties{
  573. Namespace: "kube-system",
  574. Labels: map[string]string{
  575. "app": "foo",
  576. },
  577. },
  578. },
  579. filter: AllocationFilterOr{[]AllocationFilter{
  580. AllocationFilterCondition{
  581. Field: FilterLabel,
  582. Op: FilterEquals,
  583. Key: "app",
  584. Value: "foo",
  585. },
  586. AllocationFilterCondition{
  587. Field: FilterNamespace,
  588. Op: FilterEquals,
  589. Value: "kubecost",
  590. },
  591. }},
  592. expected: true,
  593. },
  594. {
  595. name: `label[app]="foo" or namespace="kubecost" -> second true`,
  596. a: &Allocation{
  597. Properties: &AllocationProperties{
  598. Namespace: "kubecost",
  599. Labels: map[string]string{
  600. "app": "bar",
  601. },
  602. },
  603. },
  604. filter: AllocationFilterOr{[]AllocationFilter{
  605. AllocationFilterCondition{
  606. Field: FilterLabel,
  607. Op: FilterEquals,
  608. Key: "app",
  609. Value: "foo",
  610. },
  611. AllocationFilterCondition{
  612. Field: FilterNamespace,
  613. Op: FilterEquals,
  614. Value: "kubecost",
  615. },
  616. }},
  617. expected: true,
  618. },
  619. {
  620. name: `label[app]="foo" or namespace="kubecost" -> both false`,
  621. a: &Allocation{
  622. Properties: &AllocationProperties{
  623. Namespace: "kube-system",
  624. Labels: map[string]string{
  625. "app": "bar",
  626. },
  627. },
  628. },
  629. filter: AllocationFilterOr{[]AllocationFilter{
  630. AllocationFilterCondition{
  631. Field: FilterLabel,
  632. Op: FilterEquals,
  633. Key: "app",
  634. Value: "foo",
  635. },
  636. AllocationFilterCondition{
  637. Field: FilterNamespace,
  638. Op: FilterEquals,
  639. Value: "kubecost",
  640. },
  641. }},
  642. expected: false,
  643. },
  644. }
  645. for _, c := range cases {
  646. result := c.filter.Matches(c.a)
  647. if result != c.expected {
  648. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  649. }
  650. }
  651. }