Fix validation error for ImagePullPolicy in charms
[osm/devops.git] / installers / charm / keystone / src / charm.py
index a8c69fe..51ee6ad 100755 (executable)
@@ -78,8 +78,13 @@ class ConfigModel(ModelValidator):
     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
 
     @validator("max_file_size")
     def validate_max_file_size(cls, v):
@@ -101,6 +106,24 @@ class ConfigModel(ModelValidator):
             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
@@ -118,7 +141,7 @@ class ConfigLdapModel(ModelValidator):
     ldap_user_filter: Optional[str]
     ldap_user_enabled_attribute: Optional[str]
     ldap_user_enabled_mask: Optional[int]
-    ldap_user_enabled_default: Optional[bool]
+    ldap_user_enabled_default: Optional[str]
     ldap_user_enabled_invert: Optional[bool]
     ldap_group_objectclass: Optional[str]
     ldap_group_tree_dn: Optional[str]
@@ -126,6 +149,13 @@ class ConfigLdapModel(ModelValidator):
     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:
@@ -163,11 +193,19 @@ class KeystoneCharm(CharmedOsmBase):
 
     def _check_missing_dependencies(self, config: ConfigModel):
         missing_relations = []
-        if self.mysql_client.is_missing_data_in_unit():
+        if not config.mysql_host 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.
 
@@ -222,13 +260,25 @@ class KeystoneCharm(CharmedOsmBase):
         # Validate config
         config = ConfigModel(**dict(self.config))
         config_ldap = ConfigLdapModel(**dict(self.config))
+
+        if config.mysql_host 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)
+
         # Create Builder for the PodSpec
         pod_spec_builder = PodSpecV3Builder()
+
         # Build Container
-        container_builder = ContainerV3Builder(self.app.name, image_info)
+        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)
         container_builder.add_volume_config(
@@ -239,10 +289,11 @@ class KeystoneCharm(CharmedOsmBase):
         )
         container_builder.add_envs(
             {
-                "DB_HOST": self.mysql_client.host,
-                "DB_PORT": self.mysql_client.port,
+                "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": self.mysql_client.root_password,
+                "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,
@@ -256,7 +307,6 @@ class KeystoneCharm(CharmedOsmBase):
         )
 
         if config_ldap.ldap_enabled:
-
             container_builder.add_envs(
                 {
                     "LDAP_AUTHENTICATION_DOMAIN_NAME": config_ldap.ldap_authentication_domain_name,
@@ -309,6 +359,11 @@ class KeystoneCharm(CharmedOsmBase):
                     {"LDAP_GROUP_TREE_DN": config_ldap.ldap_group_tree_dn}
                 )
 
+            if config_ldap.ldap_tls_cacert_base64:
+                container_builder.add_envs(
+                    {"LDAP_TLS_CACERT_BASE64": config_ldap.ldap_tls_cacert_base64}
+                )
+
             if config_ldap.ldap_use_starttls:
                 container_builder.add_envs(
                     {
@@ -318,8 +373,10 @@ class KeystoneCharm(CharmedOsmBase):
                     }
                 )
         container = container_builder.build()
+
         # Add container to pod spec
         pod_spec_builder.add_container(container)
+
         # Add ingress resources to pod spec if site url exists
         if config.site_url:
             parsed = urlparse(config.site_url)
@@ -328,8 +385,10 @@ class KeystoneCharm(CharmedOsmBase):
                     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
             )