diff --git a/lib/charms/mongodb/v1/mongodb_provider.py b/lib/charms/mongodb/v1/mongodb_provider.py index 034e246c7..2a1034a3c 100644 --- a/lib/charms/mongodb/v1/mongodb_provider.py +++ b/lib/charms/mongodb/v1/mongodb_provider.py @@ -76,8 +76,8 @@ def __init__(self, charm: CharmBase, substrate="k8s", relation_name: str = REL_N self.database_provides.on.database_requested, self._on_relation_event ) - def pass_hook_checks(self, event: EventBase) -> bool: - """Runs the pre-hooks checks for MongoDBProvider, returns True if all pass.""" + def pass_sanity_hook_checks(self) -> bool: + """Runs reusable and event agnostic checks.""" # We shouldn't try to create or update users if the database is not # initialised. We will create users as part of initialisation. if not self.charm.db_initialised: @@ -92,6 +92,13 @@ def pass_hook_checks(self, event: EventBase) -> bool: if not self.charm.unit.is_leader(): return False + return True + + def pass_hook_checks(self, event: EventBase) -> bool: + """Runs the pre-hooks checks for MongoDBProvider, returns True if all pass.""" + if not self.pass_sanity_hook_checks(): + return False + if self.charm.upgrade_in_progress: logger.warning( "Adding relations is not supported during an upgrade. The charm may be in a broken, unrecoverable state." @@ -173,7 +180,7 @@ def oversee_users(self, departed_relation_id: Optional[int], event): mongo.drop_user(username) for username in relation_users - database_users: - config = self._get_config(username, None) + config = self._get_config(username, None, event) if config.database is None: # We need to wait for the moment when the provider library # set the database name into the relation. @@ -240,7 +247,7 @@ def _diff(self, event: RelationChangedEvent) -> Diff: def update_app_relation_data(self) -> None: """Helper function to update application relation data.""" - if not self.charm.db_initialised: + if not self.pass_sanity_hook_checks(): return database_users = set() @@ -282,7 +289,9 @@ def _get_or_set_password(self, relation: Relation) -> str: self.database_provides.update_relation_data(relation.id, {"password": password}) return password - def _get_config(self, username: str, password: Optional[str]) -> MongoConfiguration: + def _get_config( + self, username: str, password: Optional[str], event=None + ) -> MongoConfiguration: """Construct the config object for future user creation.""" relation = self._get_relation_from_username(username) if not password: @@ -299,6 +308,7 @@ def _get_config(self, username: str, password: Optional[str]) -> MongoConfigurat "tls_external": False, "tls_internal": False, } + if self.charm.is_role(Config.Role.MONGOS): mongo_args["port"] = Config.MONGOS_PORT if self.substrate == Config.Substrate.K8S: diff --git a/poetry.lock b/poetry.lock index 78c88b144..0a4bd8d64 100644 --- a/poetry.lock +++ b/poetry.lock @@ -186,13 +186,13 @@ files = [ [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -635,13 +635,13 @@ test = ["pytest (>=6)"] [[package]] name = "executing" -version = "2.0.1" +version = "2.1.0" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, + {file = "executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf"}, + {file = "executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab"}, ] [package.extras] @@ -789,13 +789,13 @@ tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < [[package]] name = "ipython" -version = "8.26.0" +version = "8.27.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.26.0-py3-none-any.whl", hash = "sha256:e6b347c27bdf9c32ee9d31ae85defc525755a1869f14057e900675b9e8d6e6ff"}, - {file = "ipython-8.26.0.tar.gz", hash = "sha256:1cec0fbba8404af13facebe83d04436a7434c7400e59f47acf467c64abd0956c"}, + {file = "ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c"}, + {file = "ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e"}, ] [package.dependencies] @@ -912,12 +912,12 @@ referencing = ">=0.31.0" [[package]] name = "juju" -version = "3.5.2.0" +version = "3.5.0.0" description = "Python library for Juju" optional = false python-versions = "*" files = [ - {file = "juju-3.5.2.0.tar.gz", hash = "sha256:dd9a36330e63acd8f62bf478fd7e385e51f44dc3918e7a67d0593fd054e1e80a"}, + {file = "juju-3.5.0.0.tar.gz", hash = "sha256:c69fbe63cb12991690787ce3d70812390bf3ca62b6c5e9ef15df00c1f03dd7e6"}, ] [package.dependencies] @@ -2036,13 +2036,13 @@ pyasn1 = ">=0.1.3" [[package]] name = "setuptools" -version = "74.0.0" +version = "74.1.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-74.0.0-py3-none-any.whl", hash = "sha256:0274581a0037b638b9fc1c6883cc71c0210865aaa76073f7882376b641b84e8f"}, - {file = "setuptools-74.0.0.tar.gz", hash = "sha256:a85e96b8be2b906f3e3e789adec6a9323abf79758ecfa3065bd740d81158b11e"}, + {file = "setuptools-74.1.1-py3-none-any.whl", hash = "sha256:fc91b5f89e392ef5b77fe143b17e32f65d3024744fba66dc3afe07201684d766"}, + {file = "setuptools-74.1.1.tar.gz", hash = "sha256:2353af060c06388be1cecbf5953dcdb1f38362f87a2356c480b6b4d5fcfc8847"}, ] [package.extras] @@ -2329,4 +2329,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10.12" -content-hash = "4d5975fea6997229e3ef84443ee586e32d5b80d56c9fcd5e47b37f11e0041bd5" +content-hash = "86d549e73f3cc46eb5bdb1419ec3b93b00c56ad24e092f2092aff47989191737" diff --git a/pyproject.toml b/pyproject.toml index 5db812d4e..b5640ee1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,8 @@ coverage = {extras = ["toml"], version = "^7.5.0"} pytest = "^8.1.1" parameterized = "^0.9.0" pymongo = "^4.7.3" -juju = "~3.5.0" +# should not be updated unless https://github.com/juju/python-libjuju/issues/1093 is fixed +juju = "==3.5.0" [tool.poetry.group.integration.dependencies] allure-pytest = "^2.13.5" @@ -63,7 +64,8 @@ ops = "^2.15.0" tenacity = "^8.2.3" pymongo = "^4.7.3" parameterized = "^0.9.0" -juju = "~3.5.0" +# should not be updated unless https://github.com/juju/python-libjuju/issues/1093 is fixed +juju = "==3.5.0" pytest = "^8.1.1" pytest-asyncio = "^0.21.1" pytest-mock = "^3.14.0" diff --git a/src/charm.py b/src/charm.py index e969b432f..1ec71c7f7 100755 --- a/src/charm.py +++ b/src/charm.py @@ -321,7 +321,8 @@ def peers_units(self) -> list[Unit]: @property def db_initialised(self) -> bool: """Check if MongoDB is initialised.""" - return "db_initialised" in self.app_peer_data + # Needs to lowercase it so it also work with older versions + return json.loads(self.app_peer_data.get("db_initialised", "false").lower()) @property def role(self) -> str: @@ -352,7 +353,7 @@ def is_role(self, role_name: str) -> bool: def db_initialised(self, value): """Set the db_initialised flag.""" if isinstance(value, bool): - self.app_peer_data["db_initialised"] = str(value) + self.app_peer_data["db_initialised"] = json.dumps(value) else: raise ValueError( f"'db_initialised' must be a boolean value. Proivded: {value} is of type {type(value)}" diff --git a/tests/integration/sharding_tests/test_sharding_relations.py b/tests/integration/sharding_tests/test_sharding_relations.py index 18b544c41..ab379f4fb 100644 --- a/tests/integration/sharding_tests/test_sharding_relations.py +++ b/tests/integration/sharding_tests/test_sharding_relations.py @@ -55,7 +55,6 @@ async def test_build_and_deploy( await ops_test.model.deploy( MONGOS_APP_NAME, channel="6/edge", - revision=3, ) await ops_test.model.deploy(S3_APP_NAME, channel="edge") diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py index 2fcaf95db..d2c5f0aca 100644 --- a/tests/unit/test_charm.py +++ b/tests/unit/test_charm.py @@ -208,8 +208,13 @@ def test_mongodb_relation_joined_non_leader_does_nothing(self, connection): @patch_network_get(private_address="1.1.1.1") @patch("charm.MongoDBConnection") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm._connect_mongodb_exporter") - def test_mongodb_relation_joined_all_replicas_not_ready(self, _, connection): + def test_mongodb_relation_joined_all_replicas_not_ready( + self, _, rev, local, is_local, connection + ): """Tests that we go into waiting when current ReplicaSet hosts are not ready. Tests the scenario that if current replica set hosts are not ready, the leader goes into @@ -217,7 +222,7 @@ def test_mongodb_relation_joined_all_replicas_not_ready(self, _, connection): """ # preset values self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.is_ready = False connection.return_value.__enter__.return_value.get_replset_members.return_value = { "1.1.1.1" @@ -236,8 +241,13 @@ def test_mongodb_relation_joined_all_replicas_not_ready(self, _, connection): @patch("ops.framework.EventBase.defer") @patch("charm.MongoDBConnection") @patch("charms.mongodb.v0.mongo.MongoClient") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm._connect_mongodb_exporter") - def test_relation_joined_get_members_failure(self, _, client, connection, defer): + def test_relation_joined_get_members_failure( + self, _, rev, local, is_local, client, connection, defer + ): """Tests reconfigure does not execute when unable to get the replica set members. Verifies in case of relation_joined and relation departed, that when the the database @@ -246,7 +256,7 @@ def test_relation_joined_get_members_failure(self, _, client, connection, defer) """ # presets self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" rel = self.harness.charm.model.get_relation("database-peers") for exception in PYMONGO_EXCEPTIONS: @@ -271,8 +281,11 @@ def test_relation_joined_get_members_failure(self, _, client, connection, defer) @patch_network_get(private_address="1.1.1.1") @patch("ops.framework.EventBase.defer") @patch("charm.MongoDBConnection") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm._connect_mongodb_exporter") - def test_reconfigure_add_member_failure(self, _, connection, defer): + def test_reconfigure_add_member_failure(self, _, rev, local, is_local, connection, defer): """Tests reconfigure does not proceed when unable to add a member. Verifies in relation joined events, that when the database cannot add a member that the @@ -280,7 +293,7 @@ def test_reconfigure_add_member_failure(self, _, connection, defer): """ # presets self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.get_replset_members.return_value = { "1.1.1.1" } @@ -357,7 +370,7 @@ def test_update_status_mongodb_error( """Tests that when MongoDB is not active, that is reported instead of pbm.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.is_ready = True pbm_statuses = [ @@ -403,7 +416,7 @@ def test_update_status_pbm_error( """Tests when MongoDB is active and pbm is in the error state, pbm status is reported.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.is_ready = True pbm_statuses = [ @@ -444,7 +457,7 @@ def test_update_status_pbm_and_mongodb_ready( """Tests when both Mongodb and pbm are ready that MongoDB status is reported.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.is_ready = True self.harness.add_relation(S3_RELATION_NAME, "s3-integrator") @@ -477,7 +490,7 @@ def test_update_status_no_s3( """Tests when the s3 relation isn't present that the MongoDB status is reported.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" connection.return_value.__enter__.return_value.is_ready = True has_backup_service.return_value = True @@ -506,7 +519,7 @@ def test_update_status_primary( """Tests that update status identifies the primary unit and updates status.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" pbm_status.return_value = ActiveStatus("") self.harness.set_leader(False) @@ -538,7 +551,7 @@ def test_update_status_secondary( """Tests that update status identifies secondary units and doesn't update status.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" pbm_status.return_value = ActiveStatus("") self.harness.set_leader(False) @@ -570,7 +583,7 @@ def test_update_status_additional_messages( """Tests status updates are correct for non-primary and non-secondary cases.""" # assume leader has already initialised the replica set self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" pbm_status.return_value = ActiveStatus("") # Case 1: Unit has not been added to replica set yet @@ -614,7 +627,7 @@ def test_update_status_not_ready( """Tests that if mongod is not running on this unit it restarts it.""" get_secret.return_value = "pass123" connection.return_value.__enter__.return_value.is_ready = False - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.charm.on.update_status.emit() self.assertEqual( diff --git a/tests/unit/test_config_server_lib.py b/tests/unit/test_config_server_lib.py index 98f35a4ba..1b11be696 100644 --- a/tests/unit/test_config_server_lib.py +++ b/tests/unit/test_config_server_lib.py @@ -31,7 +31,7 @@ def is_not_config_mock_call(*args): assert args == ("config-server",) return False - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" # fails due to being run on non-config-server self.harness.charm.is_role = is_not_config_mock_call @@ -96,7 +96,7 @@ def is_not_config_mock_call(*args): assert args == ("config-server",) return False - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.add_relation("cluster", "mongos") # fails due to being run on non-config-server @@ -155,7 +155,7 @@ def is_shard_mock_call(*args): # unit is not the leader nor is the wrong wrole event = mock.Mock() event.params = {} - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.charm.config_server.pass_hook_checks(event) event.defer.assert_not_called() @@ -190,7 +190,7 @@ def is_config_mock_call(*args): # unit is not the leader nor is the wrong wrole event = mock.Mock() event.params = {} - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.charm.shard.pass_hook_checks(event) event.defer.assert_not_called() @@ -214,7 +214,7 @@ def get_cluster_mismatched_revision_status_mock_success(*unused): event = mock.Mock() event.params = {} - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.charm.config_server.pass_hook_checks(event) event.defer.assert_called() @@ -249,7 +249,7 @@ def get_cluster_mismatched_revision_status_mock_success(*unused): event = mock.Mock() event.params = {} - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.charm.shard.pass_hook_checks(event) event.defer.assert_called() diff --git a/tests/unit/test_mongodb_backups.py b/tests/unit/test_mongodb_backups.py index 1fb9b3341..c3da752df 100644 --- a/tests/unit/test_mongodb_backups.py +++ b/tests/unit/test_mongodb_backups.py @@ -269,7 +269,7 @@ def test_s3_credentials_set_pbm_failure(self, _set_config_options, service): service.return_value = True _set_config_options.side_effect = SetPBMConfigError - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" # triggering s3 event with correct fields mock_s3_info = mock.Mock() @@ -295,7 +295,7 @@ def test_s3_credentials_config_error( self, pbm_status, service, defer, resync, _set_config_options ): """Test charm defers when more time is needed to sync pbm.""" - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" service.return_value = True pbm_status.return_value = ActiveStatus() resync.side_effect = SetPBMConfigError @@ -321,7 +321,7 @@ def test_s3_credentials_config_error( @patch("charm.MongoDBBackups.get_pbm_status") def test_s3_credentials_syncing(self, pbm_status, service, defer, resync, _set_config_options): """Test charm defers when more time is needed to sync pbm credentials.""" - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" service.return_value = True resync.side_effect = ResyncError @@ -350,7 +350,7 @@ def test_s3_credentials_pbm_busy( self, pbm_status, service, defer, resync, _set_config_options ): """Test charm defers when more time is needed to sync pbm.""" - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" service.return_value = True resync.side_effect = PBMBusyError @@ -381,7 +381,7 @@ def test_s3_credentials_pbm_error( ): """Test charm defers when more time is needed to sync pbm.""" service.return_value = True - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" resync.side_effect = ExecError( command=["/usr/bin/pbm status"], exit_code=1, stdout="status code: 403", stderr="" ) diff --git a/tests/unit/test_mongodb_provider.py b/tests/unit/test_mongodb_provider.py index 493bb3d18..022f634dc 100644 --- a/tests/unit/test_mongodb_provider.py +++ b/tests/unit/test_mongodb_provider.py @@ -7,6 +7,7 @@ from ops.charm import RelationEvent from ops.testing import Harness +from parameterized import parameterized from pymongo.errors import ConfigurationError, ConnectionFailure, OperationFailure from charm import MongodbOperatorCharm @@ -76,7 +77,7 @@ def test_relation_event_oversee_users_mongo_failure( """Tests the errors related to pymongo when overseeing users result in a defer.""" # presets self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" relation_id = self.harness.add_relation("database", "consumer") for exception, expected_raise in PYMONGO_EXCEPTIONS: @@ -114,7 +115,7 @@ def test_relation_event_oversee_users_fails_to_get_relation( """Verifies that when users are formatted incorrectly an assertion error is raised.""" # presets self.harness.set_leader(True) - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" relation_id = self.harness.add_relation("database", "consumer") # AssertionError is raised when unable to attain users from relation (due to name @@ -339,3 +340,40 @@ def test_oversee_users_drop_database_failure( self.harness.charm.client_relations.oversee_users( dep_id, RelationEvent(mock.Mock(), mock.Mock()) ) + + @parameterized.expand( + [ + ["shard", "true", True], + ["database", "false", True], + ["database", "true", False], + ] + ) + @patch_network_get(private_address="1.1.1.1") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") + @patch("charms.mongodb.v1.mongodb_provider.MongoDBProvider._get_relations") + def test_update_app_relation_data_protected( + self, + role: str, + db_init: str, + is_leader: bool, + _get_relations_mock, + charm_rev, + is_integrater, + is_local, + ): + def mock_role_call(*args): + return args == (role,) + + self.harness.charm.is_role = mock_role_call + self.harness.charm.app_peer_data["db_initialised"] = db_init + self.harness.set_leader(is_leader) + + self.harness.add_relation("database", "consumer") + + # Should fail because the role is "shard" + assert not self.charm.client_relations.pass_sanity_hook_checks() + + self.harness.charm.client_relations.update_app_relation_data() + _get_relations_mock.assert_not_called() diff --git a/tests/unit/test_tls_lib.py b/tests/unit/test_tls_lib.py index 10e5e4099..f60c3c4c5 100644 --- a/tests/unit/test_tls_lib.py +++ b/tests/unit/test_tls_lib.py @@ -15,20 +15,26 @@ class TestMongoTLS(unittest.TestCase): + @patch("charm.get_charm_revision") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") @patch("charm.get_charm_revision") @patch_network_get(private_address="1.1.1.1") def setUp(self, *unused): self.harness = Harness(MongodbOperatorCharm) self.harness.begin() self.harness.add_relation("database-peers", "database-peers") - self.harness.charm.app_peer_data["db_initialised"] = "True" + self.harness.charm.app_peer_data["db_initialised"] = "true" self.harness.set_leader(True) self.charm = self.harness.charm self.addCleanup(self.harness.cleanup) @parameterized.expand([True, False]) + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch_network_get(private_address="1.1.1.1") - def test_set_tls_private_keys(self, leader): + def test_set_tls_private_keys(self, leader, *unused): """Tests setting of TLS private key via the leader, ie both internal and external. Note: this implicitly tests: _request_certificate & _parse_tls_file @@ -57,7 +63,10 @@ def test_set_tls_private_keys(self, leader): @parameterized.expand([True, False]) @patch_network_get(private_address="1.1.1.1") - def test_tls_relation_joined(self, leader): + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") + def test_tls_relation_joined(self, leader, *unused): """Test that leader units set both external and internal certificates.""" self.harness.set_leader(leader) self.relate_to_tls_certificates_operator() @@ -65,9 +74,12 @@ def test_tls_relation_joined(self, leader): self.verify_external_rsa_csr() @parameterized.expand([True, False]) + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm.restart_charm_services") @patch_network_get(private_address="1.1.1.1") - def test_tls_relation_broken(self, leader, restart_charm_services): + def test_tls_relation_broken(self, leader, restart_charm_services, *unused): """Test removes both external and internal certificates.""" self.harness.set_leader(leader) # set initial certificate values @@ -88,7 +100,10 @@ def test_tls_relation_broken(self, leader, restart_charm_services): restart_charm_services.assert_called() @patch_network_get(private_address="1.1.1.1") - def test_external_certificate_expiring(self): + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") + def test_external_certificate_expiring(self, *unused): """Verifies that when an external certificate expires a csr is made.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -106,7 +121,10 @@ def test_external_certificate_expiring(self): self.assertNotEqual(old_csr, new_csr) @patch_network_get(private_address="1.1.1.1") - def test_internal_certificate_expiring(self): + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") + def test_internal_certificate_expiring(self, *unused): """Verifies that when an internal certificate expires a csr is made.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -120,7 +138,10 @@ def test_internal_certificate_expiring(self): self.assertNotEqual(old_csr, new_csr) @patch_network_get(private_address="1.1.1.1") - def test_unknown_certificate_expiring(self): + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") + def test_unknown_certificate_expiring(self, *unused): """Verifies that when an unknown certificate expires nothing happens.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -140,9 +161,12 @@ def test_unknown_certificate_expiring(self): self.assertEqual(old_unit_csr, new_unit_csr) @patch_network_get(private_address="1.1.1.1") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm.push_tls_certificate_to_workload") @patch("charm.MongodbOperatorCharm.restart_charm_services") - def test_external_certificate_available(self, restart_charm_services, _): + def test_external_certificate_available(self, restart_charm_services, *unused): """Tests behavior when external certificate is made available.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -168,9 +192,12 @@ def test_external_certificate_available(self, restart_charm_services, _): restart_charm_services.assert_called() @patch_network_get(private_address="1.1.1.1") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm.push_tls_certificate_to_workload") @patch("charm.MongodbOperatorCharm.restart_charm_services") - def test_internal_certificate_available(self, restart_charm_services, _): + def test_internal_certificate_available(self, restart_charm_services, *unused): """Tests behavior when internal certificate is made available.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -196,9 +223,12 @@ def test_internal_certificate_available(self, restart_charm_services, _): restart_charm_services.assert_called() @patch_network_get(private_address="1.1.1.1") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("charm.MongodbOperatorCharm.push_tls_certificate_to_workload") @patch("charm.MongodbOperatorCharm.restart_charm_services") - def test_unknown_certificate_available(self, restart_charm_services, _): + def test_unknown_certificate_available(self, restart_charm_services, *unused): """Tests that when an unknown certificate is available, nothing is updated.""" # assume relation exists with a current certificate self.relate_to_tls_certificates_operator() @@ -227,8 +257,11 @@ def test_unknown_certificate_available(self, restart_charm_services, _): @patch_network_get(private_address="1.1.1.1") @patch("charm.MongodbOperatorCharm.push_tls_certificate_to_workload") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("ops.framework.EventBase.defer") - def test_external_certificate_available_deferred(self, defer, _): + def test_external_certificate_available_deferred(self, defer, *unused): """Tests behavior when external certificate is made available.""" del self.harness.charm.app_peer_data["db_initialised"] @@ -248,8 +281,11 @@ def test_external_certificate_available_deferred(self, defer, _): @patch_network_get(private_address="1.1.1.1") @patch("charm.MongodbOperatorCharm.push_tls_certificate_to_workload") + @patch("charm.CrossAppVersionChecker.is_local_charm") + @patch("charm.CrossAppVersionChecker.is_integrated_to_locally_built_charm") + @patch("charm.get_charm_revision") @patch("ops.framework.EventBase.defer") - def test_external_certificate_broken_deferred(self, defer, _): + def test_external_certificate_broken_deferred(self, defer, *unused): """Tests behavior when external certificate is made available.""" del self.harness.charm.app_peer_data["db_initialised"]