1from typing import Generator, Tuple, Union
2
3import tests.integration.manifests as integration_manifests
4from abstract_tests import HTTP, AmbassadorTest, Node, ServiceType
5from kat.harness import Query
6
7LOADBALANCER_POD = """
8---
9apiVersion: v1
10kind: Pod
11metadata:
12 name: {name}
13 labels:
14 backend: {backend}
15 scope: AmbassadorTest
16spec:
17 containers:
18 - name: backend
19 image: {images[kat-server]}
20 ports:
21 - containerPort: 8080
22 env:
23 - name: BACKEND_8080
24 value: {backend_env}
25 - name: BACKEND_8443
26 value: {backend_env}
27"""
28
29
30class LoadBalancerTest(AmbassadorTest):
31 target: ServiceType
32
33 def init(self):
34 self.target = HTTP()
35
36 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
37 yield self, self.format(
38 """
39---
40apiVersion: getambassador.io/v3alpha1
41kind: Mapping
42name: {self.name}-0
43hostname: "*"
44prefix: /{self.name}-0/
45service: {self.target.path.fqdn}
46---
47apiVersion: getambassador.io/v3alpha1
48kind: Mapping
49name: {self.name}-1
50hostname: "*"
51prefix: /{self.name}-1/
52service: {self.target.path.fqdn}
53resolver: endpoint
54load_balancer:
55 policy: round_robin
56---
57apiVersion: getambassador.io/v3alpha1
58kind: Mapping
59name: {self.name}-2
60hostname: "*"
61prefix: /{self.name}-2/
62service: {self.target.path.fqdn}
63resolver: endpoint
64load_balancer:
65 policy: ring_hash
66 header: test-header
67---
68apiVersion: getambassador.io/v3alpha1
69kind: Mapping
70name: {self.name}-3
71hostname: "*"
72prefix: /{self.name}-3/
73service: {self.target.path.fqdn}
74resolver: endpoint
75load_balancer:
76 policy: ring_hash
77 source_ip: True
78---
79apiVersion: getambassador.io/v3alpha1
80kind: Mapping
81name: {self.name}-4
82hostname: "*"
83prefix: /{self.name}-4/
84service: {self.target.path.fqdn}
85resolver: endpoint
86load_balancer:
87 policy: ring_hash
88 cookie:
89 name: test-cookie
90---
91apiVersion: getambassador.io/v3alpha1
92kind: Mapping
93name: {self.name}-5
94hostname: "*"
95prefix: /{self.name}-5/
96service: {self.target.path.fqdn}
97resolver: endpoint
98load_balancer:
99 policy: ring_hash
100 cookie:
101 name: test-cookie
102 header: test-header
103 source_ip: True
104---
105apiVersion: getambassador.io/v3alpha1
106kind: Mapping
107name: {self.name}-6
108hostname: "*"
109prefix: /{self.name}-6/
110service: {self.target.path.fqdn}
111resolver: endpoint
112load_balancer:
113 policy: round_robin
114 cookie:
115 name: test-cookie
116---
117apiVersion: getambassador.io/v3alpha1
118kind: Mapping
119name: {self.name}-7
120hostname: "*"
121prefix: /{self.name}-7/
122service: {self.target.path.fqdn}
123resolver: endpoint
124load_balancer:
125 policy: rr
126---
127apiVersion: getambassador.io/v3alpha1
128kind: Mapping
129name: {self.name}-8
130hostname: "*"
131prefix: /{self.name}-8/
132service: {self.target.path.fqdn}
133resolver: endpoint
134load_balancer:
135 policy: least_request
136---
137apiVersion: getambassador.io/v3alpha1
138kind: Mapping
139name: {self.name}-9
140hostname: "*"
141prefix: /{self.name}-9/
142service: {self.target.path.fqdn}
143resolver: endpoint
144load_balancer:
145 policy: least_request
146 cookie:
147 name: test-cookie
148"""
149 )
150
151 def queries(self):
152 yield Query(self.url(self.name + "-0/"))
153 yield Query(self.url(self.name + "-1/"))
154 yield Query(self.url(self.name + "-2/"))
155 yield Query(self.url(self.name + "-3/"))
156 yield Query(self.url(self.name + "-4/"))
157 yield Query(self.url(self.name + "-5/"), expected=404)
158 yield Query(self.url(self.name + "-6/"), expected=404)
159 yield Query(self.url(self.name + "-7/"), expected=404)
160 yield Query(self.url(self.name + "-8/"))
161 yield Query(self.url(self.name + "-9/"), expected=404)
162
163
164class GlobalLoadBalancing(AmbassadorTest):
165 target: ServiceType
166
167 def init(self):
168 self.target = HTTP()
169
170 def manifests(self) -> str:
171 backend = self.name.lower() + "-backend"
172 return (
173 integration_manifests.format(
174 LOADBALANCER_POD,
175 name="{}-1".format(self.path.k8s),
176 backend=backend,
177 backend_env="{}-1".format(self.path.k8s),
178 )
179 + integration_manifests.format(
180 LOADBALANCER_POD,
181 name="{}-2".format(self.path.k8s),
182 backend=backend,
183 backend_env="{}-2".format(self.path.k8s),
184 )
185 + integration_manifests.format(
186 LOADBALANCER_POD,
187 name="{}-3".format(self.path.k8s),
188 backend=backend,
189 backend_env="{}-3".format(self.path.k8s),
190 )
191 + """
192---
193apiVersion: v1
194kind: Service
195metadata:
196 labels:
197 scope: AmbassadorTest
198 name: globalloadbalancing-service
199spec:
200 ports:
201 - name: http
202 port: 80
203 targetPort: 8080
204 selector:
205 backend: {backend}
206""".format(
207 backend=backend
208 )
209 + super().manifests()
210 )
211
212 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
213 yield self, self.format(
214 """
215apiVersion: getambassador.io/v3alpha1
216kind: Module
217name: ambassador
218config:
219 resolver: endpoint
220 load_balancer:
221 policy: ring_hash
222 header: LB-HEADER
223---
224apiVersion: getambassador.io/v3alpha1
225kind: Mapping
226name: {self.name}-header
227hostname: "*"
228prefix: /{self.name}-header/
229service: globalloadbalancing-service
230load_balancer:
231 policy: ring_hash
232 cookie:
233 name: lb-cookie
234---
235apiVersion: getambassador.io/v3alpha1
236kind: Mapping
237name: {self.name}-generic
238hostname: "*"
239prefix: /{self.name}-generic/
240service: globalloadbalancing-service
241"""
242 )
243
244 def queries(self):
245 # generic header queries
246 for i in range(50):
247 yield Query(self.url(self.name) + "-header/")
248
249 # header queries
250 for i in range(50):
251 yield Query(self.url(self.name) + "-header/", headers={"LB-HEADER": "yes"})
252
253 # cookie queries
254 for i in range(50):
255 yield Query(
256 self.url(self.name) + "-header/", cookies=[{"name": "lb-cookie", "value": "yes"}]
257 )
258
259 # generic - generic queries
260 for i in range(50):
261 yield Query(self.url(self.name) + "-generic/")
262
263 # generic - header queries
264 for i in range(50):
265 yield Query(self.url(self.name) + "-generic/", headers={"LB-HEADER": "yes"})
266
267 # generic - cookie queries
268 for i in range(50):
269 yield Query(
270 self.url(self.name) + "-generic/", cookies=[{"name": "lb-cookie", "value": "yes"}]
271 )
272
273 def check(self):
274 assert len(self.results) == 300
275
276 generic_queries = self.results[:50]
277 header_queries = self.results[50:100]
278 cookie_queries = self.results[100:150]
279
280 generic_generic_queries = self.results[150:200]
281 generic_header_queries = self.results[200:250]
282 generic_cookie_queries = self.results[250:300]
283
284 # generic header queries - no cookie, no header
285 generic_dict = {}
286 for result in generic_queries:
287 generic_dict[result.backend.name] = (
288 generic_dict[result.backend.name] + 1 if result.backend.name in generic_dict else 1
289 )
290 assert len(generic_dict) == 3
291
292 # header queries - no cookie - no sticky expected
293 header_dict = {}
294 for result in header_queries:
295 header_dict[result.backend.name] = (
296 header_dict[result.backend.name] + 1 if result.backend.name in header_dict else 1
297 )
298 assert len(header_dict) == 3
299
300 # cookie queries - no headers - sticky expected
301 cookie_dict = {}
302 for result in cookie_queries:
303 cookie_dict[result.backend.name] = (
304 cookie_dict[result.backend.name] + 1 if result.backend.name in cookie_dict else 1
305 )
306 assert len(cookie_dict) == 1
307
308 # generic header queries - no cookie, no header
309 generic_generic_dict = {}
310 for result in generic_generic_queries:
311 generic_generic_dict[result.backend.name] = (
312 generic_generic_dict[result.backend.name] + 1
313 if result.backend.name in generic_generic_dict
314 else 1
315 )
316 assert len(generic_generic_dict) == 3
317
318 # header queries - no cookie - sticky expected
319 generic_header_dict = {}
320 for result in generic_header_queries:
321 generic_header_dict[result.backend.name] = (
322 generic_header_dict[result.backend.name] + 1
323 if result.backend.name in generic_header_dict
324 else 1
325 )
326 assert len(generic_header_dict) == 1
327
328 # cookie queries - no headers - no sticky expected
329 generic_cookie_dict = {}
330 for result in generic_cookie_queries:
331 generic_cookie_dict[result.backend.name] = (
332 generic_cookie_dict[result.backend.name] + 1
333 if result.backend.name in generic_cookie_dict
334 else 1
335 )
336 assert len(generic_cookie_dict) == 3
337
338
339class PerMappingLoadBalancing(AmbassadorTest):
340 target: ServiceType
341 policy: str
342
343 def init(self):
344 self.target = HTTP()
345
346 def manifests(self) -> str:
347 backend = self.name.lower() + "-backend"
348 return (
349 integration_manifests.format(
350 LOADBALANCER_POD,
351 name="{}-1".format(self.path.k8s),
352 backend=backend,
353 backend_env="{}-1".format(self.path.k8s),
354 )
355 + integration_manifests.format(
356 LOADBALANCER_POD,
357 name="{}-2".format(self.path.k8s),
358 backend=backend,
359 backend_env="{}-2".format(self.path.k8s),
360 )
361 + integration_manifests.format(
362 LOADBALANCER_POD,
363 name="{}-3".format(self.path.k8s),
364 backend=backend,
365 backend_env="{}-3".format(self.path.k8s),
366 )
367 + """
368---
369apiVersion: v1
370kind: Service
371metadata:
372 labels:
373 scope: AmbassadorTest
374 name: permappingloadbalancing-service
375spec:
376 ports:
377 - name: http
378 port: 80
379 targetPort: 8080
380 selector:
381 backend: {backend}
382""".format(
383 backend=backend
384 )
385 + super().manifests()
386 )
387
388 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
389 for policy in ["ring_hash", "maglev"]:
390 self.policy = policy
391 yield self, self.format(
392 """
393---
394apiVersion: getambassador.io/v3alpha1
395kind: Mapping
396name: {self.name}-header-{self.policy}
397hostname: "*"
398prefix: /{self.name}-header-{self.policy}/
399service: permappingloadbalancing-service
400resolver: endpoint
401load_balancer:
402 policy: {self.policy}
403 header: LB-HEADER
404---
405apiVersion: getambassador.io/v3alpha1
406kind: Mapping
407name: {self.name}-sourceip-{self.policy}
408hostname: "*"
409prefix: /{self.name}-sourceip-{self.policy}/
410service: permappingloadbalancing-service
411resolver: endpoint
412load_balancer:
413 policy: {self.policy}
414 source_ip: true
415---
416apiVersion: getambassador.io/v3alpha1
417kind: Mapping
418name: {self.name}-cookie-{self.policy}
419hostname: "*"
420prefix: /{self.name}-cookie-{self.policy}/
421service: permappingloadbalancing-service
422resolver: endpoint
423load_balancer:
424 policy: {self.policy}
425 cookie:
426 name: lb-cookie
427 ttl: 125s
428 path: /foo
429---
430apiVersion: getambassador.io/v3alpha1
431kind: Mapping
432name: {self.name}-cookie-no-ttl-{self.policy}
433hostname: "*"
434prefix: /{self.name}-cookie-no-ttl-{self.policy}/
435service: permappingloadbalancing-service
436resolver: endpoint
437load_balancer:
438 policy: {self.policy}
439 cookie:
440 name: lb-cookie
441"""
442 )
443
444 def queries(self):
445 for policy in ["ring_hash", "maglev"]:
446 # generic header queries
447 for i in range(50):
448 yield Query(self.url(self.name) + "-header-{}/".format(policy))
449
450 # header queries
451 for i in range(50):
452 yield Query(
453 self.url(self.name) + "-header-{}/".format(policy), headers={"LB-HEADER": "yes"}
454 )
455
456 # source IP queries
457 for i in range(50):
458 yield Query(self.url(self.name) + "-sourceip-{}/".format(policy))
459
460 # generic cookie queries
461 for i in range(50):
462 yield Query(self.url(self.name) + "-cookie-{}/".format(policy))
463
464 # cookie queries
465 for i in range(50):
466 yield Query(
467 self.url(self.name) + "-cookie-{}/".format(policy),
468 cookies=[{"name": "lb-cookie", "value": "yes"}],
469 )
470
471 # cookie no TTL queries
472 for i in range(50):
473 yield Query(
474 self.url(self.name) + "-cookie-no-ttl-{}/".format(policy),
475 cookies=[{"name": "lb-cookie", "value": "yes"}],
476 )
477
478 def check(self):
479 assert len(self.results) == 600
480
481 for i in [0, 300]:
482 generic_header_queries = self.results[0 + i : 50 + i]
483 header_queries = self.results[50 + i : 100 + i]
484 source_ip_queries = self.results[100 + i : 150 + i]
485 generic_cookie_queries = self.results[150 + i : 200 + i]
486 cookie_queries = self.results[200 + i : 250 + i]
487 cookie_no_ttl_queries = self.results[250 + i : 300 + i]
488
489 # generic header queries
490 generic_header_dict = {}
491 for result in generic_header_queries:
492 generic_header_dict[result.backend.name] = (
493 generic_header_dict[result.backend.name] + 1
494 if result.backend.name in generic_header_dict
495 else 1
496 )
497 assert len(generic_header_dict) == 3
498
499 # header queries
500 header_dict = {}
501 for result in header_queries:
502 header_dict[result.backend.name] = (
503 header_dict[result.backend.name] + 1
504 if result.backend.name in header_dict
505 else 1
506 )
507 assert len(header_dict) == 1
508
509 # source IP queries
510 source_ip_dict = {}
511 for result in source_ip_queries:
512 source_ip_dict[result.backend.name] = (
513 source_ip_dict[result.backend.name] + 1
514 if result.backend.name in source_ip_dict
515 else 1
516 )
517 assert len(source_ip_dict) == 1
518 assert list(source_ip_dict.values())[0] == 50
519
520 # generic cookie queries - results must include Set-Cookie header
521 generic_cookie_dict = {}
522 for result in generic_cookie_queries:
523 assert "Set-Cookie" in result.headers
524 assert len(result.headers["Set-Cookie"]) == 1
525 assert "lb-cookie=" in result.headers["Set-Cookie"][0]
526 assert "Max-Age=125" in result.headers["Set-Cookie"][0]
527 assert "Path=/foo" in result.headers["Set-Cookie"][0]
528
529 generic_cookie_dict[result.backend.name] = (
530 generic_cookie_dict[result.backend.name] + 1
531 if result.backend.name in generic_cookie_dict
532 else 1
533 )
534 assert len(generic_cookie_dict) == 3
535
536 # cookie queries
537 cookie_dict = {}
538 for result in cookie_queries:
539 assert "Set-Cookie" not in result.headers
540
541 cookie_dict[result.backend.name] = (
542 cookie_dict[result.backend.name] + 1
543 if result.backend.name in cookie_dict
544 else 1
545 )
546 assert len(cookie_dict) == 1
547
548 # cookie no TTL queries
549 cookie_no_ttl_dict = {}
550 for result in cookie_no_ttl_queries:
551 assert "Set-Cookie" not in result.headers
552
553 cookie_no_ttl_dict[result.backend.name] = (
554 cookie_no_ttl_dict[result.backend.name] + 1
555 if result.backend.name in cookie_no_ttl_dict
556 else 1
557 )
558 assert len(cookie_no_ttl_dict) == 1
View as plain text