Add secret-management in Charmed OSM 58/11158/8
authorDavid Garcia <david.garcia@canonical.com>
Wed, 8 Sep 2021 15:48:40 +0000 (17:48 +0200)
committersousaedu <eduardo.sousa@canonical.com>
Tue, 28 Sep 2021 09:19:42 +0000 (11:19 +0200)
Change-Id: Ic5714571c673e4d82e3a905daa57f631640b94bb
Signed-off-by: David Garcia <david.garcia@canonical.com>
15 files changed:
installers/charm/grafana/src/charm.py
installers/charm/kafka-exporter/src/charm.py
installers/charm/keystone/config.yaml
installers/charm/keystone/src/charm.py
installers/charm/keystone/tests/test_charm.py
installers/charm/lcm/src/charm.py
installers/charm/lcm/tests/test_charm.py
installers/charm/local_osm_bundle.yaml
installers/charm/mon/src/charm.py
installers/charm/mongodb-exporter/src/charm.py
installers/charm/mysqld-exporter/src/charm.py
installers/charm/nbi/src/charm.py
installers/charm/pla/src/charm.py
installers/charm/pol/src/charm.py
installers/charm/ro/src/charm.py

index e20a052..78ec0e3 100755 (executable)
@@ -39,6 +39,7 @@ from opslib.osm.pod import (
     ContainerV3Builder,
     FilesV3Builder,
     IngressResourceV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -184,6 +185,16 @@ class GrafanaCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        grafana_secret_name = f"{self.app.name}-admin-secret"
+        pod_spec_builder.add_secret(
+            grafana_secret_name,
+            {
+                "admin-password": admin_initial_password,
+                "mysql-url": mysql_config.mysql_uri or self.mysql_client.get_uri(),
+            },
+        )
+
         # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -220,24 +231,23 @@ class GrafanaCharm(CharmedOsmBase):
                 "GF_SERVER_HTTP_PORT": config.port,
                 "GF_LOG_LEVEL": config.log_level,
                 "GF_SECURITY_ADMIN_USER": config.admin_user,
-                "GF_SECURITY_ADMIN_PASSWORD": {
-                    "secret": {"name": "grafana-admin-secret", "key": "admin-password"}
-                },
-                "GF_DATABASE_URL": {
-                    "secret": {"name": "grafana-admin-secret", "key": "mysql-url"}
-                },
+            }
+        )
+        container_builder.add_secret_envs(
+            secret_name=grafana_secret_name,
+            envs={
+                "GF_SECURITY_ADMIN_PASSWORD": "admin-password",
+                "GF_DATABASE_URL": "mysql-url",
             },
         )
         container = container_builder.build()
-        # Add container to pod spec
         pod_spec_builder.add_container(container)
-        pod_spec_builder.add_secret(
-            "grafana-admin-secret",
-            {
-                "admin-password": admin_initial_password,
-                "mysql-url": mysql_config.mysql_uri or self.mysql_client.get_uri(),
-            },
-        )
+
+        # Add Pod restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(secret_names=(grafana_secret_name,))
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add ingress resources to pod spec if site url exists
         if config.site_url:
             parsed = urlparse(config.site_url)
index 7eaa2a0..a8ffab1 100755 (executable)
@@ -236,8 +236,6 @@ class KafkaExporterCharm(CharmedOsmBase):
             ingress_resource = ingress_resource_builder.build()
             pod_spec_builder.add_ingress_resource(ingress_resource)
 
-        logger.debug(pod_spec_builder.build())
-
         return pod_spec_builder.build()
 
 
index 945ea48..e15d035 100644 (file)
@@ -56,15 +56,11 @@ options:
     type: string
     description: Keystone DB Password
     default: admin
-  mysql_host:
+  mysql_uri:
     type: string
-    description: MySQL Host (external database)
-  mysql_port:
-    type: int
-    description: MySQL Port (external database)
-  mysql_root_password:
-    type: string
-    description: MySQL Root Password (external database)
+    description: |
+      Mysql uri with the following format:
+        mysql://<user>:<pass>@<host>:<port>/<database>
   admin_username:
     type: string
     description: Admin username to be created when starting the service
