Certificate addition support to mon and ro charms 49/10549/5
authorDavid Garcia <david.garcia@canonical.com>
Thu, 25 Mar 2021 14:04:52 +0000 (15:04 +0100)
committerDavid Garcia <david.garcia@canonical.com>
Thu, 25 Mar 2021 17:40:23 +0000 (18:40 +0100)
This commit adds the `certificates` config in both RO and MON charms.

Changes:
 - Add `certificates` config option in mon and ro charms
 - Use charmed-osm/ops-lib-charmed-osm library in all charms

Change-Id: I14e3cf7ad2c846c4a2af57f317d4151119ce5fbb
Signed-off-by: David Garcia <david.garcia@canonical.com>
26 files changed:
installers/charm/grafana/requirements.txt
installers/charm/grafana/tests/__init__.py
installers/charm/keystone/requirements.txt
installers/charm/keystone/tests/__init__.py
installers/charm/lcm/requirements.txt
installers/charm/lcm/tests/__init__.py
installers/charm/mon/config.yaml
installers/charm/mon/requirements.txt
installers/charm/mon/src/charm.py
installers/charm/mon/tests/__init__.py
installers/charm/mon/tests/test_charm.py
installers/charm/nbi/requirements.txt
installers/charm/nbi/tests/__init__.py
installers/charm/ng-ui/requirements.txt
installers/charm/ng-ui/tests/__init__.py
installers/charm/pla/requirements.txt
installers/charm/pla/tests/__init__.py
installers/charm/pol/requirements.txt
installers/charm/pol/tests/__init__.py
installers/charm/prometheus/requirements.txt
installers/charm/prometheus/tests/__init__.py
installers/charm/ro/config.yaml
installers/charm/ro/requirements.txt
installers/charm/ro/src/charm.py
installers/charm/ro/tests/__init__.py
installers/charm/ro/tests/test_charm.py

index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index 0967ea6..446d5ce 100644 (file)
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
 #!/usr/bin/env python3
-# Copyright 2021 Canonical Ltd.
+# 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
 #
 # 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
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index d42bd9e..5c18be8 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 cryptography
 # osm-charmers@lists.launchpad.net
 ##
 cryptography
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ee5553b..446d5ce 100644 (file)
 
 import sys
 
 
 import sys
 
+
 import mock
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index 5391497..f6d1ae1 100644 (file)
@@ -76,3 +76,11 @@ options:
     description: MON will use Keystone backend
     type: boolean
     default: false
     description: MON will use Keystone backend
     type: boolean
     default: false
+  certificates:
+    type: string
+    description: |
+      comma-separated list of <name>:<content> certificates.
+      Where:
+        name: name of the file for the certificate
+        content: base64 content of the certificate
+      The path for the files is /certs.
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index d0145d4..ab6dd2d 100755 (executable)
@@ -23,8 +23,9 @@
 # pylint: disable=E0213
 
 
 # pylint: disable=E0213
 
 
+import base64
 import logging
 import logging
-from typing import NoReturn
+from typing import NoReturn, Optional
 
 
 from ops.main import main
 
 
 from ops.main import main
@@ -33,7 +34,7 @@ from opslib.osm.interfaces.kafka import KafkaClient
 from opslib.osm.interfaces.keystone import KeystoneClient
 from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.interfaces.prometheus import PrometheusClient
 from opslib.osm.interfaces.keystone import KeystoneClient
 from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.interfaces.prometheus import PrometheusClient
-from opslib.osm.pod import ContainerV3Builder, PodSpecV3Builder
+from opslib.osm.pod import ContainerV3Builder, FilesV3Builder, PodSpecV3Builder
 from opslib.osm.validator import ModelValidator, validator
 
 
 from opslib.osm.validator import ModelValidator, validator
 
 
@@ -42,6 +43,26 @@ logger = logging.getLogger(__name__)
 PORT = 8000
 
 
 PORT = 8000
 
 
