Skip to content

Commit

Permalink
Charm updates
Browse files Browse the repository at this point in the history
- updated the openfga library
- updated the charm to us the openfga token secret
- fixed the k8s charm that would get into an error state
  trying to start if there wasn't an openfga relation
  • Loading branch information
alesstimec committed Jun 29, 2023
1 parent c368108 commit c4bdccc
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 108 deletions.
15 changes: 11 additions & 4 deletions charms/jimm-k8s/lib/charms/openfga_k8s/v0/openfga.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent):
logger.info("address {}".format(event.address))
logger.info("port {}".format(event.port))
logger.info("scheme {}".format(event.scheme))
if event.token_secret_id:
secret = self.model.get_secret(id=event.token_secret_id)
content = secret.get_content()
# and get the token with content["token"]
```
"""
Expand All @@ -71,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 = 2
LIBPATCH = 3

logger = logging.getLogger(__name__)

Expand All @@ -86,8 +91,8 @@ def store_id(self):
return self.relation.data[self.relation.app].get("store_id")

@property
def token(self):
return self.relation.data[self.relation.app].get("token")
def token_secret_id(self):
return self.relation.data[self.relation.app].get("token_secret_id")

@property
def address(self):
Expand Down Expand Up @@ -149,5 +154,7 @@ def _on_relation_changed(self, event: RelationChangedEvent):
"""Handle the relation-changed event."""
if self.model.unit.is_leader():
self.on.openfga_store_created.emit(
event.relation, app=event.app, unit=event.unit
event.relation,
app=event.app,
unit=event.unit,
)
6 changes: 3 additions & 3 deletions charms/jimm-k8s/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ issues: https://github.com/canonical/jimm/issues

description: |
JIMM is a juju controller, used in conjunction with the JaaS dashboard to provide a seamless way
to manage models, regardless of where their controllers reside or what cloud they may be running on.
to manage models, regardless of where their controllers reside or what cloud they may be running on.
peers:
jimm:
interface: jimm
peer:
interface: jimm-peer

provides:
dashboard:
Expand Down
2 changes: 1 addition & 1 deletion charms/jimm-k8s/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Jinja2 >= 2.11.3
ops >= 1.5.2
ops >= 2.0.0
charmhelpers >= 0.20.22
jsonschema >= 3.2.0
cryptography >= 3.4.8
Expand Down
125 changes: 35 additions & 90 deletions charms/jimm-k8s/src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import logging
import os
import socket
import functools

import hvac
import requests
Expand Down Expand Up @@ -56,7 +57,7 @@
WaitingStatus,
)

from state import State
from state import State, requires_state, requires_state_setter

logger = logging.getLogger(__name__)

Expand All @@ -66,6 +67,12 @@
"JIMM_UUID",
"JIMM_DSN",
"CANDID_URL",
"OPENFGA_STORE",
"OPENFGA_AUTH_MODEL",
"OPENFGA_HOST",
"OPENFGA_SCHEME",
"OPENFGA_TOKEN",
"OPENFGA_PORT",
]

DATABASE_NAME = "jimm"
Expand All @@ -81,8 +88,9 @@ class JimmOperatorCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)

self._state = State(self.app, lambda: self.model.get_relation("jimm"))

self._state = State(self.app, 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)
self.framework.observe(self.on.config_changed, self._on_config_changed)
self.framework.observe(self.on.update_status, self._on_update_status)
Expand Down Expand Up @@ -183,19 +191,18 @@ def __init__(self, *args):
self._dashboard_path = "/root/dashboard"
self._dashboard_hash_path = "/root/dashboard/hash"

def _on_peer_relation_changed(self, event):
self._update_workload(event)

def _on_jimm_pebble_ready(self, event):
self._update_workload(event)

def _on_config_changed(self, event):
self._update_workload(event)

@requires_state_setter
def _on_leader_elected(self, event):
if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

if self.unit.is_leader() and not self._state.private_key:
if not self._state.private_key:
private_key: bytes = generate_private_key(key_size=4096)
self._state.private_key = private_key.decode()

Expand All @@ -218,16 +225,11 @@ def _ensure_bakery_agent_file(self, event):

self._push_to_workload(self._agent_filename, agent_data, event)

@requires_state
def _update_workload(self, event):
"""' Update workload with all available configuration
data."""

if not self._state.is_ready():
event.defer()
print(self._state.is_ready())
logger.warning("State is not ready")
return

container = self.unit.get_container(WORKLOAD_CONTAINER)
if not container.can_connect():
logger.info("cannot connect to the workload container - deferring the event")
Expand Down Expand Up @@ -338,14 +340,8 @@ def _on_update_status(self, _):
"""Update the status of the charm."""
self._ready()

@requires_state_setter
def _on_dashboard_relation_joined(self, event: RelationJoinedEvent):
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

dns_name = self._get_dns_name(event)
if not dns_name:
Expand All @@ -359,14 +355,10 @@ def _on_dashboard_relation_joined(self, event: RelationJoinedEvent):
}
)

@requires_state_setter
def _on_database_event(self, event: DatabaseEvent) -> None:
"""Database event handler."""

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

# get the first endpoint from a comma separate list
ep = event.endpoints.split(",", 1)[0]
# compose the db connection string
Expand All @@ -379,12 +371,9 @@ def _on_database_event(self, event: DatabaseEvent) -> None:

self._update_workload(event)

@requires_state_setter
def _on_database_relation_broken(self, event: DatabaseEvent) -> None:
"""Database relation broken handler."""
if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

# when the database relation is broken, we unset the
# connection string and schema-created from the application
Expand Down Expand Up @@ -509,11 +498,8 @@ 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):
if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

container = self.unit.get_container(WORKLOAD_CONTAINER)

Expand Down Expand Up @@ -576,26 +562,24 @@ def _hash(self, filename):
md5.update(data)
return md5.hexdigest()

@requires_state_setter
def _on_openfga_store_created(self, event: OpenFGAStoreCreateEvent):
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

if not event.store_id:
return

secret = self.model.get_secret(id=event.token_secret_id)
secret_content = secret.get_content()

self._state.openfga_store_id = event.store_id
self._state.openfga_token = event.token
self._state.openfga_token = secret_content["token"]
self._state.openfga_address = event.address
self._state.openfga_port = event.port
self._state.openfga_scheme = event.scheme

self._update_workload(event)

@requires_state
def _get_dns_name(self, event):
if not self._state.is_ready():
event.defer()
Expand All @@ -613,14 +597,8 @@ def _get_dns_name(self, event):

return dns_name

@requires_state_setter
def _on_certificates_relation_joined(self, event: RelationJoinedEvent) -> None:
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

dns_name = self._get_dns_name(event)
if not dns_name:
Expand All @@ -635,29 +613,17 @@ def _on_certificates_relation_joined(self, event: RelationJoinedEvent) -> None:

self.certificates.request_certificate_creation(certificate_signing_request=csr)

@requires_state_setter
def _on_certificate_available(self, event: CertificateAvailableEvent) -> None:
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return


self._state.certificate = event.certificate
self._state.ca = event.ca
self._state.chain = event.chain

self._update_workload(event)

@requires_state_setter
def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None:
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

old_csr = self._state.csr
private_key = self._state.private_key
Expand All @@ -677,14 +643,8 @@ def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None:

self._update_workload()

@requires_state_setter
def _on_certificate_revoked(self, event: CertificateRevokedEvent) -> None:
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

old_csr = self._state.csr
private_key = self._state.private_key
Expand All @@ -709,37 +669,22 @@ def _on_certificate_revoked(self, event: CertificateRevokedEvent) -> None:
self.unit.status = WaitingStatus("Waiting for new certificate")
self._update_workload()

@requires_state_setter
def _on_ingress_ready(self, event: IngressPerAppReadyEvent):
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

self._state.dns_name = event.url

self._update_workload(event)

@requires_state_setter
def _on_ingress_revoked(self, event: IngressPerAppRevokedEvent):
if not self.unit.is_leader():
return

if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

del self._state.dns_name

self._update_workload(event)

@requires_state_setter
def _on_create_authorization_model_action(self, event: ActionEvent):
if not self._state.is_ready():
event.defer()
logger.warning("State is not ready")
return

model = event.params["model"]
if not model:
Expand Down
25 changes: 25 additions & 0 deletions charms/jimm-k8s/src/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,34 @@

"""Manager for handling charm state."""

import functools
import json



def requires_state_setter(func):
@functools.wraps(func)
def wrapper(self, event):
if self.unit.is_leader() and self._state.is_ready():
return func(self, event)
else:
return

return wrapper


def requires_state(func):
@functools.wraps(func)
def wrapper(self, event):
if self._state.is_ready():
return func(self, event)
else:
event.defer()
return

return wrapper


class State:
"""A magic state that uses a relation as the data store.
Expand Down
1 change: 1 addition & 0 deletions charms/jimm-k8s/tests/integration/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ async def test_build_and_deploy(ops_test: OpsTest, local_charm):
"candid-url": "https://api.jujucharms.com/identity",
"public-key": "izcYsQy3TePp6bLjqOo3IRPFvkQd2IKtyODGqC6SdFk=",
"private-key": "ly/dzsI9Nt/4JxUILQeAX79qZ4mygDiuYGqc2ZEiDEc=",
"dns-name": "jimm.test.canonical.com",
},
)
await ops_test.model.deploy(
Expand Down
Loading

0 comments on commit c4bdccc

Please sign in to comment.