diff --git a/README.md b/README.md index cbef436..4952eb9 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,11 @@ A Charmed Operator for SD-Core's Unified Data Repository (UDR) component. juju deploy mongodb-k8s --trust --channel=5/edge juju deploy sdcore-nrf --trust --channel=edge juju deploy sdcore-udr --trust --channel=edge +juju deploy self-signed-certificates --channel=beta juju integrate mongodb-k8s sdcore-nrf juju integrate mongodb-k8s sdcore-udr:database +juju integrate sdcore-nrf:certificates self-signed-certificates:certificates juju integrate sdcore-nrf sdcore-udr:fiveg_nrf -``` - -## Optional - -```bash -juju deploy self-signed-certificates --channel=edge juju integrate sdcore-udr:certificates self-signed-certificates:certificates ``` diff --git a/src/charm.py b/src/charm.py index c02145d..ff8d40e 100755 --- a/src/charm.py +++ b/src/charm.py @@ -89,7 +89,7 @@ def _configure_udr(self, event: EventBase) -> None: Args: event: Juju event """ - for relation in ["database", "fiveg_nrf"]: + for relation in ["database", "fiveg_nrf", "certificates"]: if not self._relation_created(relation): self.unit.status = BlockedStatus( f"Waiting for the `{relation}` relation to be created" @@ -116,6 +116,10 @@ def _configure_udr(self, event: EventBase) -> None: self.unit.status = WaitingStatus("Waiting for pod IP address to be available") event.defer() return + if not self._certificate_is_stored(): + self.unit.status = WaitingStatus("Waiting for certificates to be stored") + event.defer() + return self._generate_udr_config_file() self._configure_udr_service() self.unit.status = ActiveStatus() @@ -143,7 +147,7 @@ def _on_certificates_relation_broken(self, event: EventBase) -> None: self._delete_private_key() self._delete_csr() self._delete_certificate() - self._configure_udr(event) + self.unit.status = BlockedStatus("Waiting for certificates relation") def _on_certificates_relation_joined(self, event: EventBase) -> None: """Generates CSR and requests new certificate.""" @@ -274,7 +278,7 @@ def _generate_udr_config_file(self) -> None: default_database_name=DEFAULT_DATABASE_NAME, default_database_url=self._get_database_data()["uris"].split(",")[0], nrf_url=self._nrf.nrf_url, - scheme="https" if self._certificate_is_stored() else "http", + scheme="https", ) if not self._config_file_content_matches(content=content): self._push_udr_config_file_to_workload(content=content) diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index 7f352e2..c000544 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -25,8 +25,8 @@ class TestUDROperatorCharm: async def setup(self, ops_test: OpsTest): await ops_test.model.set_config({"update-status-hook-interval": "5s"}) # type: ignore[union-attr] # noqa: E501 await self._deploy_mongodb(ops_test) - await self._deploy_sdcore_nrf_operator(ops_test) await self._deploy_tls_provider(ops_test) + await self._deploy_sdcore_nrf_operator(ops_test) @pytest.fixture(scope="module") @pytest.mark.abort_on_fail @@ -54,7 +54,7 @@ async def _deploy_tls_provider(ops_test: OpsTest): await ops_test.model.deploy( # type: ignore[union-attr] TLS_PROVIDER_CHARM_NAME, application_name=TLS_PROVIDER_CHARM_NAME, - channel="edge", + channel="beta", ) @staticmethod @@ -68,6 +68,9 @@ async def _deploy_sdcore_nrf_operator(ops_test: OpsTest): await ops_test.model.add_relation( # type: ignore[union-attr] relation1=DB_APPLICATION_NAME, relation2=NRF_APPLICATION_NAME ) + await ops_test.model.add_relation( # type: ignore[union-attr] + relation1=TLS_PROVIDER_CHARM_NAME, relation2=NRF_APPLICATION_NAME + ) @pytest.mark.abort_on_fail async def test_wait_for_blocked_status(self, ops_test: OpsTest, setup, build_and_deploy_charm): @@ -110,4 +113,27 @@ async def test_restore_nrf_and_wait_for_active_status( relation1=f"{NRF_APPLICATION_NAME}:database", relation2=DB_APPLICATION_NAME ) await ops_test.model.add_relation(relation1=APPLICATION_NAME, relation2=NRF_APPLICATION_NAME) # type: ignore[union-attr] # noqa: E501 + await ops_test.model.add_relation(relation1=TLS_PROVIDER_CHARM_NAME, relation2=NRF_APPLICATION_NAME) # type: ignore[union-attr] # noqa: E501 await ops_test.model.wait_for_idle(apps=[APPLICATION_NAME], status="active", timeout=300) # type: ignore[union-attr] # noqa: E501 + + @pytest.mark.abort_on_fail + async def test_remove_tls_and_wait_for_blocked_status( + self, ops_test: OpsTest, build_and_deploy_charm + ): + await ops_test.model.remove_application(TLS_PROVIDER_CHARM_NAME, block_until_done=True) # type: ignore[union-attr] # noqa: E501 + await ops_test.model.wait_for_idle(apps=[APPLICATION_NAME], status="blocked", timeout=60) # type: ignore[union-attr] # noqa: E501 + + @pytest.mark.abort_on_fail + async def test_restore_tls_and_wait_for_active_status( + self, ops_test: OpsTest, build_and_deploy_charm + ): + await ops_test.model.deploy( # type: ignore[union-attr] + TLS_PROVIDER_CHARM_NAME, + application_name=TLS_PROVIDER_CHARM_NAME, + channel="beta", + trust=True, + ) + await ops_test.model.add_relation( # type: ignore[union-attr] + relation1=APPLICATION_NAME, relation2=TLS_PROVIDER_CHARM_NAME + ) + await ops_test.model.wait_for_idle(apps=[APPLICATION_NAME], status="active", timeout=1000) # type: ignore[union-attr] # noqa: E501 diff --git a/tests/unit/resources/expected_udrcfg.yaml b/tests/unit/resources/expected_udrcfg.yaml index d02cae8..5582862 100644 --- a/tests/unit/resources/expected_udrcfg.yaml +++ b/tests/unit/resources/expected_udrcfg.yaml @@ -4,7 +4,7 @@ info: configuration: sbi: # Service-based interface information - scheme: http + scheme: https registerIPv4: 1.2.3.4 bindingIPv4: 0.0.0.0 port: 29504 diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index a4f983d..2e286a4 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -71,6 +71,17 @@ def test_given_fiveg_nrf_relation_not_created_when_pebble_ready_then_status_is_b BlockedStatus("Waiting for the `fiveg_nrf` relation to be created"), ) + def test_given_certificates_relation_not_created_when_pebble_ready_then_status_is_blocked( + self, + ): + self.harness.add_relation(relation_name="database", remote_app="some_db_app") + self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") + self.harness.container_pebble_ready("udr") + self.assertEqual( + self.harness.model.unit.status, + BlockedStatus("Waiting for the `certificates` relation to be created"), + ) + @patch("charm.check_output") @patch("ops.model.Container.exists") @patch("charms.sdcore_nrf.v0.fiveg_nrf.NRFRequires.nrf_url", new_callable=PropertyMock) @@ -100,6 +111,9 @@ def test_given_relations_created_but_database_not_available_when_pebble_ready_th ): self.harness.add_relation(relation_name="database", remote_app="some_db_app") self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") self.assertEqual( self.harness.model.unit.status, @@ -113,6 +127,9 @@ def test_give_database_info_not_available_when_pebble_ready_then_status_is_waiti patched_is_resource_created.return_value = True self.harness.add_relation(relation_name="database", remote_app="some_db_app") self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") self.assertEqual( self.harness.model.unit.status, @@ -126,6 +143,9 @@ def test_given_nrf_data_not_available_when_pebble_ready_then_status_is_waiting( patched_is_resource_created.return_value = True self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") self.assertEqual( self.harness.model.unit.status, WaitingStatus("Waiting for the NRF to be available") @@ -140,11 +160,39 @@ def test_given_relations_created_and_database_available_and_nrf_available_but_st patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") self.assertEqual( self.harness.model.unit.status, WaitingStatus("Waiting for the storage to be attached") ) + @patch("charm.check_output") + @patch("ops.Container.push") + @patch("charms.sdcore_nrf.v0.fiveg_nrf.NRFRequires.nrf_url", new_callable=PropertyMock) + @patch("charms.data_platform_libs.v0.data_interfaces.DatabaseRequires.is_resource_created") + def test_given_relations_created_and_database_available_and_nrf_available_but_certificate_not_stored_when_pebble_ready_then_then_status_is_waiting( # noqa: E501 + self, + patched_is_resource_created, + patched_nrf_url, + _, + patched_check_output, + ): + patched_check_output.return_value = "1.2.3.4".encode() + patched_is_resource_created.return_value = True + self.harness.charm._storage_is_attached = Mock(return_value=True) + patched_nrf_url.return_value = "http://nrf:8081" + self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") + self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) + self.harness.container_pebble_ready("udr") + self.assertEqual( + self.harness.model.unit.status, WaitingStatus("Waiting for certificates to be stored") + ) + @patch("ops.model.Container.push") @patch("charm.check_output") @patch("ops.model.Container.exists") @@ -158,12 +206,15 @@ def test_given_udr_operator_ready_to_be_configured_when_pebble_ready_then_config patched_check_output, patched_push, ): - patched_exists.side_effect = [True, False] + patched_exists.side_effect = [True, True, True, False] patched_check_output.return_value = "1.2.3.4".encode() patched_is_resource_created.return_value = True patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") @@ -190,13 +241,16 @@ def test_given_udr_config_is_different_from_the_newly_generated_config_when_pebb patched_pull, patched_push, ): - patched_exists.side_effect = [True, False] + patched_exists.side_effect = [True, True, True, False] patched_check_output.return_value = "1.2.3.4".encode() patched_pull.return_value = StringIO("Dummy content") patched_is_resource_created.return_value = True patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") @@ -259,6 +313,9 @@ def test_given_udr_service_already_configured_and_udr_config_is_different_from_t patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.set_can_connect(container="udr", val=True) self._container.add_layer("udr", TEST_PEBBLE_LAYER, combine=True) @@ -314,6 +371,9 @@ def test_given_udr_config_is_pushed_when_pebble_ready_then_udr_service_is_config patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") @@ -339,6 +399,9 @@ def test_given_udr_config_is_pushed_when_pebble_ready_then_udr_service_is_restar patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") patched_restart.assert_called_once_with("udr") @@ -359,6 +422,9 @@ def test_given_udr_config_is_pushed_when_pebble_ready_then_status_is_active( patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr") self.assertEqual( self.harness.model.unit.status, @@ -382,6 +448,9 @@ def test_given_ip_not_available_when_pebble_ready_then_status_is_waiting( patched_nrf_url.return_value = "http://nrf:8081" self.harness.add_relation(relation_name="fiveg_nrf", remote_app="some_nrf_app") self._database_is_available() + self.harness.add_relation( + relation_name="certificates", remote_app="tls-certificates-operator" + ) self.harness.container_pebble_ready("udr")