842769c12b8feac221f74d0fd8c2c1fac24d8bbd
[osm/devops.git] / installers / charm / grafana / 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 = 3000
35
36 expected_result = [
37 {
38 "name": "grafana",
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 "prometheus_host": "prometheus",
53 "prometheus_port": "9090",
54 }
55
56 expected_result = {}
57
58 pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state)
59
60 self.assertDictEqual(expected_result, pod_envconfig)
61
62 def test_make_pod_ingress_resources_without_site_url(self) -> NoReturn:
63 """Testing make pod ingress resources without site_url."""
64 config = {"site_url": ""}
65 app_name = "grafana"
66 port = 3000
67
68 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
69 config, app_name, port
70 )
71
72 self.assertIsNone(pod_ingress_resources)
73
74 def test_make_pod_ingress_resources(self) -> NoReturn:
75 """Testing make pod ingress resources."""
76 config = {
77 "site_url": "http://grafana",
78 "max_file_size": 0,
79 "ingress_whitelist_source_range": "",
80 }
81 app_name = "grafana"
82 port = 3000
83
84 expected_result = [
85 {
86 "name": f"{app_name}-ingress",
87 "annotations": {
88 "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
89 "nginx.ingress.kubernetes.io/ssl-redirect": "false",
90 },
91 "spec": {
92 "rules": [
93 {
94 "host": app_name,
95 "http": {
96 "paths": [
97 {
98 "path": "/",
99 "backend": {
100 "serviceName": app_name,
101 "servicePort": port,
102 },
103 }
104 ]
105 },
106 }
107 ]
108 },
109 }
110 ]
111
112 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
113 config, app_name, port
114 )
115
116 self.assertListEqual(expected_result, pod_ingress_resources)
117
118 def test_make_pod_ingress_resources_with_whitelist_source_range(self) -> NoReturn:
119 """Testing make pod ingress resources with whitelist_source_range."""
120 config = {
121 "site_url": "http://grafana",
122 "max_file_size": 0,
123 "ingress_whitelist_source_range": "0.0.0.0/0",
124 }
125 app_name = "grafana"
126 port = 3000
127
128 expected_result = [
129 {
130 "name": f"{app_name}-ingress",
131 "annotations": {
132 "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
133 "nginx.ingress.kubernetes.io/ssl-redirect": "false",
134 "nginx.ingress.kubernetes.io/whitelist-source-range": config[
135 "ingress_whitelist_source_range"
136 ],
137 },
138 "spec": {
139 "rules": [
140 {
141 "host": app_name,
142 "http": {
143 "paths": [
144 {
145 "path": "/",
146 "backend": {
147 "serviceName": app_name,
148 "servicePort": port,
149 },
150 }
151 ]
152 },
153 }
154 ]
155 },
156 }
157 ]
158
159 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
160 config, app_name, port
161 )
162
163 self.assertListEqual(expected_result, pod_ingress_resources)
164
165 def test_make_pod_ingress_resources_with_https(self) -> NoReturn:
166 """Testing make pod ingress resources with HTTPs."""
167 config = {
168 "site_url": "https://grafana",
169 "max_file_size": 0,
170 "ingress_whitelist_source_range": "",
171 "tls_secret_name": "",
172 }
173 app_name = "grafana"
174 port = 3000
175
176 expected_result = [
177 {
178 "name": f"{app_name}-ingress",
179 "annotations": {
180 "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
181 },
182 "spec": {
183 "rules": [
184 {
185 "host": app_name,
186 "http": {
187 "paths": [
188 {
189 "path": "/",
190 "backend": {
191 "serviceName": app_name,
192 "servicePort": port,
193 },
194 }
195 ]
196 },
197 }
198 ],
199 "tls": [{"hosts": [app_name]}],
200 },
201 }
202 ]
203
204 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
205 config, app_name, port
206 )
207
208 self.assertListEqual(expected_result, pod_ingress_resources)
209
210 def test_make_pod_ingress_resources_with_https_tls_secret_name(self) -> NoReturn:
211 """Testing make pod ingress resources with HTTPs and TLS secret name."""
212 config = {
213 "site_url": "https://grafana",
214 "max_file_size": 0,
215 "ingress_whitelist_source_range": "",
216 "tls_secret_name": "secret_name",
217 }
218 app_name = "grafana"
219 port = 3000
220
221 expected_result = [
222 {
223 "name": f"{app_name}-ingress",
224 "annotations": {
225 "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
226 },
227 "spec": {
228 "rules": [
229 {
230 "host": app_name,
231 "http": {
232 "paths": [
233 {
234 "path": "/",
235 "backend": {
236 "serviceName": app_name,
237 "servicePort": port,
238 },
239 }
240 ]
241 },
242 }
243 ],
244 "tls": [
245 {"hosts": [app_name], "secretName": config["tls_secret_name"]}
246 ],
247 },
248 }
249 ]
250
251 pod_ingress_resources = pod_spec._make_pod_ingress_resources(
252 config, app_name, port
253 )
254
255 self.assertListEqual(expected_result, pod_ingress_resources)
256
257 def test_make_pod_files(self) -> NoReturn:
258 """Testing make pod files."""
259 relation_state = {
260 "prometheus_host": "prometheus",
261 "prometheus_port": "9090",
262 }
263
264 expected_result = [
265 {
266 "name": "dashboards",
267 "mountPath": "/etc/grafana/provisioning/dashboards/",
268 "files": [
269 {
270 "path": "dashboard-osm.yml",
271 "content": (
272 "apiVersion: 1\n"
273 "providers:\n"
274 " - name: 'osm'\n"
275 " orgId: 1\n"
276 " folder: ''\n"
277 " type: file\n"
278 " options:\n"
279 " path: /etc/grafana/provisioning/dashboards/\n"
280 ),
281 }
282 ],
283 },
284 {
285 "name": "datasources",
286 "mountPath": "/etc/grafana/provisioning/datasources/",
287 "files": [
288 {
289 "path": "datasource-prometheus.yml",
290 "content": (
291 "datasources:\n"
292 " - access: proxy\n"
293 " editable: true\n"
294 " is_default: true\n"
295 " name: osm_prometheus\n"
296 " orgId: 1\n"
297 " type: prometheus\n"
298 " version: 1\n"
299 " url: http://{}:{}\n".format(
300 relation_state.get("prometheus_host"),
301 relation_state.get("prometheus_port"),
302 )
303 ),
304 }
305 ],
306 },
307 ]
308
309 pod_envconfig = pod_spec._make_pod_files(relation_state)
310
311 self.assertListEqual(expected_result, pod_envconfig)
312
313 def test_make_readiness_probe(self) -> NoReturn:
314 """Testing make readiness probe."""
315 port = 3000
316
317 expected_result = {
318 "httpGet": {
319 "path": "/api/health",
320 "port": port,
321 },
322 "initialDelaySeconds": 10,
323 "periodSeconds": 10,
324 "timeoutSeconds": 5,
325 "successThreshold": 1,
326 "failureThreshold": 3,
327 }
328
329 readiness_probe = pod_spec._make_readiness_probe(port)
330
331 self.assertDictEqual(expected_result, readiness_probe)
332
333 def test_make_liveness_probe(self) -> NoReturn:
334 """Testing make liveness probe."""
335 port = 3000
336
337 expected_result = {
338 "httpGet": {
339 "path": "/api/health",
340 "port": port,
341 },
342 "initialDelaySeconds": 60,
343 "timeoutSeconds": 30,
344 "failureThreshold": 10,
345 }
346
347 liveness_probe = pod_spec._make_liveness_probe(port)
348
349 self.assertDictEqual(expected_result, liveness_probe)
350
351 def test_make_pod_spec(self) -> NoReturn:
352 """Testing make pod spec."""
353 image_info = {"upstream-source": "ubuntu/grafana:latest"}
354 config = {
355 "site_url": "",
356 }
357 relation_state = {
358 "prometheus_host": "prometheus",
359 "prometheus_port": "9090",
360 }
361 app_name = "grafana"
362 port = 3000
363
364 expected_result = {
365 "version": 3,
366 "containers": [
367 {
368 "name": app_name,
369 "imageDetails": image_info,
370 "imagePullPolicy": "Always",
371 "ports": [
372 {
373 "name": app_name,
374 "containerPort": port,
375 "protocol": "TCP",
376 }
377 ],
378 "envConfig": {},
379 "volumeConfig": [
380 {
381 "name": "dashboards",
382 "mountPath": "/etc/grafana/provisioning/dashboards/",
383 "files": [
384 {
385 "path": "dashboard-osm.yml",
386 "content": (
387 "apiVersion: 1\n"
388 "providers:\n"
389 " - name: 'osm'\n"
390 " orgId: 1\n"
391 " folder: ''\n"
392 " type: file\n"
393 " options:\n"
394 " path: /etc/grafana/provisioning/dashboards/\n"
395 ),
396 }
397 ],
398 },
399 {
400 "name": "datasources",
401 "mountPath": "/etc/grafana/provisioning/datasources/",
402 "files": [
403 {
404 "path": "datasource-prometheus.yml",
405 "content": (
406 "datasources:\n"
407 " - access: proxy\n"
408 " editable: true\n"
409 " is_default: true\n"
410 " name: osm_prometheus\n"
411 " orgId: 1\n"
412 " type: prometheus\n"
413 " version: 1\n"
414 " url: http://{}:{}\n".format(
415 relation_state.get("prometheus_host"),
416 relation_state.get("prometheus_port"),
417 )
418 ),
419 }
420 ],
421 },
422 ],
423 "kubernetes": {
424 "readinessProbe": {
425 "httpGet": {
426 "path": "/api/health",
427 "port": port,
428 },
429 "initialDelaySeconds": 10,
430 "periodSeconds": 10,
431 "timeoutSeconds": 5,
432 "successThreshold": 1,
433 "failureThreshold": 3,
434 },
435 "livenessProbe": {
436 "httpGet": {
437 "path": "/api/health",
438 "port": port,
439 },
440 "initialDelaySeconds": 60,
441 "timeoutSeconds": 30,
442 "failureThreshold": 10,
443 },
444 },
445 }
446 ],
447 "kubernetesResources": {"ingressResources": []},
448 }
449
450 spec = pod_spec.make_pod_spec(
451 image_info, config, relation_state, app_name, port
452 )
453
454 self.assertDictEqual(expected_result, spec)
455
456 def test_make_pod_spec_with_ingress(self) -> NoReturn:
457 """Testing make pod spec."""
458 image_info = {"upstream-source": "ubuntu/grafana:latest"}
459 config = {
460 "site_url": "https://grafana",
461 "tls_secret_name": "grafana",
462 "max_file_size": 0,
463 "ingress_whitelist_source_range": "0.0.0.0/0",
464 }
465 relation_state = {
466 "prometheus_host": "prometheus",
467 "prometheus_port": "9090",
468 }
469 app_name = "grafana"
470 port = 3000
471
472 expected_result = {
473 "version": 3,
474 "containers": [
475 {
476 "name": app_name,
477 "imageDetails": image_info,
478 "imagePullPolicy": "Always",
479 "ports": [
480 {
481 "name": app_name,
482 "containerPort": port,
483 "protocol": "TCP",
484 }
485 ],
486 "envConfig": {},
487 "volumeConfig": [
488 {
489 "name": "dashboards",
490 "mountPath": "/etc/grafana/provisioning/dashboards/",
491 "files": [
492 {
493 "path": "dashboard-osm.yml",
494 "content": (
495 "apiVersion: 1\n"
496 "providers:\n"
497 " - name: 'osm'\n"
498 " orgId: 1\n"
499 " folder: ''\n"
500 " type: file\n"
501 " options:\n"
502 " path: /etc/grafana/provisioning/dashboards/\n"
503 ),
504 }
505 ],
506 },
507 {
508 "name": "datasources",
509 "mountPath": "/etc/grafana/provisioning/datasources/",
510 "files": [
511 {
512 "path": "datasource-prometheus.yml",
513 "content": (
514 "datasources:\n"
515 " - access: proxy\n"
516 " editable: true\n"
517 " is_default: true\n"
518 " name: osm_prometheus\n"
519 " orgId: 1\n"
520 " type: prometheus\n"
521 " version: 1\n"
522 " url: http://{}:{}\n".format(
523 relation_state.get("prometheus_host"),
524 relation_state.get("prometheus_port"),
525 )
526 ),
527 }
528 ],
529 },
530 ],
531 "kubernetes": {
532 "readinessProbe": {
533 "httpGet": {
534 "path": "/api/health",
535 "port": port,
536 },
537 "initialDelaySeconds": 10,
538 "periodSeconds": 10,
539 "timeoutSeconds": 5,
540 "successThreshold": 1,
541 "failureThreshold": 3,
542 },
543 "livenessProbe": {
544 "httpGet": {
545 "path": "/api/health",
546 "port": port,
547 },
548 "initialDelaySeconds": 60,
549 "timeoutSeconds": 30,
550 "failureThreshold": 10,
551 },
552 },
553 }
554 ],
555 "kubernetesResources": {
556 "ingressResources": [
557 {
558 "name": "{}-ingress".format(app_name),
559 "annotations": {
560 "nginx.ingress.kubernetes.io/proxy-body-size": str(
561 config.get("max_file_size")
562 ),
563 "nginx.ingress.kubernetes.io/whitelist-source-range": config.get(
564 "ingress_whitelist_source_range"
565 ),
566 },
567 "spec": {
568 "rules": [
569 {
570 "host": app_name,
571 "http": {
572 "paths": [
573 {
574 "path": "/",
575 "backend": {
576 "serviceName": app_name,
577 "servicePort": port,
578 },
579 }
580 ]
581 },
582 }
583 ],
584 "tls": [
585 {
586 "hosts": [app_name],
587 "secretName": config.get("tls_secret_name"),
588 }
589 ],
590 },
591 }
592 ],
593 },
594 }
595
596 spec = pod_spec.make_pod_spec(
597 image_info, config, relation_state, app_name, port
598 )
599
600 self.assertDictEqual(expected_result, spec)
601
602 def test_make_pod_spec_without_image_info(self) -> NoReturn:
603 """Testing make pod spec without image_info."""
604 image_info = None
605 config = {
606 "site_url": "",
607 }
608 relation_state = {
609 "prometheus_host": "prometheus",
610 "prometheus_port": "9090",
611 }
612 app_name = "grafana"
613 port = 3000
614
615 spec = pod_spec.make_pod_spec(
616 image_info, config, relation_state, app_name, port
617 )
618
619 self.assertIsNone(spec)
620
621 def test_make_pod_spec_without_relation_state(self) -> NoReturn:
622 """Testing make pod spec without relation_state."""
623 image_info = {"upstream-source": "ubuntu/grafana:latest"}
624 config = {
625 "site_url": "",
626 }
627 relation_state = {}
628 app_name = "grafana"
629 port = 3000
630
631 with self.assertRaises(ValueError):
632 pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port)
633
634
635 if __name__ == "__main__":
636 unittest.main()