Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DPE-2992] Part-1 Recieve keyfile from config-server #4

Merged
merged 5 commits into from
Nov 30, 2023

Conversation

MiaAltieri
Copy link
Contributor

@MiaAltieri MiaAltieri commented Nov 28, 2023

Issue

DPE-2992 is to start the mongos subordinate charm with the provided config server. In order for the config server and for mongos to be connected they both must have the same keyfile

Solution

Recieve the keyfile

Follow up PRs

  1. Updating the library to actually start the mongos service with the information of the config server
  2. Integration tests

Testing

juju deploy ./*charm --config role="config-server"
juju deploy application
juju deploy mongos
juju integrate application mongos
juju integrate mongos:cluster mongodb:cluster
juju ssh mongos
sudo cat KEY_FILE_PATH

src/charm.py Outdated
Comment on lines 128 to 252
self_address = self._unit_ip(self.unit)
addresses = []
if peer_addresses:
addresses.extend(peer_addresses)
addresses.append(self_address)
return addresses

@property
def _peers(self) -> Optional[Relation]:
"""Fetch the peer relation.

Returns:
An `ops.model.Relation` object representing the peer relation.
"""
return self.model.get_relation(Config.Relations.PEERS)

def get_secret(self, scope: str, key: str) -> Optional[str]:
"""Get secret from the secret storage."""
label = generate_secret_label(self, scope)
secret = self.secrets.get(label)
if not secret:
return

value = secret.get_content().get(key)
if value != Config.Secrets.SECRET_DELETED_LABEL:
return value

def set_secret(self, scope: str, key: str, value: Optional[str]) -> Optional[str]:
"""Set secret in the secret storage.

Juju versions > 3.0 use `juju secrets`, this function first checks
which secret store is being used before setting the secret.
"""
if not value:
return self.remove_secret(scope, key)

label = generate_secret_label(self, scope)
secret = self.secrets.get(label)
if not secret:
self.secrets.add(label, {key: value}, scope)
else:
content = secret.get_content()
content.update({key: value})
secret.set_content(content)
return label

def remove_secret(self, scope, key) -> None:
"""Removing a secret."""
label = generate_secret_label(self, scope)
secret = self.secrets.get(label)

if not secret:
return

content = secret.get_content()

if not content.get(key) or content[key] == Config.Secrets.SECRET_DELETED_LABEL:
logger.error(
f"Non-existing secret {scope}:{key} was attempted to be removed."
)
return

content[key] = Config.Secrets.SECRET_DELETED_LABEL
secret.set_content(content)

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)
print(key_file.is_file())
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)
file_name = f"{parent_dir}/{file_name}"
with open(file_name, "w") as write_file:
write_file.write(file_contents)

# MongoDB limitation; it is needed 400 rights for keyfile and we need 440 rights on tls
# certs to be able to connect via MongoDB shell
if Config.TLS.KEY_FILE_NAME in file_name:
os.chmod(file_name, 0o400)
else:
os.chmod(file_name, 0o440)
mongodb_user = pwd.getpwnam(MONGO_USER)
os.chown(file_name, mongodb_user.pw_uid, ROOT_USER_GID)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copied from VM charm

Comment on lines +42 to +74
@parameterized.expand([("app"), ("unit")])
def test_set_secret_returning_secret_id(self, scope):
secret_id = self.harness.charm.set_secret(scope, "somekey", "bla")
assert re.match(f"mongos.{scope}", secret_id)

@parameterized.expand([("app"), ("unit")])
def test_set_reset_new_secret(self, scope):
"""NOTE: currently ops.testing seems to allow for non-leader to set secrets too!"""
# Getting current password
self.harness.charm.set_secret(scope, "new-secret", "bla")
assert self.harness.charm.get_secret(scope, "new-secret") == "bla"

# Reset new secret
self.harness.charm.set_secret(scope, "new-secret", "blablabla")
assert self.harness.charm.get_secret(scope, "new-secret") == "blablabla"

# Set another new secret
self.harness.charm.set_secret(scope, "new-secret2", "blablabla")
assert self.harness.charm.get_secret(scope, "new-secret2") == "blablabla"

@parameterized.expand([("app"), ("unit")])
def test_invalid_secret(self, scope):
with self.assertRaises(TypeError):
self.harness.charm.set_secret("unit", "somekey", 1)

self.harness.charm.set_secret("unit", "somekey", "")
assert self.harness.charm.get_secret(scope, "somekey") is None

@patch("charm.MongosOperatorCharm.get_secret", return_value=None)
def test_get_keyfile_contents_no_secret(self, get_secret):
"""Tests file isn't checked if secret isn't set."""
self.assertEqual(self.harness.charm.get_keyfile_contents(), None)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copied from VM charm

@MiaAltieri MiaAltieri changed the title Recieve keyfile Part-1 Recieve keyfile from config-server Nov 28, 2023
@MiaAltieri MiaAltieri changed the title Part-1 Recieve keyfile from config-server [DPE-2992] Part-1 Recieve keyfile from config-server Nov 28, 2023
src/charm.py Outdated Show resolved Hide resolved
src/charm.py Outdated Show resolved Hide resolved
src/charm.py Outdated
@property
def mongos_config(self) -> MongoDBConfiguration:
"""Generates a MongoDBConfiguration object for mongos in the deployment of MongoDB."""
return self._get_mongos_config_for_user(OperatorUser, set(self._unit_ips))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the case of subordinate charm, we should connect only to local mongos

Suggested change
return self._get_mongos_config_for_user(OperatorUser, set(self._unit_ips))
return self._get_mongos_config_for_user(OperatorUser, {"/tmp/mongos.sock"})

mongos should be bond to socket mongos --bind_ip /tmp/mongos.sock

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the risk of sounding naive, why not mongos --bind_ip localhost

@MiaAltieri MiaAltieri merged commit 06228ba into 6/edge Nov 30, 2023
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants