diff --git a/src/charm.py b/src/charm.py index 593dd9c..e1eca98 100755 --- a/src/charm.py +++ b/src/charm.py @@ -113,6 +113,7 @@ def __init__(self, *args): ], ) self.framework.observe(self.on.database_relation_joined, self._configure_nrf) + self.framework.observe(self.on.database_relation_broken, self._on_database_relation_broken) self.framework.observe(self.on.nrf_pebble_ready, self._configure_nrf) self.framework.observe(self._database.on.database_created, self._configure_nrf) self.framework.observe( @@ -226,6 +227,14 @@ def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None: return self._request_new_certificate() + def _on_database_relation_broken(self, event: EventBase) -> None: + """Event handler for database relation broken. + + Args: + event: Juju event + """ + self.unit.status = BlockedStatus("Waiting for database relation") + def _generate_private_key(self) -> None: """Generates and stores private key.""" private_key = generate_private_key() diff --git a/tests/integration/test_integration.py b/tests/integration/test_integration.py index d2e72d1..0b8b058 100644 --- a/tests/integration/test_integration.py +++ b/tests/integration/test_integration.py @@ -17,6 +17,7 @@ METADATA = yaml.safe_load(Path("./metadata.yaml").read_text()) APP_NAME = METADATA["name"] DB_APPLICATION_NAME = "mongodb" +DB_CHARM_NAME = "mongodb-k8s" TLS_APPLICATION_NAME = "self-signed-certificates" @@ -24,7 +25,7 @@ @pytest.mark.abort_on_fail async def deploy_mongodb(ops_test): await ops_test.model.deploy( - "mongodb-k8s", application_name=DB_APPLICATION_NAME, channel="5/edge", trust=True + DB_CHARM_NAME, application_name=DB_APPLICATION_NAME, channel="5/edge", trust=True ) @@ -98,6 +99,32 @@ async def test_restore_tls_and_wait_for_active_status(ops_test: OpsTest, build_a await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) +@pytest.mark.skip( + reason="Bug in MongoDB: https://github.com/canonical/mongodb-k8s-operator/issues/218" +) +@pytest.mark.abort_on_fail +async def test_remove_database_and_wait_for_blocked_status(ops_test: OpsTest, build_and_deploy): + assert ops_test.model + await ops_test.model.remove_application(DB_APPLICATION_NAME, block_until_done=True) + await ops_test.model.wait_for_idle(apps=[APP_NAME], status="blocked", timeout=60) + + +@pytest.mark.skip( + reason="Bug in MongoDB: https://github.com/canonical/mongodb-k8s-operator/issues/218" +) +@pytest.mark.abort_on_fail +async def test_restore_database_and_wait_for_active_status(ops_test: OpsTest, build_and_deploy): + assert ops_test.model + await ops_test.model.deploy( + DB_CHARM_NAME, + application_name=DB_APPLICATION_NAME, + channel="5/edge", + trust=True, + ) + await ops_test.model.integrate(relation1=APP_NAME, relation2=DB_APPLICATION_NAME) + await ops_test.model.wait_for_idle(apps=[APP_NAME], status="active", timeout=1000) + + @pytest.mark.abort_on_fail async def test_when_scale_nrf_beyond_1_then_only_one_unit_is_active( ops_test: OpsTest, build_and_deploy diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 08dfac8..02574b3 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -44,8 +44,12 @@ def _create_database_relation(self) -> int: ) return relation_id - def _database_is_available(self) -> None: - """Create a database relation and set the database information.""" + def _create_database_relation_and_populate_data(self) -> int: + """Create a database relation and set the database information. + + Returns: + relation_id: ID of the created relation + """ database_relation_id = self._create_database_relation() self.harness.update_relation_data( relation_id=database_relation_id, @@ -56,6 +60,7 @@ def _database_is_available(self) -> None: "uris": "http://dummy", }, ) + return database_relation_id @staticmethod def _read_file(path: str) -> str: @@ -90,6 +95,33 @@ def test_given_certificates_relation_not_created_when_pebble_ready_then_status_i BlockedStatus(f"Waiting for {TLS_RELATION_NAME} relation to be created"), ) + @patch("ops.model.Container.pull") + @patch("ops.model.Container.exists") + @patch("ops.model.Container.push") + @patch("charm.check_output") + def test_given_nrf_charm_in_active_state_when_database_relation_breaks_then_status_is_blocked( + self, + patch_check_output, + patch_push, + patch_exists, + patch_pull, + ): + patch_check_output.return_value = b"1.1.1.1" + patch_pull.return_value = StringIO( + self._read_file("tests/unit/expected_config/config.conf").strip() + ) + patch_exists.return_value = True + database_relation_id = self._create_database_relation_and_populate_data() + self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) + self.harness.container_pebble_ready(container_name="nrf") + + self.harness.remove_relation(database_relation_id) + + self.assertEqual( + self.harness.model.unit.status, + BlockedStatus("Waiting for database relation"), + ) + def test_given_database_not_available_when_pebble_ready_then_status_is_waiting( self, ): @@ -118,7 +150,7 @@ def test_given_database_information_not_available_when_pebble_ready_then_status_ def test_given_storage_not_attached_when_pebble_ready_then_status_is_waiting( self, ): - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready(container_name="nrf") self.assertEqual( @@ -142,7 +174,7 @@ def test_given_certificates_not_stored_when_pebble_ready_then_status_is_waiting( patch_check_output.return_value = b"1.1.1.1" patch_exists.side_effect = [True, False, True, False] self.harness.set_can_connect(container="nrf", val=True) - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready("nrf") self.assertEqual( @@ -173,7 +205,7 @@ def test_given_database_info_and_storage_attached_and_certs_stored_when_pebble_r event.certificate_signing_request = csr patch_pull.side_effect = [StringIO(csr), StringIO("Dummy Content")] patch_exists.side_effect = [True, True, False, False] - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.charm._on_certificate_available(event=event) self.harness.container_pebble_ready(container_name="nrf") @@ -200,7 +232,7 @@ def test_given_content_of_config_file_not_changed_when_pebble_ready_then_config_ StringIO(self._read_file("tests/unit/expected_config/config.conf").strip()), ] patch_exists.side_effect = [True, False, True] - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.container_pebble_ready(container_name="nrf") patch_push.assert_not_called() @@ -221,7 +253,7 @@ def test_given_config_pushed_when_pebble_ready_then_pebble_plan_is_applied( ) patch_exists.return_value = True - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready(container_name="nrf") @@ -264,7 +296,7 @@ def test_given_database_relation_is_created_and_config_file_is_written_when_pebb ) patch_exists.return_value = True - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready("nrf") @@ -288,7 +320,7 @@ def test_given_ip_not_available_when_pebble_ready_then_status_is_waiting( ) patch_exists.return_value = True - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready(container_name="nrf") @@ -312,7 +344,7 @@ def test_given_https_nrf_url_and_service_is_running_when_fiveg_nrf_relation_join self._read_file("tests/unit/expected_config/config.conf").strip() ) - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.set_can_connect(container="nrf", val=True) @@ -360,7 +392,7 @@ def test_service_starts_running_after_nrf_relation_joined_when_fiveg_pebble_read relation_id=relation_2_id, remote_unit_name="nrf-requirer-2/0" ) - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.container_pebble_ready("nrf") @@ -412,7 +444,7 @@ def test_given_certificates_are_stored_when_on_certificates_relation_broken_then ): patch_exists.return_value = True self.harness.set_can_connect(container="nrf", val=True) - self._database_is_available() + self._create_database_relation_and_populate_data() self.harness.add_relation(relation_name=TLS_RELATION_NAME, remote_app=TLS_APPLICATION_NAME) self.harness.charm._on_certificates_relation_broken(event=Mock()) self.assertEqual(