index 51ee6ad..808af3b 100755 (executable)
@@ -39,6 +39,7 @@ from opslib.osm.pod import (
     ContainerV3Builder,
     FilesV3Builder,
     IngressResourceV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -159,7 +160,11 @@ class ConfigLdapModel(ModelValidator):
 
 class KeystoneCharm(CharmedOsmBase):
     def __init__(self, *args) -> NoReturn:
-        super().__init__(*args, oci_image="image")
+        super().__init__(
+            *args,
+            oci_image="image",
+            mysql_uri=True,
+        )
         self.state.set_default(fernet_keys=None)
         self.state.set_default(credential_keys=None)
         self.state.set_default(keys_timestamp=0)
@@ -168,6 +173,7 @@ class KeystoneCharm(CharmedOsmBase):
         self.mysql_client = MysqlClient(self, "db")
         self.framework.observe(self.on["db"].relation_changed, self.configure_pod)
         self.framework.observe(self.on["db"].relation_broken, self.configure_pod)
+        self.framework.observe(self.on.update_status, self.configure_pod)
 
         self.framework.observe(
             self.on["keystone"].relation_joined, self._publish_keystone_info
@@ -191,21 +197,13 @@ class KeystoneCharm(CharmedOsmBase):
                 admin_project_name=config.admin_project,
             )
 
-    def _check_missing_dependencies(self, config: ConfigModel):
+    def _check_missing_dependencies(self, config: ConfigModel, external_db: bool):
         missing_relations = []
-        if not config.mysql_host and self.mysql_client.is_missing_data_in_unit():
+        if not external_db and self.mysql_client.is_missing_data_in_unit():
             missing_relations.append("mysql")
         if missing_relations:
             raise RelationsMissing(missing_relations)
 
-    def _validate_mysql_config(self, config: ConfigModel):
-        invalid_values = []
-        if not config.mysql_root_password:
-            invalid_values.append("Mysql root password must be provided")
-
-        if invalid_values:
-            raise ValueError("Invalid values: " + ", ".join(invalid_values))
-
     def _generate_keys(self) -> Tuple[List[str], List[str]]:
         """Generating new fernet tokens.
 
@@ -244,139 +242,215 @@ class KeystoneCharm(CharmedOsmBase):
             self.state.keys_timestamp = now
         return credential_keys, fernet_keys
 
-    def _build_files(self, config: ConfigModel):
+    def _build_files(
+        self, config: ConfigModel, credential_keys: List, fernet_keys: List
+    ):
         credentials_files_builder = FilesV3Builder()
         fernet_files_builder = FilesV3Builder()
-
-        credential_keys, fernet_keys = self._get_keys()
-
-        for (key_id, value) in enumerate(credential_keys):
-            credentials_files_builder.add_file(str(key_id), value)
-        for (key_id, value) in enumerate(fernet_keys):
-            fernet_files_builder.add_file(str(key_id), value)
+        for (key_id, _) in enumerate(credential_keys):
+            credentials_files_builder.add_file(str(key_id), str(key_id), secret=True)
+        for (key_id, _) in enumerate(fernet_keys):
+            fernet_files_builder.add_file(str(key_id), str(key_id), secret=True)
         return credentials_files_builder.build(), fernet_files_builder.build()
 
-    def build_pod_spec(self, image_info):
+    def build_pod_spec(self, image_info, **kwargs):
         # Validate config
         config = ConfigModel(**dict(self.config))
+        mysql_config = kwargs["mysql_config"]
         config_ldap = ConfigLdapModel(**dict(self.config))
 
-        if config.mysql_host and not self.mysql_client.is_missing_data_in_unit():
+        if mysql_config.mysql_uri and not self.mysql_client.is_missing_data_in_unit():
             raise Exception("Mysql data cannot be provided via config and relation")
-
-        if config.mysql_host:
-            self._validate_mysql_config(config)
-
         # Check relations
-        self._check_missing_dependencies(config)
+        external_db = True if mysql_config.mysql_uri else False
+        self._check_missing_dependencies(config, external_db)
 
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
-
-        # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
         )
-        container_builder.add_port(name=self.app.name, port=PORT)
 
         # Build files
-        credential_files, fernet_files = self._build_files(config)
+        credential_keys, fernet_keys = self._get_keys()
+        credential_files, fernet_files = self._build_files(
+            config, credential_keys, fernet_keys
+        )
+
+        # Add pod secrets
+        fernet_keys_secret_name = f"{self.app.name}-fernet-keys-secret"
+        pod_spec_builder.add_secret(
+            fernet_keys_secret_name,
+            {str(key_id): value for (key_id, value) in enumerate(fernet_keys)},
+        )
+        credential_keys_secret_name = f"{self.app.name}-credential-keys-secret"
+        pod_spec_builder.add_secret(
+            credential_keys_secret_name,
+            {str(key_id): value for (key_id, value) in enumerate(credential_keys)},
+        )
+        mysql_secret_name = f"{self.app.name}-mysql-secret"
+
+        pod_spec_builder.add_secret(
+            mysql_secret_name,
+            {
+                "host": mysql_config.host,
+                "port": str(mysql_config.port),
+                "user": mysql_config.username,
+                "password": mysql_config.password,
+            }
+            if mysql_config.mysql_uri
+            else {
+                "host": self.mysql_client.host,
+                "port": str(self.mysql_client.port),
+                "user": "root",
+                "password": self.mysql_client.root_password,
+            },
+        )
+        keystone_secret_name = f"{self.app.name}-keystone-secret"
+        pod_spec_builder.add_secret(
+            keystone_secret_name,
+            {
+                "db_password": config.keystone_db_password,
+                "admin_username": config.admin_username,
+                "admin_password": config.admin_password,
+                "admin_project": config.admin_project,
+                "service_username": config.service_username,
+                "service_password": config.service_password,
+                "service_project": config.service_project,
+            },
+        )
+        # Build Container
         container_builder.add_volume_config(
-            "credential-keys", CREDENTIAL_KEYS_PATH, credential_files
+            "credential-keys",
+            CREDENTIAL_KEYS_PATH,
+            credential_files,
+            secret_name=credential_keys_secret_name,
         )
         container_builder.add_volume_config(
-            "fernet-keys", FERNET_KEYS_PATH, fernet_files
+            "fernet-keys",
+            FERNET_KEYS_PATH,
+            fernet_files,
+            secret_name=fernet_keys_secret_name,
         )
+        container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_envs(
             {
-                "DB_HOST": config.mysql_host or self.mysql_client.host,
-                "DB_PORT": config.mysql_port or self.mysql_client.port,
-                "ROOT_DB_USER": "root",
-                "ROOT_DB_PASSWORD": config.mysql_root_password
-                or self.mysql_client.root_password,
-                "KEYSTONE_DB_PASSWORD": config.keystone_db_password,
                 "REGION_ID": config.region_id,
                 "KEYSTONE_HOST": self.app.name,
-                "ADMIN_USERNAME": config.admin_username,
-                "ADMIN_PASSWORD": config.admin_password,
-                "ADMIN_PROJECT": config.admin_project,
-                "SERVICE_USERNAME": config.service_username,
-                "SERVICE_PASSWORD": config.service_password,
-                "SERVICE_PROJECT": config.service_project,
             }
         )
-
+        container_builder.add_secret_envs(
+            secret_name=mysql_secret_name,
+            envs={
+                "DB_HOST": "host",
+                "DB_PORT": "port",
+                "ROOT_DB_USER": "user",
+                "ROOT_DB_PASSWORD": "password",
+            },
+        )
+        container_builder.add_secret_envs(
+            secret_name=keystone_secret_name,
+            envs={
+                "KEYSTONE_DB_PASSWORD": "db_password",
+                "ADMIN_USERNAME": "admin_username",
+                "ADMIN_PASSWORD": "admin_password",
+                "ADMIN_PROJECT": "admin_project",
+                "SERVICE_USERNAME": "service_username",
+                "SERVICE_PASSWORD": "service_password",
+                "SERVICE_PROJECT": "service_project",
+            },
+        )
+        ldap_secret_name = f"{self.app.name}-ldap-secret"
         if config_ldap.ldap_enabled:
-            container_builder.add_envs(
-                {
-                    "LDAP_AUTHENTICATION_DOMAIN_NAME": config_ldap.ldap_authentication_domain_name,
-                    "LDAP_URL": config_ldap.ldap_url,
-                    "LDAP_PAGE_SIZE": config_ldap.ldap_page_size,
-                    "LDAP_USER_OBJECTCLASS": config_ldap.ldap_user_objectclass,
-                    "LDAP_USER_ID_ATTRIBUTE": config_ldap.ldap_user_id_attribute,
-                    "LDAP_USER_NAME_ATTRIBUTE": config_ldap.ldap_user_name_attribute,
-                    "LDAP_USER_PASS_ATTRIBUTE": config_ldap.ldap_user_pass_attribute,
-                    "LDAP_USER_ENABLED_MASK": config_ldap.ldap_user_enabled_mask,
-                    "LDAP_USER_ENABLED_DEFAULT": config_ldap.ldap_user_enabled_default,
-                    "LDAP_USER_ENABLED_INVERT": config_ldap.ldap_user_enabled_invert,
-                    "LDAP_GROUP_OBJECTCLASS": config_ldap.ldap_group_objectclass,
-                }
-            )
+            # Add ldap secrets and envs
+            ldap_secrets = {
+                "authentication_domain_name": config_ldap.ldap_authentication_domain_name,
+                "url": config_ldap.ldap_url,
+                "page_size": config_ldap.ldap_page_size,
+                "user_objectclass": config_ldap.ldap_user_objectclass,
+                "user_id_attribute": config_ldap.ldap_user_id_attribute,
+                "user_name_attribute": config_ldap.ldap_user_name_attribute,
+                "user_pass_attribute": config_ldap.ldap_user_pass_attribute,
+                "user_enabled_mask": config_ldap.ldap_user_enabled_mask,
+                "user_enabled_default": config_ldap.ldap_user_enabled_default,
+                "user_enabled_invert": config_ldap.ldap_user_enabled_invert,
+                "group_objectclass": config_ldap.ldap_group_objectclass,
+            }
+            ldap_envs = {
+                "LDAP_AUTHENTICATION_DOMAIN_NAME": "authentication_domain_name",
+                "LDAP_URL": "url",
+                "LDAP_PAGE_SIZE": "page_size",
+                "LDAP_USER_OBJECTCLASS": "user_objectclass",
+                "LDAP_USER_ID_ATTRIBUTE": "user_id_attribute",
+                "LDAP_USER_NAME_ATTRIBUTE": "user_name_attribute",
+                "LDAP_USER_PASS_ATTRIBUTE": "user_pass_attribute",
+                "LDAP_USER_ENABLED_MASK": "user_enabled_mask",
+                "LDAP_USER_ENABLED_DEFAULT": "user_enabled_default",
+                "LDAP_USER_ENABLED_INVERT": "user_enabled_invert",
+                "LDAP_GROUP_OBJECTCLASS": "group_objectclass",
+            }
             if config_ldap.ldap_bind_user:
-                container_builder.add_envs(
-                    {"LDAP_BIND_USER": config_ldap.ldap_bind_user}
-                )
+                ldap_secrets["bind_user"] = config_ldap.ldap_bind_user
+                ldap_envs["LDAP_BIND_USER"] = "bind_user"
 
             if config_ldap.ldap_bind_password:
-                container_builder.add_envs(
-                    {"LDAP_BIND_PASSWORD": config_ldap.ldap_bind_password}
-                )
+                ldap_secrets["bind_password"] = config_ldap.ldap_bind_password
+                ldap_envs["LDAP_BIND_PASSWORD"] = "bind_password"
 
             if config_ldap.ldap_user_tree_dn:
-                container_builder.add_envs(
-                    {"LDAP_USER_TREE_DN": config_ldap.ldap_user_tree_dn}
-                )
+                ldap_secrets["user_tree_dn"] = config_ldap.ldap_user_tree_dn
+                ldap_envs["LDAP_USER_TREE_DN"] = "user_tree_dn"
 
             if config_ldap.ldap_user_filter:
-                container_builder.add_envs(
-                    {"LDAP_USER_FILTER": config_ldap.ldap_user_filter}
-                )
+                ldap_secrets["user_filter"] = config_ldap.ldap_user_filter
+                ldap_envs["LDAP_USER_FILTER"] = "user_filter"
 
             if config_ldap.ldap_user_enabled_attribute:
-                container_builder.add_envs(
-                    {
-                        "LDAP_USER_ENABLED_ATTRIBUTE": config_ldap.ldap_user_enabled_attribute
-                    }
-                )
-
+                ldap_secrets[
+                    "user_enabled_attribute"
+                ] = config_ldap.ldap_user_enabled_attribute
+                ldap_envs["LDAP_USER_ENABLED_ATTRIBUTE"] = "user_enabled_attribute"
             if config_ldap.ldap_chase_referrals:
-                container_builder.add_envs(
-                    {"LDAP_CHASE_REFERRALS": config_ldap.ldap_chase_referrals}
-                )
+                ldap_secrets["chase_referrals"] = config_ldap.ldap_chase_referrals
+                ldap_envs["LDAP_CHASE_REFERRALS"] = "chase_referrals"
 
             if config_ldap.ldap_group_tree_dn:
-                container_builder.add_envs(
-                    {"LDAP_GROUP_TREE_DN": config_ldap.ldap_group_tree_dn}
-                )
+                ldap_secrets["group_tree_dn"] = config_ldap.ldap_group_tree_dn
+                ldap_envs["LDAP_GROUP_TREE_DN"] = "group_tree_dn"
 
             if config_ldap.ldap_tls_cacert_base64:
-                container_builder.add_envs(
-                    {"LDAP_TLS_CACERT_BASE64": config_ldap.ldap_tls_cacert_base64}
-                )
+                ldap_secrets["tls_cacert_base64"] = config_ldap.ldap_tls_cacert_base64
+                ldap_envs["LDAP_TLS_CACERT_BASE64"] = "tls_cacert_base64"
 
             if config_ldap.ldap_use_starttls:
-                container_builder.add_envs(
-                    {
-                        "LDAP_USE_STARTTLS": config_ldap.ldap_use_starttls,
-                        "LDAP_TLS_CACERT_BASE64": config_ldap.ldap_tls_cacert_base64,
-                        "LDAP_TLS_REQ_CERT": config_ldap.ldap_tls_req_cert,
-                    }
-                )
+                ldap_secrets["use_starttls"] = config_ldap.ldap_use_starttls
+                ldap_secrets["tls_cacert_base64"] = config_ldap.ldap_tls_cacert_base64
+                ldap_secrets["tls_req_cert"] = config_ldap.ldap_tls_req_cert
+                ldap_envs["LDAP_USE_STARTTLS"] = "use_starttls"
+                ldap_envs["LDAP_TLS_CACERT_BASE64"] = "tls_cacert_base64"
+                ldap_envs["LDAP_TLS_REQ_CERT"] = "tls_req_cert"
+
+            pod_spec_builder.add_secret(
+                ldap_secret_name,
+                ldap_secrets,
+            )
+            container_builder.add_secret_envs(
+                secret_name=ldap_secret_name,
+                envs=ldap_envs,
+            )
+
         container = container_builder.build()
 
         # Add container to pod spec
         pod_spec_builder.add_container(container)
 
+        # Add Pod Restart Policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(
+            secret_names=(mysql_secret_name, keystone_secret_name, ldap_secret_name)
+        )
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add ingress resources to pod spec if site url exists
         if config.site_url:
             parsed = urlparse(config.site_url)
index e281b20..2a95029 100644 (file)
@@ -110,13 +110,7 @@ class TestCharm(unittest.TestCase):
         self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)
 
     def initialize_mysql_config(self):
-        self.harness.update_config(
-            {
-                "mysql_host": "mysql",
-                "mysql_port": 3306,
-                "mysql_root_password": "manopw",
-            }
-        )
+        self.harness.update_config({"mysql_uri": "mysql://root:manopw@mysql:3306/"})
 
     def initialize_mysql_relation(self):
         relation_id = self.harness.add_relation("db", "mysql")
index 01ac0bf..b034624 100755 (executable)
@@ -32,7 +32,7 @@ from opslib.osm.charm import CharmedOsmBase, RelationsMissing
 from opslib.osm.interfaces.http import HttpClient
 from opslib.osm.interfaces.kafka import KafkaClient
 from opslib.osm.interfaces.mongo import MongoClient
-from opslib.osm.pod import ContainerV3Builder, PodSpecV3Builder
+from opslib.osm.pod import ContainerV3Builder, PodRestartPolicy, PodSpecV3Builder
 from opslib.osm.validator import ModelValidator, validator
 
 
@@ -184,6 +184,17 @@ class LcmCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        lcm_secret_name = f"{self.app.name}-lcm-secret"
+        pod_spec_builder.add_secret(
+            lcm_secret_name,
+            {
+                "uri": config.mongodb_uri or self.mongodb_client.connection_string,
+                "commonkey": config.database_commonkey,
+                "helm_ca_certs": config.vca_helm_ca_certs,
+            },
+        )
+
         # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -204,32 +215,50 @@ class LcmCharm(CharmedOsmBase):
                 "OSMLCM_MESSAGE_PORT": self.kafka_client.port,
                 # Database configuration
                 "OSMLCM_DATABASE_DRIVER": "mongo",
-                "OSMLCM_DATABASE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
-                "OSMLCM_DATABASE_COMMONKEY": config.database_commonkey,
                 # Storage configuration
                 "OSMLCM_STORAGE_DRIVER": "mongo",
                 "OSMLCM_STORAGE_PATH": "/app/storage",
                 "OSMLCM_STORAGE_COLLECTION": "files",
-                "OSMLCM_STORAGE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
                 "OSMLCM_VCA_STABLEREPOURL": config.vca_stablerepourl,
-                "OSMLCM_VCA_HELM_CA_CERTS": config.vca_helm_ca_certs,
             }
         )
+        container_builder.add_secret_envs(
+            secret_name=lcm_secret_name,
+            envs={
+                "OSMLCM_DATABASE_URI": "uri",
+                "OSMLCM_DATABASE_COMMONKEY": "commonkey",
+                "OSMLCM_STORAGE_URI": "uri",
+                "OSMLCM_VCA_HELM_CA_CERTS": "helm_ca_certs",
+            },
+        )
         if config.vca_host:
-            container_builder.add_envs(
+            vca_secret_name = f"{self.app.name}-vca-secret"
+            pod_spec_builder.add_secret(
+                vca_secret_name,
                 {
+                    "host": config.vca_host,
+                    "port": str(config.vca_port),
+                    "user": config.vca_user,
+                    "pubkey": config.vca_pubkey,
+                    "secret": config.vca_secret,
+                    "cacert": config.vca_cacert,
+                    "cloud": config.vca_cloud,
+                    "k8s_cloud": config.vca_k8s_cloud,
+                },
+            )
+            container_builder.add_secret_envs(
+                secret_name=vca_secret_name,
+                envs={
                     # VCA configuration
-                    "OSMLCM_VCA_HOST": config.vca_host,
-                    "OSMLCM_VCA_PORT": config.vca_port,
-                    "OSMLCM_VCA_USER": config.vca_user,
-                    "OSMLCM_VCA_PUBKEY": config.vca_pubkey,
-                    "OSMLCM_VCA_SECRET": config.vca_secret,
-                    "OSMLCM_VCA_CACERT": config.vca_cacert,
-                    "OSMLCM_VCA_CLOUD": config.vca_cloud,
-                    "OSMLCM_VCA_K8S_CLOUD": config.vca_k8s_cloud,
-                }
+                    "OSMLCM_VCA_HOST": "host",
+                    "OSMLCM_VCA_PORT": "port",
+                    "OSMLCM_VCA_USER": "user",
+                    "OSMLCM_VCA_PUBKEY": "pubkey",
+                    "OSMLCM_VCA_SECRET": "secret",
+                    "OSMLCM_VCA_CACERT": "cacert",
+                    "OSMLCM_VCA_CLOUD": "cloud",
+                    "OSMLCM_VCA_K8S_CLOUD": "k8s_cloud",
+                },
             )
             if config.vca_apiproxy:
                 container_builder.add_env("OSMLCM_VCA_APIPROXY", config.vca_apiproxy)
@@ -246,6 +275,11 @@ class LcmCharm(CharmedOsmBase):
         # Add container to pod spec
         pod_spec_builder.add_container(container)
 
+        # Add restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets()
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         return pod_spec_builder.build()
 
 
index 1378e5c..776b384 100644 (file)
@@ -109,32 +109,32 @@ class TestCharm(unittest.TestCase):
         # Verifying status
         self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)
 
-    def test_build_pod_spec(
-        self,
-    ) -> NoReturn:
-        expected_config = {
-            "OSMLCM_GLOBAL_LOGLEVEL": self.config["log_level"],
-            "OSMLCM_DATABASE_COMMONKEY": self.config["database_commonkey"],
-        }
-        expected_config.update(
-            {
-                f"OSMLCM_{k.upper()}": v
-                for k, v in self.config.items()
-                if k.startswith("vca_")
-            }
-        )
-        self.harness.charm._check_missing_dependencies = mock.Mock()
-        pod_spec = self.harness.charm.build_pod_spec(
-            {"imageDetails": {"imagePath": "lcm-image"}}
-        )
-        actual_config = pod_spec["containers"][0]["envConfig"]
-
-        self.assertDictContainsSubset(
-            expected_config,
-            actual_config,
-        )
-        for config_key in actual_config:
-            self.assertNotIn("VCA_MODEL_CONFIG", config_key)
+    def test_build_pod_spec(
+        self,
+    ) -> NoReturn:
+        expected_config = {
+            "OSMLCM_GLOBAL_LOGLEVEL": self.config["log_level"],
+            "OSMLCM_DATABASE_COMMONKEY": self.config["database_commonkey"],
+        }
+        expected_config.update(
+            {
+                f"OSMLCM_{k.upper()}": v
+                for k, v in self.config.items()
+                if k.startswith("vca_")
+            }
+        )
+        self.harness.charm._check_missing_dependencies = mock.Mock()
+        pod_spec = self.harness.charm.build_pod_spec(
+            {"imageDetails": {"imagePath": "lcm-image"}}
+        )
+        actual_config = pod_spec["containers"][0]["envConfig"]
+
+        self.assertDictContainsSubset(
+            expected_config,
+            actual_config,
+        )
+        for config_key in actual_config:
+            self.assertNotIn("VCA_MODEL_CONFIG", config_key)
 
     def test_build_pod_spec_with_model_config(
         self,
index 4b4f809..e8198eb 100644 (file)
@@ -48,66 +48,73 @@ applications:
     annotations:
       gui-x: 0
       gui-y: 300
-  mongodb-k8s:
-    charm: "cs:~charmed-osm/mongodb-k8s"
-    channel: "stable"
+  mongodb:
+    charm: ch:mongodb-k8s
     scale: 1
     series: kubernetes
     storage:
-      database: 50M
-    options:
-      replica-set: rs0
-      namespace: osm
-      enable-sidecar: true
+      db: 50M
     annotations:
       gui-x: 0
-      gui-y: 50
+      gui-y: 0
   nbi:
-    charm: "./nbi/build"
+    charm: "./nbi/nbi.charm"
     scale: 1
     series: kubernetes
     options:
       database_commonkey: osm
       auth_backend: keystone
+    resources:
+      image: opensourcemano/nbi:testing-daily
     annotations:
       gui-x: 0
       gui-y: -200
   ro:
-    charm: "./ro/build"
+    charm: "./ro/ro.charm"
     scale: 1
     series: kubernetes
+    resources:
+      image: opensourcemano/ro:testing-daily
     annotations:
       gui-x: -250
       gui-y: 300
   ng-ui:
-    charm: "./ng-ui/build"
+    charm: "./ng-ui/ng-ui.charm"
     scale: 1
     series: kubernetes
+    resources:
+      image: opensourcemano/ng-ui:testing-daily
     annotations:
       gui-x: 500
       gui-y: 100
   lcm:
-    charm: "./lcm/build"
+    charm: "./lcm/lcm.charm"
     scale: 1
     series: kubernetes
     options:
       database_commonkey: osm
+    resources:
+      image: opensourcemano/lcm:testing-daily
     annotations:
       gui-x: -250
       gui-y: 50
   mon:
-    charm: "./mon/build"
+    charm: "./mon/mon.charm"
     scale: 1
     series: kubernetes
     options:
       database_commonkey: osm
+    resources:
+      image: opensourcemano/mon:testing-daily
     annotations:
       gui-x: 250
       gui-y: 50
   pol:
-    charm: "./pol/build"
+    charm: "./pol/pol.charm"
     scale: 1
     series: kubernetes
+    resources:
+      image: opensourcemano/pol:testing-daily
     annotations:
       gui-x: -250
       gui-y: 550
@@ -115,11 +122,13 @@ applications:
     charm: "./pla/build"
     scale: 1
     series: kubernetes
+    resources:
+      image: opensourcemano/pla:testing-daily
     annotations:
       gui-x: 500
       gui-y: -200
   prometheus:
-    charm: "./prometheus/build"
+    charm: "./prometheus/prometheus.charm"
     channel: "stable"
     scale: 1
     series: kubernetes
@@ -127,19 +136,26 @@ applications:
       data: 50M
     options:
       default-target: "mon:8000"
+    resources:
+      image: ubuntu/prometheus:latest
+      backup-image: ed1000/prometheus-backup:latest
     annotations:
       gui-x: 250
       gui-y: 300
   grafana:
-    charm: "./grafana/build"
+    charm: "./grafana/grafana.charm"
     channel: "stable"
     scale: 1
     series: kubernetes
+    resources:
+      image: ubuntu/grafana:latest
     annotations:
       gui-x: 250
       gui-y: 550
   keystone:
-    charm: "./keystone/build"
+    charm: "./keystone/keystone.charm"
+    resources:
+      image: opensourcemano/keystone:testing-daily
     scale: 1
     series: kubernetes
     annotations:
@@ -155,27 +171,27 @@ relations:
   - - lcm:kafka
     - kafka-k8s:kafka
   - - lcm:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - ro:ro
     - lcm:ro
   - - ro:kafka
     - kafka-k8s:kafka
   - - ro:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - pol:kafka
     - kafka-k8s:kafka
   - - pol:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - mon:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - mon:kafka
     - kafka-k8s:kafka
   - - pla:kafka
     - kafka-k8s:kafka
   - - pla:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - nbi:mongodb
-    - mongodb-k8s:mongo
+    - mongodb:database
   - - nbi:kafka
     - kafka-k8s:kafka
   - - nbi:prometheus
@@ -186,3 +202,7 @@ relations:
     - prometheus:prometheus
   - - ng-ui:nbi
     - nbi:nbi
+  - - mon:keystone
+    - keystone:keystone
+  - - mariadb-k8s:mysql
+    - pol:mysql
index e134041..2721939 100755 (executable)
@@ -34,7 +34,12 @@ 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.pod import ContainerV3Builder, FilesV3Builder, PodSpecV3Builder
+from opslib.osm.pod import (
+    ContainerV3Builder,
+    FilesV3Builder,
+    PodRestartPolicy,
+    PodSpecV3Builder,
+)
 from opslib.osm.validator import ModelValidator, validator
 
 
@@ -185,6 +190,36 @@ class MonCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+        pod_spec_builder.add_secret(
+            mongodb_secret_name,
+            {
+                "uri": config.mongodb_uri or self.mongodb_client.connection_string,
+                "commonkey": config.database_commonkey,
+            },
+        )
+        grafana_secret_name = f"{self.app.name}-grafana-secret"
+        pod_spec_builder.add_secret(
+            grafana_secret_name,
+            {
+                "url": config.grafana_url,
+                "user": config.grafana_user,
+                "password": config.grafana_password,
+            },
+        )
+
+        vca_secret_name = f"{self.app.name}-vca-secret"
+        pod_spec_builder.add_secret(
+            vca_secret_name,
+            {
+                "host": config.vca_host,
+                "user": config.vca_user,
+                "secret": config.vca_secret,
+                "cacert": config.vca_cacert,
+            },
+        )
+
         # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -210,35 +245,66 @@ class MonCharm(CharmedOsmBase):
                 "OSMMON_MESSAGE_PORT": self.kafka_client.port,
                 # Database configuration
                 "OSMMON_DATABASE_DRIVER": "mongo",
-                "OSMMON_DATABASE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
-                "OSMMON_DATABASE_COMMONKEY": config.database_commonkey,
                 # Prometheus configuration
                 "OSMMON_PROMETHEUS_URL": f"http://{self.prometheus_client.hostname}:{self.prometheus_client.port}",
-                # VCA configuration
-                "OSMMON_VCA_HOST": config.vca_host,
-                "OSMMON_VCA_USER": config.vca_user,
-                "OSMMON_VCA_SECRET": config.vca_secret,
-                "OSMMON_VCA_CACERT": config.vca_cacert,
-                "OSMMON_GRAFANA_URL": config.grafana_url,
-                "OSMMON_GRAFANA_USER": config.grafana_user,
-                "OSMMON_GRAFANA_PASSWORD": config.grafana_password,
             }
         )
+        container_builder.add_secret_envs(
+            secret_name=mongodb_secret_name,
+            envs={
+                "OSMMON_DATABASE_URI": "uri",
+                "OSMMON_DATABASE_COMMONKEY": "commonkey",
+            },
+        )
+        container_builder.add_secret_envs(
+            secret_name=vca_secret_name,
+            envs={
+                "OSMMON_VCA_HOST": "host",
+                "OSMMON_VCA_USER": "user",
+                "OSMMON_VCA_SECRET": "secret",
+                "OSMMON_VCA_CACERT": "cacert",
+            },
+        )
+        container_builder.add_secret_envs(
+            secret_name=grafana_secret_name,
+            envs={
+                "OSMMON_GRAFANA_URL": "url",
+                "OSMMON_GRAFANA_USER": "user",
+                "OSMMON_GRAFANA_PASSWORD": "password",
+            },
+        )
         if config.keystone_enabled:
-            container_builder.add_envs(
+            keystone_secret_name = f"{self.app.name}-keystone-secret"
+            pod_spec_builder.add_secret(
+                keystone_secret_name,
                 {
-                    "OSMMON_KEYSTONE_ENABLED": True,
-                    "OSMMON_KEYSTONE_URL": self.keystone_client.host,
-                    "OSMMON_KEYSTONE_DOMAIN_NAME": self.keystone_client.user_domain_name,
-                    "OSMMON_KEYSTONE_PROJECT_DOMAIN_NAME": self.keystone_client.project_domain_name,
-                    "OSMMON_KEYSTONE_SERVICE_USER": self.keystone_client.username,
-                    "OSMMON_KEYSTONE_SERVICE_PASSWORD": self.keystone_client.password,
-                    "OSMMON_KEYSTONE_SERVICE_PROJECT": self.keystone_client.service,
-                }
+                    "url": self.keystone_client.host,
+                    "user_domain": self.keystone_client.user_domain_name,
+                    "project_domain": self.keystone_client.project_domain_name,
+                    "service_username": self.keystone_client.username,
+                    "service_password": self.keystone_client.password,
+                    "service_project": self.keystone_client.service,
+                },
+            )
+            container_builder.add_env("OSMMON_KEYSTONE_ENABLED", True)
+            container_builder.add_secret_envs(
+                secret_name=keystone_secret_name,
+                envs={
+                    "OSMMON_KEYSTONE_URL": "url",
+                    "OSMMON_KEYSTONE_DOMAIN_NAME": "user_domain",
+                    "OSMMON_KEYSTONE_PROJECT_DOMAIN_NAME": "project_domain",
+                    "OSMMON_KEYSTONE_SERVICE_USER": "service_username",
+                    "OSMMON_KEYSTONE_SERVICE_PASSWORD": "service_password",
+                    "OSMMON_KEYSTONE_SERVICE_PROJECT": "service_project",
+                },
             )
         container = container_builder.build()
 
+        # Add restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets()
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add container to pod spec
         pod_spec_builder.add_container(container)
 
index d839d82..0b89931 100755 (executable)
@@ -36,6 +36,7 @@ from opslib.osm.interfaces.prometheus import PrometheusScrapeTarget
 from opslib.osm.pod import (
     ContainerV3Builder,
     IngressResourceV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -182,9 +183,23 @@ class MongodbExporterCharm(CharmedOsmBase):
         # Check relations
         self._check_missing_dependencies(config)
 
+        unparsed = (
+            config.mongodb_uri
+            if config.mongodb_uri
+            else self.mongodb_client.connection_string
+        )
+        parsed = urlparse(unparsed)
+        mongodb_uri = f"mongodb://{parsed.netloc.split(',')[0]}{parsed.path}"
+        if parsed.query:
+            mongodb_uri += f"?{parsed.query}"
+
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+        pod_spec_builder.add_secret(mongodb_secret_name, {"uri": mongodb_uri})
+
         # Build container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -207,26 +222,17 @@ class MongodbExporterCharm(CharmedOsmBase):
             failure_threshold=10,
         )
 
-        unparsed = (
-            config.mongodb_uri
-            if config.mongodb_uri
-            else self.mongodb_client.connection_string
-        )
-        parsed = urlparse(unparsed)
-        mongodb_uri = f"mongodb://{parsed.netloc.split(',')[0]}{parsed.path}"
-        if parsed.query:
-            mongodb_uri += f"?{parsed.query}"
-
-        container_builder.add_envs(
-            {
-                "MONGODB_URI": mongodb_uri,
-            }
-        )
+        container_builder.add_secret_envs(mongodb_secret_name, {"MONGODB_URI": "uri"})
         container = container_builder.build()
 
         # Add container to PodSpec
         pod_spec_builder.add_container(container)
 
+        # Add Pod restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(secret_names=(mongodb_secret_name,))
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add ingress resources to PodSpec if site url exists
         if config.site_url:
             parsed = urlparse(config.site_url)
@@ -256,8 +262,6 @@ class MongodbExporterCharm(CharmedOsmBase):
             ingress_resource = ingress_resource_builder.build()
             pod_spec_builder.add_ingress_resource(ingress_resource)
 
-        logger.debug(pod_spec_builder.build())
-
         return pod_spec_builder.build()
 
 
index a0015cc..6aeea5d 100755 (executable)
@@ -36,6 +36,7 @@ from opslib.osm.interfaces.prometheus import PrometheusScrapeTarget
 from opslib.osm.pod import (
     ContainerV3Builder,
     IngressResourceV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -182,9 +183,22 @@ class MysqlExporterCharm(CharmedOsmBase):
         # Check relations
         self._check_missing_dependencies(config)
 
+        data_source = (
+            config.mysql_uri.replace("mysql://", "").split("/")[0]
+            if config.mysql_uri
+            else f"root:{self.mysql_client.root_password}@{self.mysql_client.host}:{self.mysql_client.port}"
+        )
+
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mysql_secret_name = f"{self.app.name}-mysql-secret"
+        pod_spec_builder.add_secret(
+            mysql_secret_name,
+            {"data_source": data_source},
+        )
+
         # Build container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -206,23 +220,20 @@ class MysqlExporterCharm(CharmedOsmBase):
             timeout_seconds=30,
             failure_threshold=10,
         )
-
-        data_source = (
-            config.mysql_uri.replace("mysql://", "").split("/")[0]
-            if config.mysql_uri
-            else f"root:{self.mysql_client.root_password}@{self.mysql_client.host}:{self.mysql_client.port}"
+        container_builder.add_secret_envs(
+            mysql_secret_name, {"DATA_SOURCE_NAME": "data_source"}
         )
 
-        container_builder.add_envs(
-            {
-                "DATA_SOURCE_NAME": data_source,
-            }
-        )
         container = container_builder.build()
 
         # Add container to PodSpec
         pod_spec_builder.add_container(container)
 
+        # Add Pod restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(secret_names=(mysql_secret_name))
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add ingress resources to PodSpec if site url exists
         if config.site_url:
             parsed = urlparse(config.site_url)
@@ -252,8 +263,6 @@ class MysqlExporterCharm(CharmedOsmBase):
             ingress_resource = ingress_resource_builder.build()
             pod_spec_builder.add_ingress_resource(ingress_resource)
 
-        logger.debug(pod_spec_builder.build())
-
         return pod_spec_builder.build()
 
 
index 1460459..a47f618 100755 (executable)
@@ -39,6 +39,7 @@ from opslib.osm.interfaces.prometheus import PrometheusClient
 from opslib.osm.pod import (
     ContainerV3Builder,
     IngressResourceV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -185,6 +186,16 @@ class NbiCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+        pod_spec_builder.add_secret(
+            mongodb_secret_name,
+            {
+                "uri": config.mongodb_uri or self.mongodb_client.connection_string,
+                "commonkey": config.database_commonkey,
+            },
+        )
+
         # Build Init Container
         pod_spec_builder.add_init_container(
             {
@@ -225,15 +236,10 @@ class NbiCharm(CharmedOsmBase):
                 "OSMNBI_MESSAGE_PORT": self.kafka_client.port,
                 # Database configuration
                 "OSMNBI_DATABASE_DRIVER": "mongo",
-                "OSMNBI_DATABASE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
-                "OSMNBI_DATABASE_COMMONKEY": config.database_commonkey,
                 # Storage configuration
                 "OSMNBI_STORAGE_DRIVER": "mongo",
                 "OSMNBI_STORAGE_PATH": "/app/storage",
                 "OSMNBI_STORAGE_COLLECTION": "files",
-                "OSMNBI_STORAGE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
                 # Prometheus configuration
                 "OSMNBI_PROMETHEUS_HOST": self.prometheus_client.hostname,
                 "OSMNBI_PROMETHEUS_PORT": self.prometheus_client.port,
@@ -241,20 +247,42 @@ class NbiCharm(CharmedOsmBase):
                 "OSMNBI_LOG_LEVEL": config.log_level,
             }
         )
+        container_builder.add_secret_envs(
+            secret_name=mongodb_secret_name,
+            envs={
+                "OSMNBI_DATABASE_URI": "uri",
+                "OSMNBI_DATABASE_COMMONKEY": "commonkey",
+                "OSMNBI_STORAGE_URI": "uri",
+            },
+        )
         if config.auth_backend == "internal":
             container_builder.add_env("OSMNBI_AUTHENTICATION_BACKEND", "internal")
         elif config.auth_backend == "keystone":
-            container_builder.add_envs(
+            keystone_secret_name = f"{self.app.name}-keystone-secret"
+            pod_spec_builder.add_secret(
+                keystone_secret_name,
                 {
-                    "OSMNBI_AUTHENTICATION_BACKEND": "keystone",
-                    "OSMNBI_AUTHENTICATION_AUTH_URL": self.keystone_client.host,
-                    "OSMNBI_AUTHENTICATION_AUTH_PORT": self.keystone_client.port,
-                    "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": self.keystone_client.user_domain_name,
-                    "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": self.keystone_client.project_domain_name,
-                    "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": self.keystone_client.username,
-                    "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": self.keystone_client.password,
-                    "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": self.keystone_client.service,
-                }
+                    "url": self.keystone_client.host,
+                    "port": self.keystone_client.port,
+                    "user_domain": self.keystone_client.user_domain_name,
+                    "project_domain": self.keystone_client.project_domain_name,
+                    "service_username": self.keystone_client.username,
+                    "service_password": self.keystone_client.password,
+                    "service_project": self.keystone_client.service,
+                },
+            )
+            container_builder.add_env("OSMNBI_AUTHENTICATION_BACKEND", "keystone")
+            container_builder.add_secret_envs(
+                secret_name=keystone_secret_name,
+                envs={
+                    "OSMNBI_AUTHENTICATION_AUTH_URL": "url",
+                    "OSMNBI_AUTHENTICATION_AUTH_PORT": "port",
+                    "OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME": "user_domain",
+                    "OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME": "project_domain",
+                    "OSMNBI_AUTHENTICATION_SERVICE_USERNAME": "service_username",
+                    "OSMNBI_AUTHENTICATION_SERVICE_PASSWORD": "service_password",
+                    "OSMNBI_AUTHENTICATION_SERVICE_PROJECT": "service_project",
+                },
             )
         container = container_builder.build()
 
@@ -297,7 +325,10 @@ class NbiCharm(CharmedOsmBase):
             ingress_resource = ingress_resource_builder.build()
             pod_spec_builder.add_ingress_resource(ingress_resource)
 
-        logger.debug(pod_spec_builder.build())
+        # Add restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets()
+        pod_spec_builder.set_restart_policy(restart_policy)
 
         return pod_spec_builder.build()
 
index ba3b1ff..d0df179 100755 (executable)
@@ -32,6 +32,7 @@ from opslib.osm.interfaces.kafka import KafkaClient
 from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.pod import (
     ContainerV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -109,6 +110,16 @@ class PlaCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+        pod_spec_builder.add_secret(
+            mongodb_secret_name,
+            {
+                "uri": config.mongodb_uri or self.mongodb_client.connection_string,
+                "commonkey": config.database_commonkey,
+            },
+        )
+
         # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -125,14 +136,24 @@ class PlaCharm(CharmedOsmBase):
                 "OSMPLA_MESSAGE_PORT": self.kafka_client.port,
                 # Database configuration
                 "OSMPLA_DATABASE_DRIVER": "mongo",
-                "OSMPLA_DATABASE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
-                "OSMPLA_DATABASE_COMMONKEY": config.database_commonkey,
             }
         )
 
+        container_builder.add_secret_envs(
+            secret_name=mongodb_secret_name,
+            envs={
+                "OSMPLA_DATABASE_URI": "uri",
+                "OSMPLA_DATABASE_COMMONKEY": "commonkey",
+            },
+        )
+
         container = container_builder.build()
 
+        # Add Pod restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(secret_names=(mongodb_secret_name))
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add container to pod spec
         pod_spec_builder.add_container(container)
 
index 36eb8c6..02c8186 100755 (executable)
@@ -34,6 +34,7 @@ from opslib.osm.interfaces.mongo import MongoClient
 from opslib.osm.interfaces.mysql import MysqlClient
 from opslib.osm.pod import (
     ContainerV3Builder,
+    PodRestartPolicy,
     PodSpecV3Builder,
 )
 from opslib.osm.validator import ModelValidator, validator
@@ -132,6 +133,21 @@ class PolCharm(CharmedOsmBase):
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
 
+        # Add secrets to the pod
+        mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+        pod_spec_builder.add_secret(
+            mongodb_secret_name,
+            {"uri": config.mongodb_uri or self.mongodb_client.connection_string},
+        )
+        mysql_secret_name = f"{self.app.name}-mysql-secret"
+        pod_spec_builder.add_secret(
+            mysql_secret_name,
+            {
+                "uri": config.mysql_uri
+                or self.mysql_client.get_root_uri(DEFAULT_MYSQL_DATABASE)
+            },
+        )
+
         # Build Container
         container_builder = ContainerV3Builder(
             self.app.name, image_info, config.image_pull_policy
@@ -148,14 +164,23 @@ class PolCharm(CharmedOsmBase):
                 "OSMPOL_MESSAGE_PORT": self.kafka_client.port,
                 # Database configuration
                 "OSMPOL_DATABASE_DRIVER": "mongo",
-                "OSMPOL_DATABASE_URI": config.mongodb_uri
-                or self.mongodb_client.connection_string,
-                "OSMPOL_SQL_DATABASE_URI": config.mysql_uri
-                or self.mysql_client.get_root_uri(DEFAULT_MYSQL_DATABASE),
             }
         )
+        container_builder.add_secret_envs(
+            mongodb_secret_name, {"OSMPOL_DATABASE_URI": "uri"}
+        )
+        container_builder.add_secret_envs(
+            mysql_secret_name, {"OSMPOL_SQL_DATABASE_URI": "uri"}
+        )
         container = container_builder.build()
 
+        # Add Pod restart policy
+        restart_policy = PodRestartPolicy()
+        restart_policy.add_secrets(
+            secret_names=(mongodb_secret_name, mysql_secret_name)
+        )
+        pod_spec_builder.set_restart_policy(restart_policy)
+
         # Add container to pod spec
         pod_spec_builder.add_container(container)
 
index 9b2934f..3b6b7e2 100755 (executable)
@@ -31,7 +31,12 @@ 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, FilesV3Builder, PodSpecV3Builder
+from opslib.osm.pod import (
+    ContainerV3Builder,
+    FilesV3Builder,
+    PodRestartPolicy,
+    PodSpecV3Builder,
+)
 from opslib.osm.validator import ModelValidator, validator
 
 logger = logging.getLogger(__name__)
@@ -247,6 +252,15 @@ class RoCharm(CharmedOsmBase):
         )
 
         if config.enable_ng_ro:
+            # Add secrets to the pod
+            mongodb_secret_name = f"{self.app.name}-mongodb-secret"
+            pod_spec_builder.add_secret(
+                mongodb_secret_name,
+                {
+                    "uri": config.mongodb_uri or self.mongodb_client.connection_string,
+                    "commonkey": config.database_commonkey,
+                },
+            )
             container_builder.add_envs(
                 {
                     "OSMRO_MESSAGE_DRIVER": "kafka",
@@ -254,11 +268,18 @@ class RoCharm(CharmedOsmBase):
                     "OSMRO_MESSAGE_PORT": self.kafka_client.port,
                     # MongoDB configuration
                     "OSMRO_DATABASE_DRIVER": "mongo",
-                    "OSMRO_DATABASE_URI": config.mongodb_uri
-                    or self.mongodb_client.connection_string,
-                    "OSMRO_DATABASE_COMMONKEY": config.database_commonkey,
                 }
             )
+            container_builder.add_secret_envs(
+                secret_name=mongodb_secret_name,
+                envs={
+                    "OSMRO_DATABASE_URI": "uri",
+                    "OSMRO_DATABASE_COMMONKEY": "commonkey",
+                },
+            )
+            restart_policy = PodRestartPolicy()
+            restart_policy.add_secrets(secret_names=(mongodb_secret_name,))
+            pod_spec_builder.set_restart_policy(restart_policy)
 
         else:
             container_builder.add_envs(