Update charm libraries

Change-Id: I95688e141fd071af654e364dc2f1a21161bd8520
This commit is contained in:
Guillaume Boutry 2023-10-10 14:41:03 +02:00
parent 36a77ba3fc
commit 577d6cc5f7
2 changed files with 85 additions and 31 deletions

View File

@ -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
)

View File

@ -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")