1
2
3
4 package framework_test
5
6 import (
7 "bytes"
8 "strings"
9 "testing"
10
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13 "sigs.k8s.io/kustomize/kyaml/yaml"
14
15 "sigs.k8s.io/kustomize/kyaml/fn/framework"
16 "sigs.k8s.io/kustomize/kyaml/kio"
17 )
18
19 func TestSelector(t *testing.T) {
20 type Test struct {
21
22 Name string
23
24
25 Fn func(*framework.Selector)
26
27
28 ValueFoo string
29
30
31 ValueBar string
32
33
34
35 Value string
36 }
37 tests := []Test{
38
39 {
40 Name: "names",
41 Fn: func(s *framework.Selector) {
42 s.Names = []string{"{{ .Value }}"}
43 },
44 ValueFoo: "foo",
45 ValueBar: "bar",
46 },
47
48
49 {
50 Name: "kinds",
51 Fn: func(s *framework.Selector) {
52 s.Kinds = []string{"{{ .Value }}"}
53 },
54 ValueFoo: "StatefulSet",
55 ValueBar: "Deployment",
56 },
57
58
59 {
60 Name: "apiVersion",
61 Fn: func(s *framework.Selector) {
62 s.APIVersions = []string{"{{ .Value }}"}
63 },
64 ValueFoo: "apps/v1beta1",
65 ValueBar: "apps/v1",
66 },
67
68
69 {
70 Name: "namespaces",
71 Fn: func(s *framework.Selector) {
72 s.Namespaces = []string{"{{ .Value }}"}
73 },
74 ValueFoo: "foo-default",
75 ValueBar: "bar-default",
76 },
77
78
79 {
80 Name: "annotations",
81 Fn: func(s *framework.Selector) {
82 s.Annotations = map[string]string{"key": "{{ .Value }}"}
83 },
84 ValueFoo: "foo-a",
85 ValueBar: "bar-a",
86 },
87
88
89 {
90 Name: "labels",
91 Fn: func(s *framework.Selector) {
92 s.Labels = map[string]string{"key": "{{ .Value }}"}
93 },
94 ValueFoo: "foo-l",
95 ValueBar: "bar-l",
96 },
97 }
98
99
100 input := `
101 apiVersion: apps/v1beta1
102 kind: StatefulSet
103 metadata:
104 name: foo
105 namespace: foo-default
106 annotations:
107 key: foo-a
108 labels:
109 key: foo-l
110 ---
111 apiVersion: apps/v1
112 kind: Deployment
113 metadata:
114 name: bar
115 namespace: bar-default
116 annotations:
117 key: bar-a
118 labels:
119 key: bar-l
120 `
121
122 expectedFoo := `
123 apiVersion: apps/v1beta1
124 kind: StatefulSet
125 metadata:
126 name: foo
127 namespace: foo-default
128 annotations:
129 key: foo-a
130 config.kubernetes.io/index: '0'
131 internal.config.kubernetes.io/index: '0'
132 labels:
133 key: foo-l
134 `
135
136 expectedBar := `
137 apiVersion: apps/v1
138 kind: Deployment
139 metadata:
140 name: bar
141 namespace: bar-default
142 annotations:
143 key: bar-a
144 config.kubernetes.io/index: '1'
145 internal.config.kubernetes.io/index: '1'
146 labels:
147 key: bar-l
148 `
149
150
151 var err error
152 for i := range tests {
153 test := tests[i]
154 t.Run(tests[i].Name+"-foo", func(t *testing.T) {
155 test.Value = test.ValueFoo
156 var out bytes.Buffer
157 rw := &kio.ByteReadWriter{
158 Reader: bytes.NewBufferString(input),
159 Writer: &out,
160 KeepReaderAnnotations: true,
161 }
162 p := func(rl *framework.ResourceList) error {
163 s := &framework.Selector{TemplateData: test}
164 test.Fn(s)
165 rl.Items, err = s.Filter(rl.Items)
166 return err
167 }
168
169 require.NoError(t, framework.Execute(framework.ResourceListProcessorFunc(p), rw))
170 require.Equal(t, strings.TrimSpace(expectedFoo), strings.TrimSpace(out.String()))
171 })
172 }
173
174
175 for i := range tests {
176 test := tests[i]
177 t.Run(tests[i].Name+"-bar", func(t *testing.T) {
178 test.Value = test.ValueBar
179 var out bytes.Buffer
180 rw := &kio.ByteReadWriter{
181 Reader: bytes.NewBufferString(input),
182 Writer: &out,
183 KeepReaderAnnotations: true,
184 }
185
186 p := func(rl *framework.ResourceList) error {
187 s := &framework.Selector{TemplateData: test}
188 test.Fn(s)
189 rl.Items, err = s.Filter(rl.Items)
190 return err
191 }
192 require.NoError(t, framework.Execute(framework.ResourceListProcessorFunc(p), rw))
193 require.Equal(t, strings.TrimSpace(expectedBar), strings.TrimSpace(out.String()))
194 })
195 }
196 }
197
198 func TestAndOrSelector_Composition(t *testing.T) {
199
200
201 s := framework.MatchAny(
202 framework.MatchAll(
203 framework.GVKMatcher("apps/v1/Deployment"),
204 framework.NameMatcher("prime-target"),
205 ),
206 framework.MatchAny(
207 framework.LabelMatcher(map[string]string{
208 "select": "yes",
209 }),
210 framework.AnnotationMatcher(map[string]string{
211 "example.io/select": "yes",
212 }),
213 ),
214 )
215
216 input, err := kio.FromBytes([]byte(`
217 apiVersion: apps/v1
218 kind: Deployment
219 metadata:
220 name: prime-target
221 ---
222 apiVersion: apps/v1
223 kind: Deployment
224 metadata:
225 name: exclude-one
226 ---
227 apiVersion: apps/v1
228 kind: Deployment
229 metadata:
230 name: exclude-two
231 labels:
232 select: no
233 ---
234 apiVersion: v1
235 kind: ConfigMap
236 metadata:
237 name: extra-target
238 labels:
239 select: yes
240 ---
241 apiVersion: apps/v1
242 kind: ConfigMap
243 metadata:
244 name: prime-target
245 data:
246 shouldSelect: false
247 ---
248 apiVersion: apps/v1
249 kind: Deployment
250 metadata:
251 name: extra-target-one
252 labels:
253 select: yes
254 ---
255 apiVersion: apps/v1
256 kind: Deployment
257 metadata:
258 name: extra-target-two
259 annotations:
260 example.io/select: yes
261 `))
262 require.NoError(t, err)
263 result, err := s.Filter(input)
264 require.NoError(t, err)
265
266 expected := `
267 apiVersion: apps/v1
268 kind: Deployment
269 metadata:
270 name: prime-target
271 ---
272 apiVersion: v1
273 kind: ConfigMap
274 metadata:
275 name: extra-target
276 labels:
277 select: yes
278 ---
279 apiVersion: apps/v1
280 kind: Deployment
281 metadata:
282 name: extra-target-one
283 labels:
284 select: yes
285 ---
286 apiVersion: apps/v1
287 kind: Deployment
288 metadata:
289 name: extra-target-two
290 annotations:
291 example.io/select: yes
292 `
293 resultStr, err := kio.StringAll(result)
294 require.NoError(t, err)
295 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(resultStr))
296 }
297
298 func TestAndOrSelector_CompositionTemplated(t *testing.T) {
299
300
301
302 type templateStruct struct {
303 GVK string
304 Name string
305 LabelValue string
306 AnnotationValue string
307 }
308
309 s := framework.OrSelector{
310
311 TemplateData: &templateStruct{
312 GVK: "apps/v1/Oops",
313 Name: "extra-target",
314 LabelValue: "yes",
315 AnnotationValue: "yes",
316 },
317 Matchers: []framework.ResourceMatcher{
318 &framework.AndSelector{
319 TemplateData: &templateStruct{
320 GVK: "apps/v1/Deployment",
321 Name: "prime-target",
322 },
323 Matchers: []framework.ResourceMatcher{
324 framework.GVKMatcher("{{.GVK}}"),
325 framework.NameMatcher("{{.Name}}"),
326 },
327 },
328 &framework.OrSelector{
329 Matchers: []framework.ResourceMatcher{
330 framework.LabelMatcher(map[string]string{
331 "select": "{{.LabelValue}}",
332 }),
333 framework.AnnotationMatcher(map[string]string{
334 "example.io/select": "{{.AnnotationValue}}",
335 }),
336 },
337 },
338 },
339 }
340
341 input, err := kio.FromBytes([]byte(`
342 apiVersion: apps/v1
343 kind: Deployment
344 metadata:
345 name: prime-target
346 ---
347 apiVersion: apps/v1
348 kind: Deployment
349 metadata:
350 name: exclude-one
351 ---
352 apiVersion: apps/v1
353 kind: Deployment
354 metadata:
355 name: exclude-two
356 labels:
357 select: no
358 ---
359 apiVersion: v1
360 kind: ConfigMap
361 metadata:
362 name: extra-target
363 labels:
364 select: yes
365 ---
366 apiVersion: apps/v1
367 kind: ConfigMap
368 metadata:
369 name: prime-target
370 data:
371 shouldSelect: false
372 ---
373 apiVersion: apps/v1
374 kind: Deployment
375 metadata:
376 name: extra-target-one
377 labels:
378 select: yes
379 ---
380 apiVersion: apps/v1
381 kind: Deployment
382 metadata:
383 name: extra-target-two
384 annotations:
385 example.io/select: yes
386 `))
387 require.NoError(t, err)
388 result, err := s.Filter(input)
389 require.NoError(t, err)
390
391 expected := `
392 apiVersion: apps/v1
393 kind: Deployment
394 metadata:
395 name: prime-target
396 ---
397 apiVersion: v1
398 kind: ConfigMap
399 metadata:
400 name: extra-target
401 labels:
402 select: yes
403 ---
404 apiVersion: apps/v1
405 kind: Deployment
406 metadata:
407 name: extra-target-one
408 labels:
409 select: yes
410 ---
411 apiVersion: apps/v1
412 kind: Deployment
413 metadata:
414 name: extra-target-two
415 annotations:
416 example.io/select: yes
417 `
418 resultStr, err := kio.StringAll(result)
419 require.NoError(t, err)
420 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(resultStr))
421 }
422
423 func TestMatchersAsFilters(t *testing.T) {
424 input, err := kio.FromBytes([]byte(`
425 apiVersion: apps/v1
426 kind: Deployment
427 metadata:
428 name: target
429 labels:
430 select: me
431 ---
432 apiVersion: extensions/v1beta2
433 kind: Deployment
434 metadata:
435 name: exclude
436 labels:
437 select: no
438 `))
439 require.NoError(t, err)
440
441 expected := `
442 apiVersion: apps/v1
443 kind: Deployment
444 metadata:
445 name: target
446 labels:
447 select: me
448 `
449 matchers := map[string]framework.ResourceMatcher{
450 "slice": framework.NameMatcher("target"),
451 "map": framework.LabelMatcher(map[string]string{"select": "me"}),
452 "func": framework.ResourceMatcherFunc(func(node *yaml.RNode) bool {
453 v := node.Field("apiVersion").Value
454 return strings.TrimSpace(v.MustString()) == "apps/v1"
455 }),
456 }
457 for desc, m := range matchers {
458 matcher := m
459 t.Run(desc, func(t *testing.T) {
460 result, err := matcher.Filter(input)
461 require.NoError(t, err)
462 resultStr, err := kio.StringAll(result)
463 require.NoError(t, err)
464 assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(resultStr))
465 })
466 }
467 }
468
View as plain text