Refactoring NBI Charm to use Operator Framework
[osm/devops.git] / installers / charm / nbi / tests / test_pod_spec.py
diff --git a/installers/charm/nbi/tests/test_pod_spec.py b/installers/charm/nbi/tests/test_pod_spec.py
new file mode 100644 (file)
index 0000000..7be4591
--- /dev/null
@@ -0,0 +1,648 @@
+#!/usr/bin/env python3
+# Copyright 2020 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact: legal@canonical.com
+#
+# To get in touch with the maintainers, please contact:
+# osm-charmers@lists.launchpad.net
+##
+
+from pydantic import ValidationError
+from typing import NoReturn
+import unittest
+
+import pod_spec
+
+
+class TestPodSpec(unittest.TestCase):
+    """Pod spec unit tests."""
+
+    def test_make_pod_ports(self) -> NoReturn:
+        """Testing make pod ports."""
+        port = 9999
+
+        expected_result = [
+            {
+                "name": "nbi",
+                "containerPort": port,
+                "protocol": "TCP",
+            }
+        ]
+
+        pod_ports = pod_spec._make_pod_ports(port)
+
+        self.assertListEqual(expected_result, pod_ports)
+
+    def test_make_pod_envconfig_without_keystone(self) -> NoReturn:
+        """Teting make pod envconfig without Keystone."""
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "internal",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+        }
+
+        expected_result = {
+            "ALLOW_ANONYMOUS_LOGIN": "yes",
+            "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"],
+            "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public",
+            "OSMNBI_MESSAGE_HOST": relation_state["message_host"],
+            "OSMNBI_MESSAGE_DRIVER": "kafka",
+            "OSMNBI_MESSAGE_PORT": relation_state["message_port"],
+            "OSMNBI_DATABASE_DRIVER": "mongo",
+            "OSMNBI_DATABASE_URI": relation_state["database_uri"],
+            "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"],
+            "OSMNBI_STORAGE_DRIVER": "mongo",
+            "OSMNBI_STORAGE_PATH": "/app/storage",
+            "OSMNBI_STORAGE_COLLECTION": "files",
+            "OSMNBI_STORAGE_URI": relation_state["database_uri"],
+            "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"],
+            "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"],
+            "OSMNBI_LOG_LEVEL": config["log_level"],
+            "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"],
+        }
+
+        pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state)
+
+        self.assertDictEqual(expected_result, pod_envconfig)
+
+    def test_make_pod_envconfig_with_keystone(self) -> NoReturn:
+        """Teting make pod envconfig with Keystone."""
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "keystone",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+            "keystone_host": "keystone",
+            "keystone_port": 5000,
+            "keystone_user_domain_name": "user_domain",
+            "keystone_project_domain_name": "project_domain",
+            "keystone_username": "username",
+            "keystone_password": "password",
+            "keystone_service": "service",
+        }
+
+        expected_result = {
+            "ALLOW_ANONYMOUS_LOGIN": "yes",
+            "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"],
+            "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public",
+            "OSMNBI_MESSAGE_HOST": relation_state["message_host"],
+            "OSMNBI_MESSAGE_DRIVER": "kafka",
+            "OSMNBI_MESSAGE_PORT": relation_state["message_port"],
+            "OSMNBI_DATABASE_DRIVER": "mongo",
+            "OSMNBI_DATABASE_URI": relation_state["database_uri"],
+            "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"],
+            "OSMNBI_STORAGE_DRIVER": "mongo",
+            "OSMNBI_STORAGE_PATH": "/app/storage",
+            "OSMNBI_STORAGE_COLLECTION": "files",
+            "OSMNBI_STORAGE_URI": relation_state["database_uri"],
+            "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"],
+            "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"],
+            "OSMNBI_LOG_LEVEL": config["log_level"],
+            "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"],
+            "OSMNBI_AUTHENTICATION_AUTH_URL": relation_state["keystone_host"],
+            "OSMNBI_AUTHENTICATION_AUTH_PORT": relation_state["keystone_port"],
+            "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": relation_state[
+                "keystone_user_domain_name"
+            ],
+            "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": relation_state[
+                "keystone_project_domain_name"
+            ],
+            "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": relation_state[
+                "keystone_username"
+            ],
+            "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": relation_state[
+                "keystone_password"
+            ],
+            "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": relation_state["keystone_service"],
+        }
+
+        pod_envconfig = pod_spec._make_pod_envconfig(config, relation_state)
+
+        self.assertDictEqual(expected_result, pod_envconfig)
+
+    def test_make_pod_envconfig_wrong_auth_backend(self) -> NoReturn:
+        """Teting make pod envconfig with wrong auth_backend."""
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "kerberos",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+            "keystone_host": "keystone",
+            "keystone_port": 5000,
+            "keystone_user_domain_name": "user_domain",
+            "keystone_project_domain_name": "project_domain",
+            "keystone_username": "username",
+            "keystone_password": "password",
+            "keystone_service": "service",
+        }
+
+        with self.assertRaises(ValueError) as exc:
+            pod_spec._make_pod_envconfig(config, relation_state)
+
+        self.assertTrue(
+            "auth_backend needs to be either internal or keystone" in str(exc.exception)
+        )
+
+    def test_make_pod_ingress_resources_without_site_url(self) -> NoReturn:
+        """Testing make pod ingress resources without site_url."""
+        config = {"site_url": ""}
+        app_name = "nbi"
+        port = 9999
+
+        pod_ingress_resources = pod_spec._make_pod_ingress_resources(
+            config, app_name, port
+        )
+
+        self.assertIsNone(pod_ingress_resources)
+
+    def test_make_pod_ingress_resources(self) -> NoReturn:
+        """Testing make pod ingress resources."""
+        config = {
+            "site_url": "http://nbi",
+            "max_file_size": 0,
+            "ingress_whitelist_source_range": "",
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = [
+            {
+                "name": f"{app_name}-ingress",
+                "annotations": {
+                    "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
+                    "nginx.ingress.kubernetes.io/ssl-redirect": "false",
+                },
+                "spec": {
+                    "rules": [
+                        {
+                            "host": app_name,
+                            "http": {
+                                "paths": [
+                                    {
+                                        "path": "/",
+                                        "backend": {
+                                            "serviceName": app_name,
+                                            "servicePort": port,
+                                        },
+                                    }
+                                ]
+                            },
+                        }
+                    ]
+                },
+            }
+        ]
+
+        pod_ingress_resources = pod_spec._make_pod_ingress_resources(
+            config, app_name, port
+        )
+
+        self.assertListEqual(expected_result, pod_ingress_resources)
+
+    def test_make_pod_ingress_resources_with_whitelist_source_range(self) -> NoReturn:
+        """Testing make pod ingress resources with whitelist_source_range."""
+        config = {
+            "site_url": "http://nbi",
+            "max_file_size": 0,
+            "ingress_whitelist_source_range": "0.0.0.0/0",
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = [
+            {
+                "name": f"{app_name}-ingress",
+                "annotations": {
+                    "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
+                    "nginx.ingress.kubernetes.io/ssl-redirect": "false",
+                    "nginx.ingress.kubernetes.io/whitelist-source-range": config[
+                        "ingress_whitelist_source_range"
+                    ],
+                },
+                "spec": {
+                    "rules": [
+                        {
+                            "host": app_name,
+                            "http": {
+                                "paths": [
+                                    {
+                                        "path": "/",
+                                        "backend": {
+                                            "serviceName": app_name,
+                                            "servicePort": port,
+                                        },
+                                    }
+                                ]
+                            },
+                        }
+                    ]
+                },
+            }
+        ]
+
+        pod_ingress_resources = pod_spec._make_pod_ingress_resources(
+            config, app_name, port
+        )
+
+        self.assertListEqual(expected_result, pod_ingress_resources)
+
+    def test_make_pod_ingress_resources_with_https(self) -> NoReturn:
+        """Testing make pod ingress resources with HTTPs."""
+        config = {
+            "site_url": "https://nbi",
+            "max_file_size": 0,
+            "ingress_whitelist_source_range": "",
+            "tls_secret_name": "",
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = [
+            {
+                "name": f"{app_name}-ingress",
+                "annotations": {
+                    "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
+                },
+                "spec": {
+                    "rules": [
+                        {
+                            "host": app_name,
+                            "http": {
+                                "paths": [
+                                    {
+                                        "path": "/",
+                                        "backend": {
+                                            "serviceName": app_name,
+                                            "servicePort": port,
+                                        },
+                                    }
+                                ]
+                            },
+                        }
+                    ],
+                    "tls": [{"hosts": [app_name]}],
+                },
+            }
+        ]
+
+        pod_ingress_resources = pod_spec._make_pod_ingress_resources(
+            config, app_name, port
+        )
+
+        self.assertListEqual(expected_result, pod_ingress_resources)
+
+    def test_make_pod_ingress_resources_with_https_tls_secret_name(self) -> NoReturn:
+        """Testing make pod ingress resources with HTTPs and TLS secret name."""
+        config = {
+            "site_url": "https://nbi",
+            "max_file_size": 0,
+            "ingress_whitelist_source_range": "",
+            "tls_secret_name": "secret_name",
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = [
+            {
+                "name": f"{app_name}-ingress",
+                "annotations": {
+                    "nginx.ingress.kubernetes.io/proxy-body-size": f"{config['max_file_size']}",
+                },
+                "spec": {
+                    "rules": [
+                        {
+                            "host": app_name,
+                            "http": {
+                                "paths": [
+                                    {
+                                        "path": "/",
+                                        "backend": {
+                                            "serviceName": app_name,
+                                            "servicePort": port,
+                                        },
+                                    }
+                                ]
+                            },
+                        }
+                    ],
+                    "tls": [
+                        {"hosts": [app_name], "secretName": config["tls_secret_name"]}
+                    ],
+                },
+            }
+        ]
+
+        pod_ingress_resources = pod_spec._make_pod_ingress_resources(
+            config, app_name, port
+        )
+
+        self.assertListEqual(expected_result, pod_ingress_resources)
+
+    def test_make_startup_probe(self) -> NoReturn:
+        """Testing make startup probe."""
+        expected_result = {
+            "exec": {"command": ["/usr/bin/pgrep python3"]},
+            "initialDelaySeconds": 60,
+            "timeoutSeconds": 5,
+        }
+
+        startup_probe = pod_spec._make_startup_probe()
+
+        self.assertDictEqual(expected_result, startup_probe)
+
+    def test_make_readiness_probe(self) -> NoReturn:
+        """Testing make readiness probe."""
+        port = 9999
+
+        expected_result = {
+            "httpGet": {
+                "path": "/osm/",
+                "port": port,
+            },
+            "initialDelaySeconds": 45,
+            "timeoutSeconds": 5,
+        }
+
+        readiness_probe = pod_spec._make_readiness_probe(port)
+
+        self.assertDictEqual(expected_result, readiness_probe)
+
+    def test_make_liveness_probe(self) -> NoReturn:
+        """Testing make liveness probe."""
+        port = 9999
+
+        expected_result = {
+            "httpGet": {
+                "path": "/osm/",
+                "port": port,
+            },
+            "initialDelaySeconds": 45,
+            "timeoutSeconds": 5,
+        }
+
+        liveness_probe = pod_spec._make_liveness_probe(port)
+
+        self.assertDictEqual(expected_result, liveness_probe)
+
+    def test_make_pod_spec_without_image_info(self) -> NoReturn:
+        """Testing make pod spec without image_info."""
+        image_info = None
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "internal",
+            "site_url": "",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+        }
+        app_name = "nbi"
+        port = 9999
+
+        spec = pod_spec.make_pod_spec(
+            image_info, config, relation_state, app_name, port
+        )
+
+        self.assertIsNone(spec)
+
+    def test_make_pod_spec_without_config(self) -> NoReturn:
+        """Testing make pod spec without config."""
+        image_info = {"upstream-source": "opensourcemano/nbi:8"}
+        config = {}
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+        }
+        app_name = "nbi"
+        port = 9999
+
+        with self.assertRaises(ValidationError) as exc:
+            pod_spec.make_pod_spec(
+                image_info, config, relation_state, app_name, port
+            )
+
+    def test_make_pod_spec_without_relation_state(self) -> NoReturn:
+        """Testing make pod spec without relation_state."""
+        image_info = {"upstream-source": "opensourcemano/nbi:8"}
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "internal",
+            "site_url": "",
+        }
+        relation_state = {}
+        app_name = "nbi"
+        port = 9999
+
+        with self.assertRaises(ValidationError) as exc:
+            pod_spec.make_pod_spec(
+                image_info, config, relation_state, app_name, port
+            )
+
+    def test_make_pod_spec(self) -> NoReturn:
+        """Testing make pod spec."""
+        image_info = {"upstream-source": "opensourcemano/nbi:8"}
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "internal",
+            "site_url": "",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = {
+            "version": 3,
+            "containers": [
+                {
+                    "name": app_name,
+                    "imageDetails": image_info,
+                    "imagePullPolicy": "Always",
+                    "ports": [
+                        {
+                            "name": "nbi",
+                            "containerPort": port,
+                            "protocol": "TCP",
+                        }
+                    ],
+                    "envConfig": {
+                        "ALLOW_ANONYMOUS_LOGIN": "yes",
+                        "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"],
+                        "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public",
+                        "OSMNBI_MESSAGE_HOST": relation_state["message_host"],
+                        "OSMNBI_MESSAGE_DRIVER": "kafka",
+                        "OSMNBI_MESSAGE_PORT": relation_state["message_port"],
+                        "OSMNBI_DATABASE_DRIVER": "mongo",
+                        "OSMNBI_DATABASE_URI": relation_state["database_uri"],
+                        "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"],
+                        "OSMNBI_STORAGE_DRIVER": "mongo",
+                        "OSMNBI_STORAGE_PATH": "/app/storage",
+                        "OSMNBI_STORAGE_COLLECTION": "files",
+                        "OSMNBI_STORAGE_URI": relation_state["database_uri"],
+                        "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"],
+                        "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"],
+                        "OSMNBI_LOG_LEVEL": config["log_level"],
+                        "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"],
+                    },
+                }
+            ],
+            "kubernetesResources": {
+                "ingressResources": [],
+            },
+        }
+
+        spec = pod_spec.make_pod_spec(
+            image_info, config, relation_state, app_name, port
+        )
+
+        self.assertDictEqual(expected_result, spec)
+
+    def test_make_pod_spec_with_keystone(self) -> NoReturn:
+        """Testing make pod spec with keystone."""
+        image_info = {"upstream-source": "opensourcemano/nbi:8"}
+        config = {
+            "enable_test": False,
+            "database_commonkey": "commonkey",
+            "log_level": "DEBUG",
+            "auth_backend": "keystone",
+            "site_url": "",
+        }
+        relation_state = {
+            "message_host": "kafka",
+            "message_port": 9090,
+            "database_uri": "mongo://mongo",
+            "prometheus_host": "prometheus",
+            "prometheus_port": 9082,
+            "keystone_host": "keystone",
+            "keystone_port": 5000,
+            "keystone_user_domain_name": "user_domain",
+            "keystone_project_domain_name": "project_domain",
+            "keystone_username": "username",
+            "keystone_password": "password",
+            "keystone_service": "service",
+        }
+        app_name = "nbi"
+        port = 9999
+
+        expected_result = {
+            "version": 3,
+            "containers": [
+                {
+                    "name": app_name,
+                    "imageDetails": image_info,
+                    "imagePullPolicy": "Always",
+                    "ports": [
+                        {
+                            "name": "nbi",
+                            "containerPort": port,
+                            "protocol": "TCP",
+                        }
+                    ],
+                    "envConfig": {
+                        "ALLOW_ANONYMOUS_LOGIN": "yes",
+                        "OSMNBI_SERVER_ENABLE_TEST": config["enable_test"],
+                        "OSMNBI_STATIC_DIR": "/app/osm_nbi/html_public",
+                        "OSMNBI_MESSAGE_HOST": relation_state["message_host"],
+                        "OSMNBI_MESSAGE_DRIVER": "kafka",
+                        "OSMNBI_MESSAGE_PORT": relation_state["message_port"],
+                        "OSMNBI_DATABASE_DRIVER": "mongo",
+                        "OSMNBI_DATABASE_URI": relation_state["database_uri"],
+                        "OSMNBI_DATABASE_COMMONKEY": config["database_commonkey"],
+                        "OSMNBI_STORAGE_DRIVER": "mongo",
+                        "OSMNBI_STORAGE_PATH": "/app/storage",
+                        "OSMNBI_STORAGE_COLLECTION": "files",
+                        "OSMNBI_STORAGE_URI": relation_state["database_uri"],
+                        "OSMNBI_PROMETHEUS_HOST": relation_state["prometheus_host"],
+                        "OSMNBI_PROMETHEUS_PORT": relation_state["prometheus_port"],
+                        "OSMNBI_LOG_LEVEL": config["log_level"],
+                        "OSMNBI_AUTHENTICATION_BACKEND": config["auth_backend"],
+                        "OSMNBI_AUTHENTICATION_AUTH_URL": relation_state[
+                            "keystone_host"
+                        ],
+                        "OSMNBI_AUTHENTICATION_AUTH_PORT": relation_state[
+                            "keystone_port"
+                        ],
+                        "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": relation_state[
+                            "keystone_user_domain_name"
+                        ],
+                        "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": relation_state[
+                            "keystone_project_domain_name"
+                        ],
+                        "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": relation_state[
+                            "keystone_username"
+                        ],
+                        "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": relation_state[
+                            "keystone_password"
+                        ],
+                        "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": relation_state[
+                            "keystone_service"
+                        ],
+                    },
+                }
+            ],
+            "kubernetesResources": {
+                "ingressResources": [],
+            },
+        }
+
+        spec = pod_spec.make_pod_spec(
+            image_info, config, relation_state, app_name, port
+        )
+
+        self.assertDictEqual(expected_result, spec)
+
+
+if __name__ == "__main__":
+    unittest.main()