k8sobjectmatcher_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package opencost
  2. import (
  3. "testing"
  4. "github.com/opencost/opencost/core/pkg/filter/ast"
  5. k8sobject "github.com/opencost/opencost/core/pkg/filter/k8sobject"
  6. appsv1 "k8s.io/api/apps/v1"
  7. batchv1 "k8s.io/api/batch/v1"
  8. corev1 "k8s.io/api/core/v1"
  9. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  10. "k8s.io/apimachinery/pkg/runtime"
  11. )
  12. func TestK8sObjectMatcher(t *testing.T) {
  13. cases := []struct {
  14. filter string
  15. o runtime.Object
  16. expected bool
  17. }{
  18. {
  19. filter: `namespace:"kubecost"`,
  20. o: &appsv1.Deployment{
  21. ObjectMeta: metav1.ObjectMeta{
  22. Namespace: "kubecost",
  23. },
  24. },
  25. expected: true,
  26. },
  27. {
  28. filter: `namespace:"kubecost"`,
  29. o: &appsv1.Deployment{
  30. ObjectMeta: metav1.ObjectMeta{
  31. Namespace: "kube-system",
  32. },
  33. },
  34. expected: false,
  35. },
  36. {
  37. filter: `pod:"foo"`,
  38. o: &corev1.Pod{
  39. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  40. },
  41. expected: true,
  42. },
  43. {
  44. filter: `pod:"foo"`,
  45. o: &corev1.Pod{
  46. ObjectMeta: metav1.ObjectMeta{Name: "bar"},
  47. },
  48. expected: false,
  49. },
  50. {
  51. filter: `pod:"foo"`,
  52. o: &appsv1.Deployment{
  53. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  54. },
  55. expected: false,
  56. },
  57. {
  58. filter: `controllerKind:"deployment"`,
  59. o: &appsv1.Deployment{},
  60. expected: true,
  61. },
  62. {
  63. filter: `controllerName:"foo"`,
  64. o: &appsv1.Deployment{
  65. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  66. },
  67. expected: true,
  68. },
  69. {
  70. filter: `controllerKind:"statefulset"`,
  71. o: &appsv1.StatefulSet{},
  72. expected: true,
  73. },
  74. {
  75. filter: `controllerName:"foo"`,
  76. o: &appsv1.StatefulSet{
  77. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  78. },
  79. expected: true,
  80. },
  81. {
  82. filter: `controllerKind:"daemonset"`,
  83. o: &appsv1.DaemonSet{},
  84. expected: true,
  85. },
  86. {
  87. filter: `controllerName:"foo"`,
  88. o: &appsv1.DaemonSet{
  89. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  90. },
  91. expected: true,
  92. },
  93. {
  94. filter: `controllerKind:"cronjob"`,
  95. o: &batchv1.CronJob{},
  96. expected: true,
  97. },
  98. {
  99. filter: `controllerName:"foo"`,
  100. o: &batchv1.CronJob{
  101. ObjectMeta: metav1.ObjectMeta{Name: "foo"},
  102. },
  103. expected: true,
  104. },
  105. {
  106. filter: `controllerKind:"pod"`,
  107. o: &corev1.Pod{},
  108. expected: true,
  109. },
  110. {
  111. filter: `controllerKind:"pod"`,
  112. o: &corev1.Pod{
  113. ObjectMeta: metav1.ObjectMeta{
  114. OwnerReferences: []metav1.OwnerReference{
  115. {}, // Having an owner reference makes this Pod "controlled"
  116. },
  117. },
  118. },
  119. expected: false,
  120. },
  121. {
  122. filter: `label[app]:"foo"`,
  123. o: &appsv1.Deployment{
  124. ObjectMeta: metav1.ObjectMeta{
  125. Labels: map[string]string{"app": "foo"},
  126. },
  127. },
  128. expected: true,
  129. },
  130. {
  131. filter: `label[app]:"foo"`,
  132. o: &appsv1.Deployment{
  133. ObjectMeta: metav1.ObjectMeta{
  134. Labels: map[string]string{"app": "bar"},
  135. },
  136. },
  137. expected: false,
  138. },
  139. {
  140. filter: `label[app]:"foo"`,
  141. o: &appsv1.Deployment{
  142. ObjectMeta: metav1.ObjectMeta{
  143. Labels: map[string]string{},
  144. },
  145. },
  146. expected: false,
  147. },
  148. {
  149. filter: `label[app]:"foo"`,
  150. o: &appsv1.Deployment{
  151. ObjectMeta: metav1.ObjectMeta{
  152. Labels: nil,
  153. },
  154. },
  155. expected: false,
  156. },
  157. {
  158. filter: `annotation[app]:"foo"`,
  159. o: &appsv1.Deployment{
  160. ObjectMeta: metav1.ObjectMeta{
  161. Annotations: map[string]string{"app": "foo"},
  162. },
  163. },
  164. expected: true,
  165. },
  166. {
  167. filter: `annotation[app]:"foo"`,
  168. o: &appsv1.Deployment{
  169. ObjectMeta: metav1.ObjectMeta{
  170. Annotations: map[string]string{"app": "bar"},
  171. },
  172. },
  173. expected: false,
  174. },
  175. {
  176. filter: `annotation[app]:"foo"`,
  177. o: &appsv1.Deployment{
  178. ObjectMeta: metav1.ObjectMeta{
  179. Annotations: map[string]string{},
  180. },
  181. },
  182. expected: false,
  183. },
  184. {
  185. filter: `annotation[app]:"foo"`,
  186. o: &appsv1.Deployment{
  187. ObjectMeta: metav1.ObjectMeta{
  188. Annotations: nil,
  189. },
  190. },
  191. expected: false,
  192. },
  193. }
  194. for _, c := range cases {
  195. t.Run(c.filter, func(t *testing.T) {
  196. parser := k8sobject.NewK8sObjectFilterParser()
  197. parsed, err := parser.Parse(c.filter)
  198. if err != nil {
  199. t.Fatalf("parsing '%s': %s", c.filter, err)
  200. }
  201. t.Logf("Parsed: %s", ast.ToPreOrderString(parsed))
  202. compiler := NewK8sObjectMatchCompiler()
  203. matcher, err := compiler.Compile(parsed)
  204. if err != nil {
  205. t.Fatalf("compiling: %s", err)
  206. }
  207. t.Logf("Compiled: %s", matcher.String())
  208. result := matcher.Matches(c.o)
  209. if result != c.expected {
  210. t.Errorf("Expected %t, got %t", c.expected, result)
  211. }
  212. })
  213. }
  214. }