Skip to content

Commit

Permalink
Merge pull request #56 from manadart/add-db-cluster
Browse files Browse the repository at this point in the history
Add dbcluster peer relation
  • Loading branch information
manadart authored Nov 29, 2023
2 parents 5db6a8d + 980e584 commit cbe61b7
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 27 deletions.
3 changes: 3 additions & 0 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ provides:
interface: http
metrics-endpoint:
interface: prometheus_scrape
peers:
dbcluster:
interface: dbcluster
46 changes: 38 additions & 8 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
import logging
import secrets
import yaml

from charms.prometheus_k8s.v0.prometheus_scrape import MetricsEndpointProvider
from ops.charm import CharmBase
from ops.charm import CharmBase, CollectStatusEvent
from ops.framework import StoredState
from ops.charm import RelationJoinedEvent, RelationDepartedEvent
from ops.main import main
Expand All @@ -21,25 +22,39 @@ class JujuControllerCharm(CharmBase):

def __init__(self, *args):
super().__init__(*args)

self.framework.observe(self.on.collect_unit_status, self._on_collect_status)
self.framework.observe(self.on.config_changed, self._on_config_changed)
self.framework.observe(self.on.start, self._on_start)
self.framework.observe(
self.on.dashboard_relation_joined, self._on_dashboard_relation_joined)
self.framework.observe(
self.on.website_relation_joined, self._on_website_relation_joined)

self._stored.set_default(db_bind_address='')
self._stored.set_default(last_bind_addresses=[])
self.framework.observe(
self.on.dbcluster_relation_changed, self._on_dbcluster_relation_changed)

self.control_socket = controlsocket.Client(
socket_path="/var/lib/juju/control.socket")
self.framework.observe(
self.on.metrics_endpoint_relation_created, self._on_metrics_endpoint_relation_created)
self.framework.observe(
self.on.metrics_endpoint_relation_broken, self._on_metrics_endpoint_relation_broken)

def _on_start(self, _):
self.unit.status = ActiveStatus()
def _on_collect_status(self, event: CollectStatusEvent):
if len(self._stored.last_bind_addresses) > 1:
event.add_status(BlockedStatus(
'multiple possible DB bind addresses; set a suitable dbcluster network binding'))

if self.api_port() is None:
event.add_status(BlockedStatus(
'charm does not appear to be running on a controller node'))

event.add_status(ActiveStatus())

def _on_config_changed(self, _):
controller_url = self.config["controller-url"]
controller_url = self.config['controller-url']
logger.info("got a new controller-url: %r", controller_url)

def _on_dashboard_relation_joined(self, event):
Expand All @@ -55,11 +70,10 @@ def _on_dashboard_relation_joined(self, event):

def _on_website_relation_joined(self, event):
"""Connect a website relation."""
logger.info("got a new website relation: %r", event)
logger.info('got a new website relation: %r', event)
port = self.api_port()
if port is None:
logger.error("machine does not appear to be a controller")
self.unit.status = BlockedStatus('machine does not appear to be a controller')
logger.error('charm does not appear to be running on a controller node')
return

address = None
Expand Down Expand Up @@ -105,6 +119,22 @@ def _on_metrics_endpoint_relation_broken(self, event: RelationDepartedEvent):
username = metrics_username(event.relation)
self.control_socket.remove_metrics_user(username)

def _on_dbcluster_relation_changed(self, event):
ips = self.model.get_binding(event.relation).network.ingress_addresses
self._stored.last_bind_addresses = ips

if len(ips) > 1:
logger.error(
'multiple possible DB bind addresses; set a suitable bcluster network binding')
return

ip = str(ips[0])
if self._stored.db_bind_address == ip:
return

self._stored.db_bind_address = ip
event.relation.data[self.unit].update({'db-bind-address': ip})

def _agent_conf(self, key: str):
"""Read a value (by key) from the agent.conf file on disk."""
unit_name = self.unit.name.replace('/', '-')
Expand Down
72 changes: 53 additions & 19 deletions tests/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import unittest
from charm import JujuControllerCharm
from ops.model import BlockedStatus, ActiveStatus
from ops.testing import Harness
from unittest.mock import mock_open, patch

