From 70f855a419b34bd8a78e3efdda1a7d5c536c1be0 Mon Sep 17 00:00:00 2001 From: Natasha Ho Date: Wed, 4 Jan 2023 18:03:42 -0500 Subject: [PATCH 1/3] rewrite event handlers --- src/charm.py | 154 +++++++++++++++++------------ templates/kratos.yaml.j2 | 9 +- tests/unit/conftest.py | 26 +++-- tests/unit/test_charm.py | 209 +++++++++++++++++++++++++++++---------- 4 files changed, 268 insertions(+), 130 deletions(-) diff --git a/src/charm.py b/src/charm.py index 52a284f7..a3ea3f52 100755 --- a/src/charm.py +++ b/src/charm.py @@ -8,7 +8,10 @@ import logging -from charms.data_platform_libs.v0.database_requires import DatabaseCreatedEvent, DatabaseRequires +from charms.data_platform_libs.v0.database_requires import ( + DatabaseEndpointsChangedEvent, + DatabaseRequires, +) from charms.observability_libs.v0.kubernetes_service_patch import KubernetesServicePatch from charms.traefik_k8s.v1.ingress import ( IngressPerAppReadyEvent, @@ -18,8 +21,8 @@ from jinja2 import Template from ops.charm import CharmBase from ops.main import main -from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus -from ops.pebble import ChangeError, ExecError, Layer +from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, ModelError, WaitingStatus +from ops.pebble import ExecError, Layer logger = logging.getLogger(__name__) KRATOS_ADMIN_PORT = 4434 @@ -62,7 +65,7 @@ def __init__(self, *args): ) self.framework.observe(self.on.kratos_pebble_ready, self._on_pebble_ready) - self.framework.observe(self.database.on.database_created, self._on_database_changed) + self.framework.observe(self.database.on.database_created, self._on_database_created) self.framework.observe(self.database.on.endpoints_changed, self._on_database_changed) self.framework.observe(self.admin_ingress.on.ready, self._on_admin_ingress_ready) self.framework.observe(self.admin_ingress.on.revoked, self._on_ingress_revoked) @@ -78,7 +81,7 @@ def _pebble_layer(self) -> Layer: self._container_name: { "override": "replace", "summary": "Kratos Operator layer", - "startup": "enabled", + "startup": "disabled", "command": f"kratos serve all --config {self._config_file_path}", } }, @@ -111,94 +114,115 @@ def _render_conf_file(self) -> None: ) return rendered - def _update_layer(self) -> None: - """Updates the Pebble configuration layer and kratos config if changed.""" - config = self._render_conf_file() - if not self._container.get_plan().to_dict(): - self.unit.status = MaintenanceStatus("Applying new pebble layer") - self._container.push(self._config_file_path, config, make_dirs=True) - with open("src/identity.default.schema.json", encoding="utf-8") as schema_file: - schema = schema_file.read() - self._container.push(self._identity_schema_file_path, schema, make_dirs=True) - self._container.add_layer(self._container_name, self._pebble_layer, combine=True) - logger.info("Pebble plan updated with new configuration, replanning") - self._container.replan() - else: - # Compare changes in kratos config - current_config = self._container.pull(self._config_file_path).read() - if current_config != config: - self.unit.status = MaintenanceStatus("Updating Kratos Config") - self._container.push(self._config_file_path, config, make_dirs=True) - logger.info("Updated kratos config") - self._container.restart(self._container_name) - def _get_database_relation_info(self) -> dict: """Get database info from relation data bag.""" relation_id = self.database.relations[0].id relation_data = self.database.fetch_relation_data()[relation_id] return { - "username": relation_data["username"], - "password": relation_data["password"], - "endpoints": relation_data["endpoints"], + "username": relation_data.get("username"), + "password": relation_data.get("password"), + "endpoints": relation_data.get("endpoints"), "database_name": self._db_name, } - def _update_container(self, event) -> None: - """Update configs, pebble layer and run database migration.""" + def _run_sql_migration(self) -> bool: + """Runs database migration. + + Returns True if migration was run successfully, else returns false. + """ + try: + process = self._container.exec( + ["kratos", "migrate", "sql", "-e", "--config", self._config_file_path, "--yes"], + ) + except ExecError as err: + logger.error(f"Exited with code {err.exit_code}. Stderr: {err.stderr}") + return False + + stdout, _ = process.wait_output() + logger.info(f"Successfully executed automigration: {stdout}") + return True + + def _on_pebble_ready(self, event) -> None: + """Event Handler for pebble ready event.""" if not self._container.can_connect(): event.defer() logger.info("Cannot connect to Kratos container. Deferring event.") self.unit.status = WaitingStatus("Waiting to connect to Kratos container") return - if not self.model.relations["pg-database"]: - logger.error("Missing required relation with postgresql") - self.model.unit.status = BlockedStatus("Missing required relation with postgresql") + self.unit.status = MaintenanceStatus("Configuring/deploying resources") + + with open("src/identity.default.schema.json", encoding="utf-8") as schema_file: + schema = schema_file.read() + self._container.push(self._identity_schema_file_path, schema, make_dirs=True) + + self._container.add_layer(self._container_name, self._pebble_layer, combine=True) + logger.info("Pebble plan updated with new configuration, replanning") + self._container.replan() + + # in case container was terminated unexpectedly + if self.database.is_database_created(): + self._container.push(self._config_file_path, self._render_conf_file(), make_dirs=True) + self._container.start(self._container_name) + self.unit.status = ActiveStatus() return - if not self.database.is_database_created(): - event.defer() - logger.info("Missing database details. Deferring event.") + if self.model.relations["pg-database"]: self.unit.status = WaitingStatus("Waiting for database creation") + else: + self.unit.status = BlockedStatus("Missing postgres database relation") + + def _on_database_created(self, event) -> None: + """Event Handler for database created event.""" + if not self._container.can_connect(): + event.defer() + logger.info("Cannot connect to Kratos container. Deferring event.") + self.unit.status = WaitingStatus("Waiting to connect to Kratos container") return + self.unit.status = MaintenanceStatus( + "Configuring container and resources for database connection" + ) + try: - self._update_layer() - except ChangeError as err: - logger.error(str(err)) - self.unit.status = BlockedStatus("Failed to replan") + self._container.get_service(self._container_name) + except (ModelError, RuntimeError): + event.defer() + self.unit.status = WaitingStatus("Waiting for Kratos service") + logger.info("Kratos service is absent. Deferring database created event.") return - if not self.unit.is_leader(): - return + logger.info("Updating Kratos config and restarting service") + self._container.push(self._config_file_path, self._render_conf_file(), make_dirs=True) - self._run_sql_migration() + if self.unit.is_leader() and not self._run_sql_migration(): + self.unit.status = BlockedStatus("Database migration failed.") + else: + self._container.start(self._container_name) + self.unit.status = ActiveStatus() - self.unit.status = ActiveStatus() + def _on_database_changed(self, event: DatabaseEndpointsChangedEvent) -> None: + """Event Handler for database changed event.""" + if not self._container.can_connect(): + event.defer() + logger.info("Cannot connect to Kratos container. Deferring event.") + self.unit.status = WaitingStatus("Waiting to connect to Kratos container") + return - def _run_sql_migration(self) -> None: - """Runs database migration.""" - process = self._container.exec( - ["kratos", "migrate", "sql", "-e", "--config", self._config_file_path, "--yes"], - timeout=20.0, - ) - try: - stdout, _ = process.wait_output() - logger.info(f"Successfully executed automigration: {stdout}") - except ExecError as err: - logger.error(f"Exited with code {err.exit_code}. Stderr: {err.stderr}") - self.unit.status = BlockedStatus("Database migration job failed") + self.unit.status = MaintenanceStatus("Updating database details") - def _on_pebble_ready(self, event) -> None: - """Event Handler for pebble ready event.""" - self.unit.status = MaintenanceStatus("Configuring/deploying resources") - self._update_container(event) + try: + self._container.get_service(self._container_name) + except (ModelError, RuntimeError): + event.defer() + self.unit.status = WaitingStatus("Waiting for Kratos service") + logger.info("Kratos service is absent. Deferring database created event.") + return - def _on_database_changed(self, event: DatabaseCreatedEvent) -> None: - """Event Handler for database created event.""" - self.unit.status = MaintenanceStatus("Retrieving database details") - self._update_container(event) + self._container.push(self._config_file_path, self._render_conf_file(), make_dirs=True) + self._container.restart(self._container_name) + self.unit.status = ActiveStatus() def _on_admin_ingress_ready(self, event: IngressPerAppReadyEvent) -> None: if self.unit.is_leader(): diff --git a/templates/kratos.yaml.j2 b/templates/kratos.yaml.j2 index ecb4f63d..013669ef 100644 --- a/templates/kratos.yaml.j2 +++ b/templates/kratos.yaml.j2 @@ -3,15 +3,16 @@ log: identity: default_schema_id: default schemas: - - id: default - url: "file://{{ identity_schema_file_path }}" + - id: default + url: 'file://{{ identity_schema_file_path }}' selfservice: - default_browser_return_url: {{ default_browser_return_url | d("http://127.0.0.1:9999/")}} + default_browser_return_url: + {{ default_browser_return_url | d("http://127.0.0.1:9999/") }} flows: registration: enabled: True ui_url: {{ registration_ui_url }} -dsn: postgres://{{ db_info['username'] }}:{{ db_info['password'] }}@{{ db_info['endpoints'] }}/{{ db_info['database_name'] }} +dsn: postgres://{{ db_info.get('username') }}:{{ db_info.get('password') }}@{{ db_info.get('endpoints') }}/{{ db_info.get('database_name') }} courier: smtp: connection_uri: {{ smtp_connection_uri }} \ No newline at end of file diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 9c11de6d..2fa082d3 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -2,16 +2,19 @@ # See LICENSE file for licensing details. import pytest +from ops.pebble import ExecError from ops.testing import Harness from charm import KratosCharm @pytest.fixture() -def harness() -> None: +def harness(mocked_kubernetes_service_patcher) -> None: harness = Harness(KratosCharm) harness.set_model_name("kratos-model") + harness.set_can_connect("kratos", True) harness.set_leader(True) + harness.begin() return harness @@ -29,13 +32,22 @@ def mocked_fqdn(mocker): return mocked_fqdn +def mocked_pebble_exec(mocker): + mocked_pebble_exec = mocker.patch("ops.model.Container.exec") + yield mocked_pebble_exec + + @pytest.fixture() -def mocked_sql_migration(mocker): - mocked_sql_migration = mocker.patch("charm.KratosCharm._run_sql_migration") - yield mocked_sql_migration +def mocked_pebble_exec_success(mocker, mocked_pebble_exec): + mocked_process = mocker.patch("ops.pebble.ExecProcess") + mocked_process.wait_output.return_value = ("Success", None) + mocked_pebble_exec.return_value = mocked_process + yield mocked_pebble_exec @pytest.fixture() -def mocked_update_container(mocker): - mocked_update_container = mocker.patch("charm.KratosCharm._update_container") - yield mocked_update_container +def mocked_pebble_exec_failed(mocked_pebble_exec): + mocked_pebble_exec.side_effect = ExecError( + exit_code=400, stderr="Failed to execute", stdout="Failed", command="test command" + ) + yield diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 5576ed44..d929cad4 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -9,6 +9,7 @@ DB_USERNAME = "fake_relation_id_1" DB_PASSWORD = "fake-password" DB_ENDPOINTS = "postgresql-k8s-primary.namespace.svc.cluster.local:5432" +MODEL_NAME = "kratos-model" def setup_postgres_relation(harness): @@ -37,13 +38,71 @@ def setup_ingress_relation(harness, type): return relation_id -def test_update_container_correct_config( - harness, mocked_kubernetes_service_patcher, mocked_sql_migration -) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) +def trigger_database_changed(harness) -> None: + db_relation_id = harness.add_relation("pg-database", "postgresql-k8s") + harness.add_relation_unit(db_relation_id, "postgresql-k8s/0") + harness.update_relation_data( + db_relation_id, + "postgresql-k8s", + { + "data": '{"database": "database", "extra-user-roles": "SUPERUSER"}', + "endpoints": DB_ENDPOINTS, + }, + ) + + +def test_on_pebble_ready_cannot_connect_container(harness) -> None: + harness.set_can_connect(CONTAINER_NAME, False) + + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + assert isinstance(harness.model.unit.status, WaitingStatus) + + +def test_on_pebble_ready_correct_plan(harness) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + expected_plan = { + "services": { + CONTAINER_NAME: { + "override": "replace", + "summary": "Kratos Operator layer", + "startup": "disabled", + "command": "kratos serve all --config /etc/config/kratos.yaml", + } + } + } + updated_plan = harness.get_container_pebble_plan(CONTAINER_NAME).to_dict() + assert expected_plan == updated_plan + + +def test_on_pebble_ready_service_not_started_when_database_not_created(harness) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + service = harness.model.unit.get_container("kratos").get_service("kratos") + assert not service.is_running() + + +def test_on_pebble_ready_service_started_when_database_is_created(harness) -> None: setup_postgres_relation(harness) + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + service = harness.model.unit.get_container("kratos").get_service("kratos") + assert service.is_running() + assert harness.model.unit.status == ActiveStatus() + + +def test_on_pebble_ready_has_correct_config_when_database_is_created(harness) -> None: + setup_postgres_relation(harness) + + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + expected_config = { "log": {"level": "trace"}, "identity": { @@ -61,98 +120,140 @@ def test_update_container_correct_config( } }, }, - "dsn": f"postgres://{DB_USERNAME}:{DB_PASSWORD}@{DB_ENDPOINTS}/{harness.charm._db_name}", + "dsn": f"postgres://{DB_USERNAME}:{DB_PASSWORD}@{DB_ENDPOINTS}/{MODEL_NAME}_{harness.charm.app.name}", "courier": { "smtp": {"connection_uri": "smtps://test:test@mailslurper:1025/?skip_ssl_verify=true"} }, } - mocked_sql_migration.assert_called_once() assert yaml.safe_load(harness.charm._render_conf_file()) == expected_config -def test_update_container_correct_pebble_layer( - harness, mocked_kubernetes_service_patcher, mocked_sql_migration -) -> None: +def test_on_pebble_ready_when_missing_database_relation(harness) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + assert isinstance(harness.model.unit.status, BlockedStatus) + + +def test_on_pebble_ready_when_database_not_created_yet(harness) -> None: + trigger_database_changed(harness) + + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + + assert isinstance(harness.model.unit.status, WaitingStatus) + + +def test_on_database_created_cannot_connect_container(harness) -> None: + harness.set_can_connect(CONTAINER_NAME, False) - harness.set_can_connect(CONTAINER_NAME, True) setup_postgres_relation(harness) - initial_plan = harness.get_container_pebble_plan(CONTAINER_NAME) - assert initial_plan.to_yaml() == "{}\n" + assert isinstance(harness.charm.unit.status, WaitingStatus) - harness.begin_with_initial_hooks() - expected_plan = { - "services": { - CONTAINER_NAME: { - "override": "replace", - "summary": "Kratos Operator layer", - "startup": "enabled", - "command": "kratos serve all --config /etc/config/kratos.yaml", - } - } - } - updated_plan = harness.get_container_pebble_plan(CONTAINER_NAME).to_dict() - assert expected_plan == updated_plan +def test_on_database_created_when_pebble_is_not_ready(harness, mocked_pebble_exec_success) -> None: + setup_postgres_relation(harness) + + assert isinstance(harness.charm.unit.status, WaitingStatus) + mocked_pebble_exec_success.assert_not_called() + + +def test_on_database_created_updated_config_and_start_service_when_pebble_is_ready_in_leader_unit( + harness, mocked_pebble_exec_success +) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + setup_postgres_relation(harness) service = harness.model.unit.get_container("kratos").get_service("kratos") assert service.is_running() - assert harness.model.unit.status == ActiveStatus() + assert isinstance(harness.charm.unit.status, ActiveStatus) + updated_config = yaml.safe_load(harness.charm._render_conf_file()) + assert DB_ENDPOINTS in updated_config["dsn"] + assert DB_PASSWORD in updated_config["dsn"] + assert DB_USERNAME in updated_config["dsn"] -def test_cannot_connect_container(harness, mocked_kubernetes_service_patcher) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, False) +def test_on_database_created_updated_config_and_start_service_when_pebble_is_ready_in_non_leader_unit( + harness, +) -> None: + harness.set_leader(False) container = harness.model.unit.get_container(CONTAINER_NAME) harness.charm.on.kratos_pebble_ready.emit(container) + setup_postgres_relation(harness) - assert isinstance(harness.charm.unit.status, WaitingStatus) + service = harness.model.unit.get_container("kratos").get_service("kratos") + assert service.is_running() + assert isinstance(harness.charm.unit.status, ActiveStatus) + updated_config = yaml.safe_load(harness.charm._render_conf_file()) + assert DB_ENDPOINTS in updated_config["dsn"] + assert DB_PASSWORD in updated_config["dsn"] + assert DB_USERNAME in updated_config["dsn"] -def test_missing_database_relation(harness, mocked_kubernetes_service_patcher) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) +def test_on_database_created_not_run_migration_in_non_leader_unit( + harness, mocked_pebble_exec +) -> None: + harness.set_leader(False) container = harness.model.unit.get_container(CONTAINER_NAME) harness.charm.on.kratos_pebble_ready.emit(container) + setup_postgres_relation(harness) - assert isinstance(harness.charm.unit.status, BlockedStatus) + mocked_pebble_exec.assert_not_called() -def test_database_not_created(harness, mocked_kubernetes_service_patcher) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) +def test_on_database_created_when_migration_is_successful( + harness, mocked_pebble_exec_success +) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) + setup_postgres_relation(harness) - db_relation_id = harness.add_relation("pg-database", "postgresql-k8s") - harness.add_relation_unit(db_relation_id, "postgresql-k8s/0") + service = harness.model.unit.get_container("kratos").get_service("kratos") + assert service.is_running() + assert isinstance(harness.charm.unit.status, ActiveStatus) + mocked_pebble_exec_success.assert_called_once() + + updated_config = yaml.safe_load(harness.charm._render_conf_file()) + assert DB_ENDPOINTS in updated_config["dsn"] + assert DB_PASSWORD in updated_config["dsn"] + assert DB_USERNAME in updated_config["dsn"] + +def test_on_database_created_when_migration_failed(harness, mocked_pebble_exec_failed) -> None: container = harness.model.unit.get_container(CONTAINER_NAME) harness.charm.on.kratos_pebble_ready.emit(container) + setup_postgres_relation(harness) + + assert isinstance(harness.charm.unit.status, BlockedStatus) + + +def test_on_database_changed_cannot_connect_container(harness) -> None: + harness.set_can_connect(CONTAINER_NAME, False) + trigger_database_changed(harness) assert isinstance(harness.charm.unit.status, WaitingStatus) -def test_on_pebble_ready( - harness, mocked_kubernetes_service_patcher, mocked_update_container -) -> None: +def test_on_database_changed_when_pebble_is_not_ready(harness) -> None: + trigger_database_changed(harness) - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) - setup_postgres_relation(harness) + assert isinstance(harness.charm.unit.status, WaitingStatus) - mocked_update_container.assert_called_once() +def test_on_database_changed_when_pebble_is_ready(harness, mocked_pebble_exec_success) -> None: + container = harness.model.unit.get_container(CONTAINER_NAME) + harness.charm.on.kratos_pebble_ready.emit(container) -def test_on_database_created( - harness, mocked_kubernetes_service_patcher, mocked_update_container -) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) - setup_postgres_relation(harness) + trigger_database_changed(harness) - mocked_update_container.assert_called_once() + updated_config = yaml.safe_load(harness.charm._render_conf_file()) + assert DB_ENDPOINTS in updated_config["dsn"] + assert isinstance(harness.charm.unit.status, ActiveStatus) @pytest.mark.parametrize("api_type,port", [("admin", "4434"), ("public", "4433")]) From dfe3a73705841d43aaab5bab41a3727056764e4a Mon Sep 17 00:00:00 2001 From: Natasha Ho Date: Thu, 5 Jan 2023 10:51:46 -0500 Subject: [PATCH 2/3] per comment update --- src/charm.py | 7 ++++--- tests/unit/test_charm.py | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/charm.py b/src/charm.py index a3ea3f52..60202e36 100755 --- a/src/charm.py +++ b/src/charm.py @@ -131,16 +131,17 @@ def _run_sql_migration(self) -> bool: Returns True if migration was run successfully, else returns false. """ - try: process = self._container.exec( ["kratos", "migrate", "sql", "-e", "--config", self._config_file_path, "--yes"], ) + try: + stdout, _ = process.wait_output() + logger.info(f"Successfully executed automigration: {stdout}") except ExecError as err: logger.error(f"Exited with code {err.exit_code}. Stderr: {err.stderr}") + self.unit.status = BlockedStatus("Database migration job failed") return False - stdout, _ = process.wait_output() - logger.info(f"Successfully executed automigration: {stdout}") return True def _on_pebble_ready(self, event) -> None: diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index d929cad4..8e4236de 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -9,7 +9,6 @@ DB_USERNAME = "fake_relation_id_1" DB_PASSWORD = "fake-password" DB_ENDPOINTS = "postgresql-k8s-primary.namespace.svc.cluster.local:5432" -MODEL_NAME = "kratos-model" def setup_postgres_relation(harness): @@ -120,7 +119,7 @@ def test_on_pebble_ready_has_correct_config_when_database_is_created(harness) -> } }, }, - "dsn": f"postgres://{DB_USERNAME}:{DB_PASSWORD}@{DB_ENDPOINTS}/{MODEL_NAME}_{harness.charm.app.name}", + "dsn": f"postgres://{DB_USERNAME}:{DB_PASSWORD}@{DB_ENDPOINTS}/{harness.model.name}_{harness.charm.app.name}", "courier": { "smtp": {"connection_uri": "smtps://test:test@mailslurper:1025/?skip_ssl_verify=true"} }, From 1e4eb486f699e7ff63e2e0bd510f597fb495338a Mon Sep 17 00:00:00 2001 From: Natasha Ho Date: Thu, 5 Jan 2023 11:00:59 -0500 Subject: [PATCH 3/3] resolve rebase errors --- src/charm.py | 2 +- tests/unit/conftest.py | 1 + tests/unit/test_charm.py | 7 +------ 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/charm.py b/src/charm.py index 60202e36..c506cc62 100755 --- a/src/charm.py +++ b/src/charm.py @@ -131,10 +131,10 @@ def _run_sql_migration(self) -> bool: Returns True if migration was run successfully, else returns false. """ + try: process = self._container.exec( ["kratos", "migrate", "sql", "-e", "--config", self._config_file_path, "--yes"], ) - try: stdout, _ = process.wait_output() logger.info(f"Successfully executed automigration: {stdout}") except ExecError as err: diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 2fa082d3..0a7fdb65 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -32,6 +32,7 @@ def mocked_fqdn(mocker): return mocked_fqdn +@pytest.fixture() def mocked_pebble_exec(mocker): mocked_pebble_exec = mocker.patch("ops.model.Container.exec") yield mocked_pebble_exec diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 8e4236de..6218eb4f 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -256,12 +256,7 @@ def test_on_database_changed_when_pebble_is_ready(harness, mocked_pebble_exec_su @pytest.mark.parametrize("api_type,port", [("admin", "4434"), ("public", "4433")]) -def test_ingress_relation_created( - harness, mocked_kubernetes_service_patcher, mocked_fqdn, api_type, port -) -> None: - harness.begin() - harness.set_can_connect(CONTAINER_NAME, True) - +def test_ingress_relation_created(harness, mocked_fqdn, api_type, port) -> None: relation_id = setup_ingress_relation(harness, api_type) app_data = harness.get_relation_data(relation_id, harness.charm.app)