1import time
2from typing import Generator, Tuple, Union
3
4from abstract_tests import AmbassadorTest, HealthCheckServer, Node
5from kat.harness import Query
6
7
8class ActiveHealthCheckTest(AmbassadorTest):
9 def init(self):
10 self.target = HealthCheckServer()
11
12 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
13 yield self, self.format(
14 """
15---
16apiVersion: getambassador.io/v3alpha1
17kind: Mapping
18name: {self.target.path.k8s}-health
19hostname: "*"
20prefix: /healthcheck/
21service: {self.target.path.fqdn}
22resolver: endpoint
23load_balancer:
24 policy: round_robin
25health_checks:
26- unhealthy_threshold: 1
27 interval: 1s
28 health_check:
29 http:
30 path: /healthcheck/actualcheck/
31"""
32 ) # The round robin load balancer is not necessary for the test but should help make the request distribution even across the pods
33
34 def queries(self):
35 yield Query(self.url("healthcheck/"), phase=1) # Just making sure things are running
36 yield Query(self.url("ambassador/v0/diag/"), phase=1)
37
38 yield Query(
39 self.url("healthcheck/makeUnhealthy/"), phase=1
40 ) # the deployment has 5 pods. This will make one of them start returning errors
41
42 # These three queries on their own in separate phases are just a hack way of getting the kat client
43 # to wait a little bit after the previous query so that the automated health checks have time to notice
44 # that one of the pods is misbehaving before we start blasting requests out.
45 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=2)
46 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=3)
47 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=4)
48
49 # Make 1000 requests split into two groups to reduce any flakes
50 for _ in range(500):
51 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=5)
52 time.sleep(0.06)
53
54 for _ in range(500):
55 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=6)
56 time.sleep(0.06)
57
58 def check(self):
59 # Add up the number of 500 and 200 responses that we got.
60 valid = 0
61 errors = 0
62 for i in range(6, 1006):
63 if self.results[i].status == 200:
64 valid += 1
65 elif self.results[i].status == 500:
66 errors += 1
67
68 # with 1000 requests and 1/5 being an error response, we should have the following distribution +/- 10
69 # assert 190 <= errors <= 210
70 # assert 790 <= valid <= 810
71
72 # But since we configure health checking we should actually see 0 errors because the health checks noticed
73 # that one of the pods was unhealthy and didn't route any traffic to it.
74 msg = "Errors: {}, Valid: {}".format(errors, valid)
75 assert errors == 0, msg
76 assert valid == 1000, msg
77
78
79class NoHealthCheckTest(AmbassadorTest):
80 def init(self):
81 self.target = HealthCheckServer()
82
83 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
84 yield self, self.format(
85 """
86---
87apiVersion: getambassador.io/v3alpha1
88kind: Mapping
89name: {self.target.path.k8s}-health
90hostname: "*"
91prefix: /healthcheck/
92service: {self.target.path.fqdn}
93resolver: endpoint
94load_balancer:
95 policy: round_robin
96"""
97 ) # The round robin load balancer is not necessary for the test but should help make the request distribution even across the pods
98
99 def queries(self):
100 yield Query(self.url("healthcheck/"), phase=1) # Just making sure things are running
101 yield Query(self.url("ambassador/v0/diag/"), phase=1)
102
103 yield Query(
104 self.url("healthcheck/makeUnhealthy/"), phase=1
105 ) # the deployment has 5 pods. This will make one of them start returning errors
106
107 # Make 1000 requests and split them up so that we're not hammering the service too much all at once.
108 for _ in range(500):
109 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=2)
110 time.sleep(0.06)
111
112 for _ in range(500):
113 yield Query(self.url("healthcheck/"), expected=[200, 500], phase=3)
114 time.sleep(0.06)
115
116 def check(self):
117 # Since we haven't configured any health checking, we should expect to see a fair number of error responses
118 valid = 0
119 errors = 0
120 for i in range(3, 1003):
121 if self.results[i].status == 200:
122 valid += 1
123 elif self.results[i].status == 500:
124 errors += 1
125 msg = "Errors: {}, Valid: {}".format(errors, valid)
126
127 # with 1000 requests and 1/5 being an error response, we should have the following distribution +/- some
128 # margin might need tuned
129 margin = 100
130 assert abs(errors - 200) < margin
131 assert abs(valid - 800) < margin
View as plain text