From b9fd0ffd0f89b9c26fd1915a156177ec2fc861e9 Mon Sep 17 00:00:00 2001 From: yazansalti Date: Mon, 10 Jun 2024 19:36:00 +0400 Subject: [PATCH 1/2] Adds number of pending certificate requests in status message --- lib/charms/lego_base_k8s/v0/lego_client.py | 16 +++++- tests/unit/test_charm.py | 63 ++++++++++++++++++++-- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/lib/charms/lego_base_k8s/v0/lego_client.py b/lib/charms/lego_base_k8s/v0/lego_client.py index bd6c919..6002c8a 100644 --- a/lib/charms/lego_base_k8s/v0/lego_client.py +++ b/lib/charms/lego_base_k8s/v0/lego_client.py @@ -95,7 +95,7 @@ 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 = 8 +LIBPATCH = 9 logger = logging.getLogger(__name__) @@ -144,7 +144,7 @@ def _on_collect_status(self, event: CollectStatusEvent) -> None: BlockedStatus(err) ) return - event.add_status(ActiveStatus()) + event.add_status(ActiveStatus(self._get_certificate_fulfillment_status())) def _sync_certificates(self, event: EventBase) -> None: """Go through all the certificates relations and handle outstanding requests.""" @@ -278,6 +278,18 @@ def _generate_signed_certificate(self, csr: str, relation_id: int): relation_id=relation_id, ) + def _get_certificate_fulfillment_status(self) -> str: + """Return the status message reflecting how many certificate requests are still pending.""" + outstanding_requests_num = len( + self.tls_certificates.get_outstanding_certificate_requests() + ) + if outstanding_requests_num > 0: + return ( + f"{outstanding_requests_num} pending certificate request, " + "check juju debug-log for more information" + ) + return "All certificate requests are fulfilled" + @property def _cmd(self) -> List[str]: """Command to run to get the certificate. diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 28b7684..85494f4 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -14,7 +14,7 @@ generate_private_key, ) from ops import testing -from ops.model import BlockedStatus, WaitingStatus +from ops.model import ActiveStatus, BlockedStatus, WaitingStatus from ops.pebble import ExecError from ops.testing import Harness @@ -307,7 +307,7 @@ def test_given_generic_config_is_not_valid_when_certificate_creation_request_the assert self.harness.charm.unit.status == BlockedStatus("Invalid email address") - def test_given_invalid_specific_config_when_certificate_creation_request_then_status_is_blocked( # noqa: E501 + def test_given_valid_config_when_update_status_then_status_is_active( # noqa: E501 self, ): self.harness.update_config( @@ -320,15 +320,47 @@ def test_given_invalid_specific_config_when_certificate_creation_request_then_st relation_id = self.harness.add_relation("certificates", "remote") self.harness.add_relation_unit(relation_id, "remote/0") self.harness.set_can_connect("lego", True) - self.harness.charm.valid_config = False + self.harness.charm.valid_config = True + + self.harness.evaluate_status() + + self.assertEqual( + self.harness.charm.unit.status, ActiveStatus("All certificate requests are fulfilled") + ) + + @patch("ops.model.Container.exec", new=MockExec) + @patch( + f"{TLS_LIB_PATH}.TLSCertificatesProvidesV3.set_relation_certificate", + ) + def test_given_valid_config_and_pending_requests_when_update_status_then_status_is_active( # noqa: E501 + self, mock_set_relation_certificate + ): + self.harness.set_leader(False) + self.harness.update_config( + { + "email": "banana@email.com", + "server": "https://acme-v02.api.letsencrypt.org/directory", + } + ) + relation_id = self.harness.add_relation("certificates", "remote") + self.harness.add_relation_unit(relation_id, "remote/0") + self.harness.set_can_connect("lego", True) + self.harness.charm.valid_config = True + container = self.harness.model.unit.get_container("lego") + container.push( + "/tmp/.lego/certificates/foo.crt", source=test_cert.read_bytes(), make_dirs=True + ) self.add_csr_to_remote_unit_relation_data(relation_id=relation_id, app_or_unit="remote/0") self.harness.evaluate_status() self.assertEqual( - self.harness.charm.unit.status, BlockedStatus("Invalid specific configuration") + self.harness.charm.unit.status, ActiveStatus( + "1 pending certificate request, check juju debug-log for more information" + ) ) + def test_given_generic_config_is_not_valid_when_update_status_then_status_is_blocked( self, ): @@ -411,6 +443,29 @@ def test_given_invalid_specific_config_when_config_changed_then_status_is_blocke self.harness.charm.unit.status, BlockedStatus("Invalid specific configuration") ) + def test_given_invalid_specific_config_when_certificate_creation_request_then_status_is_blocked( # noqa: E501 + self, + ): + self.harness.update_config( + { + "email": "banana@email.com", + "server": "https://acme-v02.api.letsencrypt.org/directory", + } + ) + self.harness.set_leader(True) + relation_id = self.harness.add_relation("certificates", "remote") + self.harness.add_relation_unit(relation_id, "remote/0") + self.harness.set_can_connect("lego", True) + self.harness.charm.valid_config = False + + self.harness.charm.on.config_changed.emit() + + self.harness.evaluate_status() + + self.assertEqual( + self.harness.charm.unit.status, BlockedStatus("Invalid specific configuration") + ) + @patch("ops.model.Container.exec", new=MockExec) @patch( f"{TLS_LIB_PATH}.TLSCertificatesProvidesV3.set_relation_certificate", From ad538c33dc1788d0f9e059b946bb4cad2cd18495 Mon Sep 17 00:00:00 2001 From: yazansalti Date: Tue, 11 Jun 2024 10:34:49 +0400 Subject: [PATCH 2/2] Uses ratio of fulfilled/total requests --- lib/charms/lego_base_k8s/v0/lego_client.py | 13 ++++++------ tests/unit/test_charm.py | 23 +--------------------- 2 files changed, 8 insertions(+), 28 deletions(-) diff --git a/lib/charms/lego_base_k8s/v0/lego_client.py b/lib/charms/lego_base_k8s/v0/lego_client.py index 6002c8a..299ccb6 100644 --- a/lib/charms/lego_base_k8s/v0/lego_client.py +++ b/lib/charms/lego_base_k8s/v0/lego_client.py @@ -283,12 +283,13 @@ def _get_certificate_fulfillment_status(self) -> str: outstanding_requests_num = len( self.tls_certificates.get_outstanding_certificate_requests() ) - if outstanding_requests_num > 0: - return ( - f"{outstanding_requests_num} pending certificate request, " - "check juju debug-log for more information" - ) - return "All certificate requests are fulfilled" + total_requests_num = len( + self.tls_certificates.get_requirer_csrs() + ) + fulfilled_certs = total_requests_num - outstanding_requests_num + return ( + f"{fulfilled_certs}/{total_requests_num} certificate requests are fulfilled" + ) @property def _cmd(self) -> List[str]: diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 85494f4..aa1f7cf 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -307,27 +307,6 @@ def test_given_generic_config_is_not_valid_when_certificate_creation_request_the assert self.harness.charm.unit.status == BlockedStatus("Invalid email address") - def test_given_valid_config_when_update_status_then_status_is_active( # noqa: E501 - self, - ): - self.harness.update_config( - { - "email": "banana@email.com", - "server": "https://acme-v02.api.letsencrypt.org/directory", - } - ) - self.harness.set_leader(True) - relation_id = self.harness.add_relation("certificates", "remote") - self.harness.add_relation_unit(relation_id, "remote/0") - self.harness.set_can_connect("lego", True) - self.harness.charm.valid_config = True - - self.harness.evaluate_status() - - self.assertEqual( - self.harness.charm.unit.status, ActiveStatus("All certificate requests are fulfilled") - ) - @patch("ops.model.Container.exec", new=MockExec) @patch( f"{TLS_LIB_PATH}.TLSCertificatesProvidesV3.set_relation_certificate", @@ -357,7 +336,7 @@ def test_given_valid_config_and_pending_requests_when_update_status_then_status_ self.assertEqual( self.harness.charm.unit.status, ActiveStatus( - "1 pending certificate request, check juju debug-log for more information" + "0/1 certificate requests are fulfilled" ) )