df70e376ff
* Add sunbeam project template to run pep8, py3 tests * Add zuul.d/zuul.yaml to run pep8, py3, cover tests * Update charmcraft and requirements for each charm * Add global tox.ini to invoke fmt, pep8, py3, cover, build * Add gitreview file * Fix py3 test failures in ciner-ceph-k8s, glance-k8s, openstack-exporter * Add jobs for charm builds using files option so that job is invoked if files within the component are modified. Add charm builds to both check and gate pipeline. * Make function tests as part of global. Split the function tests into core, ceph, caas, misc mainly to accomodate function tests to run on 8GB. Add function tests as part of check pipeline. * Add zuul job to publish charms in promote pipeline Add charmhub token as secret that can be used to publish charms. Note: Charmhub token is generated with ttl of 90 days. * Run tox formatting * Make .gitignore, .jujuignore, .stestr.conf global and remove the files from all charms. * Make libs and templates global. Split libs to internal and external so that internal libs can adhere to sunbeam formatting styles. * Add script to copy common files necessary libs, config templates, stestr conf, jujuignore during py3 tests and charm builds. * Tests for keystone-ldap-k8s are commented due to intermittent bug LP#2045206 Change-Id: I804ca64182c109d16bd820ac00f129aa6dcf4496
287 lines
8.6 KiB
Python
287 lines
8.6 KiB
Python
"""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")
|