Merge "Update relation data from configure_charm" into main

This commit is contained in:
Zuul 2023-10-10 08:58:38 +00:00 committed by Gerrit Code Review
commit f24edf6a06
2 changed files with 57 additions and 26 deletions
ops-sunbeam/ops_sunbeam

@ -236,6 +236,14 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
"Not all relations are ready"
)
def update_relations(self):
"""Update relation data."""
for handler in self.relation_handlers:
try:
handler.update_relation_data()
except NotImplementedError:
logging.debug(f"send_requests not implemented for {handler}")
def configure_unit(self, event: ops.framework.EventBase) -> None:
"""Run configuration on this unit."""
self.check_leader_ready()
@ -270,6 +278,11 @@ class OSBaseOperatorCharm(ops.charm.CharmBase):
def configure_charm(self, event: ops.framework.EventBase) -> None:
"""Catchall handler to configure charm services."""
with sunbeam_guard.guard(self, "Bootstrapping"):
# Publishing relation data may be dependent on something else (like
# receiving a piece of data from the leader). To cover that
# republish relation if the relation adapter has implemented an
# update method.
self.update_relations()
self.configure_unit(event)
self.configure_app(event)
self.bootstrap_status.set(ActiveStatus())

@ -138,6 +138,10 @@ class RelationHandler(ops.charm.Object):
"""Pull together context for rendering templates."""
return self.interface_properties()
def update_relation_data(self):
"""Update relation outside of relation context."""
raise NotImplementedError
class IngressHandler(RelationHandler):
"""Base class to handle Ingress relations."""
@ -826,6 +830,7 @@ class TlsCertificatesHandler(RelationHandler):
) -> None:
"""Run constructor."""
self.sans = sans
self._private_key = None
super().__init__(charm, relation_name, callback_f, mandatory)
try:
self.store = self.PeerKeyStore(
@ -833,6 +838,7 @@ class TlsCertificatesHandler(RelationHandler):
)
except KeyError:
self.store = self.LocalDBKeyStore(charm._state)
self.setup_private_key()
def setup_event_handler(self) -> None:
"""Configure event handlers for tls relation."""
@ -846,7 +852,6 @@ class TlsCertificatesHandler(RelationHandler):
self.certificates = TLSCertificatesRequiresV1(
self.charm, "certificates"
)
self.framework.observe(self.charm.on.install, self._on_install)
self.framework.observe(
self.charm.on.certificates_relation_joined,
self._on_certificates_relation_joined,
@ -869,7 +874,8 @@ class TlsCertificatesHandler(RelationHandler):
)
return self.certificates
def _on_install(self, event: ops.framework.EventBase) -> None:
def setup_private_key(self) -> None:
"""Create and store private key if needed."""
# Lazy import to ensure this lib is only required if the charm
# has this relation.
from charms.tls_certificates_interface.v1.tls_certificates import (
@ -877,45 +883,65 @@ class TlsCertificatesHandler(RelationHandler):
)
if not self.store.store_ready():
event.defer()
logger.debug("Store not ready, cannot generate key")
return
if self.store.get_private_key():
# Secret already saved
logger.debug("Private key already present")
self._private_key = self.store.get_private_key()
private_key_secret_id = self.store.get_private_key()
private_key_secret = self.model.get_secret(
id=private_key_secret_id
)
self._private_key = private_key_secret.get_content().get(
"private-key"
)
return
private_key = generate_private_key()
self._private_key = generate_private_key()
private_key_secret = self.model.unit.add_secret(
{"private-key": private_key.decode()},
{"private-key": self._private_key.decode()},
label=f"{self.charm.model.unit}-private-key",
)
self.store.set_private_key(private_key_secret.id)
@property
def private_key(self):
"""Private key for certificates."""
logger.debug("Returning private key: {}".format(self._private_key))
return self._private_key
def update_relation_data(self):
"""Request certificates outside of relation context."""
self._request_certificates()
def _on_certificates_relation_joined(
self, event: ops.framework.EventBase
) -> None:
"""Request certificates in response to relation join event."""
self._request_certificates()
def _request_certificates(self):
"""Request certificates from remote provider."""
# Lazy import to ensure this lib is only required if the charm
# has this relation.
from charms.tls_certificates_interface.v1.tls_certificates import (
generate_csr,
)
if not self.store.store_ready():
event.defer()
if self.ready:
logger.debug("Certificate request already complete.")
return
private_key = None
private_key_secret_id = self.store.get_private_key()
if private_key_secret_id:
private_key_secret = self.model.get_secret(
id=private_key_secret_id
)
private_key = private_key_secret.get_content().get("private-key")
if self.private_key:
logger.debug("Private key found, requesting certificates")
else:
logger.debug("Cannot request certificates, private key not found")
return
csr = generate_csr(
private_key=private_key.encode(),
private_key=self.private_key.encode(),
subject=self.charm.model.unit.name.replace("/", "-"),
sans=self.sans,
)
@ -998,16 +1024,8 @@ class TlsCertificatesHandler(RelationHandler):
cert = certs["cert"]
ca_cert = certs["ca"] + "\n" + "\n".join(certs["chain"])
key = None
private_key_secret_id = self.store.get_private_key()
if private_key_secret_id:
private_key_secret = self.model.get_secret(
id=private_key_secret_id
)
key = private_key_secret.get_content().get("private-key")
ctxt = {
"key": key,
"key": self.private_key,
"cert": cert,
"ca_cert": ca_cert,
}