From 14fdb597d5aec5de16b5ce0e8e0899c370b02aca Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 16 Aug 2023 15:58:06 -0400 Subject: [PATCH] cephadm: move container engine types and funcs to container_engines.py Signed-off-by: John Mulligan Pair-programmed-with: Adam King Co-authored-by: Adam King --- src/cephadm/cephadm.py | 75 ++------------------ src/cephadm/cephadmlib/container_engines.py | 77 +++++++++++++++++++++ src/cephadm/tests/test_cephadm.py | 8 ++- src/cephadm/tests/test_container_engine.py | 11 +-- 4 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 src/cephadm/cephadmlib/container_engines.py diff --git a/src/cephadm/cephadm.py b/src/cephadm/cephadm.py index 6294c08bba1..48f4b6a0aae 100755 --- a/src/cephadm/cephadm.py +++ b/src/cephadm/cephadm.py @@ -81,7 +81,6 @@ from cephadmlib.constants import ( LOGROTATE_DIR, LOG_DIR, LOG_DIR_MODE, - MIN_PODMAN_VERSION, NO_DEPRECATED, PIDS_LIMIT_UNLIMITED_PODMAN_VERSION, QUIET_LOG_LEVEL, @@ -104,7 +103,12 @@ from cephadmlib.call_wrappers import ( call_timeout, concurrent_tasks, ) -from cephadmlib.container_engine_base import ContainerEngine +from cephadmlib.container_engines import ( + Docker, + Podman, + check_container_engine, + find_container_engine, +) FuncT = TypeVar('FuncT', bound=Callable) @@ -184,35 +188,6 @@ class DeploymentType(Enum): RECONFIG = 'Reconfig' -class Podman(ContainerEngine): - EXE = 'podman' - - def __init__(self) -> None: - super().__init__() - self._version: Optional[Tuple[int, ...]] = None - - @property - def version(self) -> Tuple[int, ...]: - if self._version is None: - raise RuntimeError('Please call `get_version` first') - return self._version - - def get_version(self, ctx: CephadmContext) -> None: - out, _, _ = call_throws(ctx, [self.path, 'version', '--format', '{{.Client.Version}}'], verbosity=CallVerbosity.QUIET) - self._version = _parse_podman_version(out) - - def __str__(self) -> str: - version = '.'.join(map(str, self.version)) - return f'{self.EXE} ({self.path}) version {version}' - - -class Docker(ContainerEngine): - EXE = 'docker' - - -CONTAINER_PREFERENCE = (Podman, Docker) # prefer podman to docker - - # During normal cephadm operations (cephadm ls, gather-facts, etc ) we use: # stdout: for JSON output only # stderr: for error, debug, info, etc @@ -1848,19 +1823,6 @@ def try_convert_datetime(s): return None -def _parse_podman_version(version_str): - # type: (str) -> Tuple[int, ...] - def to_int(val: str, org_e: Optional[Exception] = None) -> int: - if not val and org_e: - raise org_e - try: - return int(val) - except ValueError as e: - return to_int(val[0:-1], org_e or e) - - return tuple(map(to_int, version_str.split('.'))) - - def get_hostname(): # type: () -> str return socket.gethostname() @@ -2343,31 +2305,6 @@ def recursive_chown(path: str, uid: int, gid: int) -> None: os.chown(os.path.join(dirpath, filename), uid, gid) -def find_container_engine(ctx: CephadmContext) -> Optional[ContainerEngine]: - if ctx.docker: - return Docker() - else: - for i in CONTAINER_PREFERENCE: - try: - return i() - except Exception: - pass - return None - - -def check_container_engine(ctx: CephadmContext) -> ContainerEngine: - engine = ctx.container_engine - if not isinstance(engine, CONTAINER_PREFERENCE): - # See https://github.com/python/mypy/issues/8993 - exes: List[str] = [i.EXE for i in CONTAINER_PREFERENCE] # type: ignore - raise Error('No container engine binary found ({}). Try run `apt/dnf/yum/zypper install `'.format(' or '.join(exes))) - elif isinstance(engine, Podman): - engine.get_version(ctx) - if engine.version < MIN_PODMAN_VERSION: - raise Error('podman version %d.%d.%d or later is required' % MIN_PODMAN_VERSION) - return engine - - def get_unit_name(fsid, daemon_type, daemon_id=None): # type: (str, str, Optional[Union[int, str]]) -> str # accept either name or type + id diff --git a/src/cephadm/cephadmlib/container_engines.py b/src/cephadm/cephadmlib/container_engines.py new file mode 100644 index 00000000000..4c3611cbea3 --- /dev/null +++ b/src/cephadm/cephadmlib/container_engines.py @@ -0,0 +1,77 @@ +# container_engines.py - container engine types and selection funcs + + +from typing import Tuple, List, Optional + +from .call_wrappers import call_throws, CallVerbosity +from .context import CephadmContext +from .container_engine_base import ContainerEngine +from .constants import MIN_PODMAN_VERSION +from .exceptions import Error + + +class Podman(ContainerEngine): + EXE = 'podman' + + def __init__(self) -> None: + super().__init__() + self._version: Optional[Tuple[int, ...]] = None + + @property + def version(self) -> Tuple[int, ...]: + if self._version is None: + raise RuntimeError('Please call `get_version` first') + return self._version + + def get_version(self, ctx: CephadmContext) -> None: + out, _, _ = call_throws(ctx, [self.path, 'version', '--format', '{{.Client.Version}}'], verbosity=CallVerbosity.QUIET) + self._version = _parse_podman_version(out) + + def __str__(self) -> str: + version = '.'.join(map(str, self.version)) + return f'{self.EXE} ({self.path}) version {version}' + + +class Docker(ContainerEngine): + EXE = 'docker' + + +CONTAINER_PREFERENCE = (Podman, Docker) # prefer podman to docker + + +def find_container_engine(ctx: CephadmContext) -> Optional[ContainerEngine]: + if ctx.docker: + return Docker() + else: + for i in CONTAINER_PREFERENCE: + try: + return i() + except Exception: + pass + return None + + +def check_container_engine(ctx: CephadmContext) -> ContainerEngine: + engine = ctx.container_engine + if not isinstance(engine, CONTAINER_PREFERENCE): + # See https://github.com/python/mypy/issues/8993 + exes: List[str] = [i.EXE for i in CONTAINER_PREFERENCE] # type: ignore + raise Error('No container engine binary found ({}). Try run `apt/dnf/yum/zypper install `'.format(' or '.join(exes))) + elif isinstance(engine, Podman): + engine.get_version(ctx) + if engine.version < MIN_PODMAN_VERSION: + raise Error('podman version %d.%d.%d or later is required' % MIN_PODMAN_VERSION) + return engine + + +def _parse_podman_version(version_str): + # type: (str) -> Tuple[int, ...] + def to_int(val: str, org_e: Optional[Exception] = None) -> int: + if not val and org_e: + raise org_e + try: + return int(val) + except ValueError as e: + return to_int(val[0:-1], org_e or e) + + return tuple(map(to_int, version_str.split('.'))) diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py index 8722bceda52..a1ee4495e96 100644 --- a/src/cephadm/tests/test_cephadm.py +++ b/src/cephadm/tests/test_cephadm.py @@ -228,11 +228,15 @@ class TestCephAdm(object): ("1.6.2-stable2", (1,6,2)), ]) def test_parse_podman_version(self, test_input, expected): - assert _cephadm._parse_podman_version(test_input) == expected + from cephadmlib.container_engines import _parse_podman_version + + assert _parse_podman_version(test_input) == expected def test_parse_podman_version_invalid(self): + from cephadmlib.container_engines import _parse_podman_version + with pytest.raises(ValueError) as res: - _cephadm._parse_podman_version('inval.id') + _parse_podman_version('inval.id') assert 'inval' in str(res.value) @mock.patch('cephadm.logger') diff --git a/src/cephadm/tests/test_container_engine.py b/src/cephadm/tests/test_container_engine.py index 9998f85d3bd..7c5ef513127 100644 --- a/src/cephadm/tests/test_container_engine.py +++ b/src/cephadm/tests/test_container_engine.py @@ -8,13 +8,16 @@ _cephadm = import_cephadm() _find_program_loc = 'cephadmlib.container_engine_base.find_program' +_call_throws_loc = 'cephadmlib.container_engines.call_throws' def test_container_engine(): + from cephadmlib.container_engine_base import ContainerEngine + with pytest.raises(NotImplementedError): - _cephadm.ContainerEngine() + ContainerEngine() - class PhonyContainerEngine(_cephadm.ContainerEngine): + class PhonyContainerEngine(ContainerEngine): EXE = "true" with mock.patch(_find_program_loc) as find_program: @@ -30,7 +33,7 @@ def test_podman(): find_program.assert_called() with pytest.raises(RuntimeError): pm.version - with mock.patch("cephadm.call_throws") as call_throws: + with mock.patch(_call_throws_loc) as call_throws: call_throws.return_value = ("4.9.9", None, None) with with_cephadm_ctx([]) as ctx: pm.get_version(ctx) @@ -43,7 +46,7 @@ def test_podman_badversion(): find_program.return_value = "/usr/bin/podman" pm = _cephadm.Podman() find_program.assert_called() - with mock.patch("cephadm.call_throws") as call_throws: + with mock.patch(_call_throws_loc) as call_throws: call_throws.return_value = ("4.10.beta2", None, None) with with_cephadm_ctx([]) as ctx: with pytest.raises(ValueError): -- 2.39.5