1
16
17 package create
18
19 import (
20 "testing"
21
22 networkingv1 "k8s.io/api/networking/v1"
23 apiequality "k8s.io/apimachinery/pkg/api/equality"
24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 )
26
27 func TestCreateIngressValidation(t *testing.T) {
28 tests := map[string]struct {
29 defaultbackend string
30 ingressclass string
31 rules []string
32 annotations []string
33 expected string
34 }{
35 "no default backend and rule": {
36 defaultbackend: "",
37 rules: []string{},
38 expected: "not enough information provided: every ingress has to either specify a default-backend (which catches all traffic) or a list of rules (which catch specific paths)",
39 },
40 "invalid default backend separator": {
41 defaultbackend: "xpto,4444",
42 expected: "default-backend should be in format servicename:serviceport",
43 },
44 "default backend without port": {
45 defaultbackend: "xpto",
46 expected: "default-backend should be in format servicename:serviceport",
47 },
48 "default backend is ok": {
49 defaultbackend: "xpto:4444",
50 expected: "",
51 },
52 "invalid annotation": {
53 defaultbackend: "xpto:4444",
54 annotations: []string{
55 "key1=value1",
56 "key2",
57 },
58 expected: "annotation key2 is invalid and should be in format key=[value]",
59 },
60 "valid annotations": {
61 defaultbackend: "xpto:4444",
62 annotations: []string{
63 "key1=value1",
64 "key2=",
65 },
66 expected: "",
67 },
68 "multiple conformant rules": {
69 rules: []string{
70 "foo.com/path*=svc:8080",
71 "bar.com/admin*=svc2:http",
72 },
73 expected: "",
74 },
75 "one invalid and two valid rules": {
76 rules: []string{
77 "foo.com=svc:redis,tls",
78 "foo.com/path/subpath*=othersvc:8080",
79 "foo.com/*=svc:8080,tls=secret1",
80 },
81 expected: "rule foo.com=svc:redis,tls is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
82 },
83 "service without port": {
84 rules: []string{
85 "foo.com/=svc,tls",
86 },
87 expected: "rule foo.com/=svc,tls is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
88 },
89 "valid tls rule without secret": {
90 rules: []string{
91 "foo.com/=svc:http,tls=",
92 },
93 expected: "",
94 },
95 "valid tls rule with secret": {
96 rules: []string{
97 "foo.com/=svc:http,tls=secret123",
98 },
99 expected: "",
100 },
101 "valid path with type prefix": {
102 rules: []string{
103 "foo.com/admin*=svc:8080",
104 },
105 expected: "",
106 },
107 "wildcard host": {
108 rules: []string{
109 "*.foo.com/admin*=svc:8080",
110 },
111 expected: "",
112 },
113 "invalid separation between ingress and service": {
114 rules: []string{
115 "*.foo.com/path,svc:8080",
116 },
117 expected: "rule *.foo.com/path,svc:8080 is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
118 },
119 "two invalid and one valid rule": {
120 rules: []string{
121 "foo.com/path/subpath*=svc:redis,tls=blo",
122 "foo.com=othersvc:8080",
123 "foo.com/admin=svc,tls=secret1",
124 },
125 expected: "rule foo.com=othersvc:8080 is invalid and should be in format host/path=svcname:svcport[,tls[=secret]]",
126 },
127 "valid catch all rule": {
128 rules: []string{
129 "/path/subpath*=svc:redis,tls=blo",
130 },
131 expected: "",
132 },
133 }
134
135 for name, tc := range tests {
136 t.Run(name, func(t *testing.T) {
137 o := &CreateIngressOptions{
138 DefaultBackend: tc.defaultbackend,
139 Rules: tc.rules,
140 IngressClass: tc.ingressclass,
141 Annotations: tc.annotations,
142 }
143
144 err := o.Validate()
145 if err != nil && err.Error() != tc.expected {
146 t.Errorf("unexpected error: %v", err)
147 }
148 if tc.expected != "" && err == nil {
149 t.Errorf("expected error, got no error")
150 }
151
152 })
153 }
154 }
155
156 func TestCreateIngress(t *testing.T) {
157 ingressName := "test-ingress"
158 ingressClass := "nginx"
159 pathTypeExact := networkingv1.PathTypeExact
160 pathTypePrefix := networkingv1.PathTypePrefix
161 tests := map[string]struct {
162 defaultbackend string
163 rules []string
164 ingressclass string
165 annotations []string
166 expected *networkingv1.Ingress
167 }{
168 "catch all host and default backend with default TLS returns empty TLS": {
169 rules: []string{
170 "/=catchall:8080,tls=",
171 },
172 ingressclass: ingressClass,
173 defaultbackend: "service1:https",
174 annotations: []string{},
175 expected: &networkingv1.Ingress{
176 TypeMeta: metav1.TypeMeta{
177 APIVersion: networkingv1.SchemeGroupVersion.String(),
178 Kind: "Ingress",
179 },
180 ObjectMeta: metav1.ObjectMeta{
181 Name: ingressName,
182 Annotations: map[string]string{},
183 },
184 Spec: networkingv1.IngressSpec{
185 IngressClassName: &ingressClass,
186 DefaultBackend: &networkingv1.IngressBackend{
187 Service: &networkingv1.IngressServiceBackend{
188 Name: "service1",
189 Port: networkingv1.ServiceBackendPort{
190 Name: "https",
191 },
192 },
193 },
194 TLS: []networkingv1.IngressTLS{},
195 Rules: []networkingv1.IngressRule{
196 {
197 Host: "",
198 IngressRuleValue: networkingv1.IngressRuleValue{
199 HTTP: &networkingv1.HTTPIngressRuleValue{
200 Paths: []networkingv1.HTTPIngressPath{
201 {
202 Path: "/",
203 PathType: &pathTypeExact,
204 Backend: networkingv1.IngressBackend{
205 Service: &networkingv1.IngressServiceBackend{
206 Name: "catchall",
207 Port: networkingv1.ServiceBackendPort{
208 Number: 8080,
209 },
210 },
211 },
212 },
213 },
214 },
215 },
216 },
217 },
218 },
219 },
220 },
221 "catch all with path of type prefix and secret name": {
222 rules: []string{
223 "/path*=catchall:8080,tls=secret1",
224 },
225 ingressclass: ingressClass,
226 defaultbackend: "service1:https",
227 annotations: []string{},
228 expected: &networkingv1.Ingress{
229 TypeMeta: metav1.TypeMeta{
230 APIVersion: networkingv1.SchemeGroupVersion.String(),
231 Kind: "Ingress",
232 },
233 ObjectMeta: metav1.ObjectMeta{
234 Name: ingressName,
235 Annotations: map[string]string{},
236 },
237 Spec: networkingv1.IngressSpec{
238 IngressClassName: &ingressClass,
239 DefaultBackend: &networkingv1.IngressBackend{
240 Service: &networkingv1.IngressServiceBackend{
241 Name: "service1",
242 Port: networkingv1.ServiceBackendPort{
243 Name: "https",
244 },
245 },
246 },
247 TLS: []networkingv1.IngressTLS{
248 {
249 SecretName: "secret1",
250 },
251 },
252 Rules: []networkingv1.IngressRule{
253 {
254 Host: "",
255 IngressRuleValue: networkingv1.IngressRuleValue{
256 HTTP: &networkingv1.HTTPIngressRuleValue{
257 Paths: []networkingv1.HTTPIngressPath{
258 {
259 Path: "/path",
260 PathType: &pathTypePrefix,
261 Backend: networkingv1.IngressBackend{
262 Service: &networkingv1.IngressServiceBackend{
263 Name: "catchall",
264 Port: networkingv1.ServiceBackendPort{
265 Number: 8080,
266 },
267 },
268 },
269 },
270 },
271 },
272 },
273 },
274 },
275 },
276 },
277 },
278 "mixed hosts with mixed TLS configuration and a default backend": {
279 rules: []string{
280 "foo.com/=foo-svc:8080,tls=",
281 "foo.com/admin=foo-admin-svc:http,tls=",
282 "bar.com/prefix*=bar-svc:8080,tls=bar-secret",
283 "bar.com/noprefix=barnp-svc:8443,tls",
284 "foobar.com/*=foobar-svc:https",
285 "foobar1.com/*=foobar1-svc:https,tls=bar-secret",
286 },
287 defaultbackend: "service2:8080",
288 annotations: []string{},
289 expected: &networkingv1.Ingress{
290 TypeMeta: metav1.TypeMeta{
291 APIVersion: networkingv1.SchemeGroupVersion.String(),
292 Kind: "Ingress",
293 },
294 ObjectMeta: metav1.ObjectMeta{
295 Name: ingressName,
296 Annotations: map[string]string{},
297 },
298 Spec: networkingv1.IngressSpec{
299 DefaultBackend: &networkingv1.IngressBackend{
300 Service: &networkingv1.IngressServiceBackend{
301 Name: "service2",
302 Port: networkingv1.ServiceBackendPort{
303 Number: 8080,
304 },
305 },
306 },
307 TLS: []networkingv1.IngressTLS{
308 {
309 Hosts: []string{
310 "foo.com",
311 },
312 },
313 {
314 Hosts: []string{
315 "bar.com",
316 "foobar1.com",
317 },
318 SecretName: "bar-secret",
319 },
320 },
321 Rules: []networkingv1.IngressRule{
322 {
323 Host: "foo.com",
324 IngressRuleValue: networkingv1.IngressRuleValue{
325 HTTP: &networkingv1.HTTPIngressRuleValue{
326 Paths: []networkingv1.HTTPIngressPath{
327 {
328 Path: "/",
329 PathType: &pathTypeExact,
330 Backend: networkingv1.IngressBackend{
331 Service: &networkingv1.IngressServiceBackend{
332 Name: "foo-svc",
333 Port: networkingv1.ServiceBackendPort{
334 Number: 8080,
335 },
336 },
337 },
338 },
339 {
340 Path: "/admin",
341 PathType: &pathTypeExact,
342 Backend: networkingv1.IngressBackend{
343 Service: &networkingv1.IngressServiceBackend{
344 Name: "foo-admin-svc",
345 Port: networkingv1.ServiceBackendPort{
346 Name: "http",
347 },
348 },
349 },
350 },
351 },
352 },
353 },
354 },
355 {
356 Host: "bar.com",
357 IngressRuleValue: networkingv1.IngressRuleValue{
358 HTTP: &networkingv1.HTTPIngressRuleValue{
359 Paths: []networkingv1.HTTPIngressPath{
360 {
361 Path: "/prefix",
362 PathType: &pathTypePrefix,
363 Backend: networkingv1.IngressBackend{
364 Service: &networkingv1.IngressServiceBackend{
365 Name: "bar-svc",
366 Port: networkingv1.ServiceBackendPort{
367 Number: 8080,
368 },
369 },
370 },
371 },
372 {
373 Path: "/noprefix",
374 PathType: &pathTypeExact,
375 Backend: networkingv1.IngressBackend{
376 Service: &networkingv1.IngressServiceBackend{
377 Name: "barnp-svc",
378 Port: networkingv1.ServiceBackendPort{
379 Number: 8443,
380 },
381 },
382 },
383 },
384 },
385 },
386 },
387 },
388 {
389 Host: "foobar.com",
390 IngressRuleValue: networkingv1.IngressRuleValue{
391 HTTP: &networkingv1.HTTPIngressRuleValue{
392 Paths: []networkingv1.HTTPIngressPath{
393 {
394 Path: "/",
395 PathType: &pathTypePrefix,
396 Backend: networkingv1.IngressBackend{
397 Service: &networkingv1.IngressServiceBackend{
398 Name: "foobar-svc",
399 Port: networkingv1.ServiceBackendPort{
400 Name: "https",
401 },
402 },
403 },
404 },
405 },
406 },
407 },
408 },
409 {
410 Host: "foobar1.com",
411 IngressRuleValue: networkingv1.IngressRuleValue{
412 HTTP: &networkingv1.HTTPIngressRuleValue{
413 Paths: []networkingv1.HTTPIngressPath{
414 {
415 Path: "/",
416 PathType: &pathTypePrefix,
417 Backend: networkingv1.IngressBackend{
418 Service: &networkingv1.IngressServiceBackend{
419 Name: "foobar1-svc",
420 Port: networkingv1.ServiceBackendPort{
421 Name: "https",
422 },
423 },
424 },
425 },
426 },
427 },
428 },
429 },
430 },
431 },
432 },
433 },
434 "simple ingress with annotation": {
435 rules: []string{
436 "foo.com/=svc:8080,tls=secret1",
437 "foo.com/subpath*=othersvc:8080,tls=secret1",
438 },
439 annotations: []string{
440 "ingress.kubernetes.io/annotation1=bla",
441 "ingress.kubernetes.io/annotation2=blo",
442 "ingress.kubernetes.io/annotation3=ble",
443 },
444 expected: &networkingv1.Ingress{
445 TypeMeta: metav1.TypeMeta{
446 APIVersion: networkingv1.SchemeGroupVersion.String(),
447 Kind: "Ingress",
448 },
449 ObjectMeta: metav1.ObjectMeta{
450 Name: ingressName,
451 Annotations: map[string]string{
452 "ingress.kubernetes.io/annotation1": "bla",
453 "ingress.kubernetes.io/annotation3": "ble",
454 "ingress.kubernetes.io/annotation2": "blo",
455 },
456 },
457 Spec: networkingv1.IngressSpec{
458 TLS: []networkingv1.IngressTLS{
459 {
460 Hosts: []string{
461 "foo.com",
462 },
463 SecretName: "secret1",
464 },
465 },
466 Rules: []networkingv1.IngressRule{
467 {
468 Host: "foo.com",
469 IngressRuleValue: networkingv1.IngressRuleValue{
470 HTTP: &networkingv1.HTTPIngressRuleValue{
471 Paths: []networkingv1.HTTPIngressPath{
472 {
473 Path: "/",
474 PathType: &pathTypeExact,
475 Backend: networkingv1.IngressBackend{
476 Service: &networkingv1.IngressServiceBackend{
477 Name: "svc",
478 Port: networkingv1.ServiceBackendPort{
479 Number: 8080,
480 },
481 },
482 },
483 },
484 {
485 Path: "/subpath",
486 PathType: &pathTypePrefix,
487 Backend: networkingv1.IngressBackend{
488 Service: &networkingv1.IngressServiceBackend{
489 Name: "othersvc",
490 Port: networkingv1.ServiceBackendPort{
491 Number: 8080,
492 },
493 },
494 },
495 },
496 },
497 },
498 },
499 },
500 },
501 },
502 },
503 },
504 }
505
506 for name, tc := range tests {
507 t.Run(name, func(t *testing.T) {
508 o := &CreateIngressOptions{
509 Name: ingressName,
510 IngressClass: tc.ingressclass,
511 Annotations: tc.annotations,
512 DefaultBackend: tc.defaultbackend,
513 Rules: tc.rules,
514 }
515 ingress := o.createIngress()
516 if !apiequality.Semantic.DeepEqual(ingress, tc.expected) {
517 t.Errorf("expected:\n%#v\ngot:\n%#v", tc.expected, ingress)
518 }
519 })
520 }
521 }
522
View as plain text