+def _check_certificate_data(name: str, content: str):
+    if not name or not content:
+        raise ValueError("certificate name and content must be a non-empty string")
+
+
+def _extract_certificates(certs_config: str):
+    certificates = {}
+    if certs_config:
+        cert_list = certs_config.split(",")
+        for cert in cert_list:
+            name, content = cert.split(":")
+            _check_certificate_data(name, content)
+            certificates[name] = content
+    return certificates
+
+
+def decode(content: str):
+    return base64.b64decode(content.encode("utf-8")).decode("utf-8")
+
+
 class ConfigModel(ModelValidator):
     keystone_enabled: bool
     vca_host: str
 class ConfigModel(ModelValidator):
     keystone_enabled: bool
     vca_host: str
@@ -57,6 +78,7 @@ class ConfigModel(ModelValidator):
     grafana_url: str
     grafana_user: str
     grafana_password: str
     grafana_url: str
     grafana_user: str
     grafana_password: str
+    certificates: Optional[str]
 
     @validator("log_level")
     def validate_log_level(cls, v):
 
     @validator("log_level")
     def validate_log_level(cls, v):
@@ -64,6 +86,16 @@ class ConfigModel(ModelValidator):
             raise ValueError("value must be INFO or DEBUG")
         return v
 
             raise ValueError("value must be INFO or DEBUG")
         return v
 
+    @validator("certificates")
+    def validate_certificates(cls, v):
+        # Raises an exception if it cannot extract the certificates
+        _extract_certificates(v)
+        return v
+
+    @property
+    def certificates_dict(cls):
+        return _extract_certificates(cls.certificates) if cls.certificates else {}
+
 
 class MonCharm(CharmedOsmBase):
     def __init__(self, *args) -> NoReturn:
 
 class MonCharm(CharmedOsmBase):
     def __init__(self, *args) -> NoReturn:
@@ -105,6 +137,15 @@ class MonCharm(CharmedOsmBase):
         if missing_relations:
             raise RelationsMissing(missing_relations)
 
         if missing_relations:
             raise RelationsMissing(missing_relations)
 
+    def _build_cert_files(
+        self,
+        config: ConfigModel,
+    ):
+        cert_files_builder = FilesV3Builder()
+        for name, content in config.certificates_dict.items():
+            cert_files_builder.add_file(name, decode(content), mode=0o600)
+        return cert_files_builder.build()
+
     def build_pod_spec(self, image_info):
         # Validate config
         config = ConfigModel(**dict(self.config))
     def build_pod_spec(self, image_info):
         # Validate config
         config = ConfigModel(**dict(self.config))
@@ -114,6 +155,9 @@ class MonCharm(CharmedOsmBase):
         pod_spec_builder = PodSpecV3Builder()
         # Build Container
         container_builder = ContainerV3Builder(self.app.name, image_info)
         pod_spec_builder = PodSpecV3Builder()
         # Build Container
         container_builder = ContainerV3Builder(self.app.name, image_info)
