From a584b82a4f357c8fe04404339ff1127721c9debf Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 17 Oct 2022 10:34:40 +0100 Subject: [PATCH] Use rehomed identity-service interface Use new rehomed identity-service interface from keystone-k8s. Depends-On: I75dac5894f456d6b9de5aa10e43c0d1ebbcceda8 Change-Id: I5d043b0096dada205aef05e0ef903ed2f495618d --- ops-sunbeam/fetch-libs.sh | 2 +- ops-sunbeam/ops_sunbeam/relation_handlers.py | 2 +- .../v0/identity_service.py | 65 ++- .../v0/cloud_credentials.py | 419 ------------------ 4 files changed, 46 insertions(+), 442 deletions(-) rename ops-sunbeam/unit_tests/lib/charms/{sunbeam_keystone_operator => keystone_k8s}/v0/identity_service.py (91%) delete mode 100644 ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/cloud_credentials.py diff --git a/ops-sunbeam/fetch-libs.sh b/ops-sunbeam/fetch-libs.sh index c009b657..3ca0ec7d 100755 --- a/ops-sunbeam/fetch-libs.sh +++ b/ops-sunbeam/fetch-libs.sh @@ -6,7 +6,7 @@ echo "WARNING: Charm interface libs are excluded from ASO python package." charmcraft fetch-lib charms.nginx_ingress_integrator.v0.ingress charmcraft fetch-lib charms.data_platform_libs.v0.database_requires -charmcraft fetch-lib charms.sunbeam_keystone_operator.v0.identity_service +charmcraft fetch-lib charms.keystone_k8s.v0.identity_service charmcraft fetch-lib charms.keystone_k8s.v0.cloud_credentials charmcraft fetch-lib charms.rabbitmq_k8s.v0.rabbitmq charmcraft fetch-lib charms.sunbeam_ovn_central_operator.v0.ovsdb diff --git a/ops-sunbeam/ops_sunbeam/relation_handlers.py b/ops-sunbeam/ops_sunbeam/relation_handlers.py index 5c90aa86..e54ca120 100644 --- a/ops-sunbeam/ops_sunbeam/relation_handlers.py +++ b/ops-sunbeam/ops_sunbeam/relation_handlers.py @@ -409,7 +409,7 @@ class IdentityServiceRequiresHandler(RelationHandler): def setup_event_handler(self) -> ops.charm.Object: """Configure event handlers for an Identity service relation.""" logger.debug("Setting up Identity Service event handler") - import charms.sunbeam_keystone_operator.v0.identity_service as sun_id + import charms.keystone_k8s.v0.identity_service as sun_id id_svc = sun_id.IdentityServiceRequires( self.charm, self.relation_name, self.service_endpoints, self.region ) diff --git a/ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/identity_service.py b/ops-sunbeam/unit_tests/lib/charms/keystone_k8s/v0/identity_service.py similarity index 91% rename from ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/identity_service.py rename to ops-sunbeam/unit_tests/lib/charms/keystone_k8s/v0/identity_service.py index 7a7f4e43..8f80a191 100644 --- a/ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/identity_service.py +++ b/ops-sunbeam/unit_tests/lib/charms/keystone_k8s/v0/identity_service.py @@ -26,7 +26,7 @@ Two events are also available to respond to: A basic example showing the usage of this relation follows: ``` -from charms.sunbeam_sunbeam_identity_service_operator.v0.identity_service import IdentityServiceRequires +from charms.sunbeam_keystone_operator.v0.identity_service import IdentityServiceRequires class IdentityServiceClientCharm(CharmBase): def __init__(self, *args): @@ -75,19 +75,8 @@ class IdentityServiceClientCharm(CharmBase): ``` """ -# The unique Charmhub library identifier, never change it -LIBID = "6a7cb19b98314ecf916e3fcb02708608" - -# Increment this major API version when introducing breaking changes -LIBAPI = 0 - -# Increment this PATCH version before using `charmcraft publish-lib` or reset -# to 0 if you are raising the major API version -LIBPATCH = 1 - import json import logging -import requests from ops.framework import ( StoredState, @@ -96,10 +85,20 @@ from ops.framework import ( EventSource, Object, ) - from ops.model import Relation -from typing import List +logger = logging.getLogger(__name__) + +# The unique Charmhub library identifier, never change it +LIBID = "0fa7fe7236c14c6e9624acf232b9a3b0" + +# Increment this major API version when introducing breaking changes +LIBAPI = 0 + +# Increment this PATCH version before using `charmcraft publish-lib` or reset +# to 0 if you are raising the major API version +LIBPATCH = 1 + logger = logging.getLogger(__name__) @@ -309,6 +308,20 @@ class IdentityServiceRequires(Object): """Return the service_user_id.""" return self.get_remote_app_data('service-user-id') + @property + def internal_auth_url(self) -> str: + """Return the internal_auth_url.""" + return self.get_remote_app_data('internal-auth-url') + + @property + def admin_auth_url(self) -> str: + """Return the admin_auth_url.""" + return self.get_remote_app_data('admin-auth-url') + + @property + def public_auth_url(self) -> str: + """Return the public_auth_url.""" + return self.get_remote_app_data('public-auth-url') def register_services(self, service_endpoints: dict, region: str) -> None: @@ -316,7 +329,9 @@ class IdentityServiceRequires(Object): if self.model.unit.is_leader(): logging.debug("Requesting service registration") app_data = self._identity_service_rel.data[self.charm.app] - app_data["service-endpoints"] = json.dumps(service_endpoints) + app_data["service-endpoints"] = json.dumps( + service_endpoints, sort_keys=True + ) app_data["region"] = region @@ -337,7 +352,6 @@ class ReadyIdentityServiceClientsEvent(EventBase): self.service_endpoints = service_endpoints self.region = region self.client_app_name = client_app_name - def snapshot(self): return { @@ -399,14 +413,13 @@ class IdentityServiceProvides(Object): REQUIRED_KEYS = [ 'service-endpoints', 'region'] - + values = [ event.relation.data[event.relation.app].get(k) - for k in REQUIRED_KEYS ] + for k in REQUIRED_KEYS + ] # Validate data on the relation if all(values): - print(event.relation.id) - print(event.relation.name) service_eps = json.loads( event.relation.data[event.relation.app]['service-endpoints']) self.on.ready_identity_service_clients.emit( @@ -439,11 +452,18 @@ class IdentityServiceProvides(Object): service_domain: str, service_password: str, service_project: str, - service_user: str): + service_user: str, + internal_auth_url: str, + admin_auth_url: str, + public_auth_url: str): logging.debug("Setting identity_service connection information.") + _identity_service_rel = None for relation in self.framework.model.relations[relation_name]: if relation.id == relation_id: _identity_service_rel = relation + if not _identity_service_rel: + # Relation has disappeared so skip send of data + return app_data = _identity_service_rel.data[self.charm.app] app_data["api-version"] = api_version app_data["auth-host"] = auth_host @@ -468,3 +488,6 @@ class IdentityServiceProvides(Object): app_data["service-user-name"] = service_user.name app_data["service-user-id"] = service_user.id app_data["service-password"] = service_password + app_data["internal-auth-url"] = internal_auth_url + app_data["admin-auth-url"] = admin_auth_url + app_data["public-auth-url"] = public_auth_url diff --git a/ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/cloud_credentials.py b/ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/cloud_credentials.py deleted file mode 100644 index 275c321d..00000000 --- a/ops-sunbeam/unit_tests/lib/charms/sunbeam_keystone_operator/v0/cloud_credentials.py +++ /dev/null @@ -1,419 +0,0 @@ -"""IdentityServiceProvides and Requires module. - - -This library contains the Requires and Provides classes for handling -the identity_service interface. - -Import `IdentityServiceRequires` in your charm, with the charm object and the -relation name: - - self - - "identity_service" - -Also provide additional parameters to the charm object: - - service - - internal_url - - public_url - - admin_url - - region - - username - - vhost - -Two events are also available to respond to: - - connected - - ready - - goneaway - -A basic example showing the usage of this relation follows: - -``` -from charms.sunbeam_sunbeam_identity_service_operator.v0.identity_service import IdentityServiceRequires - -class IdentityServiceClientCharm(CharmBase): - def __init__(self, *args): - super().__init__(*args) - # IdentityService Requires - self.identity_service = IdentityServiceRequires( - self, "identity_service", - service = "my-service" - internal_url = "http://internal-url" - public_url = "http://public-url" - admin_url = "http://admin-url" - region = "region" - ) - self.framework.observe( - self.identity_service.on.connected, self._on_identity_service_connected) - self.framework.observe( - self.identity_service.on.ready, self._on_identity_service_ready) - self.framework.observe( - self.identity_service.on.goneaway, self._on_identity_service_goneaway) - - def _on_identity_service_connected(self, event): - '''React to the IdentityService connected event. - - This event happens when n IdentityService relation is added to the - model before credentials etc have been provided. - ''' - # Do something before the relation is complete - pass - - def _on_identity_service_ready(self, event): - '''React to the IdentityService ready event. - - The IdentityService interface will use the provided config for the - request to the identity server. - ''' - # IdentityService Relation is ready. Do something with the completed relation. - pass - - def _on_identity_service_goneaway(self, event): - '''React to the IdentityService goneaway event. - - This event happens when an IdentityService relation is removed. - ''' - # IdentityService Relation has goneaway. shutdown services or suchlike - pass -``` -""" - -# The unique Charmhub library identifier, never change it -LIBID = "deadbeef" - -# Increment this major API version when introducing breaking changes -LIBAPI = 0 - -# Increment this PATCH version before using `charmcraft publish-lib` or reset -# to 0 if you are raising the major API version -LIBPATCH = 1 - -import json -import logging -import requests - -from ops.framework import ( - StoredState, - EventBase, - ObjectEvents, - EventSource, - Object, -) - -from ops.model import Relation - -from typing import List - -logger = logging.getLogger(__name__) - - -class CloudCredentialsConnectedEvent(EventBase): - """CloudCredentials connected Event.""" - - pass - - -class CloudCredentialsReadyEvent(EventBase): - """CloudCredentials ready for use Event.""" - - pass - - -class CloudCredentialsGoneAwayEvent(EventBase): - """CloudCredentials relation has gone-away Event""" - - pass - - -class CloudCredentialsServerEvents(ObjectEvents): - """Events class for `on`""" - - connected = EventSource(CloudCredentialsConnectedEvent) - ready = EventSource(CloudCredentialsReadyEvent) - goneaway = EventSource(CloudCredentialsGoneAwayEvent) - - -class CloudCredentialsRequires(Object): - """ - CloudCredentialsRequires class - """ - - on = CloudCredentialsServerEvents() - _stored = StoredState() - - def __init__(self, charm, relation_name: str): - super().__init__(charm, relation_name) - self.charm = charm - self.relation_name = relation_name - self.framework.observe( - self.charm.on[relation_name].relation_joined, - self._on_cloud_credentials_relation_joined, - ) - self.framework.observe( - self.charm.on[relation_name].relation_changed, - self._on_cloud_credentials_relation_changed, - ) - self.framework.observe( - self.charm.on[relation_name].relation_departed, - self._on_cloud_credentials_relation_changed, - ) - self.framework.observe( - self.charm.on[relation_name].relation_broken, - self._on_cloud_credentials_relation_broken, - ) - - def _on_cloud_credentials_relation_joined(self, event): - """CloudCredentials relation joined.""" - logging.debug("CloudCredentials on_joined") - self.on.connected.emit() - self.request_credentials() - - def _on_cloud_credentials_relation_changed(self, event): - """CloudCredentials relation changed.""" - logging.debug("CloudCredentials on_changed") - try: - self.on.ready.emit() - except AttributeError: - logger.exception('Error when emitting event') - - def _on_cloud_credentials_relation_broken(self, event): - """CloudCredentials relation broken.""" - logging.debug("CloudCredentials on_broken") - self.on.goneaway.emit() - - @property - def _cloud_credentials_rel(self) -> Relation: - """The CloudCredentials relation.""" - return self.framework.model.get_relation(self.relation_name) - - def get_remote_app_data(self, key: str) -> str: - """Return the value for the given key from remote app data.""" - data = self._cloud_credentials_rel.data[self._cloud_credentials_rel.app] - return data.get(key) - - @property - def api_version(self) -> str: - """Return the api_version.""" - return self.get_remote_app_data('api-version') - - @property - def auth_host(self) -> str: - """Return the auth_host.""" - return self.get_remote_app_data('auth-host') - - @property - def auth_port(self) -> str: - """Return the auth_port.""" - return self.get_remote_app_data('auth-port') - - @property - def auth_protocol(self) -> str: - """Return the auth_protocol.""" - return self.get_remote_app_data('auth-protocol') - - @property - def internal_host(self) -> str: - """Return the internal_host.""" - return self.get_remote_app_data('internal-host') - - @property - def internal_port(self) -> str: - """Return the internal_port.""" - return self.get_remote_app_data('internal-port') - - @property - def internal_protocol(self) -> str: - """Return the internal_protocol.""" - return self.get_remote_app_data('internal-protocol') - - @property - def username(self) -> str: - """Return the username.""" - return self.get_remote_app_data('username') - - @property - def password(self) -> str: - """Return the password.""" - return self.get_remote_app_data('password') - - @property - def project_name(self) -> str: - """Return the project name.""" - return self.get_remote_app_data('project-name') - - @property - def project_id(self) -> str: - """Return the project id.""" - return self.get_remote_app_data('project-id') - - @property - def user_domain_name(self) -> str: - """Return the name of the user domain.""" - return self.get_remote_app_data('user-domain-name') - - @property - def user_domain_id(self) -> str: - """Return the id of the user domain.""" - return self.get_remote_app_data('user-domain-id') - - @property - def project_domain_name(self) -> str: - """Return the name of the project domain.""" - return self.get_remote_app_data('project-domain-name') - - @property - def project_domain_id(self) -> str: - """Return the id of the project domain.""" - return self.get_remote_app_data('project-domain-id') - - @property - def region(self) -> str: - """Return the region for the auth urls.""" - return self.get_remote_app_data('region') - - def request_credentials(self) -> None: - """Request credentials from the CloudCredentials server.""" - if self.model.unit.is_leader(): - logging.debug(f'Requesting credentials for {self.charm.app.name}') - app_data = self._cloud_credentials_rel.data[self.charm.app] - app_data['username'] = self.charm.app.name - - -class HasCloudCredentialsClientsEvent(EventBase): - """Has CloudCredentialsClients Event.""" - - pass - - -class ReadyCloudCredentialsClientsEvent(EventBase): - """CloudCredentialsClients Ready Event.""" - - def __init__(self, handle, relation_id, relation_name, username): - super().__init__(handle) - self.relation_id = relation_id - self.relation_name = relation_name - self.username = username - - def snapshot(self): - return { - "relation_id": self.relation_id, - "relation_name": self.relation_name, - "username": self.username, - } - - def restore(self, snapshot): - super().restore(snapshot) - self.relation_id = snapshot["relation_id"] - self.relation_name = snapshot["relation_name"] - self.username = snapshot["username"] - - -class CloudCredentialsClientsGoneAwayEvent(EventBase): - """Has CloudCredentialsClientsGoneAwayEvent Event.""" - - pass - - -class CloudCredentialsClientEvents(ObjectEvents): - """Events class for `on`""" - - has_cloud_credentials_clients = EventSource( - HasCloudCredentialsClientsEvent - ) - ready_cloud_credentials_clients = EventSource( - ReadyCloudCredentialsClientsEvent - ) - cloud_credentials_clients_gone = EventSource( - CloudCredentialsClientsGoneAwayEvent - ) - - -class CloudCredentialsProvides(Object): - """ - CloudCredentialsProvides class - """ - - on = CloudCredentialsClientEvents() - _stored = StoredState() - - def __init__(self, charm, relation_name): - super().__init__(charm, relation_name) - self.charm = charm - self.relation_name = relation_name - self.framework.observe( - self.charm.on[relation_name].relation_joined, - self._on_cloud_credentials_relation_joined, - ) - self.framework.observe( - self.charm.on[relation_name].relation_changed, - self._on_cloud_credentials_relation_changed, - ) - self.framework.observe( - self.charm.on[relation_name].relation_broken, - self._on_cloud_credentials_relation_broken, - ) - - def _on_cloud_credentials_relation_joined(self, event): - """Handle CloudCredentials joined.""" - logging.debug("CloudCredentialsProvides on_joined") - self.on.has_cloud_credentials_clients.emit() - - def _on_cloud_credentials_relation_changed(self, event): - """Handle CloudCredentials changed.""" - logging.debug("CloudCredentials on_changed") - REQUIRED_KEYS = ['username'] - - values = [ - event.relation.data[event.relation.app].get(k) - for k in REQUIRED_KEYS - ] - # Validate data on the relation - if all(values): - username = event.relation.data[event.relation.app]['username'] - self.on.ready_cloud_credentials_clients.emit( - event.relation.id, - event.relation.name, - username, - ) - - def _on_cloud_credentials_relation_broken(self, event): - """Handle CloudCredentials broken.""" - logging.debug("CloudCredentialsProvides on_departed") - self.on.cloud_credentials_clients_gone.emit() - - def set_cloud_credentials(self, relation_name: int, - relation_id: str, - api_version: str, - auth_host: str, - auth_port: str, - auth_protocol: str, - internal_host: str, - internal_port: str, - internal_protocol: str, - username: str, - password: str, - project_name: str, - project_id: str, - user_domain_name: str, - user_domain_id: str, - project_domain_name: str, - project_domain_id: str, - region: str): - logging.debug("Setting cloud_credentials connection information.") - for relation in self.framework.model.relations[relation_name]: - if relation.id == relation_id: - _cloud_credentials_rel = relation - app_data = _cloud_credentials_rel.data[self.charm.app] - app_data["api-version"] = api_version - app_data["auth-host"] = auth_host - app_data["auth-port"] = str(auth_port) - app_data["auth-protocol"] = auth_protocol - app_data["internal-host"] = internal_host - app_data["internal-port"] = str(internal_port) - app_data["internal-protocol"] = internal_protocol - app_data["username"] = username - app_data["password"] = password - app_data["project-name"] = project_name - app_data["project-id"] = project_id - app_data["user-domain-name"] = user_domain_name - app_data["user-domain-id"] = user_domain_id - app_data["project-domain-name"] = project_domain_name - app_data["project-domain-id"] = project_domain_id - app_data["region"] = region