From 577d6cc5f7685f943dc5494f87b0346e8ec7faae Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Tue, 10 Oct 2023 14:41:03 +0200 Subject: [PATCH] Update charm libraries Change-Id: I95688e141fd071af654e364dc2f1a21161bd8520 --- .../v0/database_requires.py | 65 ++++++++++++++----- .../keystone_k8s/v0/identity_resource.py | 51 ++++++++++----- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/charms/barbican-k8s/lib/charms/data_platform_libs/v0/database_requires.py b/charms/barbican-k8s/lib/charms/data_platform_libs/v0/database_requires.py index 6f425e71..11ffd6ca 100644 --- a/charms/barbican-k8s/lib/charms/data_platform_libs/v0/database_requires.py +++ b/charms/barbican-k8s/lib/charms/data_platform_libs/v0/database_requires.py @@ -1,4 +1,4 @@ -# Copyright 2022 Canonical Ltd. +# Copyright 2023 Canonical Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""[DEPRECATED] Relation 'requires' side abstraction for database relation. +r"""[DEPRECATED] Relation 'requires' side abstraction for database relation. This library is a uniform interface to a selection of common database metadata, with added custom events that add convenience to database management, @@ -160,7 +160,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version. -LIBPATCH = 5 +LIBPATCH = 6 logger = logging.getLogger(__name__) @@ -171,16 +171,25 @@ class DatabaseEvent(RelationEvent): @property def endpoints(self) -> Optional[str]: """Returns a comma separated list of read/write endpoints.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("endpoints") @property def password(self) -> Optional[str]: """Returns the password for the created user.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("password") @property def read_only_endpoints(self) -> Optional[str]: """Returns a comma separated list of read only endpoints.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("read-only-endpoints") @property @@ -189,16 +198,25 @@ class DatabaseEvent(RelationEvent): MongoDB only. """ + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("replset") @property def tls(self) -> Optional[str]: """Returns whether TLS is configured.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("tls") @property def tls_ca(self) -> Optional[str]: """Returns TLS CA.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("tls-ca") @property @@ -207,11 +225,17 @@ class DatabaseEvent(RelationEvent): MongoDB, Redis, OpenSearch and Kafka only. """ + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("uris") @property def username(self) -> Optional[str]: """Returns the created username.""" + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("username") @property @@ -220,6 +244,9 @@ class DatabaseEvent(RelationEvent): Version as informed by the database daemon. """ + if not self.relation.app: + return None + return self.relation.data[self.relation.app].get("version") @@ -259,15 +286,15 @@ A tuple for storing the diff between two data mappings. class DatabaseRequires(Object): """Requires-side of the database relation.""" - on = DatabaseEvents() + on = DatabaseEvents() # pyright: ignore [reportGeneralTypeIssues] def __init__( self, charm, relation_name: str, database_name: str, - extra_user_roles: str = None, - relations_aliases: List[str] = None, + extra_user_roles: Optional[str] = None, + relations_aliases: Optional[List[str]] = None, ): """Manager of database client relations.""" super().__init__(charm, relation_name) @@ -352,9 +379,11 @@ class DatabaseRequires(Object): # Retrieve the old data from the data key in the local unit relation databag. old_data = json.loads(event.relation.data[self.local_unit].get("data", "{}")) # Retrieve the new data from the event relation databag. - new_data = { - key: value for key, value in event.relation.data[event.app].items() if key != "data" - } + new_data = ( + {key: value for key, value in event.relation.data[event.app].items() if key != "data"} + if event.app + else {} + ) # These are the keys that were added to the databag and triggered this event. added = new_data.keys() - old_data.keys() @@ -413,9 +442,11 @@ class DatabaseRequires(Object): """ data = {} for relation in self.relations: - data[relation.id] = { - key: value for key, value in relation.data[relation.app].items() if key != "data" - } + data[relation.id] = ( + {key: value for key, value in relation.data[relation.app].items() if key != "data"} + if relation.app + else {} + ) return data def _update_relation_data(self, relation_id: int, data: dict) -> None: @@ -461,7 +492,9 @@ class DatabaseRequires(Object): if "username" in diff.added and "password" in diff.added: # Emit the default event (the one without an alias). logger.info("database created at %s", datetime.now()) - self.on.database_created.emit(event.relation, app=event.app, unit=event.unit) + getattr(self.on, "database_created").emit( + event.relation, app=event.app, unit=event.unit + ) # Emit the aliased event (if any). self._emit_aliased_event(event, "database_created") @@ -475,7 +508,9 @@ class DatabaseRequires(Object): if "endpoints" in diff.added or "endpoints" in diff.changed: # Emit the default event (the one without an alias). logger.info("endpoints changed on %s", datetime.now()) - self.on.endpoints_changed.emit(event.relation, app=event.app, unit=event.unit) + getattr(self.on, "endpoints_changed").emit( + event.relation, app=event.app, unit=event.unit + ) # Emit the aliased event (if any). self._emit_aliased_event(event, "endpoints_changed") @@ -489,7 +524,7 @@ class DatabaseRequires(Object): if "read-only-endpoints" in diff.added or "read-only-endpoints" in diff.changed: # Emit the default event (the one without an alias). logger.info("read-only-endpoints changed on %s", datetime.now()) - self.on.read_only_endpoints_changed.emit( + getattr(self.on, "read_only_endpoints_changed").emit( event.relation, app=event.app, unit=event.unit ) diff --git a/charms/barbican-k8s/lib/charms/keystone_k8s/v0/identity_resource.py b/charms/barbican-k8s/lib/charms/keystone_k8s/v0/identity_resource.py index 6ef944ef..154fab83 100644 --- a/charms/barbican-k8s/lib/charms/keystone_k8s/v0/identity_resource.py +++ b/charms/barbican-k8s/lib/charms/keystone_k8s/v0/identity_resource.py @@ -1,6 +1,5 @@ """IdentityResourceProvides and Requires module. - This library contains the Requires and Provides classes for handling the identity_ops interface. @@ -85,9 +84,16 @@ The secret content should hold the sensitive data with same name as param name. import json import logging +from typing import ( + Optional, +) from ops.charm import ( + CharmBase, + RelationBrokenEvent, + RelationChangedEvent, RelationEvent, + RelationJoinedEvent, ) from ops.framework import ( EventBase, @@ -111,7 +117,7 @@ LIBAPI = 0 # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 2 +LIBPATCH = 3 REQUEST_NOT_SENT = 1 @@ -151,7 +157,7 @@ class IdentityResourceRequires(Object): on = IdentityResourceResponseEvents() _stored = StoredState() - def __init__(self, charm, relation_name): + def __init__(self, charm: CharmBase, relation_name: str): super().__init__(charm, relation_name) self.charm = charm self.relation_name = relation_name @@ -169,24 +175,30 @@ class IdentityResourceRequires(Object): self._on_identity_resource_relation_broken, ) - def _on_identity_resource_relation_joined(self, event): + def _on_identity_resource_relation_joined( + self, event: RelationJoinedEvent + ): """Handle IdentityResource joined.""" self._stored.provider_ready = True self.on.provider_ready.emit(event.relation) - def _on_identity_resource_relation_changed(self, event): + def _on_identity_resource_relation_changed( + self, event: RelationChangedEvent + ): """Handle IdentityResource changed.""" id_ = self.response.get("id") self.save_request_in_store(id_, None, None, REQUEST_PROCESSED) self.on.response_available.emit(event.relation) - def _on_identity_resource_relation_broken(self, event): + def _on_identity_resource_relation_broken( + self, event: RelationBrokenEvent + ): """Handle IdentityResource broken.""" self._stored.provider_ready = False self.on.provider_goneaway.emit(event.relation) @property - def _identity_resource_rel(self) -> Relation: + def _identity_resource_rel(self) -> Optional[Relation]: """The IdentityResource relation.""" return self.framework.model.get_relation(self.relation_name) @@ -204,7 +216,9 @@ class IdentityResourceRequires(Object): return {} - def save_request_in_store(self, id: str, tag: str, ops: list, state: int): + def save_request_in_store( + self, id: str, tag: str, ops: list, state: int + ) -> None: """Save request in the store.""" if id is None: return @@ -242,12 +256,15 @@ class IdentityResourceRequires(Object): return False - def get_remote_app_data(self, key: str) -> str: + def get_remote_app_data(self, key: str) -> Optional[str]: """Return the value for the given key from remote app data.""" - data = self._identity_resource_rel.data[ - self._identity_resource_rel.app - ] - return data.get(key) + if self._identity_resource_rel: + data = self._identity_resource_rel.data[ + self._identity_resource_rel.app + ] + return data.get(key) + + return None def ready(self) -> bool: """Interface is ready or not. @@ -337,7 +354,7 @@ class IdentityResourceProvides(Object): on = IdentityResourceProviderEvents() - def __init__(self, charm, relation_name): + def __init__(self, charm: CharmBase, relation_name: str): super().__init__(charm, relation_name) self.charm = charm self.relation_name = relation_name @@ -346,7 +363,9 @@ class IdentityResourceProvides(Object): self._on_identity_resource_relation_changed, ) - def _on_identity_resource_relation_changed(self, event): + def _on_identity_resource_relation_changed( + self, event: RelationChangedEvent + ): """Handle IdentityResource changed.""" request = event.relation.data[event.relation.app].get("request", {}) self.on.process_op.emit( @@ -355,7 +374,7 @@ class IdentityResourceProvides(Object): def set_ops_response( self, relation_id: str, relation_name: str, ops_response: dict - ): + ) -> None: """Set response to ops request.""" if not self.model.unit.is_leader(): logger.debug("Not a leader unit, not sending response")