Adding cluster-issuer annotation for TLS provisioning
[osm/devops.git] / installers / charm / mysqld-exporter / tests / test_pod_spec.py
1 #!/usr/bin/env python3
2 # Copyright 2021 Canonical Ltd.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15 #
16 # For those usages not covered by the Apache License, Version 2.0 please
17 # contact: legal@canonical.com
18 #
19 # To get in touch with the maintainers, please contact:
20 # osm-charmers@lists.launchpad.net
21 ##
22
23 from typing import NoReturn
24 import unittest
25
26 import pod_spec
27
28
29 class TestPodSpec(unittest.TestCase):
30 """Pod spec unit tests."""
31
32 def test_make_pod_ports(self) -> NoReturn:
33 """Testing make pod ports."""
34 port = 9104
35
36 expected_result = [
37 {
38 "name": "mysqld-exporter",
39 "containerPort": port,
40 "protocol": "TCP",
41 }
42 ]
43
44 pod_ports = pod_spec._make_pod_ports(port)
45
46 self.assertListEqual(expected_result, pod_ports)
47
48 def test_make_pod_envconfig(self) -> NoReturn:
49 """Teting make pod envconfig."""
50 config = {}
51 relation_state = {
52 "mysql_host": "mysql",
53 "mysql_port": "3306",
54 "mysql_user": "mano",
55 "mysql_password": "manopw",
56 "mysql_root_password": "rootpw",
57 }
58
59 expected_result = {
60 "DATA_SOURCE_NAME": "root:{mysql_root_password}@({mysql_host}:{mysql_port})/".format(
61 **relation_state
62 )
63 }
64
65 pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state)
66
67 self.assertDictEqual(expected_result, pod_envconfig)
68
69 def test_make_pod_ingress_resources_without_site_url(self) -> NoReturn:
70 """Testing make pod ingress resources without site_url."""
71 config = {
72 "site_url": "",
73 "cluster_issuer": "",
74 }
75 app_name = "mysqld-exporter"
76 port = 9104
77
78 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
79 config, app_name, port
80 )
81
82 self.assertIsNone(pod_ingress_resources)
83
84 def test_make_pod_ingress_resources(self) -> NoReturn:
85 """Testing make pod ingress resources."""
86 config = {
87 "site_url": "http://mysqld-exporter",
88 "cluster_issuer": "",
89 "ingress_whitelist_source_range": "",
90 }
91 app_name = "mysqld-exporter"
92 port = 9104
93
94 expected_result = [
95 {
96 "name": f"{app_name}-ingress",
97 "annotations": {
98 "nginx.ingress.kubernetes.io/ssl-redirect": "false",
99 },
100 "spec": {
101 "rules": [
102 {
103 "host": app_name,
104 "http": {
105 "paths": [
106 {
107 "path": "/",
108 "backend": {
109 "serviceName": app_name,
110 "servicePort": port,
111 },
112 }
113 ]
114 },
115 }
116 ]
117 },
118 }
119 ]
120
121 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
122 config, app_name, port
123 )
124
125 self.assertListEqual(expected_result, pod_ingress_resources)
126
127 def test_make_pod_ingress_resources_with_whitelist_source_range(self) -> NoReturn:
128 """Testing make pod ingress resources with whitelist_source_range."""
129 config = {
130 "site_url": "http://mysqld-exporter",
131 "cluster_issuer": "",
132 "ingress_whitelist_source_range": "0.0.0.0/0",
133 }
134 app_name = "mysqld-exporter"
135 port = 9104
136
137 expected_result = [
138 {
139 "name": f"{app_name}-ingress",
140 "annotations": {
141 "nginx.ingress.kubernetes.io/ssl-redirect": "false",
142 "nginx.ingress.kubernetes.io/whitelist-source-range": config[
143 "ingress_whitelist_source_range"
144 ],
145 },
146 "spec": {
147 "rules": [
148 {
149 "host": app_name,
150 "http": {
151 "paths": [
152 {
153 "path": "/",
154 "backend": {
155 "serviceName": app_name,
156 "servicePort": port,
157 },
158 }
159 ]
160 },
161 }
162 ]
163 },
164 }
165 ]
166
167 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
168 config, app_name, port
169 )
170
171 self.assertListEqual(expected_result, pod_ingress_resources)
172
173 def test_make_pod_ingress_resources_with_https(self) -> NoReturn:
174 """Testing make pod ingress resources with HTTPs."""
175 config = {
176 "site_url": "https://mysqld-exporter",
177 "cluster_issuer": "",
178 "ingress_whitelist_source_range": "",
179 "tls_secret_name": "",
180 }
181 app_name = "mysqld-exporter"
182 port = 9104
183
184 expected_result = [
185 {
186 "name": f"{app_name}-ingress",
187 "annotations": {},
188 "spec": {
189 "rules": [
190 {
191 "host": app_name,
192 "http": {
193 "paths": [
194 {
195 "path": "/",
196 "backend": {
197 "serviceName": app_name,
198 "servicePort": port,
199 },
200 }
201 ]
202 },
203 }
204 ],
205 "tls": [{"hosts": [app_name]}],
206 },
207 }
208 ]
209
210 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
211 config, app_name, port
212 )
213
214 self.assertListEqual(expected_result, pod_ingress_resources)
215
216 def test_make_pod_ingress_resources_with_https_tls_secret_name(self) -> NoReturn:
217 """Testing make pod ingress resources with HTTPs and TLS secret name."""
218 config = {
219 "site_url": "https://mysqld-exporter",
220 "cluster_issuer": "",
221 "ingress_whitelist_source_range": "",
222 "tls_secret_name": "secret_name",
223 }
224 app_name = "mysqld-exporter"
225 port = 9104
226
227 expected_result = [
228 {
229 "name": f"{app_name}-ingress",
230 "annotations": {},
231 "spec": {
232 "rules": [
233 {
234 "host": app_name,
235 "http": {
236 "paths": [
237 {
238 "path": "/",
239 "backend": {
240 "serviceName": app_name,
241 "servicePort": port,
242 },
243 }
244 ]
245 },
246 }
247 ],
248 "tls": [
249 {"hosts": [app_name], "secretName": config["tls_secret_name"]}
250 ],
251 },
252 }
253 ]
254
255 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
256 config, app_name, port
257 )
258
259 self.assertListEqual(expected_result, pod_ingress_resources)
260
261 def test_make_readiness_probe(self) -> NoReturn:
262 """Testing make readiness probe."""
263 port = 9104
264
265 expected_result = {
266 "httpGet": {
267 "path": "/api/health",
268 "port": port,
269 },
270 "initialDelaySeconds": 10,
271 "periodSeconds": 10,
272 "timeoutSeconds": 5,
273 "successThreshold": 1,
274 "failureThreshold": 3,
275 }
276
277 readiness_probe = pod_spec._make_readiness_probe(port)
278
279 self.assertDictEqual(expected_result, readiness_probe)
280
281 def test_make_liveness_probe(self) -> NoReturn:
282 """Testing make liveness probe."""
283 port = 9104
284
285 expected_result = {
286 "httpGet": {
287 "path": "/api/health",
288 "port": port,
289 },
290 "initialDelaySeconds": 60,
291 "timeoutSeconds": 30,
292 "failureThreshold": 10,
293 }
294
295 liveness_probe = pod_spec._make_liveness_probe(port)
296
297 self.assertDictEqual(expected_result, liveness_probe)
298
299 def test_make_pod_spec(self) -> NoReturn:
300 """Testing make pod spec."""
301 image_info = {"upstream-source": "bitnami/mysqld-exporter:latest"}
302 config = {
303 "site_url": "",
304 "cluster_issuer": "",
305 }
306 relation_state = {
307 "mysql_host": "mysql",
308 "mysql_port": "3306",
309 "mysql_user": "mano",
310 "mysql_password": "manopw",
311 "mysql_root_password": "rootpw",
312 }
313 app_name = "mysqld-exporter"
314 port = 9104
315
316 expected_result = {
317 "version": 3,
318 "containers": [
319 {
320 "name": app_name,
321 "imageDetails": image_info,
322 "imagePullPolicy": "Always",
323 "ports": [
324 {
325 "name": app_name,
326 "containerPort": port,
327 "protocol": "TCP",
328 }
329 ],
330 "envConfig": {
331 "DATA_SOURCE_NAME": "root:{mysql_root_password}@({mysql_host}:{mysql_port})/".format(
332 **relation_state
333 )
334 },
335 "kubernetes": {
336 "readinessProbe": {
337 "httpGet": {
338 "path": "/api/health",
339 "port": port,
340 },
341 "initialDelaySeconds": 10,
342 "periodSeconds": 10,
343 "timeoutSeconds": 5,
344 "successThreshold": 1,
345 "failureThreshold": 3,
346 },
347 "livenessProbe": {
348 "httpGet": {
349 "path": "/api/health",
350 "port": port,
351 },
352 "initialDelaySeconds": 60,
353 "timeoutSeconds": 30,
354 "failureThreshold": 10,
355 },
356 },
357 }
358 ],
359 "kubernetesResources": {"ingressResources": []},
360 }
361
362 spec = pod_spec.make_pod_spec(
363 image_info, config, relation_state, app_name, port
364 )
365
366 self.assertDictEqual(expected_result, spec)
367
368 def test_make_pod_spec_with_ingress(self) -> NoReturn:
369 """Testing make pod spec."""
370 image_info = {"upstream-source": "bitnami/mysqld-exporter:latest"}
371 config = {
372 "site_url": "https://mysqld-exporter",
373 "cluster_issuer": "",
374 "tls_secret_name": "mysqld-exporter",
375 "ingress_whitelist_source_range": "0.0.0.0/0",
376 }
377 relation_state = {
378 "mysql_host": "mysql",
379 "mysql_port": "3306",
380 "mysql_user": "mano",
381 "mysql_password": "manopw",
382 "mysql_root_password": "rootpw",
383 }
384 app_name = "mysqld-exporter"
385 port = 9104
386
387 expected_result = {
388 "version": 3,
389 "containers": [
390 {
391 "name": app_name,
392 "imageDetails": image_info,
393 "imagePullPolicy": "Always",
394 "ports": [
395 {
396 "name": app_name,
397 "containerPort": port,
398 "protocol": "TCP",
399 }
400 ],
401 "envConfig": {
402 "DATA_SOURCE_NAME": "root:{mysql_root_password}@({mysql_host}:{mysql_port})/".format(
403 **relation_state
404 )
405 },
406 "kubernetes": {
407 "readinessProbe": {
408 "httpGet": {
409 "path": "/api/health",
410 "port": port,
411 },
412 "initialDelaySeconds": 10,
413 "periodSeconds": 10,
414 "timeoutSeconds": 5,
415 "successThreshold": 1,
416 "failureThreshold": 3,
417 },
418 "livenessProbe": {
419 "httpGet": {
420 "path": "/api/health",
421 "port": port,
422 },
423 "initialDelaySeconds": 60,
424 "timeoutSeconds": 30,
425 "failureThreshold": 10,
426 },
427 },
428 }
429 ],
430 "kubernetesResources": {
431 "ingressResources": [
432 {
433 "name": "{}-ingress".format(app_name),
434 "annotations": {
435 "nginx.ingress.kubernetes.io/whitelist-source-range": config.get(
436 "ingress_whitelist_source_range"
437 ),
438 },
439 "spec": {
440 "rules": [
441 {
442 "host": app_name,
443 "http": {
444 "paths": [
445 {
446 "path": "/",
447 "backend": {
448 "serviceName": app_name,
449 "servicePort": port,
450 },
451 }
452 ]
453 },
454 }
455 ],
456 "tls": [
457 {
458 "hosts": [app_name],
459 "secretName": config.get("tls_secret_name"),
460 }
461 ],
462 },
463 }
464 ],
465 },
466 }
467
468 spec = pod_spec.make_pod_spec(
469 image_info, config, relation_state, app_name, port
470 )
471
472 self.assertDictEqual(expected_result, spec)
473
474 def test_make_pod_spec_without_image_info(self) -> NoReturn:
475 """Testing make pod spec without image_info."""
476 image_info = None
477 config = {
478 "site_url": "",
479 "cluster_issuer": "",
480 }
481 relation_state = {
482 "mysql_host": "mysql",
483 "mysql_port": 3306,
484 "mysql_user": "mano",
485 "mysql_password": "manopw",
486 "mysql_root_password": "rootpw",
487 }
488 app_name = "mysqld-exporter"
489 port = 9104
490
491 spec = pod_spec.make_pod_spec(
492 image_info, config, relation_state, app_name, port
493 )
494
495 self.assertIsNone(spec)
496
497 def test_make_pod_spec_without_relation_state(self) -> NoReturn:
498 """Testing make pod spec without relation_state."""
499 image_info = {"upstream-source": "bitnami/mysqld-exporter:latest"}
500 config = {
501 "site_url": "",
502 "cluster_issuer": "",
503 }
504 relation_state = {}
505 app_name = "mysqld-exporter"
506 port = 9104
507
508 with self.assertRaises(ValueError):
509 pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port)
510
511
512 if __name__ == "__main__":
513 unittest.main()