Skip to content

Commit

Permalink
Merge branch 'share-secrets' into add-shards
Browse files Browse the repository at this point in the history
  • Loading branch information
MiaAltieri committed Sep 29, 2023
2 parents a940412 + 98ad1b1 commit 659f22d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 15 deletions.
53 changes: 38 additions & 15 deletions lib/charms/mongodb/v0/shards_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from charms.mongodb.v0.users import MongoDBUser, OperatorUser
from ops.charm import CharmBase, RelationBrokenEvent
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
Expand All @@ -34,8 +34,8 @@
# to 0 if you are raising the major API version
LIBPATCH = 1
KEYFILE_KEY = "key-file"
OPERATOR_PASSWORD_KEY = "operator-password"
HOSTS_KEY = "host"
HOSTS_KEY = "hosts"
OPERATOR_PASSWORD_KEY = MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username())


class ShardingProvider(Object):
Expand All @@ -61,7 +61,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

Expand All @@ -76,14 +76,15 @@ def _on_relation_joined(self, event):

if not self.charm.db_initialised:
event.defer()
return

# TODO Future PR, sync tls secrets and PBM password
self._update_relation_data(
event.relation.id,
{
OPERATOR_PASSWORD_KEY: self.charm.get_secret(
Config.Relations.APP_SCOPE,
MongoDBUser.get_password_key_name_for_user("operator"),
OPERATOR_PASSWORD_KEY,
),
KEYFILE_KEY: self.charm.get_secret(
Config.Relations.APP_SCOPE, Config.Secrets.SECRET_KEYFILE_NAME
Expand Down Expand Up @@ -203,7 +204,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

Expand All @@ -215,21 +216,36 @@ def _on_relation_changed(self, event):

if not self.charm.db_initialised:
event.defer()
return

# 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))
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

# 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

# TODO Future work, see if needed to check for all units restarted / primary elected

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")
logger.error(
"Shard could not be added to config server, failed to set operator password."
)
return

# send hosts to mongos to be added to the cluster
self._update_relation_data(
event.relation.id,
Expand All @@ -244,13 +260,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,
OPERATOR_PASSWORD_KEY,
),
)

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
Expand All @@ -273,13 +293,16 @@ def update_operator_password(self, new_password: str) -> None:

self.charm.set_secret(
Config.Relations.APP_SCOPE,
MongoDBUser.get_password_key_name_for_user(OperatorUser.get_username()),
OPERATOR_PASSWORD_KEY,
new_password,
)

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
Expand Down
18 changes: 18 additions & 0 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,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)
Expand Down

0 comments on commit 659f22d

Please sign in to comment.