From e1b0857cba3780968781de1a4feef0eb7983d9c0 Mon Sep 17 00:00:00 2001 From: Stamatis Katsaounis Date: Fri, 30 Aug 2024 18:07:24 +0300 Subject: [PATCH] fix: update maas_url when region goes down --- maas-region/charmcraft.yaml | 7 +++++ maas-region/src/charm.py | 56 ++++++++++++++++++++++++++++++------- maas-region/src/helper.py | 17 ++++++++++- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/maas-region/charmcraft.yaml b/maas-region/charmcraft.yaml index 81bfa19..543d121 100644 --- a/maas-region/charmcraft.yaml +++ b/maas-region/charmcraft.yaml @@ -133,3 +133,10 @@ config: default: "" description: Whether to enable TLS termination at HA Proxy ('termination'), or no TLS ('') type: string + maas_url: + default: "" + description: | + The maas_url to set. If not provided, the maas_url is formulated by the IP of maas-region + leader and MAAS API port. If application is related to HAProxy, the proxy port is used + instead. + type: string diff --git a/maas-region/src/charm.py b/maas-region/src/charm.py index 5dea3f3..8a19fb8 100755 --- a/maas-region/src/charm.py +++ b/maas-region/src/charm.py @@ -69,6 +69,7 @@ def __init__(self, *args): self.framework.observe(self.on.remove, self._on_remove) self.framework.observe(self.on.start, self._on_start) self.framework.observe(self.on.collect_unit_status, self._on_collect_status) + self.framework.observe(self.on.leader_elected, self._on_leader_elected) # MAAS Region self.maas_region = maas.MaasRegionProvider(self) @@ -175,13 +176,16 @@ def maas_api_url(self) -> str: Returns: str: The API URL """ - if relation := self.model.get_relation(MAAS_API_RELATION): - unit = next(iter(relation.units), None) - if unit and (addr := relation.data[unit].get("public-address")): - return f"http://{addr}:{MAAS_PROXY_PORT}/MAAS" - if bind := self.bind_address: - return f"http://{bind}:{MAAS_HTTP_PORT}/MAAS" - return "" + if maas_url := self.config["maas_url"]: + return str(maas_url) + if self.model.unit.is_leader() and (bind := self.bind_address): + if self.model.get_relation(MAAS_API_RELATION): + port = MAAS_PROXY_PORT + else: + port = MAAS_HTTP_PORT + return f"http://{bind}:{port}/MAAS" + else: + return self.get_peer_data(self.app, "maas_url") @property def maas_id(self) -> Union[str, None]: @@ -230,9 +234,25 @@ def _setup_network(self) -> bool: return True def _initialize_maas(self) -> bool: + if ( + maas_details := MaasHelper.get_maas_details() + ) and self.get_operational_mode() == MaasHelper.get_maas_mode(): + u, p, h, d = ( + maas_details["database_user"], + maas_details["database_pass"], + maas_details["database_host"], + maas_details["database_name"], + ) + if ( + self.get_peer_data(self.app, "maas_url") == maas_details["maas_url"] + and f"postgres://{u}:{p}@{h}/{d}" == self.connection_string + ): + return True try: MaasHelper.setup_region( - self.maas_api_url, self.connection_string, self.get_operational_mode() + self.get_peer_data(self.app, "maas_url"), + self.connection_string, + self.get_operational_mode(), ) return True except subprocess.CalledProcessError: @@ -313,9 +333,10 @@ def _on_install(self, _event: ops.InstallEvent) -> None: event (ops.InstallEvent): Event from ops framework """ self.unit.status = ops.MaintenanceStatus("installing...") - channel = str(self.config.get("channel", MAAS_SNAP_CHANNEL)) + if self.unit.is_leader(): + self.set_peer_data(self.app, "maas_url", "") try: - MaasHelper.install(channel) + MaasHelper.install(MAAS_SNAP_CHANNEL) except Exception as ex: logger.error(str(ex)) @@ -343,6 +364,12 @@ def _on_collect_status(self, e: ops.CollectStatusEvent) -> None: else: self.unit.status = ops.ActiveStatus() + def _on_leader_elected(self, e: ops.LeaderElectedEvent) -> None: + maas_url = self.maas_api_url + if self.get_peer_data(self.app, "maas_url") != maas_url: + self.set_peer_data(self.app, "maas_url", maas_url) + self._initialize_maas() + def _on_maasdb_created(self, event: db.DatabaseCreatedEvent) -> None: """Database is ready. @@ -377,6 +404,7 @@ def _on_maas_peer_changed(self, event: ops.RelationEvent) -> None: self.set_peer_data(self.unit, "system-name", socket.getfqdn()) if self.unit.is_leader(): self._publish_tokens() + self._initialize_maas() def _on_maas_cluster_changed(self, event: ops.RelationEvent) -> None: logger.info(event) @@ -439,6 +467,14 @@ def _on_config_changed(self, event: ops.ConfigChangedEvent): msg = f"Invalid tls_mode configuration: '{tls_mode}'. Valid options are: {self._TLS_MODES}" self.unit.status = ops.BlockedStatus(msg) raise ValueError(msg) + + if maas_url := self.config["maas_url"]: + if self.unit.is_leader(): + maas_url = self.maas_api_url + if self.get_peer_data(self.app, "maas_url") != maas_url: + self.set_peer_data(self.app, "maas_url", maas_url) + self._initialize_maas() + self._update_ha_proxy() diff --git a/maas-region/src/helper.py b/maas-region/src/helper.py index 608f8b5..9f58837 100644 --- a/maas-region/src/helper.py +++ b/maas-region/src/helper.py @@ -5,14 +5,16 @@ import subprocess from pathlib import Path -from typing import Union +from typing import Dict, Union +import yaml from charms.operator_libs_linux.v2.snap import SnapCache, SnapState MAAS_SNAP_NAME = "maas" MAAS_MODE = Path("/var/snap/maas/common/snap_mode") MAAS_SECRET = Path("/var/snap/maas/common/maas/secret") MAAS_ID = Path("/var/snap/maas/common/maas/maas_id") +MAAS_CONF = Path("/var/snap/maas/current/regiond.conf") MAAS_SERVICE = "pebble" @@ -84,6 +86,19 @@ def get_maas_mode() -> Union[str, None]: except OSError: return None + @staticmethod + def get_maas_details() -> Dict[str, str]: + """Get MAAS operation mode. + + Returns: + Dict[str, str]: MAAS details + """ + try: + with MAAS_CONF.open() as file: + return yaml.safe_load(file) + except (OSError, yaml.YAMLError): + return {} + @staticmethod def is_running() -> bool: """Check if MAAS is running.