From 253968a2bafd93216543a41deee21702a01c23ae Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Mon, 13 Jan 2020 14:34:03 +0100 Subject: [PATCH 1/2] cephadm: podman inspect: image field was called `ImageID`: See https://github.com/containers/libpod/pull/4195 Signed-off-by: Sebastian Wagner --- src/cephadm/cephadm | 31 ++++++++++++++++++++++++++++++- src/cephadm/tests/test_cephadm.py | 16 ++++++++++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/cephadm/cephadm b/src/cephadm/cephadm index d4424846a2d..a3f79c7bdec 100755 --- a/src/cephadm/cephadm +++ b/src/cephadm/cephadm @@ -590,6 +590,29 @@ def pathify(p): return os.path.join(os.getcwd(), p) return p + +def get_podman_version(): + # type: () -> Tuple[int, ...] + if 'podman' not in container_path: + raise ValueError('not using podman') + out, _, _ = call_throws([container_path, '--version']) + return _parse_podman_version(out) + +def _parse_podman_version(out): + # type: (str) -> Tuple[int, ...] + _, _, version_str = out.strip().split() + + def to_int(val, org_e=None): + 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() @@ -2179,10 +2202,16 @@ def list_daemons(detail=True, legacy_dir=None): image_name = None image_id = None version = None + + if 'podman' in container_path and get_podman_version() < (1, 6, 2): + image_field = '.ImageID' + else: + image_field = '.Image' + out, err, code = call( [ container_path, 'inspect', - '--format', '{{.Id}},{{.Config.Image}},{{.Image}}', + '--format', '{{.Id}},{{.Config.Image}},{{%s}}' % image_field, 'ceph-%s-%s' % (fsid, j) ], verbose_on_failure=False) diff --git a/src/cephadm/tests/test_cephadm.py b/src/cephadm/tests/test_cephadm.py index d9bc45ce6f1..79b6db41825 100644 --- a/src/cephadm/tests/test_cephadm.py +++ b/src/cephadm/tests/test_cephadm.py @@ -13,9 +13,9 @@ else: import imp cd = imp.load_source('cephadm', 'cephadm') -class TestCephAdm(unittest.TestCase): +class TestCephAdm(object): def test_is_fsid(self): - self.assertFalse(cd.is_fsid('no-uuid')) + assert not cd.is_fsid('no-uuid') def test__get_parser_image(self): p = cd._get_parser() @@ -34,3 +34,15 @@ class TestCephAdm(unittest.TestCase): with pytest.raises(SystemExit): p.parse_args(['deploy', '--name', 'wrong', '--fsid', 'fsid']) + + @pytest.mark.parametrize("test_input, expected", [ + ("podman version 1.6.2", (1,6,2)), + ("podman version 1.6.2-stable2", (1,6,2)), + ]) + def test_parse_podman_version(self, test_input, expected): + assert cd._parse_podman_version(test_input) == expected + + def test_parse_podman_version_invalid(self): + with pytest.raises(ValueError) as res: + cd._parse_podman_version('podman version inval.id') + assert 'inval' in str(res.value) From 1a70b239d7f2647aa0e92f6d9401e5ae5bea2bf1 Mon Sep 17 00:00:00 2001 From: Sebastian Wagner Date: Tue, 21 Jan 2020 12:47:44 +0100 Subject: [PATCH 2/2] cephadm: annotate call() yet another small step in order to increase testing coverage. Signed-off-by: Sebastian Wagner --- src/cephadm/cephadm | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/cephadm/cephadm b/src/cephadm/cephadm index a3f79c7bdec..e9f13d4d63d 100755 --- a/src/cephadm/cephadm +++ b/src/cephadm/cephadm @@ -50,7 +50,7 @@ import tempfile import time import errno try: - from typing import Dict, List, Tuple, Optional, Union + from typing import Dict, List, Tuple, Optional, Union, Any except ImportError: pass import uuid @@ -70,7 +70,7 @@ if sys.version_info >= (3, 2): else: from ConfigParser import SafeConfigParser -container_path = None +container_path = '' class Error(Exception): pass @@ -373,11 +373,11 @@ class FileLock(object): ################################## # Popen wrappers, lifted from ceph-volume -def call(command, - desc=None, - verbose=False, - verbose_on_failure=True, - timeout=DEFAULT_TIMEOUT, +def call(command, # type: List[str] + desc=None, # type: Optional[str] + verbose=False, # type: bool + verbose_on_failure=True, # type: bool + timeout=DEFAULT_TIMEOUT, # type: Optional[int] **kwargs): """ Wrap subprocess.Popen to @@ -436,9 +436,11 @@ def call(command, ) for fd in reads: try: - message = os.read(fd, 1024) - if not isinstance(message, str): - message = message.decode('utf-8') + message_b = os.read(fd, 1024) + if isinstance(message_b, bytes): + message = message_b.decode('utf-8') + if isinstance(message_b, str): + message = message_b if fd == process.stdout.fileno(): out += message message = out_buffer + message @@ -489,6 +491,7 @@ def call(command, def call_throws(command, **kwargs): + # type: (List[str], Any) -> Tuple[str, str, int] out, err, ret = call(command, **kwargs) if ret: raise RuntimeError('Failed command: %s' % ' '.join(command))