]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cephadm: add generic methods for sharing namespaces across containers
authorJohn Mulligan <jmulligan@redhat.com>
Sun, 3 Dec 2023 16:01:05 +0000 (11:01 -0500)
committerAdam King <adking@redhat.com>
Mon, 15 Apr 2024 15:01:20 +0000 (11:01 -0400)
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 <jmulligan@redhat.com>
(cherry picked from commit b6fa001cdb49a4db6c469bb9f80b9baee7b91ae4)

src/cephadm/cephadmlib/container_types.py
src/cephadm/tests/test_util_funcs.py

index 01fbb41d2392da3d4493992c89c647e6d500a95d..665c4d89652a659b79e652552cbb60f5f11cdb14 100644 (file)
@@ -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))
index 6b5380711f336826a69f13ccfd0e95c2cb967c36..aa64d54b073446ce184f5c129b6c5d3061fa318e 100644 (file)
@@ -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',
+    ]