Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Update charm libraries #81

Merged
merged 1 commit into from
Mar 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 48 additions & 11 deletions lib/charms/lego_base_k8s/v0/lego_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ def _plugin_config(self):

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 5
LIBPATCH = 6


logger = logging.getLogger(__name__)

CERTIFICATES_RELATION_NAME = "certificates"


class AcmeClient(CharmBase):
"""Base charm for charms that use the ACME protocol to get certificates.
Expand All @@ -123,11 +125,12 @@ def __init__(self, *args, plugin: str):
self._container_name = list(self.meta.containers.values())[0].name
self._container = self.unit.get_container(self._container_name)
self._logging = LogForwarder(self, relation_name="logging")
self.tls_certificates = TLSCertificatesProvidesV3(self, "certificates")
self.tls_certificates = TLSCertificatesProvidesV3(self, CERTIFICATES_RELATION_NAME)
self.framework.observe(
self.tls_certificates.on.certificate_creation_request,
self._on_certificate_creation_request,
)
self.framework.observe(self.on.update_status, self._sync_certificates)
self._plugin = plugin

def validate_generic_acme_config(self) -> bool:
Expand Down Expand Up @@ -200,6 +203,25 @@ def _pull_certificates_from_workload(self, csr_subject: str) -> List[Union[bytes
chain_pem = self._container.pull(path=f"{self._certs_path}{csr_subject}.crt")
return list(chain_pem.read().split("\n\n"))

def _sync_certificates(self, event: EventBase) -> None:
"""Goes through all the certificates relations and handles outstanding requests."""
self._on_config_changed(event)
if not isinstance(self.unit.status, ActiveStatus):
logger.debug(
"Charm is not active, skipping certificate generation, \
will try again in during the next update status event."
)
return
for relation in self.model.relations.get(CERTIFICATES_RELATION_NAME, []):
outstanding_requests = self.tls_certificates.get_outstanding_certificate_requests(
relation_id=relation.id
)
for request in outstanding_requests:
self._generate_signed_certificate(
csr=request.csr,
relation_id=relation.id,
)

def _on_certificate_creation_request(self, event: CertificateCreationRequestEvent) -> None:
"""Handle certificate creation request event.

Expand All @@ -211,30 +233,45 @@ def _on_certificate_creation_request(self, event: CertificateCreationRequestEven
"""
self._on_config_changed(event)
if not isinstance(self.unit.status, ActiveStatus):
event.defer()
logger.debug(
"Charm is not active, skipping certificate generation, \
will try again in during the next update status event."
)
return
self._generate_signed_certificate(event.certificate_signing_request, event.relation_id)

def _generate_signed_certificate(self, csr: str, relation_id: int):
"""Generate signed certificate from the ACME provider."""
if not self.unit.is_leader():
logger.debug("Only the leader can handle certificate requests")
return
if not self._container.can_connect():
logger.info("Waiting for container to be ready")
event.defer()
logger.info("Container is not ready")
return
csr_subject = self._get_subject_from_csr(event.certificate_signing_request)
csr_subject = self._get_subject_from_csr(csr)
if len(csr_subject) > 64:
logger.error("Subject is too long (> 64 characters): %s", csr_subject)
return
logger.info("Received Certificate Creation Request for domain %s", csr_subject)
self._push_csr_to_workload(event.certificate_signing_request)
self._push_csr_to_workload(csr=csr)
if not self._execute_lego_cmd():
logger.error("Failed to execute lego command")
logger.error(
"Failed to execute lego command \
will try again in during the next update status event."
)
return
if not (signed_certificates := self._pull_certificates_from_workload(csr_subject)):
logger.error(
"Failed to pull certificates from workload \
will try again in during the next update status event."
)
return
signed_certificates = self._pull_certificates_from_workload(csr_subject)
self.tls_certificates.set_relation_certificate(
certificate=signed_certificates[0],
certificate_signing_request=event.certificate_signing_request,
certificate_signing_request=csr,
ca=signed_certificates[-1],
chain=list(reversed(signed_certificates)),
relation_id=event.relation_id,
relation_id=relation_id,
)

@property
Expand Down
Loading