]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cephadm: inspect_image also returns repo_digest
authorSebastian Wagner <sebastian.wagner@suse.com>
Mon, 3 Aug 2020 12:00:47 +0000 (14:00 +0200)
committerSebastian Wagner <sebastian.wagner@suse.com>
Wed, 2 Sep 2020 08:09:33 +0000 (10:09 +0200)
Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/cephadm/cephadm
src/cephadm/tests/test_cephadm.py
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/upgrade.py

index dca03019bec4b60536d88507c8a2c9087398eddf..f0dae0e5a0a8f3c3d0a038b491602cab5b8cae7f 100755 (executable)
@@ -2490,18 +2490,33 @@ def command_inspect_image():
     # type: () -> int
     out, err, ret = call_throws([
         container_path, 'inspect',
-        '--format', '{{.Id}}',
+        '--format', '{{.ID}},{{json .RepoDigests}}',
         args.image])
     if ret:
         return errno.ENOENT
-    image_id = normalize_container_id(out.strip())
+    info_from = get_image_info_from_inspect(out.strip(), args.image)
+
     ver = CephContainer(args.image, 'ceph', ['--version']).run().strip()
+    info_from['ceph_version'] = ver
+
+    print(json.dumps(info_from, indent=4, sort_keys=True))
+    return 0
+
+
+def get_image_info_from_inspect(out, image):
+    # type: (str, str) -> Dict[str, str]
+    image_id, digests = out.split(',', 1)
+    if not out:
+        raise Error('inspect {}: empty result'.format(image))
     r = {
-        'image_id': image_id,
-        'ceph_version': ver,
+        'image_id': normalize_container_id(image_id)
     }
-    print(json.dumps(r, indent=4, sort_keys=True))
-    return 0
+    if digests:
+        json_digests = json.loads(digests)
+        if json_digests:
+            r['repo_digest'] = json_digests[0]
+    return r
+
 
 ##################################
 
index 19aa8e254882af7d7a3140016b0f24a9fa9aa097..91c13a2038ae6085cad3d957d18ded2fe3d609fc 100644 (file)
@@ -238,3 +238,20 @@ default via fe80::2480:28ec:5097:3fe2 dev wlp2s0 proto ra metric 20600 pref medi
         with pytest.raises(Exception) as e:
             cd.command_registry_login()
         assert str(e.value) == "Failed to login to custom registry @ sample-url as sample-user with given password"
+
+    def test_get_image_info_from_inspect(self):
+        # podman
+        out = """204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1,["docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992"]"""
+        r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest')
+        assert r == {
+            'image_id': '204a01f9b0b6710dd0c0af7f37ce7139c47ff0f0105d778d7104c69282dfbbf1',
+            'repo_digest': 'docker.io/ceph/ceph@sha256:1cc9b824e1b076cdff52a9aa3f0cc8557d879fb2fbbba0cafed970aca59a3992'
+        }
+
+        # docker
+        out = """sha256:16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552,["quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f"]"""
+        r = cd.get_image_info_from_inspect(out, 'registry/ceph/ceph:latest')
+        assert r == {
+            'image_id': '16f4549cf7a8f112bbebf7946749e961fbbd1b0838627fe619aab16bc17ce552',
+            'repo_digest': 'quay.ceph.io/ceph-ci/ceph@sha256:4e13da36c1bd6780b312a985410ae678984c37e6a9493a74c87e4a50b9bda41f'
+        }
index 2d2b6308f9075490dc9c6b8b4809857a0f2139da..bd3ad75e45dddc6a48d1949fdd89fc1c780b25c0 100644 (file)
@@ -9,7 +9,7 @@ from threading import Event
 
 import string
 from typing import List, Dict, Optional, Callable, Tuple, TypeVar, \
-    Any, Set, TYPE_CHECKING, cast, Iterator, Union
+    Any, Set, TYPE_CHECKING, cast, Iterator, Union, NamedTuple
 
 import datetime
 import os
@@ -105,6 +105,12 @@ def trivial_completion(f: Callable[..., T]) -> Callable[..., CephadmCompletion[T
     return wrapper
 
 
+class ContainerInspectInfo(NamedTuple):
+    image_id: str
+    ceph_version: Optional[str]
+    repo_digest: Optional[str]
+
+
 class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
                           metaclass=CLICommandMeta):
 
@@ -2490,7 +2496,7 @@ To check that the host is reachable:
     def apply_alertmanager(self, spec: ServiceSpec) -> str:
         return self._apply(spec)
 
-    def _get_container_image_id(self, image_name):
+    def _get_container_image_info(self, image_name) -> ContainerInspectInfo:
         # pick a random host...
         host = None
         for host_name in self.inventory.keys():
@@ -2509,12 +2515,19 @@ To check that the host is reachable:
         if code:
             raise OrchestratorError('Failed to pull %s on %s: %s' % (
                 image_name, host, '\n'.join(out)))
-        j = json.loads('\n'.join(out))
-        image_id = j.get('image_id')
-        ceph_version = j.get('ceph_version')
-        self.log.debug('image %s -> id %s version %s' %
-                       (image_name, image_id, ceph_version))
-        return image_id, ceph_version
+        try:
+            j = json.loads('\n'.join(out))
+            r = ContainerInspectInfo(
+                j['image_id'],
+                j.get('ceph_version'),
+                j.get('repo_digest')
+            )
+            self.log.debug(f'image {image_name} -> {r}')
+            return r
+        except (ValueError, KeyError) as _:
+            msg = 'Failed to pull %s on %s: %s' % (image_name, host, '\n'.join(out))
+            self.log.exception(msg)
+            raise OrchestratorError(msg)
 
     @trivial_completion
     def upgrade_check(self, image, version) -> str:
@@ -2525,19 +2538,18 @@ To check that the host is reachable:
         else:
             raise OrchestratorError('must specify either image or version')
 
-        target_id, target_version = self._get_container_image_id(target_name)
-        self.log.debug('Target image %s id %s version %s' % (
-            target_name, target_id, target_version))
+        image_info = self._get_container_image_info(target_name)
+        self.log.debug(f'image info {image} -> {image_info}')
         r = {
             'target_name': target_name,
-            'target_id': target_id,
-            'target_version': target_version,
+            'target_id': image_info.image_id,
+            'target_version': image_info.ceph_version,
             'needs_update': dict(),
             'up_to_date': list(),
         }
         for host, dm in self.cache.daemons.items():
             for name, dd in dm.items():
-                if target_id == dd.container_image_id:
+                if image_info.image_id == dd.container_image_id:
                     r['up_to_date'].append(dd.name())
                 else:
                     r['needs_update'][dd.name()] = {
index eb1929bc96110ee019df79419eaf558c9b7d392f..22bc9a71da0beca8b7e18a304534a4b402db823e 100644 (file)
@@ -217,7 +217,8 @@ class CephadmUpgrade:
             # need to learn the container hash
             logger.info('Upgrade: First pull of %s' % target_name)
             try:
-                target_id, target_version = self.mgr._get_container_image_id(target_name)
+                target_id, target_version, repo_digest = self.mgr._get_container_image_info(
+                    target_name)
             except OrchestratorError as e:
                 self._fail_upgrade('UPGRADE_FAILED_PULL', {
                     'severity': 'warning',