diff --git a/charms/jimm-k8s/lib/charms/openfga_k8s/v0/openfga.py b/charms/jimm-k8s/lib/charms/openfga_k8s/v0/openfga.py index 8160715ce..4cd3e8a31 100644 --- a/charms/jimm-k8s/lib/charms/openfga_k8s/v0/openfga.py +++ b/charms/jimm-k8s/lib/charms/openfga_k8s/v0/openfga.py @@ -76,7 +76,7 @@ def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent): # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 3 +LIBPATCH = 4 logger = logging.getLogger(__name__) @@ -88,23 +88,27 @@ class OpenFGAEvent(RelationEvent): @property def store_id(self): - return self.relation.data[self.relation.app].get("store_id") + return self.relation.data[self.relation.app].get("store_id", "") @property def token_secret_id(self): - return self.relation.data[self.relation.app].get("token_secret_id") + return self.relation.data[self.relation.app].get("token_secret_id", "") + + @property + def token(self): + return self.relation.data[self.relation.app].get("token", "") @property def address(self): - return self.relation.data[self.relation.app].get("address") + return self.relation.data[self.relation.app].get("address", "") @property def scheme(self): - return self.relation.data[self.relation.app].get("scheme") + return self.relation.data[self.relation.app].get("scheme", "") @property def port(self): - return self.relation.data[self.relation.app].get("port") + return self.relation.data[self.relation.app].get("port", "") class OpenFGAStoreCreateEvent(OpenFGAEvent): diff --git a/charms/jimm-k8s/src/charm.py b/charms/jimm-k8s/src/charm.py index 04c2ee8c7..1037c80cb 100755 --- a/charms/jimm-k8s/src/charm.py +++ b/charms/jimm-k8s/src/charm.py @@ -62,17 +62,17 @@ WORKLOAD_CONTAINER = "jimm" -REQUIRED_SETTINGS = [ - "JIMM_UUID", - "JIMM_DSN", - "CANDID_URL", - "OPENFGA_STORE", - "OPENFGA_AUTH_MODEL", - "OPENFGA_HOST", - "OPENFGA_SCHEME", - "OPENFGA_TOKEN", - "OPENFGA_PORT", -] +REQUIRED_SETTINGS = { + "JIMM_UUID": "missing uuid configuration", + "JIMM_DSN": "missing postgresql relation", + "CANDID_URL": "missing candid-url configuration", + "OPENFGA_STORE": "missing openfga relation", + "OPENFGA_AUTH_MODEL": "run create-authorization-model action", + "OPENFGA_HOST": "missing openfga relation", + "OPENFGA_SCHEME": "missing openfga relation", + "OPENFGA_TOKEN": "missing openfga relation", + "OPENFGA_PORT": "missing openfga relation", +} DATABASE_NAME = "jimm" OPENFGA_STORE_NAME = "jimm" @@ -88,6 +88,7 @@ def __init__(self, *args): super().__init__(*args) self._state = State(self.app, lambda: self.model.get_relation("peer")) + self._unit_state = State(self.unit, lambda: self.model.get_relation("peer")) self.framework.observe(self.on.peer_relation_changed, self._on_peer_relation_changed) self.framework.observe(self.on.jimm_pebble_ready, self._on_jimm_pebble_ready) @@ -236,6 +237,7 @@ def _update_workload(self, event): return self._ensure_bakery_agent_file(event) + self._ensure_vault_file(event) self._install_dashboard(event) dns_name = self._get_dns_name(event) @@ -313,6 +315,7 @@ def _update_workload(self, event): else: logger.info("workload container not ready - defering") event.defer() + return dashboard_relation = self.model.get_relation("dashboard") if dashboard_relation and self.unit.is_leader(): @@ -332,7 +335,7 @@ def _on_stop(self, _): """Stop JIMM.""" container = self.unit.get_container(WORKLOAD_CONTAINER) if container.can_connect(): - container.stop() + container.stop("jimm") self._ready() def _on_update_status(self, _): @@ -392,10 +395,10 @@ def _ready(self): env_vars = plan.services.get("jimm").environment - for setting in REQUIRED_SETTINGS: + for setting, message in REQUIRED_SETTINGS.items(): if not env_vars.get(setting, ""): self.unit.status = BlockedStatus( - "{} configuration value not set".format(setting), + "{} configuration value not set: {}".format(setting, message), ) return False @@ -416,6 +419,7 @@ def _install_dashboard(self, event): # this event. if not container.can_connect(): event.defer() + return # fetch the resource filename try: @@ -496,10 +500,14 @@ def _on_vault_relation_joined(self, event): event.relation.data[self.unit]["access_address"] = json.dumps(self._get_network_address(event)) event.relation.data[self.unit]["isolated"] = json.dumps(False) - @requires_state_setter - def _on_vault_relation_changed(self, event): + def _ensure_vault_file(self, event): container = self.unit.get_container(WORKLOAD_CONTAINER) + if not self._unit_state.is_ready(): + logger.info("unit state not ready") + event.defer() + return + # if we can't connect to the container we should defer # this event. if not container.can_connect(): @@ -509,6 +517,16 @@ def _on_vault_relation_changed(self, event): if container.exists(self._vault_secret_filename): container.remove_path(self._vault_secret_filename) + secret_data = self._unit_state.vault_secret_data + if secret_data: + self._push_to_workload(self._vault_secret_filename, secret_data, event) + + def _on_vault_relation_changed(self, event): + if not self._unit_state.is_ready() or not self._state.is_ready(): + logger.info("state not ready") + event.defer() + return + addr = _json_data(event, "vault_url") if not addr: return @@ -523,9 +541,13 @@ def _on_vault_relation_changed(self, event): secret["data"]["role_id"] = role_id secret_data = json.dumps(secret) - self._push_to_workload(self._vault_secret_filename, secret_data, event) - self._state.vault_address = addr + logger.error("setting unit state data {}".format(secret_data)) + self._unit_state.vault_secret_data = secret_data + if self.unit.is_leader(): + self._state.vault_address = addr + + self._update_workload(event) def _path_exists_in_workload(self, path: str): """Returns true if the specified path exists in the @@ -564,11 +586,14 @@ def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent): if not event.store_id: return - # secret = self.model.get_secret(id=event.token_secret_id) - # secret_content = secret.get_content() + token = event.token + if event.token_secret_id: + secret = self.model.get_secret(id=event.token_secret_id) + secret_content = secret.get_content() + token = secret_content["token"] self._state.openfga_store_id = event.store_id - self._state.openfga_token = event.token # secret_content["token"] + self._state.openfga_token = token self._state.openfga_address = event.address self._state.openfga_port = event.port self._state.openfga_scheme = event.scheme diff --git a/charms/jimm-k8s/tests/unit/test_charm.py b/charms/jimm-k8s/tests/unit/test_charm.py index 44950da1e..e197bcc16 100644 --- a/charms/jimm-k8s/tests/unit/test_charm.py +++ b/charms/jimm-k8s/tests/unit/test_charm.py @@ -44,7 +44,8 @@ def setUp(self): self.addCleanup(self.tempdir.cleanup) self.harness.charm.framework.charm_dir = pathlib.Path(self.tempdir.name) - self.harness.add_relation("peer", "jimm") + jimm_id = self.harness.add_relation("peer", "juju-jimm-k8s") + self.harness.add_relation_unit(jimm_id, "juju-jimm-k8s/1") self.harness.container_pebble_ready("jimm") rel_id = self.harness.add_relation("ingress", "nginx-ingress") diff --git a/charms/jimm/charmcraft.yaml b/charms/jimm/charmcraft.yaml index 61ae36388..9d6375eb3 100644 --- a/charms/jimm/charmcraft.yaml +++ b/charms/jimm/charmcraft.yaml @@ -4,16 +4,19 @@ type: "charm" parts: charm: prime: - - ./templates - - ./files - - README.md - charm-python-packages: [setuptools] + - ./templates + - ./files + - README.md + charm-python-packages: + - setuptools + - cosl + - pydantic==1.10 bases: # Ensure run-on is the same or newer than build-on # since jimm-server is a Go binary using CGO dependencies - build-on: - - name: "ubuntu" - channel: "20.04" + - name: "ubuntu" + channel: "20.04" run-on: - - name: "ubuntu" - channel: "20.04" + - name: "ubuntu" + channel: "20.04" diff --git a/charms/jimm/lib/charms/grafana_agent/v0/cos_agent.py b/charms/jimm/lib/charms/grafana_agent/v0/cos_agent.py index 0acaed361..a75683fb1 100644 --- a/charms/jimm/lib/charms/grafana_agent/v0/cos_agent.py +++ b/charms/jimm/lib/charms/grafana_agent/v0/cos_agent.py @@ -191,7 +191,7 @@ class _MetricsEndpointDict(TypedDict): LIBAPI = 0 LIBPATCH = 3 -PYDEPS = ["cosl", "pydantic"] +PYDEPS = ["cosl", "pydantic==1.10"] DEFAULT_RELATION_NAME = "cos-agent" DEFAULT_PEER_RELATION_NAME = "peers" diff --git a/charms/jimm/lib/charms/openfga_k8s/v0/openfga.py b/charms/jimm/lib/charms/openfga_k8s/v0/openfga.py index 8160715ce..09d86f20e 100644 --- a/charms/jimm/lib/charms/openfga_k8s/v0/openfga.py +++ b/charms/jimm/lib/charms/openfga_k8s/v0/openfga.py @@ -76,7 +76,7 @@ def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent): # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 3 +LIBPATCH = 4 logger = logging.getLogger(__name__) @@ -93,6 +93,10 @@ def store_id(self): @property def token_secret_id(self): return self.relation.data[self.relation.app].get("token_secret_id") + + @property + def token(self): + return self.relation.data[self.relation.app].get("token") @property def address(self): diff --git a/charms/jimm/requirements.txt b/charms/jimm/requirements.txt index 222ed3a3d..ece277268 100644 --- a/charms/jimm/requirements.txt +++ b/charms/jimm/requirements.txt @@ -3,5 +3,5 @@ Jinja2 >= 2.11.3 ops >= 2.0.0 charmhelpers >= 0.20.22 hvac >= 0.11.0 -pydantic +pydantic == 1.10 cosl diff --git a/charms/jimm/src/charm.py b/charms/jimm/src/charm.py index 09d11d981..1ed190546 100755 --- a/charms/jimm/src/charm.py +++ b/charms/jimm/src/charm.py @@ -409,16 +409,19 @@ def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent): if not event.store_id: return - logger.error("token secret {}".format(event.token_secret_id)) - secret = self.model.get_secret(id=event.token_secret_id) - secret_content = secret.get_content() + token = event.token + if event.token_secret_id: + logger.error("token secret {}".format(event.token_secret_id)) + secret = self.model.get_secret(id=event.token_secret_id) + secret_content = secret.get_content() + token = secret_content["token"] args = { "openfga_host": event.address, "openfga_port": event.port, "openfga_scheme": event.scheme, "openfga_store": event.store_id, - "openfga_token": secret_content["token"], + "openfga_token": token, } with open(self._env_filename("openfga"), "wt") as f: