From fd6c92ff66753f683946406b40c56ac9cbd44ce8 Mon Sep 17 00:00:00 2001 From: Guillaume Belanger Date: Fri, 27 Sep 2024 08:47:09 -0400 Subject: [PATCH] feat: set juju workload version using Notary's API (#44) Signed-off-by: guillaume --- src/charm.py | 10 ++++++++ src/notary.py | 5 ++++ tests/unit/test_charm.py | 55 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/src/charm.py b/src/charm.py index 9d904fb..e4956d5 100755 --- a/src/charm.py +++ b/src/charm.py @@ -139,6 +139,7 @@ def configure(self, event: ops.EventBase): self._configure_access_certificates() self._configure_charm_authorization() self._configure_certificate_requirers() + self._configure_juju_workload_version() def _on_collect_status(self, event: ops.CollectStatusEvent): if not self.unit.is_leader(): @@ -282,6 +283,15 @@ def _configure_certificate_requirers(self): ) ) + def _configure_juju_workload_version(self): + """Set the Juju workload version to the Notary version.""" + if not self.unit.is_leader(): + return + if not self.client.is_api_available(): + return + if version := self.client.get_version(): + self.unit.set_workload_version(version) + ## Properties ## @property def _pebble_layer(self) -> ops.pebble.LayerDict: diff --git a/src/notary.py b/src/notary.py index af42375..61a4d3b 100644 --- a/src/notary.py +++ b/src/notary.py @@ -180,6 +180,11 @@ def is_api_available(self) -> bool: status = self.get_status() return status is not None + def get_version(self) -> str | None: + """Return the version of the Notary server.""" + status = self.get_status() + return status.version if status else None + def login(self, username: str, password: str) -> LoginResponse | None: """Login to notary by sending the username and password and return a Token.""" login_params = LoginParams(username=username, password=password) diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 7db9682..8c22de8 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -2961,6 +2961,51 @@ def test_given_notary_available_and_initialized_when_collect_status_then_status_ out = context.run(context.on.collect_unit_status(), state) assert out.unit_status == ops.ActiveStatus() + def test_given_notary_available_when_configure_then_workload_version_is_set( + self, context, tmpdir + ): + config_mount = Mount(location="/etc/notary/config", source=tmpdir) + state = State( + storages={Storage(name="config"), Storage(name="database")}, + containers=[ + Container( + name="notary", + can_connect=True, + mounts={"config": config_mount}, + layers={ + "notary": Layer( + { + "summary": "notary layer", + "description": "pebble config layer for notary", + "services": { + "notary": { + "override": "replace", + "summary": "notary", + "command": "notary -config /etc/notary/config/config.yaml", + "startup": "enabled", + } + }, + } + ) + }, + ) + ], + leader=True, + ) + + with patch( + "notary.Notary.__new__", + return_value=Mock( + **{ + "is_api_available.return_value": True, + "login.return_value": None, + "get_version.return_value": "1.2.3", + }, + ), + ): + out = context.run(context.on.update_status(), state) + assert out.workload_version == "1.2.3" + def test_given_notary_available_and_not_initialized_when_configure_then_admin_user_created( self, context, tmpdir ): @@ -3001,6 +3046,7 @@ def test_given_notary_available_and_not_initialized_when_configure_then_admin_us "is_initialized.return_value": False, "login.return_value": LoginResponse(token="example-token"), "token_is_valid.return_value": False, + "get_version.return_value": None, }, ), ): @@ -3049,6 +3095,7 @@ def test_given_tls_requirer_available_when_notary_unreachable_then_no_error_rais "is_initialized.return_value": False, "login.return_value": LoginResponse(token="example-token"), "token_is_valid.return_value": False, + "get_version.return_value": None, }, ), ): @@ -3111,6 +3158,7 @@ def test_given_tls_requirer_available_when_configure_then_csrs_posted_to_notary( "token_is_valid.return_value": True, "list_certificate_requests.return_value": [], "create_certificate_request": post_call, + "get_version.return_value": None, }, ), ): @@ -3177,6 +3225,7 @@ def test_given_tls_requirers_available_when_csrs_already_posted_then_duplicate_c CertificateRequestEntry(id=1, csr=str(csr), certificate_chain="") ], "post_csr": post_call, + "get_version.return_value": None, }, ), ): @@ -3247,6 +3296,7 @@ def test_given_tls_requirers_available_when_certificate_available_then_certs_pro id=1, csr=str(csr), certificate_chain=[str(cert), str(ca)] ) ], + "get_version.return_value": None, }, ), ): @@ -3331,6 +3381,7 @@ def test_given_tls_requirers_when_invalid_certificate_available_when_configure_t id=1, csr=str(csr), certificate_chain=[str(new_cert), str(ca)] ) ], + "get_version.return_value": None, }, ), ): @@ -3412,6 +3463,7 @@ def test_given_certificate_rejected_in_notary_when_configure_then_certificate_re "list_certificate_requests.return_value": [ CertificateRequestEntry(id=1, csr=str(csr), certificate_chain="rejected") ], + "get_version.return_value": None, }, ), ): @@ -3463,6 +3515,7 @@ def test_given_access_relation_created_when_configure_then_certificate_not_repla "is_initialized.return_value": True, "login.return_value": LoginResponse(token="example-token"), "token_is_valid.return_value": True, + "get_version.return_value": None, }, ), ): @@ -3520,6 +3573,7 @@ def test_given_new_certificate_available_when_configure_then_certificate_replace "is_initialized.return_value": True, "login.return_value": LoginResponse(token="example-token"), "token_is_valid.return_value": True, + "get_version.return_value": None, }, ), ): @@ -3575,6 +3629,7 @@ def test_given_new_certificate_available_and_new_cert_already_saved_when_configu "is_initialized.return_value": True, "login.return_value": LoginResponse(token="example-token"), "token_is_valid.return_value": True, + "get_version.return_value": None, }, ), ):