+        certs_files = self._build_cert_files(config)
+        if certs_files:
+            container_builder.add_volume_config("certs", "/certs", certs_files)
         container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_envs(
             {
         container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_envs(
             {
index ee5553b..446d5ce 100644 (file)
 
 import sys
 
 
 import sys
 
+
 import mock
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index 858ff7c..dcf74ed 100644 (file)
@@ -20,6 +20,7 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
+import base64
 import sys
 from typing import NoReturn
 import unittest
 import sys
 from typing import NoReturn
 import unittest
@@ -29,6 +30,37 @@ from ops.model import ActiveStatus, BlockedStatus
 from ops.testing import Harness
 
 
 from ops.testing import Harness
 
 
+def encode(content: str):
+    return base64.b64encode(content.encode("ascii")).decode("utf-8")
+
+
+certificate_pem = encode(
+    """
+-----BEGIN CERTIFICATE-----
+MIIDazCCAlOgAwIBAgIUf1b0s3UKtrxHXH2rge7UaQyfJAMwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjIxNzEyMjdaFw0zMTAz
+MjAxNzEyMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCgCfCBgYAN6ON0yHDXuW407rFtJVRf0u46Jrp0Dk7J
+kkSZ1e7Kq14r7yFHazEBWv78oOdwBocvWrd8leLuf3bYGcHR65hRy6A/fbYm5Aje
+cKpwlFwaqfR4BLelwJl79jZ2rJX738cCBVrIk1nAVdOxGrXV4MTWUaKR2c+uKKvc
+OKRT+5VqCeP4N5FWeATZ/KqGu8uV9E9WhFgwIZyStemLyLaDbn5PmAQ6S9oeR5jJ
+o2gEEp/lDKvsqOWs76KFumSKa9hQs5Dw2lj0mb1UoyYK1gYc4ubzVChJadv44AU8
+MYtIjlFn1X1P+RjaKZNUIAGXkoLwYn6SizF6y6LiuFS9AgMBAAGjUzBRMB0GA1Ud
+DgQWBBRl+/23CB+FXczeAZRQyYcfOdy9YDAfBgNVHSMEGDAWgBRl+/23CB+FXcze
+AZRQyYcfOdy9YDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd
+dkeDym6lRN8kWFtfu3IyiLF8G8sn91qNbH3Yr4TuTBhgcjYyW6PgisSbrNgA9ysE
+GoaF7ohb8GeVfCsQdK23+NpAlj/+DZ3OnGcxwXj1RUAz4yr9kanV1yuEtr1q2xJI
+UaECWr8HZlwGBAKNTGx2EXT2/2aFzgULpDcxzTKD+MRpKpMUrWhf9ULvVrclvHWe
+POLYhobUFuBHuo6rt5Rcq16j67zCX9EVTlAE3o2OECIWByK22sXdeOidYMpTkl4q
+8FrOqjNsx5d+SBPJBv/pqtBm4bA47Vx1P8tbWOQ4bXS0UmXgwpeBOU/O/ot30+KS
+JnKEy+dYyvVBKg77sRHw
+-----END CERTIFICATE-----
+"""
+)
+
+
 class TestCharm(unittest.TestCase):
     """Prometheus Charm unit tests."""
 
 class TestCharm(unittest.TestCase):
     """Prometheus Charm unit tests."""
 
@@ -50,6 +82,7 @@ class TestCharm(unittest.TestCase):
             "collector_interval": 30,
             "evaluator_interval": 30,
             "keystone_enabled": True,
             "collector_interval": 30,
             "evaluator_interval": 30,
             "keystone_enabled": True,
+            "certificates": f"cert1:{certificate_pem}",
         }
         self.harness.update_config(self.config)
 
         }
         self.harness.update_config(self.config)
 
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ee5553b..446d5ce 100644 (file)
 
 import sys
 
 
 import sys
 
+
 import mock
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index 2eaba28..10ade5d 100644 (file)
@@ -20,4 +20,4 @@
 ##
 
 pydantic  # TODO: remove it
 ##
 
 pydantic  # TODO: remove it
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index af61a10..bb667d7 100644 (file)
@@ -44,3 +44,11 @@ options:
     type: string
     description: "Openmano Tenant"
     default: "osm"
     type: string
     description: "Openmano Tenant"
     default: "osm"
+  certificates:
+    type: string
+    description: |
+      comma-separated list of <name>:<content> certificates.
+      Where:
+        name: name of the file for the certificate
+        content: base64 content of the certificate
+      The path for the files is /certs.
index f10a199..1a8928c 100644 (file)
@@ -19,4 +19,4 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-git+https://github.com/davigar15/ops-lib-charmed-osm/@e7f26cd29b322e175a23cadbe4546b7f2bbf111c
\ No newline at end of file
+git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master
\ No newline at end of file
index 6851600..5b40c16 100755 (executable)
 
 # pylint: disable=E0213
 
 
 # pylint: disable=E0213
 
+import base64
 import logging
 import logging
-from typing import NoReturn
+from typing import NoReturn, Optional
 
 from ops.main import main
 from opslib.osm.charm import CharmedOsmBase, RelationsMissing
 from opslib.osm.interfaces.kafka import KafkaClient
 from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.interfaces.mysql import MysqlClient
 
 from ops.main import main
 from opslib.osm.charm import CharmedOsmBase, RelationsMissing
 from opslib.osm.interfaces.kafka import KafkaClient
 from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.interfaces.mysql import MysqlClient
-from opslib.osm.pod import (
-    ContainerV3Builder,
-    PodSpecV3Builder,
-)
+from opslib.osm.pod import ContainerV3Builder, FilesV3Builder, PodSpecV3Builder
 from opslib.osm.validator import ModelValidator, validator
 
 from opslib.osm.validator import ModelValidator, validator
 
-
 logger = logging.getLogger(__name__)
 
 PORT = 9090
 
 
 logger = logging.getLogger(__name__)
 
 PORT = 9090
 
 
+def _check_certificate_data(name: str, content: str):
+    if not name or not content:
+        raise ValueError("certificate name and content must be a non-empty string")
+
+
+def _extract_certificates(certs_config: str):
+    certificates = {}
+    if certs_config:
+        cert_list = certs_config.split(",")
+        for cert in cert_list:
+            name, content = cert.split(":")
+            _check_certificate_data(name, content)
+            certificates[name] = content
+    return certificates
+
+
+def decode(content: str):
+    return base64.b64decode(content.encode("utf-8")).decode("utf-8")
+
+
 class ConfigModel(ModelValidator):
     enable_ng_ro: bool
     database_commonkey: str
 class ConfigModel(ModelValidator):
     enable_ng_ro: bool
     database_commonkey: str
@@ -49,6 +66,7 @@ class ConfigModel(ModelValidator):
     vim_database: str
     ro_database: str
     openmano_tenant: str
     vim_database: str
     ro_database: str
     openmano_tenant: str
+    certificates: Optional[str]
 
     @validator("log_level")
     def validate_log_level(cls, v):
 
     @validator("log_level")
     def validate_log_level(cls, v):
@@ -56,6 +74,16 @@ class ConfigModel(ModelValidator):
             raise ValueError("value must be INFO or DEBUG")
         return v
 
             raise ValueError("value must be INFO or DEBUG")
         return v
 
+    @validator("certificates")
+    def validate_certificates(cls, v):
+        # Raises an exception if it cannot extract the certificates
+        _extract_certificates(v)
+        return v
+
+    @property
+    def certificates_dict(cls):
+        return _extract_certificates(cls.certificates) if cls.certificates else {}
+
 
 class RoCharm(CharmedOsmBase):
     """GrafanaCharm Charm."""
 
 class RoCharm(CharmedOsmBase):
     """GrafanaCharm Charm."""
@@ -106,6 +134,15 @@ class RoCharm(CharmedOsmBase):
         if missing_relations:
             raise RelationsMissing(missing_relations)
 
         if missing_relations:
             raise RelationsMissing(missing_relations)
 
+    def _build_cert_files(
+        self,
+        config: ConfigModel,
+    ):
+        cert_files_builder = FilesV3Builder()
+        for name, content in config.certificates_dict.items():
+            cert_files_builder.add_file(name, decode(content), mode=0o600)
+        return cert_files_builder.build()
+
     def build_pod_spec(self, image_info):
         # Validate config
         config = ConfigModel(**dict(self.config))
     def build_pod_spec(self, image_info):
         # Validate config
         config = ConfigModel(**dict(self.config))
@@ -115,6 +152,9 @@ class RoCharm(CharmedOsmBase):
         pod_spec_builder = PodSpecV3Builder()
         # Build Container
         container_builder = ContainerV3Builder(self.app.name, image_info)
         pod_spec_builder = PodSpecV3Builder()
         # Build Container
         container_builder = ContainerV3Builder(self.app.name, image_info)
+        certs_files = self._build_cert_files(config)
+        if certs_files:
+            container_builder.add_volume_config("certs", "/certs", certs_files)
         container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_http_readiness_probe(
             "/ro/" if config.enable_ng_ro else "/openmano/tenants",
         container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_http_readiness_probe(
             "/ro/" if config.enable_ng_ro else "/openmano/tenants",
index ca37e3b..446d5ce 100644 (file)
@@ -27,7 +27,14 @@ import sys
 
 import mock
 
 
 import mock
 
+
+class OCIImageResourceErrorMock(Exception):
+    pass
+
+
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
 sys.path.append("src")
 
 oci_image = mock.MagicMock()
+oci_image.OCIImageResourceError = OCIImageResourceErrorMock
 sys.modules["oci_image"] = oci_image
 sys.modules["oci_image"] = oci_image
+sys.modules["oci_image"].OCIImageResource().fetch.return_value = {}
index 4610b25..9cced45 100644 (file)
@@ -20,7 +20,7 @@
 # osm-charmers@lists.launchpad.net
 ##
 
 # osm-charmers@lists.launchpad.net
 ##
 
-import sys
+import base64
 from typing import NoReturn
 import unittest
 
 from typing import NoReturn
 import unittest
 
@@ -29,12 +29,42 @@ from ops.model import ActiveStatus, BlockedStatus
 from ops.testing import Harness
 
 
 from ops.testing import Harness
 
 
+def encode(content: str):
+    return base64.b64encode(content.encode("ascii")).decode("utf-8")
+
+
+certificate_pem = encode(
+    """
+-----BEGIN CERTIFICATE-----
+MIIDazCCAlOgAwIBAgIUf1b0s3UKtrxHXH2rge7UaQyfJAMwDQYJKoZIhvcNAQEL
+BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
+GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjIxNzEyMjdaFw0zMTAz
+MjAxNzEyMjdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
+HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQCgCfCBgYAN6ON0yHDXuW407rFtJVRf0u46Jrp0Dk7J
+kkSZ1e7Kq14r7yFHazEBWv78oOdwBocvWrd8leLuf3bYGcHR65hRy6A/fbYm5Aje
+cKpwlFwaqfR4BLelwJl79jZ2rJX738cCBVrIk1nAVdOxGrXV4MTWUaKR2c+uKKvc
+OKRT+5VqCeP4N5FWeATZ/KqGu8uV9E9WhFgwIZyStemLyLaDbn5PmAQ6S9oeR5jJ
+o2gEEp/lDKvsqOWs76KFumSKa9hQs5Dw2lj0mb1UoyYK1gYc4ubzVChJadv44AU8
+MYtIjlFn1X1P+RjaKZNUIAGXkoLwYn6SizF6y6LiuFS9AgMBAAGjUzBRMB0GA1Ud
+DgQWBBRl+/23CB+FXczeAZRQyYcfOdy9YDAfBgNVHSMEGDAWgBRl+/23CB+FXcze
+AZRQyYcfOdy9YDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAd
+dkeDym6lRN8kWFtfu3IyiLF8G8sn91qNbH3Yr4TuTBhgcjYyW6PgisSbrNgA9ysE
+GoaF7ohb8GeVfCsQdK23+NpAlj/+DZ3OnGcxwXj1RUAz4yr9kanV1yuEtr1q2xJI
+UaECWr8HZlwGBAKNTGx2EXT2/2aFzgULpDcxzTKD+MRpKpMUrWhf9ULvVrclvHWe
+POLYhobUFuBHuo6rt5Rcq16j67zCX9EVTlAE3o2OECIWByK22sXdeOidYMpTkl4q
+8FrOqjNsx5d+SBPJBv/pqtBm4bA47Vx1P8tbWOQ4bXS0UmXgwpeBOU/O/ot30+KS
+JnKEy+dYyvVBKg77sRHw
+-----END CERTIFICATE-----
+"""
+)
+
+
 class TestCharm(unittest.TestCase):
     """Prometheus Charm unit tests."""
 
     def setUp(self) -> NoReturn:
         """Test setup"""
 class TestCharm(unittest.TestCase):
     """Prometheus Charm unit tests."""
 
     def setUp(self) -> NoReturn:
         """Test setup"""
-        self.image_info = sys.modules["oci_image"].OCIImageResource().fetch()
         self.harness = Harness(RoCharm)
         self.harness.set_leader(is_leader=True)
         self.harness.begin()
         self.harness = Harness(RoCharm)
         self.harness.set_leader(is_leader=True)
         self.harness.begin()
@@ -45,6 +75,7 @@ class TestCharm(unittest.TestCase):
             "vim_database": "db_name",
             "ro_database": "ro_db_name",
             "openmano_tenant": "mano",
             "vim_database": "db_name",
             "ro_database": "ro_db_name",
             "openmano_tenant": "mano",
+            "certificates": f"cert1:{certificate_pem}",
         }
         self.harness.update_config(self.config)
 
         }
         self.harness.update_config(self.config)