X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=installers%2Fcharm%2Fkeystone%2Fsrc%2Fcharm.py;h=446d2e0ce5e4e0d9983d80b0161fcae432589019;hb=refs%2Fchanges%2F54%2F13154%2F5;hp=4e04e88ae35b4d97c90f2026b45b7441abb1ea1b;hpb=3ddbbd1f6c70306d13db0976e1e6b3bda0c69abd;p=osm%2Fdevops.git diff --git a/installers/charm/keystone/src/charm.py b/installers/charm/keystone/src/charm.py index 4e04e88a..446d2e0c 100755 --- a/installers/charm/keystone/src/charm.py +++ b/installers/charm/keystone/src/charm.py @@ -39,6 +39,7 @@ from opslib.osm.pod import ( ContainerV3Builder, FilesV3Builder, IngressResourceV3Builder, + PodRestartPolicy, PodSpecV3Builder, ) from opslib.osm.validator import ModelValidator, validator @@ -84,7 +85,8 @@ class ConfigModel(ModelValidator): mysql_host: Optional[str] mysql_port: Optional[int] mysql_root_password: Optional[str] - image_pull_policy: Optional[str] + image_pull_policy: str + security_context: bool @validator("max_file_size") def validate_max_file_size(cls, v): @@ -159,7 +161,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 +174,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 +198,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 +243,220 @@ 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 + pod_spec_builder = PodSpecV3Builder( + enable_security_context=config.security_context + ) container_builder = ContainerV3Builder( - self.app.name, image_info, config.image_pull_policy + self.app.name, + image_info, + config.image_pull_policy, + run_as_non_root=config.security_context, ) - 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": str(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": str(config_ldap.ldap_user_enabled_mask), + "user_enabled_default": config_ldap.ldap_user_enabled_default, + "user_enabled_invert": str(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"] = str(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)