Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup accounts when amqp is removed #14

Merged
merged 1 commit into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions lib/charms/rabbitmq_k8s/v0/rabbitmq.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,25 @@ def _on_amqp_goneaway(self, event):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 1
LIBPATCH = 2

import logging
from typing import (
List,
)

from ops.charm import (
RelationEvent,
)
from ops.framework import (
StoredState,
EventBase,
ObjectEvents,
EventSource,
Object,
ObjectEvents,
)
from ops.model import (
Relation,
)

from ops.model import Relation

from typing import List

logger = logging.getLogger(__name__)

Expand All @@ -106,23 +110,21 @@ class RabbitMQReadyEvent(EventBase):


class RabbitMQGoneAwayEvent(EventBase):
"""RabbitMQ relation has gone-away Event"""
"""RabbitMQ relation has gone-away Event."""

pass


class RabbitMQServerEvents(ObjectEvents):
"""Events class for `on`"""
"""Events class for `on`."""

connected = EventSource(RabbitMQConnectedEvent)
ready = EventSource(RabbitMQReadyEvent)
goneaway = EventSource(RabbitMQGoneAwayEvent)


class RabbitMQRequires(Object):
"""
RabbitMQRequires class
"""
"""RabbitMQRequires class."""

on = RabbitMQServerEvents()

Expand Down Expand Up @@ -150,19 +152,16 @@ def __init__(self, charm, relation_name: str, username: str, vhost: str):
)

def _on_amqp_relation_joined(self, event):
"""RabbitMQ relation joined."""
logging.debug("RabbitMQRabbitMQRequires on_joined")
self.on.connected.emit()
self.request_access(self.username, self.vhost)

def _on_amqp_relation_changed(self, event):
"""RabbitMQ relation changed."""
logging.debug("RabbitMQRabbitMQRequires on_changed/departed")
if self.password:
self.on.ready.emit()

def _on_amqp_relation_broken(self, event):
"""RabbitMQ relation broken."""
logging.debug("RabbitMQRabbitMQRequires on_broken")
self.on.goneaway.emit()

Expand All @@ -178,22 +177,22 @@ def password(self) -> str:

@property
def hostname(self) -> str:
"""Return the hostname from the RabbitMQ relation"""
"""Return the hostname from the RabbitMQ relation."""
return self._amqp_rel.data[self._amqp_rel.app].get("hostname")

@property
def ssl_port(self) -> str:
"""Return the SSL port from the RabbitMQ relation"""
"""Return the SSL port from the RabbitMQ relation."""
return self._amqp_rel.data[self._amqp_rel.app].get("ssl_port")

@property
def ssl_ca(self) -> str:
"""Return the SSL port from the RabbitMQ relation"""
"""Return the SSL port from the RabbitMQ relation."""
return self._amqp_rel.data[self._amqp_rel.app].get("ssl_ca")

@property
def hostnames(self) -> List[str]:
"""Return a list of remote RMQ hosts from the RabbitMQ relation"""
"""Return a list of remote RMQ hosts from the RabbitMQ relation."""
_hosts = []
for unit in self._amqp_rel.units:
_hosts.append(self._amqp_rel.data[unit].get("ingress-address"))
Expand All @@ -207,29 +206,34 @@ def request_access(self, username: str, vhost: str) -> None:
self._amqp_rel.data[self.charm.app]["vhost"] = vhost


class HasRabbitMQClientsEvent(EventBase):
class HasRabbitMQClientsEvent(RelationEvent):
"""Has RabbitMQClients Event."""

pass


class ReadyRabbitMQClientsEvent(EventBase):
class ReadyRabbitMQClientsEvent(RelationEvent):
"""RabbitMQClients Ready Event."""

pass


class GoneAwayRabbitMQClientsEvent(RelationEvent):
"""RabbitMQClients GoneAway Event."""

pass


class RabbitMQClientEvents(ObjectEvents):
"""Events class for `on`"""
"""Events class for `on`."""

