1from typing import Generator, Tuple, Union
2
3from abstract_tests import DEV, HTTP, AmbassadorTest, Node, ServiceType, StatsDSink
4from kat.harness import Query
5
6STATSD_TEST_CLUSTER = "statsdtest_http"
7ALT_STATSD_TEST_CLUSTER = "short-stats-name"
8DOGSTATSD_TEST_CLUSTER = "dogstatsdtest_http"
9
10
11class StatsdTest(AmbassadorTest):
12 sink: ServiceType
13
14 def init(self):
15 self.target = HTTP()
16 self.target2 = HTTP(name="alt-statsd")
17 self.sink = StatsDSink(target_cluster=f"{STATSD_TEST_CLUSTER}:{ALT_STATSD_TEST_CLUSTER}")
18 self.stats_name = ALT_STATSD_TEST_CLUSTER
19 if DEV:
20 self.skip_node = True
21
22 def manifests(self) -> str:
23 self.manifest_envs += f"""
24 - name: STATSD_ENABLED
25 value: 'true'
26 - name: STATSD_HOST
27 value: {self.sink.path.fqdn}
28"""
29 return super().manifests()
30
31 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
32 yield self.target, self.format(
33 """
34---
35apiVersion: getambassador.io/v3alpha1
36kind: Mapping
37name: {self.name}
38hostname: "*"
39prefix: /{self.name}/
40service: http://{self.target.path.fqdn}
41---
42apiVersion: getambassador.io/v3alpha1
43kind: Mapping
44name: {self.name}-alt
45hostname: "*"
46prefix: /{self.name}-alt/
47stats_name: {self.stats_name}
48service: http://{self.target2.path.fqdn}
49---
50apiVersion: getambassador.io/v3alpha1
51kind: Mapping
52name: {self.name}-reset
53hostname: "*"
54case_sensitive: false
55prefix: /reset/
56rewrite: /RESET/
57service: {self.sink.path.fqdn}
58---
59apiVersion: getambassador.io/v3alpha1
60kind: Mapping
61name: metrics
62hostname: "*"
63prefix: /metrics
64rewrite: /metrics
65service: http://127.0.0.1:8877
66"""
67 )
68
69 def requirements(self):
70 yield from super().requirements()
71 yield ("url", Query(self.url("RESET/")))
72
73 def queries(self):
74 for i in range(1000):
75 yield Query(self.url(self.name + "/"), phase=1)
76 yield Query(self.url(self.name + "-alt/"), phase=1)
77
78 yield Query(f"http://{self.sink.path.fqdn}/DUMP/", phase=2)
79 yield Query(self.url("metrics"), phase=2)
80
81 def check(self):
82 # self.results[-2] is the JSON dump from our test self.sink service.
83 stats = self.results[-2].json or {}
84
85 cluster_stats = stats.get(STATSD_TEST_CLUSTER, {})
86 rq_total = cluster_stats.get("upstream_rq_total", -1)
87 rq_200 = cluster_stats.get("upstream_rq_200", -1)
88
89 assert rq_total == 1000, f"{STATSD_TEST_CLUSTER}: expected 1000 total calls, got {rq_total}"
90 assert rq_200 > 990, f"{STATSD_TEST_CLUSTER}: expected 1000 successful calls, got {rq_200}"
91
92 cluster_stats = stats.get(ALT_STATSD_TEST_CLUSTER, {})
93 rq_total = cluster_stats.get("upstream_rq_total", -1)
94 rq_200 = cluster_stats.get("upstream_rq_200", -1)
95
96 assert (
97 rq_total == 1000
98 ), f"{ALT_STATSD_TEST_CLUSTER}: expected 1000 total calls, got {rq_total}"
99 assert (
100 rq_200 > 990
101 ), f"{ALT_STATSD_TEST_CLUSTER}: expected 1000 successful calls, got {rq_200}"
102
103 # self.results[-1] is the text dump from Envoy's '/metrics' endpoint.
104 metrics = self.results[-1].text
105
106 # Somewhere in here, we want to see a metric explicitly for both our "real"
107 # cluster and our alt cluster, returning a 200. Are they there?
108 wanted_metric = "envoy_cluster_internal_upstream_rq"
109 wanted_status = 'envoy_response_code="200"'
110 wanted_cluster_name = f'envoy_cluster_name="{STATSD_TEST_CLUSTER}"'
111 alt_wanted_cluster_name = f'envoy_cluster_name="{ALT_STATSD_TEST_CLUSTER}"'
112
113 found_normal = False
114 found_alt = False
115
116 for line in metrics.split("\n"):
117 if wanted_metric in line and wanted_status in line and wanted_cluster_name in line:
118 print(f"line '{line}'")
119 found_normal = True
120
121 if wanted_metric in line and wanted_status in line and alt_wanted_cluster_name in line:
122 print(f"line '{line}'")
123 found_alt = True
124
125 assert (
126 found_normal
127 ), f"wanted {STATSD_TEST_CLUSTER} in Prometheus metrics, but didn't find it"
128 assert (
129 found_alt
130 ), f"wanted {ALT_STATSD_TEST_CLUSTER} in Prometheus metrics, but didn't find it"
131
132
133class DogstatsdTest(AmbassadorTest):
134 dogstatsd: ServiceType
135
136 def init(self):
137 self.target = HTTP()
138 self.sink = StatsDSink(target_cluster=DOGSTATSD_TEST_CLUSTER)
139 if DEV:
140 self.skip_node = True
141
142 def manifests(self) -> str:
143 self.manifest_envs += f"""
144 - name: STATSD_ENABLED
145 value: 'true'
146 - name: STATSD_HOST
147 value: {self.sink.path.fqdn}
148 - name: DOGSTATSD
149 value: 'true'
150"""
151 return super().manifests()
152
153 def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
154 yield self.target, self.format(
155 """
156---
157apiVersion: getambassador.io/v3alpha1
158kind: Mapping
159name: {self.name}
160hostname: "*"
161prefix: /{self.name}/
162service: http://{self.target.path.fqdn}
163---
164apiVersion: getambassador.io/v3alpha1
165kind: Mapping
166name: {self.name}-reset
167case_sensitive: false
168hostname: "*"
169prefix: /reset/
170rewrite: /RESET/
171service: {self.sink.path.fqdn}
172"""
173 )
174
175 def requirements(self):
176 yield from super().requirements()
177 yield ("url", Query(self.url("RESET/")))
178
179 def queries(self):
180 for i in range(1000):
181 yield Query(self.url(self.name + "/"), phase=1)
182
183 yield Query(f"http://{self.sink.path.fqdn}/DUMP/", phase=2)
184
185 def check(self):
186 stats = self.results[-1].json or {}
187
188 cluster_stats = stats.get(DOGSTATSD_TEST_CLUSTER, {})
189 rq_total = cluster_stats.get("upstream_rq_total", -1)
190 rq_200 = cluster_stats.get("upstream_rq_200", -1)
191
192 assert rq_total == 1000, f"expected 1000 total calls, got {rq_total}"
193 assert rq_200 > 990, f"expected 1000 successful calls, got {rq_200}"
View as plain text