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

fix: go into blocked status when database relation is removed #49

Merged
merged 2 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
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
9 changes: 9 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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()
Expand Down
29 changes: 28 additions & 1 deletion tests/integration/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
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"


@pytest.fixture(scope="module")
@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
)


Expand Down Expand Up @@ -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
Expand Down
56 changes: 44 additions & 12 deletions tests/unit/test_charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -56,6 +60,7 @@ def _database_is_available(self) -> None:
"uris": "http://dummy",
},
)
return database_relation_id

@staticmethod
def _read_file(path: str) -> str:
Expand Down Expand Up @@ -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,
):
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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")
Expand All @@ -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()

Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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)
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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(
Expand Down
Loading