Skip to content

Commit

Permalink
Fix password rotate + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
MiaAltieri committed Feb 28, 2024
1 parent 327458c commit e52c0ef
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 3 deletions.
51 changes: 50 additions & 1 deletion lib/charms/mongodb/v1/shards_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@

# Increment this PATCH version before using `charmcraft publish-lib` or reset
# to 0 if you are raising the major API version
LIBPATCH = 7
LIBPATCH = 8
KEYFILE_KEY = "key-file"
HOSTS_KEY = "host"
OPERATOR_PASSWORD_KEY = MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username())
Expand Down Expand Up @@ -466,6 +466,10 @@ def __init__(
charm.on[self.relation_name].relation_changed, self._on_relation_changed
)

self.framework.observe(
getattr(self.charm.on, "secret_changed"), self._handle_changed_secrets
)

self.framework.observe(
charm.on[self.relation_name].relation_departed,
self.charm.check_relation_broken_or_scale_down,
Expand All @@ -475,6 +479,51 @@ def __init__(
charm.on[self.relation_name].relation_broken, self._on_relation_broken
)

def _handle_changed_secrets(self, event) -> None:
"""Update operator and backup user passwords when rotation occurs.
Changes in secrets do not re-trigger a relation changed event, so it is necessary to listen
to secret changes events.
"""
if (
not self.charm.unit.is_leader()
or not event.secret.label
or not self.model.get_relation(self.relation_name)
):
return

# only one related config-server is possible
config_server_relation = self.charm.model.relations[self.relation_name][0]

# many secret changed events occur, only listen to those related to our interface with the
# config-server
secret_changing_label = event.secret.label
sharding_secretes_label = f"{self.relation_name}.{config_server_relation.id}.extra.secret"
if secret_changing_label != sharding_secretes_label:
logger.info(
"A secret unrelated to this sharding relation %s is changing, igorning secret changed event.",
str(config_server_relation.id),
)
return

operator_password = self.database_requires.fetch_relation_field(
config_server_relation.id, OPERATOR_PASSWORD_KEY
)
backup_password = self.database_requires.fetch_relation_field(
config_server_relation.id, BACKUP_PASSWORD_KEY
)

try:
self.update_password(
username=OperatorUser.get_username(), new_password=operator_password
)
self.update_password(BackupUser.get_username(), new_password=backup_password)
except RetryError:
self.charm.unit.status = BlockedStatus("Failed to rotate cluster secrets")
logger.error("Shard failed to rotate cluster secrets.")
event.defer()
return

def _on_relation_changed(self, event):
"""Retrieves secrets from config-server and updates them within the shard."""
if not self.pass_hook_checks(event):
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/sharding_tests/test_sharding_backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,12 @@ async def test_rotate_backup_password(ops_test: OpsTest) -> None:
shard_backup_password = await get_password(
ops_test, username="backup", app_name=SHARD_ONE_APP_NAME
)
assert shard_backup_password != new_password, "Application shard-one did not rotate password"
assert shard_backup_password == new_password, "Application shard-one did not rotate password"

shard_backup_password = await get_password(
ops_test, username="backup", app_name=SHARD_TWO_APP_NAME
)
assert shard_backup_password != new_password, "Application shard-two did not rotate password"
assert shard_backup_password == new_password, "Application shard-two did not rotate password"

# verify backup actions work after password rotation
leader_unit = await backup_helpers.get_leader_unit(
Expand Down

0 comments on commit e52c0ef

Please sign in to comment.