Expand All @@ -14,10 +15,14 @@


class TestCharm(unittest.TestCase):
def test_relation_joined(self):
harness = Harness(JujuControllerCharm)
self.addCleanup(harness.cleanup)
harness.begin()
def setUp(self):
self.harness = Harness(JujuControllerCharm)
self.addCleanup(self.harness.cleanup)
self.harness.begin()

def test_dashboard_relation_joined(self):
harness = self.harness

harness.set_leader(True)
harness.update_config({"controller-url": "wss://controller/api"})
harness.update_config({"identity-provider-url": ""})
Expand All @@ -36,12 +41,10 @@ def test_relation_joined(self):
})
@patch("ops.model.Model.get_binding")
@patch("builtins.open", new_callable=mock_open, read_data=agent_conf)
def test_website_relation_joined(self, _, ingress_address):
ingress_address.return_value = MockBinding("192.168.1.17")
def test_website_relation_joined(self, _, binding):
harness = self.harness
binding.return_value = mockBinding(["192.168.1.17"])

harness = Harness(JujuControllerCharm)
self.addCleanup(harness.cleanup)
harness.begin()
harness.set_leader()
relation_id = harness.add_relation('website', 'haproxy')
harness.add_relation_unit(relation_id, 'haproxy/0')
Expand All @@ -58,10 +61,7 @@ def test_website_relation_joined(self, _, ingress_address):
@patch("controlsocket.Client.remove_metrics_user")
def test_metrics_endpoint_relation(self, mock_remove_user, mock_add_user,
mock_metrics_provider, _):
harness = Harness(JujuControllerCharm)
self.addCleanup(harness.cleanup)
harness.begin()

harness = self.harness
harness.add_network(address="192.168.1.17", endpoint="metrics-endpoint")

relation_id = harness.add_relation('metrics-endpoint', 'prometheus-k8s')
Expand All @@ -88,12 +88,46 @@ def test_metrics_endpoint_relation(self, mock_remove_user, mock_add_user,
harness.remove_relation(relation_id)
mock_remove_user.assert_called_once_with(f'juju-metrics-r{relation_id}')

@patch("builtins.open", new_callable=mock_open, read_data=agent_conf)
@patch("ops.model.Model.get_binding")
def test_dbcluster_relation_changed_single_addr(self, binding, _):
harness = self.harness
binding.return_value = mockBinding(["192.168.1.17"])

relation_id = harness.add_relation('dbcluster', 'controller')
harness.add_relation_unit(relation_id, 'juju-controller/1')

harness.charm._on_dbcluster_relation_changed(
harness.charm.model.get_relation('dbcluster').data[harness.charm.unit])

data = harness.get_relation_data(relation_id, 'juju-controller/0')
self.assertEqual(data["db-bind-address"], "192.168.1.17")

harness.evaluate_status()
self.assertIsInstance(harness.charm.unit.status, ActiveStatus)

@patch("builtins.open", new_callable=mock_open, read_data=agent_conf)
@patch("ops.model.Model.get_binding")
def test_dbcluster_relation_changed_multi_addr_error(self, binding, _):
harness = self.harness
binding.return_value = mockBinding(["192.168.1.17", "192.168.1.18"])

relation_id = harness.add_relation('dbcluster', 'controller')
harness.add_relation_unit(relation_id, 'juju-controller/1')

harness.charm._on_dbcluster_relation_changed(
harness.charm.model.get_relation('dbcluster').data[harness.charm.unit])

harness.evaluate_status()
self.assertIsInstance(harness.charm.unit.status, BlockedStatus)


class MockBinding:
def __init__(self, address):
self.network = MockNetwork(address)
class mockNetwork:
def __init__(self, addresses):
self.ingress_addresses = addresses
self.ingress_address = addresses[0]


class MockNetwork:
def __init__(self, address):
self.ingress_address = address
class mockBinding:
def __init__(self, addresses):
self.network = mockNetwork(addresses)

0 comments on commit cbe61b7

Please sign in to comment.