Rabbitmq K8S

  • OpenStack Charmers
Channel Revision Published Runs on
3.12/stable 34 19 Sep 2024
Ubuntu 22.04
3.12/candidate 34 05 Apr 2024
Ubuntu 22.04
3.12/beta 37 10 Sep 2024
Ubuntu 22.04
3.12/edge 38 24 Oct 2024
Ubuntu 24.04 Ubuntu 22.04
3.12/edge 37 31 Jul 2024
Ubuntu 24.04 Ubuntu 22.04
3.11/beta 10 10 Mar 2023
Ubuntu 22.04
3.11/edge 22 26 May 2023
Ubuntu 22.04
3.9/stable 30 26 Sep 2023
Ubuntu 22.04
3.9/candidate 30 12 Sep 2023
Ubuntu 22.04
3.9/beta 30 10 Jan 2024
Ubuntu 22.04
3.9/edge 30 12 Sep 2023
Ubuntu 22.04
juju deploy rabbitmq-k8s --channel 3.12/stable
Show information

Platform:

charms.rabbitmq_k8s.v0.rabbitmq

"""RabbitMQProvides and Requires module.

This library contains the Requires and Provides classes for handling
the rabbitmq interface.

Import `RabbitMQRequires` in your charm, with the charm object and the
relation name:
    - self
    - "amqp"

Also provide two additional parameters to the charm object:
    - 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.rabbitmq_k8s.v0.rabbitmq import RabbitMQRequires

class RabbitMQClientCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        # RabbitMQ Requires
        self.amqp = RabbitMQRequires(
            self, "amqp",
            username="myusername",
            vhost="vhostname"
        )
        self.framework.observe(
            self.amqp.on.connected, self._on_amqp_connected)
        self.framework.observe(
            self.amqp.on.ready, self._on_amqp_ready)
        self.framework.observe(
            self.amqp.on.goneaway, self._on_amqp_goneaway)

    def _on_amqp_connected(self, event):
        '''React to the RabbitMQ connected event.

        This event happens when n RabbitMQ relation is added to the
        model before credentials etc have been provided.
        '''
        # Do something before the relation is complete
        pass

    def _on_amqp_ready(self, event):
        '''React to the RabbitMQ ready event.

        The RabbitMQ interface will use the provided username and vhost for the
        request to the rabbitmq server.
        '''
        # RabbitMQ Relation is ready. Do something with the completed relation.
        pass

    def _on_amqp_goneaway(self, event):
        '''React to the RabbitMQ goneaway event.

        This event happens when an RabbitMQ relation is removed.
        '''
        # RabbitMQ Relation has goneaway. shutdown services or suchlike
        pass
```
"""

# The unique Charmhub library identifier, never change it
LIBID = "45622352791142fd9cf87232e3bd6f2a"

# 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 ops.framework import (
    StoredState,
    EventBase,
    ObjectEvents,
    EventSource,
    Object,
)

from ops.model import Relation

from typing import List

logger = logging.getLogger(__name__)


class RabbitMQConnectedEvent(EventBase):
    """RabbitMQ connected Event."""

    pass


class RabbitMQReadyEvent(EventBase):
    """RabbitMQ ready for use Event."""

    pass


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

    pass


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

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


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

    on = RabbitMQServerEvents()

    def __init__(self, charm, relation_name: str, username: str, vhost: str):
        super().__init__(charm, relation_name)
        self.charm = charm
        self.relation_name = relation_name
        self.username = username
        self.vhost = vhost
        self.framework.observe(
            self.charm.on[relation_name].relation_joined,
            self._on_amqp_relation_joined,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_changed,
            self._on_amqp_relation_changed,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_departed,
            self._on_amqp_relation_changed,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_broken,
            self._on_amqp_relation_broken,
        )

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

    @property
    def _amqp_rel(self) -> Relation:
        """The RabbitMQ relation."""
        return self.framework.model.get_relation(self.relation_name)

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

    @property
    def hostname(self) -> str:
        """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 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 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"""
        _hosts = []
        for unit in self._amqp_rel.units:
            _hosts.append(self._amqp_rel.data[unit].get("ingress-address"))
        return _hosts

    def request_access(self, username: str, vhost: str) -> None:
        """Request access to the RabbitMQ server."""
        if self.model.unit.is_leader():
            logging.debug("Requesting RabbitMQ user and vhost")
            self._amqp_rel.data[self.charm.app]["username"] = username
            self._amqp_rel.data[self.charm.app]["vhost"] = vhost


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

    pass


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

    pass


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

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


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

    on = RabbitMQClientEvents()

    def __init__(self, charm, relation_name, callback):
        super().__init__(charm, relation_name)
        self.charm = charm
        self.relation_name = relation_name
        self.callback = callback
        self.framework.observe(
            self.charm.on[relation_name].relation_joined,
            self._on_amqp_relation_joined,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_changed,
            self._on_amqp_relation_changed,
        )
        self.framework.observe(
            self.charm.on[relation_name].relation_broken,
            self._on_amqp_relation_broken,
        )

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

    def _on_amqp_relation_changed(self, event):
        """Handle RabbitMQ changed."""
        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()
            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?")

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

    def username(self, event):
        """Return the RabbitMQ username from the client side of the relation."""
        return event.relation.data[event.relation.app].get("username")

    def vhost(self, event):
        """Return the RabbitMQ vhost from the client side of the relation."""
        return event.relation.data[event.relation.app].get("vhost")