Update relation data from configure_charm
This change adds a mechanism for charms to be able to update relation data as part of configure_charm rather than responding to an explicit relation event. The reason for this is that pod can be recycled at any point causing it to loose its defered events. So, if a relation adapter method to send relation data contains a defer that deferred event can be lost and when a pod comes back it is never resent. Closes-Bug: 2036188 Change-Id: I2e6b27cdc05e93ebb25a7859b3c039ae64d82d9d
This commit is contained in:
parent
a7ac636d28
commit
92ec0fa3e8
@ -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."""
|
||||
@ -811,6 +815,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(
|
||||
@ -818,6 +823,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."""
|
||||
@ -831,7 +837,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,
|
||||
@ -854,7 +859,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 (
|
||||
@ -862,45 +868,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,
|
||||
)
|
||||
@ -983,16 +1009,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,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user