From 43a3ab6a94b2a40f32334dcc8384c37b2553b880 Mon Sep 17 00:00:00 2001 From: Patricia Reinoso Date: Wed, 24 May 2023 08:08:35 +0000 Subject: [PATCH] Remove legacy keystone charm Change-Id: Ieef80de0273facc15c42aaf216867c6866f183ab Signed-off-by: Patricia Reinoso --- devops-stages/stage-test.sh | 2 +- installers/charm/keystone/.gitignore | 30 - installers/charm/keystone/.jujuignore | 34 - installers/charm/keystone/.yamllint.yaml | 34 - installers/charm/keystone/README.md | 17 - installers/charm/keystone/charmcraft.yaml | 41 - installers/charm/keystone/config.yaml | 257 ----- installers/charm/keystone/metadata.yaml | 35 - .../charm/keystone/requirements-test.txt | 20 - installers/charm/keystone/requirements.txt | 22 - installers/charm/keystone/src/charm.py | 895 ------------------ installers/charm/keystone/tests/__init__.py | 40 - installers/charm/keystone/tests/test_charm.py | 217 ----- installers/charm/keystone/tox.ini | 126 --- 14 files changed, 1 insertion(+), 1769 deletions(-) delete mode 100644 installers/charm/keystone/.gitignore delete mode 100644 installers/charm/keystone/.jujuignore delete mode 100644 installers/charm/keystone/.yamllint.yaml delete mode 100644 installers/charm/keystone/README.md delete mode 100644 installers/charm/keystone/charmcraft.yaml delete mode 100644 installers/charm/keystone/config.yaml delete mode 100644 installers/charm/keystone/metadata.yaml delete mode 100644 installers/charm/keystone/requirements-test.txt delete mode 100644 installers/charm/keystone/requirements.txt delete mode 100755 installers/charm/keystone/src/charm.py delete mode 100644 installers/charm/keystone/tests/__init__.py delete mode 100644 installers/charm/keystone/tests/test_charm.py delete mode 100644 installers/charm/keystone/tox.ini diff --git a/devops-stages/stage-test.sh b/devops-stages/stage-test.sh index af5953a0..4f32d7ea 100755 --- a/devops-stages/stage-test.sh +++ b/devops-stages/stage-test.sh @@ -21,7 +21,7 @@ CURRENT_DIR=`pwd` # Execute tests for charms CHARM_PATH="./installers/charm" NEW_CHARMS_NAMES="osm-lcm osm-mon osm-nbi osm-ng-ui osm-pol osm-ro vca-integrator-operator" -OLD_CHARMS_NAMES="keystone prometheus grafana" +OLD_CHARMS_NAMES="prometheus grafana" for charm in $NEW_CHARMS_NAMES; do if [ $(git diff --name-only "origin/${GERRIT_BRANCH}" -- "installers/charm/${charm}" | wc -l) -ne 0 ]; then echo "Running tox for ${charm}" diff --git a/installers/charm/keystone/.gitignore b/installers/charm/keystone/.gitignore deleted file mode 100644 index 493739ef..00000000 --- a/installers/charm/keystone/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# 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 -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.stestr -cover -release diff --git a/installers/charm/keystone/.jujuignore b/installers/charm/keystone/.jujuignore deleted file mode 100644 index 3ae3e7dc..00000000 --- a/installers/charm/keystone/.jujuignore +++ /dev/null @@ -1,34 +0,0 @@ -# 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 -## - -venv -.vscode -build -*.charm -.coverage -coverage.xml -.gitignore -.stestr -cover -release -tests/ -requirements* -tox.ini diff --git a/installers/charm/keystone/.yamllint.yaml b/installers/charm/keystone/.yamllint.yaml deleted file mode 100644 index d71fb69f..00000000 --- a/installers/charm/keystone/.yamllint.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# 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 -## - ---- -extends: default - -yaml-files: - - "*.yaml" - - "*.yml" - - ".yamllint" -ignore: | - .tox - cover/ - build/ - venv - release/ diff --git a/installers/charm/keystone/README.md b/installers/charm/keystone/README.md deleted file mode 100644 index 1ca9764c..00000000 --- a/installers/charm/keystone/README.md +++ /dev/null @@ -1,17 +0,0 @@ - -# Keystone operator Charm for Kubernetes - -## Requirements - diff --git a/installers/charm/keystone/charmcraft.yaml b/installers/charm/keystone/charmcraft.yaml deleted file mode 100644 index 04b659ac..00000000 --- a/installers/charm/keystone/charmcraft.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# 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 -## - -type: charm -bases: - - build-on: - - name: ubuntu - channel: "20.04" - architectures: ["amd64"] - run-on: - - name: ubuntu - channel: "20.04" - architectures: - - amd64 - - aarch64 - - arm64 -parts: - charm: - build-packages: - - git - - libffi-dev - charm-binary-python-packages: - - cryptography diff --git a/installers/charm/keystone/config.yaml b/installers/charm/keystone/config.yaml deleted file mode 100644 index dc0953a9..00000000 --- a/installers/charm/keystone/config.yaml +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright 2020 Canonical Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain 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. -options: - max_file_size: - type: int - description: | - The maximum file size, in megabytes. - - If there is a reverse proxy in front of Keystone, it may - need to be configured to handle the requested size. - default: 5 - ingress_class: - type: string - description: | - Ingress class name. This is useful for selecting the ingress to be used - in case there are multiple ingresses in the underlying k8s clusters. - ingress_whitelist_source_range: - type: string - description: | - A comma-separated list of CIDRs to store in the - ingress.kubernetes.io/whitelist-source-range annotation. - - This can be used to lock down access to - Keystone based on source IP address. - default: "" - tls_secret_name: - type: string - description: TLS Secret name - default: "" - site_url: - type: string - description: Ingress URL - default: "" - image_pull_policy: - type: string - description: | - ImagePullPolicy configuration for the pod. - Possible values: always, ifnotpresent, never - default: always - security_context: - description: Enables the security context of the pods - type: boolean - default: false - region_id: - type: string - description: Region ID to be created when starting the service - default: RegionOne - keystone_db_password: - type: string - description: Keystone DB Password - default: admin - mysql_uri: - type: string - description: | - Mysql uri with the following format: - mysql://:@:/ - admin_username: - type: string - description: Admin username to be created when starting the service - default: admin - admin_password: - type: string - description: Admin password to be created when starting the service - default: admin - admin_project: - type: string - description: Admin project to be created when starting the service - default: admin - service_username: - type: string - description: Service Username to be created when starting the service - default: nbi - service_password: - type: string - description: Service Password to be created when starting the service - default: nbi - service_project: - type: string - description: Service Project to be created when starting the service - default: service - user_domain_name: - type: string - description: User domain name (Hardcoded in the container start.sh script) - default: default - project_domain_name: - type: string - description: | - Project domain name (Hardcoded in the container start.sh script) - default: default - token_expiration: - type: int - description: Token keys expiration in seconds - default: 172800 - ldap_enabled: - type: boolean - description: Boolean to enable/disable LDAP authentication - default: false - ldap_authentication_domain_name: - type: string - description: Name of the domain which use LDAP authentication - default: "" - ldap_url: - type: string - description: URL of the LDAP server - default: "ldap://localhost" - ldap_bind_user: - type: string - description: User to bind and search for users - default: "" - ldap_bind_password: - type: string - description: Password to bind and search for users - default: "" - ldap_chase_referrals: - type: string - description: | - Sets keystone’s referral chasing behavior across directory partitions. - If left unset, the system’s default behavior will be used. - default: "" - ldap_page_size: - type: int - description: | - Defines the maximum number of results per page that keystone should - request from the LDAP server when listing objects. A value of zero (0) - disables paging. - default: 0 - ldap_user_tree_dn: - type: string - description: | - Root of the tree in LDAP server in which Keystone will search for users - default: "" - ldap_user_objectclass: - type: string - description: | - LDAP object class that Keystone will filter on within user_tree_dn to - find user objects. Any objects of other classes will be ignored. - default: inetOrgPerson - ldap_user_id_attribute: - type: string - description: | - This set of options define the mapping to LDAP attributes for the three - key user attributes supported by Keystone. The LDAP attribute chosen for - user_id must be something that is immutable for a user and no more than - 64 characters in length. Notice that Distinguished Name (DN) may be - longer than 64 characters and thus is not suitable. An uid, or mail may - be appropriate. - default: cn - ldap_user_name_attribute: - type: string - description: | - This set of options define the mapping to LDAP attributes for the three - key user attributes supported by Keystone. The LDAP attribute chosen for - user_id must be something that is immutable for a user and no more than - 64 characters in length. Notice that Distinguished Name (DN) may be - longer than 64 characters and thus is not suitable. An uid, or mail may - be appropriate. - default: sn - ldap_user_pass_attribute: - type: string - description: | - This set of options define the mapping to LDAP attributes for the three - key user attributes supported by Keystone. The LDAP attribute chosen for - user_id must be something that is immutable for a user and no more than - 64 characters in length. Notice that Distinguished Name (DN) may be - longer than 64 characters and thus is not suitable. An uid, or mail may - be appropriate. - default: userPassword - ldap_user_filter: - type: string - description: | - This filter option allow additional filter (over and above - user_objectclass) to be included into the search of user. One common use - of this is to provide more efficient searching, where the recommended - search for user objects is (&(objectCategory=person)(objectClass=user)). - By specifying user_objectclass as user and user_filter as - objectCategory=person in the Keystone configuration file, this can be - achieved. - default: "" - ldap_user_enabled_attribute: - type: string - description: | - In Keystone, a user entity can be either enabled or disabled. Setting - the above option will give a mapping to an equivalent attribute in LDAP, - allowing your LDAP management tools to disable a user. - default: enabled - ldap_user_enabled_mask: - type: int - description: | - Some LDAP schemas, rather than having a dedicated attribute for user - enablement, use a bit within a general control attribute (such as - userAccountControl) to indicate this. Setting user_enabled_mask will - cause Keystone to look at only the status of this bit in the attribute - specified by user_enabled_attribute, with the bit set indicating the - user is enabled. - default: 0 - ldap_user_enabled_default: - type: string - description: | - Most LDAP servers use a boolean or bit in a control field to indicate - enablement. However, some schemas might use an integer value in an - attribute. In this situation, set user_enabled_default to the integer - value that represents a user being enabled. - default: "true" - ldap_user_enabled_invert: - type: boolean - description: | - Some LDAP schemas have an “account locked” attribute, which is the - equivalent to account being “disabled.” In order to map this to the - Keystone enabled attribute, you can utilize the user_enabled_invert - setting in conjunction with user_enabled_attribute to map the lock - status to disabled in Keystone. - default: false - ldap_group_objectclass: - type: string - description: The LDAP object class to use for groups. - default: groupOfNames - ldap_group_tree_dn: - type: string - description: The search base to use for groups. - default: "" - ldap_use_starttls: - type: boolean - description: | - Enable Transport Layer Security (TLS) for providing a secure connection - from Keystone to LDAP (StartTLS, not LDAPS). - default: false - ldap_tls_cacert_base64: - type: string - description: | - CA certificate in Base64 format (if you have the PEM file, text inside - "-----BEGIN CERTIFICATE-----"/"-----END CERTIFICATE-----" tags). - default: "" - ldap_tls_req_cert: - type: string - description: | - Defines how the certificates are checked for validity in the client - (i.e., Keystone end) of the secure connection (this doesn’t affect what - level of checking the server is doing on the certificates it receives - from Keystone). Possible values are "demand", "never", and "allow". The - default of demand means the client always checks the certificate and - will drop the connection if it is not provided or invalid. never is the - opposite—it never checks it, nor requires it to be provided. allow means - that if it is not provided then the connection is allowed to continue, - but if it is provided it will be checked—and if invalid, the connection - will be dropped. - default: demand diff --git a/installers/charm/keystone/metadata.yaml b/installers/charm/keystone/metadata.yaml deleted file mode 100644 index 38c03ed7..00000000 --- a/installers/charm/keystone/metadata.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2020 Canonical Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain 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. -name: keystone -summary: A Keystone K8s charm -description: | - A CAAS charm to deploy Keystone -series: - - kubernetes -min-juju-version: 2.8.0 -resources: - image: - type: oci-image - description: OSM docker image for NBI - upstream-source: "opensourcemano/keystone:latest" -requires: - db: - interface: mysql - limit: 1 -provides: - keystone: - interface: keystone -deployment: - type: stateless - service: cluster diff --git a/installers/charm/keystone/requirements-test.txt b/installers/charm/keystone/requirements-test.txt deleted file mode 100644 index cf61dd4e..00000000 --- a/installers/charm/keystone/requirements-test.txt +++ /dev/null @@ -1,20 +0,0 @@ -# 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 -mock==4.0.3 diff --git a/installers/charm/keystone/requirements.txt b/installers/charm/keystone/requirements.txt deleted file mode 100644 index 5c18be82..00000000 --- a/installers/charm/keystone/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -# 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 -## -cryptography -git+https://github.com/charmed-osm/ops-lib-charmed-osm/@master \ No newline at end of file diff --git a/installers/charm/keystone/src/charm.py b/installers/charm/keystone/src/charm.py deleted file mode 100755 index 446d2e0c..00000000 --- a/installers/charm/keystone/src/charm.py +++ /dev/null @@ -1,895 +0,0 @@ -#!/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) diff --git a/installers/charm/keystone/tests/__init__.py b/installers/charm/keystone/tests/__init__.py deleted file mode 100644 index 446d5cee..00000000 --- a/installers/charm/keystone/tests/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python3 -# Copyright 2020 Canonical Ltd. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# 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 -## - -"""Init mocking for unit tests.""" - -import sys - - -import mock - - -class OCIImageResourceErrorMock(Exception): - pass - - -sys.path.append("src") - -oci_image = mock.MagicMock() -oci_image.OCIImageResourceError = OCIImageResourceErrorMock -sys.modules["oci_image"] = oci_image -sys.modules["oci_image"].OCIImageResource().fetch.return_value = {} diff --git a/installers/charm/keystone/tests/test_charm.py b/installers/charm/keystone/tests/test_charm.py deleted file mode 100644 index 2a950290..00000000 --- a/installers/charm/keystone/tests/test_charm.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/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 -## - -import sys -from typing import NoReturn -import unittest - - -from charm import KeystoneCharm -from ops.model import ActiveStatus, BlockedStatus -from ops.testing import Harness - - -class TestCharm(unittest.TestCase): - """Keystone Charm unit tests.""" - - def setUp(self) -> NoReturn: - """Test setup""" - self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() - self.harness = Harness(KeystoneCharm) - self.harness.set_leader(is_leader=True) - self.harness.begin() - self.config = { - "region_id": "str", - "keystone_db_password": "str", - "mysql_host": "", - "mysql_port": 3306, - "mysql_root_password": "manopw", - "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": 10, - "max_file_size": 1, - "site_url": "http://keystone.com", - "ldap_enabled": False, - } - self.harness.update_config(self.config) - - def test_config_changed_no_relations( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - self.assertTrue( - all( - relation in self.harness.charm.unit.status.message - for relation in ["mysql"] - ) - ) - - def test_config_changed_non_leader( - self, - ) -> NoReturn: - """Test ingress resources without HTTP.""" - self.harness.set_leader(is_leader=False) - self.harness.charm.on.config_changed.emit() - - # Assertions - self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - - def test_with_config(self) -> NoReturn: - "Test with mysql config" - self.initialize_mysql_config() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_with_relations( - self, - ) -> NoReturn: - "Test with relations" - self.initialize_mysql_relation() - # Verifying status - self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def test_exception_mysql_relation_and_config( - self, - ) -> NoReturn: - "Test with relations and config. Must throw exception" - self.initialize_mysql_config() - self.initialize_mysql_relation() - # Verifying status - self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) - - def initialize_mysql_config(self): - self.harness.update_config({"mysql_uri": "mysql://root:manopw@mysql:3306/"}) - - def initialize_mysql_relation(self): - relation_id = self.harness.add_relation("db", "mysql") - self.harness.add_relation_unit(relation_id, "mysql/0") - self.harness.update_relation_data( - relation_id, - "mysql/0", - { - "host": "mysql", - "port": 3306, - "user": "mano", - "password": "manopw", - "root_password": "rootmanopw", - }, - ) - - -if __name__ == "__main__": - unittest.main() - - -# class TestCharm(unittest.TestCase): -# """Prometheus Charm unit tests.""" - -# def setUp(self) -> NoReturn: -# """Test setup""" -# self.image_info = sys.modules["oci_image"].OCIImageResource().fetch() -# self.harness = Harness(KeystoneCharm) -# self.harness.set_leader(is_leader=True) -# self.harness.begin() -# self.config = { -# "enable_ng_ro": True, -# "database_commonkey": "commonkey", -# "log_level": "INFO", -# "vim_database": "db_name", -# "ro_database": "ro_db_name", -# "openmano_tenant": "mano", -# } - -# def test_config_changed_no_relations( -# self, -# ) -> NoReturn: -# """Test ingress resources without HTTP.""" - -# self.harness.charm.on.config_changed.emit() - -# # Assertions -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) -# self.assertTrue( -# all( -# relation in self.harness.charm.unit.status.message -# for relation in ["mongodb", "kafka"] -# ) -# ) - -# # Disable ng-ro -# self.harness.update_config({"enable_ng_ro": False}) -# self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus) -# self.assertTrue( -# all( -# relation in self.harness.charm.unit.status.message -# for relation in ["mysql"] -# ) -# ) - -# def test_config_changed_non_leader( -# self, -# ) -> NoReturn: -# """Test ingress resources without HTTP.""" -# self.harness.set_leader(is_leader=False) -# self.harness.charm.on.config_changed.emit() - -# # Assertions -# self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus) - -# def test_with_relations_ng( -# self, -# ) -> NoReturn: -# "Test with relations (ng-ro)" - -# # Initializing the kafka relation -# kafka_relation_id = self.harness.add_relation("kafka", "kafka") -# self.harness.add_relation_unit(kafka_relation_id, "kafka/0") -# self.harness.update_relation_data( -# kafka_relation_id, "kafka/0", {"host": "kafka", "port": 9092} -# ) - -# # Initializing the mongo relation -# mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb") -# self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0") -# self.harness.update_relation_data( -# mongodb_relation_id, -# "mongodb/0", -# {"connection_string": "mongodb://mongo:27017"}, -# ) - -# self.harness.charm.on.config_changed.emit() - -# # Verifying status -# self.assertNotIsInstance(self.harness.charm.unit.status, BlockedStatus) - - -# if __name__ == "__main__": -# unittest.main() diff --git a/installers/charm/keystone/tox.ini b/installers/charm/keystone/tox.ini deleted file mode 100644 index 88fd16a2..00000000 --- a/installers/charm/keystone/tox.ini +++ /dev/null @@ -1,126 +0,0 @@ -# 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 -## -####################################################################################### - -[tox] -envlist = black, cover, flake8, pylint, yamllint, safety -skipsdist = true - -[tox:jenkins] -toxworkdir = /tmp/.tox - -[testenv] -basepython = python3.8 -setenv = VIRTUAL_ENV={envdir} - PYTHONDONTWRITEBYTECODE = 1 -deps = -r{toxinidir}/requirements.txt - - -####################################################################################### -[testenv:black] -deps = black -commands = - black --check --diff src/ tests/ - - -####################################################################################### -[testenv:cover] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - coverage - nose2 -commands = - sh -c 'rm -f nosetests.xml' - coverage erase - nose2 -C --coverage src - coverage report --omit='*tests*' - coverage html -d ./cover --omit='*tests*' - coverage xml -o coverage.xml --omit=*tests* -whitelist_externals = sh - - -####################################################################################### -[testenv:flake8] -deps = flake8 - flake8-import-order -commands = - flake8 src/ tests/ - - -####################################################################################### -[testenv:pylint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - pylint==2.10.2 -commands = - pylint -E src/ tests/ - - -####################################################################################### -[testenv:safety] -setenv = - LC_ALL=C.UTF-8 - LANG=C.UTF-8 -deps = {[testenv]deps} - safety -commands = - - safety check --full-report - - -####################################################################################### -[testenv:yamllint] -deps = {[testenv]deps} - -r{toxinidir}/requirements-test.txt - yamllint -commands = yamllint . - -####################################################################################### -[testenv:build] -passenv=HTTP_PROXY HTTPS_PROXY NO_PROXY -whitelist_externals = - charmcraft - sh -commands = - charmcraft pack - sh -c 'ubuntu_version=20.04; \ - architectures="amd64-aarch64-arm64"; \ - charm_name=`cat metadata.yaml | grep -E "^name: " | cut -f 2 -d " "`; \ - mv $charm_name"_ubuntu-"$ubuntu_version-$architectures.charm $charm_name.charm' - -####################################################################################### -[flake8] -ignore = - W291, - W293, - W503, - E123, - E125, - E226, - E241, -exclude = - .git, - __pycache__, - .tox, -max-line-length = 120 -show-source = True -builtins = _ -; max-complexity = 10 -import-order-style = google -- 2.25.1