diff --git a/lib/charms/mongodb/v0/shards_interface.py b/lib/charms/mongodb/v0/shards_interface.py index ae932e3d0..ad7dfe717 100644 --- a/lib/charms/mongodb/v0/shards_interface.py +++ b/lib/charms/mongodb/v0/shards_interface.py @@ -13,7 +13,7 @@ from charms.mongodb.v0.users import MongoDBUser, OperatorUser from ops.charm import CharmBase from ops.framework import Object -from ops.model import BlockedStatus, MaintenanceStatus +from ops.model import BlockedStatus, MaintenanceStatus, WaitingStatus from tenacity import RetryError, Retrying, stop_after_delay, wait_fixed from config import Config @@ -53,7 +53,7 @@ def __init__( def _on_relation_joined(self, event): """Handles providing shards with secrets and adding shards to the config server.""" if self.charm.is_role(Config.Role.REPLICATION): - self.unit.status = BlockedStatus("role replication does not support sharding") + self.charm.unit.status = BlockedStatus("role replication does not support sharding") logger.error("sharding interface not supported with config role=replication") return @@ -75,7 +75,7 @@ def _on_relation_joined(self, event): { OPERATOR_PASSWORD_KEY: self.charm.get_secret( Config.Relations.APP_SCOPE, - MongoDBUser.get_password_key_name_for_user("operator"), + MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username()), ), KEYFILE_KEY: self.charm.get_secret( Config.Relations.APP_SCOPE, Config.Secrets.SECRET_KEYFILE_NAME @@ -123,7 +123,7 @@ def __init__( def _on_relation_changed(self, event): """Retrieves secrets from config-server and updates them within the shard.""" if self.charm.is_role(Config.Role.REPLICATION): - self.unit.status = BlockedStatus("role replication does not support sharding") + self.charm.unit.status = BlockedStatus("role replication does not support sharding") logger.error("sharding interface not supported with config role=replication") return @@ -139,17 +139,27 @@ def _on_relation_changed(self, event): # shards rely on the config server for secrets relation_data = event.relation.data[event.app] self.update_keyfile(key_file_contents=relation_data.get(KEYFILE_KEY)) + + # restart on high loaded databases can be very slow (e.g. up to 10-20 minutes). + with MongoDBConnection(self.charm.mongodb_config) as mongo: + if not mongo.is_ready: + logger.info("shard has not started yet, deferfing") + self.charm.unit.status = WaitingStatus("Waiting for MongoDB to start") + event.defer() + return + + self.charm.unit.status = MaintenanceStatus("Adding shard to config-server") + + if not self.charm.unit.is_leader(): + return + try: self.update_operator_password(new_password=relation_data.get(OPERATOR_PASSWORD_KEY)) except RetryError: self.charm.unit.status = BlockedStatus("Shard not added to config-server") return - self.charm.unit.status = MaintenanceStatus("Adding shard to config-server") - # TODO future PR, leader unit verifies shard was added to cluster - if not self.charm.unit.is_leader(): - return def update_operator_password(self, new_password: str) -> None: """Updates the password for the operator user. @@ -157,13 +167,17 @@ def update_operator_password(self, new_password: str) -> None: Raises: RetryError """ + if not new_password or not self.charm.unit.is_leader(): + return + current_password = ( self.charm.get_secret( - Config.Relations.APP_SCOPE, MongoDBUser.get_password_key_name_for_user("operator") + Config.Relations.APP_SCOPE, + MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username()), ), ) - if not new_password or new_password == current_password or not self.charm.unit.is_leader(): + if new_password == current_password: return # updating operator password, usually comes after keyfile was updated, hence, the mongodb @@ -192,7 +206,10 @@ def update_operator_password(self, new_password: str) -> None: def update_keyfile(self, key_file_contents: str) -> None: """Updates keyfile on all units.""" - if not key_file_contents: + # keyfile is set by leader in application data, application data does not necessarily + # match what is on the machine. + current_key_file = self.charm.get_keyfile_contents() + if not key_file_contents or key_file_contents == current_key_file: return # put keyfile on the machine with appropriate permissions diff --git a/src/charm.py b/src/charm.py index fba47bd19..d5c34e40e 100755 --- a/src/charm.py +++ b/src/charm.py @@ -898,6 +898,24 @@ def _instatiate_keyfile(self, event: StartEvent) -> None: file_contents=self.get_secret(APP_SCOPE, Config.Secrets.SECRET_KEYFILE_NAME), ) + def get_keyfile_contents(self) -> str: + """Retrieves the contents of the keyfile on host machine.""" + # wait for keyFile to be created by leader unit + if not self.get_secret(APP_SCOPE, Config.Secrets.SECRET_KEYFILE_NAME): + logger.debug("waiting for leader unit to generate keyfile contents") + return + + key_file_path = f"{Config.MONGOD_CONF_DIR}/{KEY_FILE}" + key_file = Path(key_file_path) + if not key_file.is_file(): + logger.info("no keyfile present") + return + + with open(key_file_path, "r") as file: + key = file.read() + + return key + def push_file_to_unit(self, parent_dir, file_name, file_contents) -> None: """K8s charms can push files to their containers easily, this is a vm charm workaround.""" Path(parent_dir).mkdir(parents=True, exist_ok=True)