diff --git a/charmcraft.yaml b/charmcraft.yaml index 15290bd..f432273 100644 --- a/charmcraft.yaml +++ b/charmcraft.yaml @@ -33,6 +33,10 @@ requires: interface: nginx-route limit: 1 + airbyte-server: + interface: airbyte-server + limit: 1 + # (Optional) Configuration options for the charm # This config section defines charm config options, and populates the Configure # tab on Charmhub. diff --git a/src/charm.py b/src/charm.py index 51a6fd2..8c10c48 100755 --- a/src/charm.py +++ b/src/charm.py @@ -10,8 +10,9 @@ from log import log_event_handler from ops import main, pebble from ops.charm import CharmBase -from ops.model import ActiveStatus, MaintenanceStatus +from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus from ops.pebble import CheckStatus +from relations.airbyte_server import AirbyteServer from state import State logger = logging.getLogger(__name__) @@ -37,6 +38,10 @@ def __init__(self, *args): self.framework.observe(self.on[self.name].pebble_ready, self._on_pebble_ready) self.framework.observe(self.on.update_status, self._on_update_status) self.framework.observe(self.on.restart_action, self._on_restart) + self.framework.observe(self.on.peer_relation_changed, self._on_peer_relation_changed) + + # Handle Airbyte server relation + self.airbyte_server = AirbyteServer(self) # Handle Ingress. self._require_nginx_route() @@ -57,6 +62,15 @@ def _on_pebble_ready(self, event): """Handle pebble-ready event.""" self._update(event) + @log_event_handler(logger) + def _on_peer_relation_changed(self, event): + """Handle peer relation changed event. + + Args: + event: The event triggered when the relation changed. + """ + self._update(event) + @log_event_handler(logger) def _on_update_status(self, event): """Handle `update-status` events. @@ -108,6 +122,21 @@ def _on_restart(self, event): event.set_results({"result": "UI successfully restarted"}) + def _validate(self): + """Validate that configuration and relations are valid and ready. + + Raises: + ValueError: in case of invalid configuration. + """ + if not self._state.is_ready(): + raise ValueError("peer relation not ready") + + if not self._state.airbyte_server: + raise ValueError("airbyte-server relation: not available") + + if not self._state.airbyte_server["status"] == "ready": + raise ValueError("airbyte-server relation: server is not ready") + @log_event_handler(logger) def _update(self, event): """Update the Airbyte UI configuration and replan its execution. @@ -115,13 +144,18 @@ def _update(self, event): Args: event: The event triggered when the relation changed. """ - # TODO (kelkawi-a): validate presence of Airbyte server relation - # TODO (kelkawi-a): update this to get server application name through charm relation + try: + self._validate() + except ValueError as err: + self.unit.status = BlockedStatus(str(err)) + return + + server_svc = self._state.airbyte_server["name"] context = { "API_URL": "/api/v1/", "AIRBYTE_EDITION": "community", - "INTERNAL_API_HOST": f"airbyte-k8s:{INTERNAL_API_PORT}", - "CONNECTOR_BUILDER_API_HOST": f"airbyte-k8s:{CONNECTOR_BUILDER_API_PORT}", + "INTERNAL_API_HOST": f"{server_svc}:{INTERNAL_API_PORT}", + "CONNECTOR_BUILDER_API_HOST": f"{server_svc}:{CONNECTOR_BUILDER_API_PORT}", "KEYCLOAK_INTERNAL_HOST": "localhost", } diff --git a/src/relations/airbyte_server.py b/src/relations/airbyte_server.py new file mode 100644 index 0000000..8a9d38a --- /dev/null +++ b/src/relations/airbyte_server.py @@ -0,0 +1,70 @@ +# Copyright 2024 Canonical Ltd. +# See LICENSE file for licensing details. + +"""Define the Airbyte server:ui relation.""" + +import logging + +from log import log_event_handler +from ops import framework + +logger = logging.getLogger(__name__) + + +class AirbyteServer(framework.Object): + """Client for server:ui relation.""" + + def __init__(self, charm): + """Construct. + + Args: + charm: The charm to attach the hooks to. + """ + super().__init__(charm, "airbyte-server") + self.charm = charm + charm.framework.observe( + charm.on.airbyte_server_relation_joined, self._on_airbyte_server_relation_changed + ) + charm.framework.observe( + charm.on.airbyte_server_relation_changed, self._on_airbyte_server_relation_changed + ) + charm.framework.observe( + charm.on.airbyte_server_relation_broken, self._on_airbyte_server_relation_broken + ) + + @log_event_handler(logger) + def _on_airbyte_server_relation_changed(self, event): + """Handle server:ui relation change event. + + Args: + event: The event triggered when the relation changed. + """ + if not self.charm.unit.is_leader(): + return + + if not self.charm._state.is_ready(): + event.defer() + return + + self.charm._state.airbyte_server = { + "name": event.relation.data[event.app].get("server_name"), + "status": event.relation.data[event.app].get("server_status"), + } + self.charm._update(event) + + @log_event_handler(logger) + def _on_airbyte_server_relation_broken(self, event): + """Handle server:ui relation broken event. + + Args: + event: The event triggered when the relation changed. + """ + if not self.charm.unit.is_leader(): + return + + if not self.charm._state.is_ready(): + event.defer() + return + + self.charm._state.airbyte_server = None + self.charm._update(event)