1from typing import Dict, 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: Dict[str, int] = {}
286 for result in generic_queries:
287 assert result.backend
288 generic_dict[result.backend.name] = (
289 generic_dict[result.backend.name] + 1 if result.backend.name in generic_dict else 1
290 )
291 assert len(generic_dict) == 3
292
293 # header queries - no cookie - no sticky expected
294 header_dict: Dict[str, int] = {}
295 for result in header_queries:
296 assert result.backend
297 header_dict[result.backend.name] = (
298 header_dict[result.backend.name] + 1 if result.backend.name in header_dict else 1
299 )
300 assert len(header_dict) == 3
301
302 # cookie queries - no headers - sticky expected
303 cookie_dict: Dict[str, int] = {}
304 for result in cookie_queries:
305 assert result.backend
306 cookie_dict[result.backend.name] = (
307 cookie_dict[result.backend.name] + 1 if result.backend.name in cookie_dict else 1
308 )
309 assert len(cookie_dict) == 1
310
311 # generic header queries - no cookie, no header
312 generic_generic_dict: Dict[str, int] = {}
313 for result in generic_generic_queries:
314 assert result.backend
315 generic_generic_dict[result.backend.name] = (
316 generic_generic_dict[result.backend.name] + 1
317 if result.backend.name in generic_generic_dict
318 else 1
319 )
320 assert len(generic_generic_dict) == 3
321
322 # header queries - no cookie - sticky expected
323 generic_header_dict: Dict[str, int] = {}
324 for result in generic_header_queries:
325 assert result.backend
326 generic_header_dict[result.backend.name] = (
327 generic_header_dict[result.backend.name] + 1
328 if result.backend.name in generic_header_dict
329 else 1
330 )
331 assert len(generic_header_dict) == 1
332
333 # cookie queries - no headers - no sticky expected
334 generic_cookie_dict: Dict[str, int] = {}
335 for result in generic_cookie_queries:
336 assert result.backend
337 generic_cookie_dict[result.backend.name] = (
338 generic_cookie_dict[result.backend.name] + 1
339 if result.backend.name in generic_cookie_dict
340 else 1
341 )
342 assert len(generic_cookie_dict) == 3
343
344
345class PerMappingLoadBalancing(AmbassadorTest):
346 target: ServiceType
347 policy: str
348
349 def init(self):
350 self.target = HTTP()
351
352 def manifests(self) -> str:
353 backend = self.name.lower() + "-backend"
354 return (
355 integration_manifests.format(
356 LOADBALANCER_POD,
357 name="{}-1".format(self.path.k8s),
358 backend=backend,
359 backend_env="{}-1".format(self.path.k8s),
360 )
361 + integration_manifests.format(
362 LOADBALANCER_POD,
363 name="{}-2".format(self.path.k8s),
364 backend=backend,
365 backend_env="{}-2".format(self.path.k8s),
366 )
367 + integration_manifests.format(
368 LOADBALANCER_POD,
369 name="{}-3".format(self.path.k8s),
370 backend=backend,
371 backend_env="{}-3".format(self.path.k8s),
372 )
373 + """
374---
375apiVersion: v1
376kind: Service
377metadata:
378 labels:
379 scope: AmbassadorTest
380 name: permappingloadbalancing-service
381spec:
382 ports:
383 - name: http
384 port: 80
385 targetPort: 8080
386 selector:
387 backend: {backend}
388""".format(
389 backend=backend
390 )
391 + super().manifests()
392 )
393
394 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
395 for policy in ["ring_hash", "maglev"]:
396 self.policy = policy
397 yield self, self.format(
398 """
399---
400apiVersion: getambassador.io/v3alpha1
401kind: Mapping
402name: {self.name}-header-{self.policy}
403hostname: "*"
404prefix: /{self.name}-header-{self.policy}/
405service: permappingloadbalancing-service
406resolver: endpoint
407load_balancer:
408 policy: {self.policy}
409 header: LB-HEADER
410---
411apiVersion: getambassador.io/v3alpha1
412kind: Mapping
413name: {self.name}-sourceip-{self.policy}
414hostname: "*"
415prefix: /{self.name}-sourceip-{self.policy}/
416service: permappingloadbalancing-service
417resolver: endpoint
418load_balancer:
419 policy: {self.policy}
420 source_ip: true
421---
422apiVersion: getambassador.io/v3alpha1
423kind: Mapping
424name: {self.name}-cookie-{self.policy}
425hostname: "*"
426prefix: /{self.name}-cookie-{self.policy}/
427service: permappingloadbalancing-service
428resolver: endpoint
429load_balancer:
430 policy: {self.policy}
431 cookie:
432 name: lb-cookie
433 ttl: 125s
434 path: /foo
435---
436apiVersion: getambassador.io/v3alpha1
437kind: Mapping
438name: {self.name}-cookie-no-ttl-{self.policy}
439hostname: "*"
440prefix: /{self.name}-cookie-no-ttl-{self.policy}/
441service: permappingloadbalancing-service
442resolver: endpoint
443load_balancer:
444 policy: {self.policy}
445 cookie:
446 name: lb-cookie
447"""
448 )
449
450 def queries(self):
451 for policy in ["ring_hash", "maglev"]:
452 # generic header queries
453 for i in range(50):
454 yield Query(self.url(self.name) + "-header-{}/".format(policy))
455
456 # header queries
457 for i in range(50):
458 yield Query(
459 self.url(self.name) + "-header-{}/".format(policy), headers={"LB-HEADER": "yes"}
460 )
461
462 # source IP queries
463 for i in range(50):
464 yield Query(self.url(self.name) + "-sourceip-{}/".format(policy))
465
466 # generic cookie queries
467 for i in range(50):
468 yield Query(self.url(self.name) + "-cookie-{}/".format(policy))
469
470 # cookie queries
471 for i in range(50):
472 yield Query(
473 self.url(self.name) + "-cookie-{}/".format(policy),
474 cookies=[{"name": "lb-cookie", "value": "yes"}],
475 )
476
477 # cookie no TTL queries
478 for i in range(50):
479 yield Query(
480 self.url(self.name) + "-cookie-no-ttl-{}/".format(policy),
481 cookies=[{"name": "lb-cookie", "value": "yes"}],
482 )
483
484 def check(self):
485 assert len(self.results) == 600
486
487 for i in [0, 300]:
488 generic_header_queries = self.results[0 + i : 50 + i]
489 header_queries = self.results[50 + i : 100 + i]
490 source_ip_queries = self.results[100 + i : 150 + i]
491 generic_cookie_queries = self.results[150 + i : 200 + i]
492 cookie_queries = self.results[200 + i : 250 + i]
493 cookie_no_ttl_queries = self.results[250 + i : 300 + i]
494
495 # generic header queries
496 generic_header_dict: Dict[str, int] = {}
497 for result in generic_header_queries:
498 assert result.backend
499 generic_header_dict[result.backend.name] = (
500 generic_header_dict[result.backend.name] + 1
501 if result.backend.name in generic_header_dict
502 else 1
503 )
504 assert len(generic_header_dict) == 3
505
506 # header queries
507 header_dict: Dict[str, int] = {}
508 for result in header_queries:
509 assert result.backend
510 header_dict[result.backend.name] = (
511 header_dict[result.backend.name] + 1
512 if result.backend.name in header_dict
513 else 1
514 )
515 assert len(header_dict) == 1
516
517 # source IP queries
518 source_ip_dict: Dict[str, int] = {}
519 for result in source_ip_queries:
520 assert result.backend
521 source_ip_dict[result.backend.name] = (
522 source_ip_dict[result.backend.name] + 1
523 if result.backend.name in source_ip_dict
524 else 1
525 )
526 assert len(source_ip_dict) == 1
527 assert list(source_ip_dict.values())[0] == 50
528
529 # generic cookie queries - results must include Set-Cookie header
530 generic_cookie_dict: Dict[str, int] = {}
531 for result in generic_cookie_queries:
532 assert "Set-Cookie" in result.headers
533 assert len(result.headers["Set-Cookie"]) == 1
534 assert "lb-cookie=" in result.headers["Set-Cookie"][0]
535 assert "Max-Age=125" in result.headers["Set-Cookie"][0]
536 assert "Path=/foo" in result.headers["Set-Cookie"][0]
537
538 assert result.backend
539 generic_cookie_dict[result.backend.name] = (
540 generic_cookie_dict[result.backend.name] + 1
541 if result.backend.name in generic_cookie_dict
542 else 1
543 )
544 assert len(generic_cookie_dict) == 3
545
546 # cookie queries
547 cookie_dict: Dict[str, int] = {}
548 for result in cookie_queries:
549 assert "Set-Cookie" not in result.headers
550
551 assert result.backend
552 cookie_dict[result.backend.name] = (
553 cookie_dict[result.backend.name] + 1
554 if result.backend.name in cookie_dict
555 else 1
556 )
557 assert len(cookie_dict) == 1
558
559 # cookie no TTL queries
560 cookie_no_ttl_dict: Dict[str, int] = {}
561 for result in cookie_no_ttl_queries:
562 assert "Set-Cookie" not in result.headers
563
564 assert result.backend
565 cookie_no_ttl_dict[result.backend.name] = (
566 cookie_no_ttl_dict[result.backend.name] + 1
567 if result.backend.name in cookie_no_ttl_dict
568 else 1
569 )
570 assert len(cookie_no_ttl_dict) == 1
View as plain text