Revert "Revert "Add relation handler for ceph-access relation""

This reverts commit 8f18062509d092330e303f94d84a8a4a80d7404f.

Reason for revert: This revert should have been targetted at the stable/2023.1.2 branch not main

Change-Id: Id446a017e3b38bf58c651102f97f72702da1347b
This commit is contained in:
gnuoy 2023-09-19 15:28:23 +00:00
parent b03ebe18fc
commit a7ac636d28
7 changed files with 370 additions and 0 deletions

View File

@ -13,5 +13,6 @@ charmcraft fetch-lib charms.rabbitmq_k8s.v0.rabbitmq
charmcraft fetch-lib charms.ovn_central_k8s.v0.ovsdb
charmcraft fetch-lib charms.traefik_k8s.v1.ingress
charmcraft fetch-lib charms.ceilometer_k8s.v0.ceilometer_service
charmcraft fetch-lib charms.cinder_ceph_k8s.v0.ceph_access
echo "Copying libs to to unit_test dir"
rsync --recursive --delete lib/ unit_tests/lib/

View File

@ -178,6 +178,14 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
"identity-credentials" in self.mandatory_relations,
)
handlers.append(self.ccreds)
if self.can_add_handler("ceph-access", handlers):
self.ceph_access = sunbeam_rhandlers.CephAccessRequiresHandler(
self,
"ceph-access",
self.configure_charm,
"ceph-access" in self.mandatory_relations,
)
handlers.append(self.ceph_access)
return handlers
def get_sans(self) -> List[str]:

View File

@ -1200,3 +1200,70 @@ class CeilometerServiceRequiresHandler(RelationHandler):
return bool(self.interface.telemetry_secret)
except (AttributeError, KeyError):
return False
class CephAccessRequiresHandler(RelationHandler):
"""Handles the ceph access relation on the requires side."""
def __init__(
self,
charm: ops.charm.CharmBase,
relation_name: str,
callback_f: Callable,
mandatory: bool = False,
) -> None:
"""Create a new ceph-access handler.
Create a new CephAccessRequiresHandler that handles initial
events from the relation and invokes the provided callbacks based on
the event raised.
:param charm: the Charm class the handler is for
:type charm: ops.charm.CharmBase
:param relation_name: the relation the handler is bound to
:type relation_name: str
:param callback_f: the function to call when the nodes are connected
:type callback_f: Callable
"""
super().__init__(charm, relation_name, callback_f, mandatory)
def setup_event_handler(self) -> ops.charm.Object:
"""Configure event handlers for ceph-access relation."""
import charms.cinder_ceph_k8s.v0.ceph_access as ceph_access
logger.debug("Setting up the ceph-access event handler")
ceph_access = ceph_access.CephAccessRequires(
self.charm,
self.relation_name,
)
self.framework.observe(ceph_access.on.ready, self._ceph_access_ready)
self.framework.observe(
ceph_access.on.goneaway, self._ceph_access_goneaway
)
return ceph_access
def _ceph_access_ready(self, event: ops.framework.EventBase) -> None:
"""React to credential ready event."""
self.callback_f(event)
def _ceph_access_goneaway(self, event: ops.framework.EventBase) -> None:
"""React to credential goneaway event."""
self.callback_f(event)
if self.mandatory:
self.status.set(BlockedStatus("integration missing"))
@property
def ready(self) -> bool:
"""Whether handler is ready for use."""
try:
return bool(self.interface.ready)
except (AttributeError, KeyError):
return False
def context(self) -> dict:
"""Context containing Ceph access data."""
ctxt = super().context()
data = self.interface.ceph_access_data
ctxt["key"] = data.get("key")
ctxt["uuid"] = data.get("uuid")
return ctxt

View File

@ -344,6 +344,26 @@ def add_amqp_relation_credentials(harness: Harness, rel_id: str) -> None:
)
def add_base_ceph_access_relation(harness: Harness) -> str:
"""Add ceph-access relation."""
rel_id = harness.add_relation(
"ceph-access", "cinder-ceph", app_data={"a": "b"}
)
return rel_id
def add_ceph_access_relation_response(harness: Harness, rel_id: str) -> None:
"""Add secret data to cinder-access relation."""
credentials_content = {"uuid": "svcuser1", "key": "svcpass1"}
credentials_id = harness.add_model_secret(
"cinder-ceph", credentials_content
)
harness.grant_secret(credentials_id, harness.charm.app.name)
harness.update_relation_data(
rel_id, "cinder-ceph", {"access-credentials": credentials_id}
)
def add_base_identity_service_relation(harness: Harness) -> str:
"""Add identity-service relation."""
rel_id = harness.add_relation("identity-service", "keystone")

View File

@ -0,0 +1,265 @@
"""CephAccess Provides and Requires module.
This library contains the Requires and Provides classes for handling
the ceph-access interface.
Import `CephAccessRequires` in your charm, with the charm object and the
relation name:
- self
- "ceph_access"
Three events are also available to respond to:
- connected
- ready
- goneaway
A basic example showing the usage of this relation follows:
```
from charms.cinder_ceph_k8s.v0.ceph_access import CephAccessRequires
class CephAccessClientCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
# CephAccess Requires
self.ceph_access = CephAccessRequires(
self,
relation_name="ceph_access",
)
self.framework.observe(
self.ceph_access.on.connected, self._on_ceph_access_connected)
self.framework.observe(
self.ceph_access.on.ready, self._on_ceph_access_ready)
self.framework.observe(
self.ceph_access.on.goneaway, self._on_ceph_access_goneaway)
def _on_ceph_access_connected(self, event):
'''React to the CephAccess connected event.
This event happens when n CephAccess relation is added to the
model before credentials etc have been provided.
'''
# Do something before the relation is complete
pass
def _on_ceph_access_ready(self, event):
'''React to the CephAccess ready event.
This event happens when an CephAccess relation is removed.
'''
# IdentityService Relation has goneaway. shutdown services or suchlike
pass
```
"""
# The unique Charmhub library identifier, never change it
LIBID = "7fa8d4f8407c4f31ab1deb51c0c046f1"
# 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 logging
from typing import Optional
from ops import (
RelationEvent
)
from ops.model import (
Relation,
Secret,
SecretNotFoundError,
)
from ops.framework import (
EventBase,
ObjectEvents,
EventSource,
Object,
)
logger = logging.getLogger(__name__)
class CephAccessConnectedEvent(EventBase):
"""CephAccess connected Event."""
pass
class CephAccessReadyEvent(EventBase):
"""CephAccess ready for use Event."""
pass
class CephAccessGoneAwayEvent(EventBase):
"""CephAccess relation has gone-away Event"""
pass
class CephAccessServerEvents(ObjectEvents):
"""Events class for `on`"""
connected = EventSource(CephAccessConnectedEvent)
ready = EventSource(CephAccessReadyEvent)
goneaway = EventSource(CephAccessGoneAwayEvent)
class CephAccessRequires(Object):
"""
CephAccessRequires class
"""
on = CephAccessServerEvents()
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_ceph_access_relation_joined,
)
self.framework.observe(
self.charm.on[relation_name].relation_changed,
self._on_ceph_access_relation_changed,
)
self.framework.observe(
self.charm.on[relation_name].relation_departed,
self._on_ceph_access_relation_changed,
)
self.framework.observe(
self.charm.on[relation_name].relation_broken,
self._on_ceph_access_relation_broken,
)
@property
def _ceph_access_rel(self) -> Relation:
"""The CephAccess relation."""
return self.framework.model.get_relation(self.relation_name)
def get_remote_app_data(self, key: str) -> Optional[str]:
"""Return the value for the given key from remote app data."""
data = self._ceph_access_rel.data[self._ceph_access_rel.app]
return data.get(key)
def _on_ceph_access_relation_joined(self, event):
"""CephAccess relation joined."""
logging.debug("CephAccess on_joined")
self.on.connected.emit()
def _on_ceph_access_relation_changed(self, event):
"""CephAccess relation changed."""
logging.debug("CephAccess on_changed")
try:
if self.ready:
self.on.ready.emit()
except (AttributeError, KeyError):
pass
def _on_ceph_access_relation_broken(self, event):
"""CephAccess relation broken."""
logging.debug("CephAccess on_broken")
self.on.goneaway.emit()
def _retrieve_secret(self) -> Optional[Secret]:
try:
credentials_id = self.get_remote_app_data('access-credentials')
if not credentials_id:
return None
credentials = self.charm.model.get_secret(id=credentials_id)
return credentials
except SecretNotFoundError:
logger.warning(f"Secret {credentials_id} not found")
return None
@property
def ceph_access_data(self) -> dict:
"""Return the service_password."""
secret = self._retrieve_secret()
if not secret:
return {}
return secret.get_content()
@property
def ready(self) -> bool:
"""Return the service_password."""
return all(k in self.ceph_access_data for k in ["uuid", "key"])
class HasCephAccessClientsEvent(EventBase):
"""Has CephAccessClients Event."""
pass
class ReadyCephAccessClientsEvent(RelationEvent):
"""Has ReadyCephAccessClients Event."""
pass
class CephAccessClientEvents(ObjectEvents):
"""Events class for `on`"""
has_ceph_access_clients = EventSource(HasCephAccessClientsEvent)
ready_ceph_access_clients = EventSource(ReadyCephAccessClientsEvent)
class CephAccessProvides(Object):
"""
CephAccessProvides class
"""
on = CephAccessClientEvents()
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_ceph_access_relation_joined,
)
self.framework.observe(
self.charm.on[relation_name].relation_changed,
self._on_ceph_access_relation_changed,
)
self.framework.observe(
self.charm.on[relation_name].relation_broken,
self._on_ceph_access_relation_broken,
)
def _on_ceph_access_relation_joined(self, event):
"""Handle CephAccess joined."""
logging.debug("CephAccess on_joined")
self.on.has_ceph_access_clients.emit()
def _on_ceph_access_relation_changed(self, event):
"""Handle CephAccess joined."""
logging.debug("CephAccess on_changed")
self.on.ready_ceph_access_clients.emit(
event.relation,
app=event.app,
unit=event.unit)
def _on_ceph_access_relation_broken(self, event):
"""Handle CephAccess broken."""
logging.debug("CephAccessProvides on_broken")
def set_ceph_access_credentials(self, relation_name: int,
relation_id: str,
access_credentials: str):
logging.debug("Setting ceph_access connection information.")
_ceph_access_rel = None
for relation in self.framework.model.relations[relation_name]:
if relation.id == relation_id:
_ceph_access_rel = relation
if not _ceph_access_rel:
# Relation has disappeared so skip send of data
return
app_data = _ceph_access_rel.data[self.charm.app]
logging.debug(access_credentials)
app_data["access-credentials"] = access_credentials

View File

@ -116,6 +116,8 @@ requires:
identity-credentials:
interface: keystone-credentials
limit: 1
ceph-access:
interface: cinder-ceph-key
peers:
peers:

View File

@ -365,6 +365,13 @@ class TestOSBaseOperatorAPICharm(_TestOSBaseOperatorAPICharm):
test_utils.add_ingress_relation_data(
self.harness, ingress_rel_id, "public"
)
ceph_access_rel_id = test_utils.add_base_ceph_access_relation(
self.harness
)
test_utils.add_ceph_access_relation_response(
self.harness, ceph_access_rel_id
)
self.assertTrue(self.harness.charm.relation_handlers_ready())
# Add an optional relation and test if relation_handlers_ready