+++ /dev/null
-#!/usr/bin/env python3
-# Copyright 2021 Canonical Ltd.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-#
-# For those usages not covered by the Apache License, Version 2.0 please
-# contact: legal@canonical.com
-#
-# To get in touch with the maintainers, please contact:
-# osm-charmers@lists.launchpad.net
-##
-
-# pylint: disable=E0213
-
-
-from datetime import datetime
-from ipaddress import ip_network
-import json
-import logging
-from typing import List, NoReturn, Optional, Tuple
-from urllib.parse import urlparse
-
-from cryptography.fernet import Fernet
-from ops.main import main
-from opslib.osm.charm import CharmedOsmBase, RelationsMissing
-from opslib.osm.interfaces.keystone import KeystoneServer
-from opslib.osm.interfaces.mysql import MysqlClient
-from opslib.osm.pod import (
- ContainerV3Builder,
- FilesV3Builder,
- IngressResourceV3Builder,
- PodRestartPolicy,
- PodSpecV3Builder,
-)
-from opslib.osm.validator import ModelValidator, validator
-
-
-logger = logging.getLogger(__name__)
-
-
-REQUIRED_SETTINGS = ["token_expiration"]
-
-# This is hardcoded in the keystone container script
-DATABASE_NAME = "keystone"
-
-# We expect the keystone container to use the default port
-PORT = 5000
-
-# Number of keys need might need to be adjusted in the future
-NUMBER_FERNET_KEYS = 2
-NUMBER_CREDENTIAL_KEYS = 2
-
-# Path for keys
-CREDENTIAL_KEYS_PATH = "/etc/keystone/credential-keys"
-FERNET_KEYS_PATH = "/etc/keystone/fernet-keys"
-
-
-class ConfigModel(ModelValidator):
- region_id: str
- keystone_db_password: str
- admin_username: str
- admin_password: str
- admin_project: str
- service_username: str
- service_password: str
- service_project: str
- user_domain_name: str
- project_domain_name: str
- token_expiration: int
- max_file_size: int
- site_url: Optional[str]
- ingress_class: Optional[str]
- ingress_whitelist_source_range: Optional[str]
- tls_secret_name: Optional[str]
- mysql_host: Optional[str]
- mysql_port: Optional[int]
- mysql_root_password: Optional[str]
- image_pull_policy: str
- security_context: bool
-
- @validator("max_file_size")
- def validate_max_file_size(cls, v):
- if v < 0:
- raise ValueError("value must be equal or greater than 0")
- return v
-
- @validator("site_url")
- def validate_site_url(cls, v):
- if v:
- parsed = urlparse(v)
- if not parsed.scheme.startswith("http"):
- raise ValueError("value must start with http")
- return v
-
- @validator("ingress_whitelist_source_range")
- def validate_ingress_whitelist_source_range(cls, v):
- if v:
- ip_network(v)
- return v
-
- @validator("mysql_port")
- def validate_mysql_port(cls, v):
- if v and (v <= 0 or v >= 65535):
- raise ValueError("Mysql port out of range")
- return v
-
- @validator("image_pull_policy")
- def validate_image_pull_policy(cls, v):
- values = {
- "always": "Always",
- "ifnotpresent": "IfNotPresent",
- "never": "Never",
- }
- v = v.lower()
- if v not in values.keys():
- raise ValueError("value must be always, ifnotpresent or never")
- return values[v]
-
-
-class ConfigLdapModel(ModelValidator):
- ldap_enabled: bool
- ldap_authentication_domain_name: Optional[str]
- ldap_url: Optional[str]
- ldap_bind_user: Optional[str]
- ldap_bind_password: Optional[str]
- ldap_chase_referrals: Optional[str]
- ldap_page_size: Optional[int]
- ldap_user_tree_dn: Optional[str]
- ldap_user_objectclass: Optional[str]
- ldap_user_id_attribute: Optional[str]
- ldap_user_name_attribute: Optional[str]
- ldap_user_pass_attribute: Optional[str]
- ldap_user_filter: Optional[str]
- ldap_user_enabled_attribute: Optional[str]
- ldap_user_enabled_mask: Optional[int]
- ldap_user_enabled_default: Optional[str]
- ldap_user_enabled_invert: Optional[bool]
- ldap_group_objectclass: Optional[str]
- ldap_group_tree_dn: Optional[str]
- ldap_use_starttls: Optional[bool]
- ldap_tls_cacert_base64: Optional[str]
- ldap_tls_req_cert: Optional[str]
-
- @validator
- def validate_ldap_user_enabled_default(cls, v):
- if v:
- if v not in ["true", "false"]:
- raise ValueError('must be equal to "true" or "false"')
- return v
-
-
-class KeystoneCharm(CharmedOsmBase):
- def __init__(self, *args) -> NoReturn:
- 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)
-
- self.keystone_server = KeystoneServer(self, "keystone")
- 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
- )
-
- def _publish_keystone_info(self, event):
- if self.unit.is_leader():
- config = ConfigModel(**dict(self.config))
- self.keystone_server.publish_info(
- host=f"http://{self.app.name}:{PORT}/v3",
- port=PORT,
- user_domain_name=config.user_domain_name,
- project_domain_name=config.project_domain_name,
- username=config.service_username,
- password=config.service_password,
- service=config.service_project,
- keystone_db_password=config.keystone_db_password,
- region_id=config.region_id,
- admin_username=config.admin_username,
- admin_password=config.admin_password,
- admin_project_name=config.admin_project,
- )
-
- def _check_missing_dependencies(self, config: ConfigModel, external_db: bool):
- missing_relations = []
- 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 _generate_keys(self) -> Tuple[List[str], List[str]]:
- """Generating new fernet tokens.
-
- Returns:
- Tuple[List[str], List[str]]: contains two lists of strings. First
- list contains strings that represent
- the keys for fernet and the second
- list contains strins that represent
- the keys for credentials.
- """
- fernet_keys = [
- Fernet.generate_key().decode() for _ in range(NUMBER_FERNET_KEYS)
- ]
- credential_keys = [
- Fernet.generate_key().decode() for _ in range(NUMBER_CREDENTIAL_KEYS)
- ]
-
- return (fernet_keys, credential_keys)
-
- def _get_keys(self):
- keys_timestamp = self.state.keys_timestamp
- if fernet_keys := self.state.fernet_keys:
- fernet_keys = json.loads(fernet_keys)
-
- if credential_keys := self.state.credential_keys:
- credential_keys = json.loads(credential_keys)
-
- now = datetime.now().timestamp()
- token_expiration = self.config["token_expiration"]
-
- valid_keys = (now - keys_timestamp) < token_expiration
- if not credential_keys or not fernet_keys or not valid_keys:
- fernet_keys, credential_keys = self._generate_keys()
- self.state.fernet_keys = json.dumps(fernet_keys)
- self.state.credential_keys = json.dumps(credential_keys)
- self.state.keys_timestamp = now
- return credential_keys, fernet_keys
-
- def _build_files(
- self, config: ConfigModel, credential_keys: List, fernet_keys: List
- ):
- credentials_files_builder = FilesV3Builder()
- fernet_files_builder = FilesV3Builder()
- 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, **kwargs):
- # Validate config
- config = ConfigModel(**dict(self.config))
- mysql_config = kwargs["mysql_config"]
- config_ldap = ConfigLdapModel(**dict(self.config))
-
- 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")
- # Check relations
- 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(
- enable_security_context=config.security_context
- )
- container_builder = ContainerV3Builder(
- self.app.name,
- image_info,
- config.image_pull_policy,
- run_as_non_root=config.security_context,
- )
-
- # Build files
- 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,
- secret_name=credential_keys_secret_name,
- )
- container_builder.add_volume_config(
- "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(
- {
- "REGION_ID": config.region_id,
- "KEYSTONE_HOST": self.app.name,
- }
- )
- 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:
- # 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:
- ldap_secrets["bind_user"] = config_ldap.ldap_bind_user
- ldap_envs["LDAP_BIND_USER"] = "bind_user"
-
- if 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:
- 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:
- ldap_secrets["user_filter"] = config_ldap.ldap_user_filter
- ldap_envs["LDAP_USER_FILTER"] = "user_filter"
-
- if 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:
- ldap_secrets["chase_referrals"] = config_ldap.ldap_chase_referrals
- ldap_envs["LDAP_CHASE_REFERRALS"] = "chase_referrals"
-
- if 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:
- 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:
- 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)
- annotations = {
- "nginx.ingress.kubernetes.io/proxy-body-size": "{}".format(
- str(config.max_file_size) + "m"
- if config.max_file_size > 0
- else config.max_file_size
- )
- }
- if config.ingress_class:
- annotations["kubernetes.io/ingress.class"] = config.ingress_class
- ingress_resource_builder = IngressResourceV3Builder(
- f"{self.app.name}-ingress", annotations
- )
-
- if config.ingress_whitelist_source_range:
- annotations[
- "nginx.ingress.kubernetes.io/whitelist-source-range"
- ] = config.ingress_whitelist_source_range
-
- if parsed.scheme == "https":
- ingress_resource_builder.add_tls(
- [parsed.hostname], config.tls_secret_name
- )
- else:
- annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
-
- ingress_resource_builder.add_rule(parsed.hostname, self.app.name, PORT)
- ingress_resource = ingress_resource_builder.build()
- pod_spec_builder.add_ingress_resource(ingress_resource)
- return pod_spec_builder.build()
-
-
-if __name__ == "__main__":
- main(KeystoneCharm)
-
-# LOGGER = logging.getLogger(__name__)
-
-
-# class ConfigurePodEvent(EventBase):
-# """Configure Pod event"""
-
-# pass
-
-
-# class KeystoneEvents(CharmEvents):
-# """Keystone Events"""
-
-# configure_pod = EventSource(ConfigurePodEvent)
-
-# class KeystoneCharm(CharmBase):
-# """Keystone K8s Charm"""
-
-# state = StoredState()
-# on = KeystoneEvents()
-
-# def __init__(self, *args) -> NoReturn:
-# """Constructor of the Charm object.
-# Initializes internal state and register events it can handle.
-# """
-# super().__init__(*args)
-# self.state.set_default(db_host=None)
-# self.state.set_default(db_port=None)
-# self.state.set_default(db_user=None)
-# self.state.set_default(db_password=None)
-# self.state.set_default(pod_spec=None)
-# self.state.set_default(fernet_keys=None)
-# self.state.set_default(credential_keys=None)
-# self.state.set_default(keys_timestamp=0)
-
-# # Register all of the events we want to observe
-# self.framework.observe(self.on.config_changed, self.configure_pod)
-# self.framework.observe(self.on.start, self.configure_pod)
-# self.framework.observe(self.on.upgrade_charm, self.configure_pod)
-# self.framework.observe(self.on.leader_elected, self.configure_pod)
-# self.framework.observe(self.on.update_status, self.configure_pod)
-
-# # Registering custom internal events
-# self.framework.observe(self.on.configure_pod, self.configure_pod)
-
-# # Register relation events
-# self.framework.observe(
-# self.on.db_relation_changed, self._on_db_relation_changed
-# )
-# self.framework.observe(
-# self.on.db_relation_broken, self._on_db_relation_broken
-# )
-# self.framework.observe(
-# self.on.keystone_relation_joined, self._publish_keystone_info
-# )
-
-# def _publish_keystone_info(self, event: EventBase) -> NoReturn:
-# """Publishes keystone information for NBI usage through the keystone
-# relation.
-
-# Args:
-# event (EventBase): Keystone relation event to update NBI.
-# """
-# config = self.model.config
-# rel_data = {
-# "host": f"http://{self.app.name}:{KEYSTONE_PORT}/v3",
-# "port": str(KEYSTONE_PORT),
-# "keystone_db_password": config["keystone_db_password"],
-# "region_id": config["region_id"],
-# "user_domain_name": config["user_domain_name"],
-# "project_domain_name": config["project_domain_name"],
-# "admin_username": config["admin_username"],
-# "admin_password": config["admin_password"],
-# "admin_project_name": config["admin_project"],
-# "username": config["service_username"],
-# "password": config["service_password"],
-# "service": config["service_project"],
-# }
-# for k, v in rel_data.items():
-# event.relation.data[self.model.unit][k] = v
-
-# def _on_db_relation_changed(self, event: EventBase) -> NoReturn:
-# """Reads information about the DB relation, in order for keystone to
-# access it.
-
-# Args:
-# event (EventBase): DB relation event to access database
-# information.
-# """
-# if not event.unit in event.relation.data:
-# return
-# relation_data = event.relation.data[event.unit]
-# db_host = relation_data.get("host")
-# db_port = int(relation_data.get("port", 3306))
-# db_user = "root"
-# db_password = relation_data.get("root_password")
-
-# if (
-# db_host
-# and db_port
-# and db_user
-# and db_password
-# and (
-# self.state.db_host != db_host
-# or self.state.db_port != db_port
-# or self.state.db_user != db_user
-# or self.state.db_password != db_password
-# )
-# ):
-# self.state.db_host = db_host
-# self.state.db_port = db_port
-# self.state.db_user = db_user
-# self.state.db_password = db_password
-# self.on.configure_pod.emit()
-
-
-# def _on_db_relation_broken(self, event: EventBase) -> NoReturn:
-# """Clears data from db relation.
-
-# Args:
-# event (EventBase): DB relation event.
-
-# """
-# self.state.db_host = None
-# self.state.db_port = None
-# self.state.db_user = None
-# self.state.db_password = None
-# self.on.configure_pod.emit()
-
-# def _check_settings(self) -> str:
-# """Check if there any settings missing from Keystone configuration.
-
-# Returns:
-# str: Information about the problems found (if any).
-# """
-# problems = []
-# config = self.model.config
-
-# for setting in REQUIRED_SETTINGS:
-# if not config.get(setting):
-# problem = f"missing config {setting}"
-# problems.append(problem)
-
-# return ";".join(problems)
-
-# def _make_pod_image_details(self) -> Dict[str, str]:
-# """Generate the pod image details.
-
-# Returns:
-# Dict[str, str]: pod image details.
-# """
-# config = self.model.config
-# image_details = {
-# "imagePath": config["image"],
-# }
-# if config["image_username"]:
-# image_details.update(
-# {
-# "username": config["image_username"],
-# "password": config["image_password"],
-# }
-# )
-# return image_details
-
-# def _make_pod_ports(self) -> List[Dict[str, Any]]:
-# """Generate the pod ports details.
-
-# Returns:
-# List[Dict[str, Any]]: pod ports details.
-# """
-# return [
-# {"name": "keystone", "containerPort": KEYSTONE_PORT, "protocol": "TCP"},
-# ]
-
-# def _make_pod_envconfig(self) -> Dict[str, Any]:
-# """Generate pod environment configuraiton.
-
-# Returns:
-# Dict[str, Any]: pod environment configuration.
-# """
-# config = self.model.config
-
-# envconfig = {
-# "DB_HOST": self.state.db_host,
-# "DB_PORT": self.state.db_port,
-# "ROOT_DB_USER": self.state.db_user,
-# "ROOT_DB_PASSWORD": self.state.db_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"],
-# }
-
-# if config.get("ldap_enabled"):
-# envconfig["LDAP_AUTHENTICATION_DOMAIN_NAME"] = config[
-# "ldap_authentication_domain_name"
-# ]
-# envconfig["LDAP_URL"] = config["ldap_url"]
-# envconfig["LDAP_PAGE_SIZE"] = config["ldap_page_size"]
-# envconfig["LDAP_USER_OBJECTCLASS"] = config["ldap_user_objectclass"]
-# envconfig["LDAP_USER_ID_ATTRIBUTE"] = config["ldap_user_id_attribute"]
-# envconfig["LDAP_USER_NAME_ATTRIBUTE"] = config["ldap_user_name_attribute"]
-# envconfig["LDAP_USER_PASS_ATTRIBUTE"] = config["ldap_user_pass_attribute"]
-# envconfig["LDAP_USER_ENABLED_MASK"] = config["ldap_user_enabled_mask"]
-# envconfig["LDAP_USER_ENABLED_DEFAULT"] = config["ldap_user_enabled_default"]
-# envconfig["LDAP_USER_ENABLED_INVERT"] = config["ldap_user_enabled_invert"]
-# envconfig["LDAP_GROUP_OBJECTCLASS"] = config["ldap_group_objectclass"]
-
-# if config["ldap_bind_user"]:
-# envconfig["LDAP_BIND_USER"] = config["ldap_bind_user"]
-
-# if config["ldap_bind_password"]:
-# envconfig["LDAP_BIND_PASSWORD"] = config["ldap_bind_password"]
-
-# if config["ldap_user_tree_dn"]:
-# envconfig["LDAP_USER_TREE_DN"] = config["ldap_user_tree_dn"]
-
-# if config["ldap_user_filter"]:
-# envconfig["LDAP_USER_FILTER"] = config["ldap_user_filter"]
-
-# if config["ldap_user_enabled_attribute"]:
-# envconfig["LDAP_USER_ENABLED_ATTRIBUTE"] = config[
-# "ldap_user_enabled_attribute"
-# ]
-
-# if config["ldap_chase_referrals"]:
-# envconfig["LDAP_CHASE_REFERRALS"] = config["ldap_chase_referrals"]
-
-# if config["ldap_group_tree_dn"]:
-# envconfig["LDAP_GROUP_TREE_DN"] = config["ldap_group_tree_dn"]
-
-# if config["ldap_use_starttls"]:
-# envconfig["LDAP_USE_STARTTLS"] = config["ldap_use_starttls"]
-# envconfig["LDAP_TLS_CACERT_BASE64"] = config["ldap_tls_cacert_base64"]
-# envconfig["LDAP_TLS_REQ_CERT"] = config["ldap_tls_req_cert"]
-
-# return envconfig
-
-# def _make_pod_ingress_resources(self) -> List[Dict[str, Any]]:
-# """Generate pod ingress resources.
-
-# Returns:
-# List[Dict[str, Any]]: pod ingress resources.
-# """
-# site_url = self.model.config["site_url"]
-
-# if not site_url:
-# return
-
-# parsed = urlparse(site_url)
-
-# if not parsed.scheme.startswith("http"):
-# return
-
-# max_file_size = self.model.config["max_file_size"]
-# ingress_whitelist_source_range = self.model.config[
-# "ingress_whitelist_source_range"
-# ]
-
-# annotations = {
-# "nginx.ingress.kubernetes.io/proxy-body-size": "{}m".format(max_file_size)
-# }
-
-# if ingress_whitelist_source_range:
-# annotations[
-# "nginx.ingress.kubernetes.io/whitelist-source-range"
-# ] = ingress_whitelist_source_range
-
-# ingress_spec_tls = None
-
-# if parsed.scheme == "https":
-# ingress_spec_tls = [{"hosts": [parsed.hostname]}]
-# tls_secret_name = self.model.config["tls_secret_name"]
-# if tls_secret_name:
-# ingress_spec_tls[0]["secretName"] = tls_secret_name
-# else:
-# annotations["nginx.ingress.kubernetes.io/ssl-redirect"] = "false"
-
-# ingress = {
-# "name": "{}-ingress".format(self.app.name),
-# "annotations": annotations,
-# "spec": {
-# "rules": [
-# {
-# "host": parsed.hostname,
-# "http": {
-# "paths": [
-# {
-# "path": "/",
-# "backend": {
-# "serviceName": self.app.name,
-# "servicePort": KEYSTONE_PORT,
-# },
-# }
-# ]
-# },
-# }
-# ],
-# },
-# }
-# if ingress_spec_tls:
-# ingress["spec"]["tls"] = ingress_spec_tls
-
-# return [ingress]
-
-# def _generate_keys(self) -> Tuple[List[str], List[str]]:
-# """Generating new fernet tokens.
-
-# Returns:
-# Tuple[List[str], List[str]]: contains two lists of strings. First
-# list contains strings that represent
-# the keys for fernet and the second
-# list contains strins that represent
-# the keys for credentials.
-# """
-# fernet_keys = [
-# Fernet.generate_key().decode() for _ in range(NUMBER_FERNET_KEYS)
-# ]
-# credential_keys = [
-# Fernet.generate_key().decode() for _ in range(NUMBER_CREDENTIAL_KEYS)
-# ]
-
-# return (fernet_keys, credential_keys)
-
-# def configure_pod(self, event: EventBase) -> NoReturn:
-# """Assemble the pod spec and apply it, if possible.
-
-# Args:
-# event (EventBase): Hook or Relation event that started the
-# function.
-# """
-# if not self.state.db_host:
-# self.unit.status = WaitingStatus("Waiting for database relation")
-# event.defer()
-# return
-
-# if not self.unit.is_leader():
-# self.unit.status = ActiveStatus("ready")
-# return
-
-# if fernet_keys := self.state.fernet_keys:
-# fernet_keys = json.loads(fernet_keys)
-
-# if credential_keys := self.state.credential_keys:
-# credential_keys = json.loads(credential_keys)
-
-# now = datetime.now().timestamp()
-# keys_timestamp = self.state.keys_timestamp
-# token_expiration = self.model.config["token_expiration"]
-
-# valid_keys = (now - keys_timestamp) < token_expiration
-# if not credential_keys or not fernet_keys or not valid_keys:
-# fernet_keys, credential_keys = self._generate_keys()
-# self.state.fernet_keys = json.dumps(fernet_keys)
-# self.state.credential_keys = json.dumps(credential_keys)
-# self.state.keys_timestamp = now
-
-# # Check problems in the settings
-# problems = self._check_settings()
-# if problems:
-# self.unit.status = BlockedStatus(problems)
-# return
-
-# self.unit.status = BlockedStatus("Assembling pod spec")
-# image_details = self._make_pod_image_details()
-# ports = self._make_pod_ports()
-# env_config = self._make_pod_envconfig()
-# ingress_resources = self._make_pod_ingress_resources()
-# files = self._make_pod_files(fernet_keys, credential_keys)
-
-# pod_spec = {
-# "version": 3,
-# "containers": [
-# {
-# "name": self.framework.model.app.name,
-# "imageDetails": image_details,
-# "ports": ports,
-# "envConfig": env_config,
-# "volumeConfig": files,
-# }
-# ],
-# "kubernetesResources": {"ingressResources": ingress_resources or []},
-# }
-
-# if self.state.pod_spec != (
-# pod_spec_json := json.dumps(pod_spec, sort_keys=True)
-# ):
-# self.state.pod_spec = pod_spec_json
-# self.model.pod.set_spec(pod_spec)
-
-# self.unit.status = ActiveStatus("ready")
-
-
-# if __name__ == "__main__":
-# main(KeystoneCharm)