From b6fa001cdb49a4db6c469bb9f80b9baee7b91ae4 Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Sun, 3 Dec 2023 11:01:05 -0500 Subject: [PATCH] cephadm: add generic methods for sharing namespaces across containers In the future, some sidecar containers will need to share namespaces with the primary container (or each other). Make it easy to set this up by creating a enable_shared_namespaces function and Namespace enum. Signed-off-by: John Mulligan --- src/cephadm/cephadmlib/container_types.py | 63 ++++++++++++++++++++++- src/cephadm/tests/test_util_funcs.py | 62 ++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/cephadm/cephadmlib/container_types.py b/src/cephadm/cephadmlib/container_types.py index 01fbb41d2392d..665c4d89652a6 100644 --- a/src/cephadm/cephadmlib/container_types.py +++ b/src/cephadm/cephadmlib/container_types.py @@ -1,9 +1,11 @@ # container_types.py - container instance wrapper types import copy +import enum +import functools import os -from typing import Dict, List, Optional, Any, Union, Tuple, cast +from typing import Dict, List, Optional, Any, Union, Tuple, Iterable, cast from .call_wrappers import call, call_throws, CallVerbosity from .constants import DEFAULT_TIMEOUT @@ -599,3 +601,62 @@ def extract_uid_gid( raise Error(f'Failed to extract uid/gid for path {ex[0]}: {ex[1]}') raise RuntimeError('uid/gid not found') + + +@functools.lru_cache() +def _opt_key(value: str) -> str: + """Return a (long) option stripped of its value.""" + return value.split('=', 1)[0] + + +def _replace_container_arg(args: List[str], new_arg: str) -> None: + """Remove and replace arguments that have the same `--xyz` part as + the given `new_arg`. If new_arg is expected to have a value it + must be part of the new_arg string following an equal sign (`=`). + The existing arg may be a single or two strings in the input list. + """ + key = _opt_key(new_arg) + has_value = key != new_arg + try: + idx = [_opt_key(v) for v in args].index(key) + if '=' in args[idx] or not has_value: + del args[idx] + else: + del args[idx] + del args[idx] + except ValueError: + pass + args.append(new_arg) + + +class Namespace(enum.Enum): + """General container namespace control options.""" + + cgroupns = 'cgroupns' + cgroup = 'cgroupns' # alias + ipc = 'ipc' + network = 'network' + pid = 'pid' + userns = 'userns' + user = 'userns' # alias + uts = 'uts' + + def to_option(self, value: str) -> str: + return f'--{self}={value}' + + def __str__(self) -> str: + return self.value + + +def enable_shared_namespaces( + args: List[str], + name: str, + ns: Iterable[Namespace], +) -> None: + """Update the args list to contain options that enable container namespace + sharing where name is the name/id of the target container and ns is a list + or set of namespaces that should be shared. + """ + cc = f'container:{name}' + for n in ns: + _replace_container_arg(args, n.to_option(cc)) diff --git a/src/cephadm/tests/test_util_funcs.py b/src/cephadm/tests/test_util_funcs.py index 6b5380711f336..aa64d54b07344 100644 --- a/src/cephadm/tests/test_util_funcs.py +++ b/src/cephadm/tests/test_util_funcs.py @@ -906,3 +906,65 @@ def test_daemon_sub_id_systemd_names(): ) with pytest.raises(ValueError): dsi.service_name + + +@pytest.mark.parametrize( + "args,new_arg,expected", + [ + (['--foo=77'], '--bar', ['--foo=77', '--bar']), + (['--foo=77'], '--foo=12', ['--foo=12']), + ( + ['--foo=77', '--quux=later', '--range=2-5'], + '--quux=now', + ['--foo=77', '--range=2-5', '--quux=now'], + ), + ( + ['--foo=77', '--quux', 'later', '--range=2-5'], + '--quux=now', + ['--foo=77', '--range=2-5', '--quux=now'], + ), + ( + ['--foo=77', '--quux', 'later', '--range=2-5'], + '--jiffy', + ['--foo=77', '--quux', 'later', '--range=2-5', '--jiffy'], + ), + ( + ['--foo=77', '--quux=buff', '--range=2-5'], + '--quux', + ['--foo=77', '--range=2-5', '--quux'], + ), + ], +) +def test_replace_container_args(args, new_arg, expected): + from cephadmlib.container_types import _replace_container_arg + + _args = list(args) # preserve the input so test input is not mutated + _replace_container_arg(_args, new_arg) + assert _args == expected + + + +def test_enable_shared_namespaces(): + from cephadmlib.container_types import enable_shared_namespaces, Namespace + + args = [] + enable_shared_namespaces(args, 'c001d00d', {Namespace.ipc}) + assert args == ['--ipc=container:c001d00d'] + + enable_shared_namespaces( + args, 'c001d00d', [Namespace.uts, Namespace.network] + ) + assert args == [ + '--ipc=container:c001d00d', + '--uts=container:c001d00d', + '--network=container:c001d00d', + ] + + enable_shared_namespaces( + args, 'badd33d5', [Namespace.network] + ) + assert args == [ + '--ipc=container:c001d00d', + '--uts=container:c001d00d', + '--network=container:badd33d5', + ] -- 2.39.5