has_amqp_clients = EventSource(HasRabbitMQClientsEvent)
ready_amqp_clients = EventSource(ReadyRabbitMQClientsEvent)
gone_away_amqp_clients = EventSource(GoneAwayRabbitMQClientsEvent)


class RabbitMQProvides(Object):
"""
RabbitMQProvides class
"""
"""RabbitMQProvides class."""

on = RabbitMQClientEvents()

Expand All @@ -253,29 +257,37 @@ def __init__(self, charm, relation_name, callback):

def _on_amqp_relation_joined(self, event):
"""Handle RabbitMQ joined."""
logging.debug("RabbitMQRabbitMQProvides on_joined data={}"
.format(event.relation.data[event.relation.app]))
self.on.has_amqp_clients.emit()
logging.debug(
"RabbitMQRabbitMQProvides on_joined data={}".format(
event.relation.data[event.relation.app]
)
)
self.on.has_amqp_clients.emit(event.relation)

def _on_amqp_relation_changed(self, event):
"""Handle RabbitMQ changed."""
logging.debug("RabbitMQRabbitMQProvides on_changed data={}"
.format(event.relation.data[event.relation.app]))
logging.debug(
"RabbitMQRabbitMQProvides on_changed data={}".format(
event.relation.data[event.relation.app]
)
)
# Validate data on the relation
if self.username(event) and self.vhost(event):
self.on.ready_amqp_clients.emit()
self.on.ready_amqp_clients.emit(event.relation)
if self.charm.unit.is_leader():
self.callback(event, self.username(event), self.vhost(event))
else:
logging.warning("Received RabbitMQ changed event without the "
"expected keys ('username', 'vhost') in the "
"application data bag. Incompatible charm in "
"other end of relation?")
logging.warning(
"Received RabbitMQ changed event without the "
"expected keys ('username', 'vhost') in the "
"application data bag. Incompatible charm in "
"other end of relation?"
)

def _on_amqp_relation_broken(self, event):
"""Handle RabbitMQ broken."""
logging.debug("RabbitMQRabbitMQProvides on_departed")
# TODO clear data on the relation
self.on.gone_away_amqp_clients.emit(event.relation)

def username(self, event):
"""Return the RabbitMQ username from the client side of the relation."""
Expand Down
17 changes: 17 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def __init__(self, *args):
self.amqp_provider.on.ready_amqp_clients,
self._on_ready_amqp_clients,
)
self.framework.observe(
self.amqp_provider.on.gone_away_amqp_clients,
self._on_gone_away_amqp_clients,
)

self._stored.set_default(enabled_plugins=[])
self._stored.set_default(rabbitmq_version=None)
Expand Down Expand Up @@ -461,6 +465,19 @@ def _on_ready_amqp_clients(self, event) -> None:
"""Event handler on AMQP clients ready."""
self._on_update_status(event)

def _on_gone_away_amqp_clients(self, event) -> None:
"""Event handler on AMQP clients goneaway."""
if not self.unit.is_leader():
logging.debug("Not a leader unit, nothing to do")
return

api = self._get_admin_api()
username = self.amqp_provider.username(event)
if username and self.does_user_exist(username):
api.delete_user(username)

self.peers.delete_user(username)

@property
def amqp_rel(self) -> Relation:
"""AMQP relation."""
Expand Down
5 changes: 5 additions & 0 deletions src/interface_rabbitmq_peers.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ def store_password(self, username: str, password: str):
logging.debug(f"Storing password for {username}")
self.peers_rel.data[self.peers_rel.app][username] = password

def delete_user(self, username: str):
"""Delete username from application data."""
if username in self.peers_rel.data[self.peers_rel.app]:
del self.peers_rel.data[self.peers_rel.app][username]

def set_nodename(self, nodename: str):
"""Advertise nodename to peers."""
logging.debug(f"Setting nodename {nodename}")
Expand Down
Loading