]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/cephadm: convert tags to repo_digest
authorSebastian Wagner <sebastian.wagner@suse.com>
Mon, 3 Aug 2020 12:36:56 +0000 (14:36 +0200)
committerSebastian Wagner <sebastian.wagner@suse.com>
Wed, 2 Sep 2020 08:13:04 +0000 (10:13 +0200)
Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/pybind/mgr/cephadm/module.py
src/pybind/mgr/cephadm/tests/test_cephadm.py
src/pybind/mgr/cephadm/upgrade.py
src/pybind/mgr/cephadm/utils.py

index 730ee429711180f7b5a3e2db525a47fe0842cebb..f6dfcd3284322e47b02e3333483397116a57ca41 100644 (file)
@@ -45,7 +45,7 @@ from .schedule import HostAssignment, HostPlacementSpec
 from .inventory import Inventory, SpecStore, HostCache, EventStore
 from .upgrade import CEPH_UPGRADE_ORDER, CephadmUpgrade
 from .template import TemplateMgr
-from .utils import forall_hosts, CephadmNoImage, cephadmNoImage
+from .utils import forall_hosts, CephadmNoImage, cephadmNoImage, is_repo_digest
 
 try:
     import remoto
@@ -255,6 +255,12 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
             'default': None,
             'desc': 'Custom repository password'
         },
+        {
+            'name': 'use_repo_digest',
+            'type': 'bool',
+            'default': False,
+            'desc': 'Automatically convert image tags to image digest. Make sure all daemons use the same image',
+        }
     ]
 
     def __init__(self, *args, **kwargs):
@@ -294,6 +300,7 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
             self.registry_url: Optional[str] = None
             self.registry_username: Optional[str] = None
             self.registry_password: Optional[str] = None
+            self.use_repo_digest = False
 
         self._cons: Dict[str, Tuple[remoto.backends.BaseConnection,
                                     remoto.backends.LegacyModuleExecute]] = {}
@@ -494,6 +501,8 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
 
             try:
 
+                self.convert_tags_to_repo_digest()
+
                 # refresh daemons
                 self.log.debug('refreshing hosts and daemons')
                 self._refresh_hosts_and_daemons()
@@ -524,6 +533,24 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule,
             self._serve_sleep()
         self.log.debug("serve exit")
 
+    def convert_tags_to_repo_digest(self):
+        if not self.use_repo_digest:
+            return
+        settings = self.upgrade.get_distinct_container_image_settings()
+        digests: Dict[str, ContainerInspectInfo] = {}
+        for container_image_ref in set(settings.values()):
+            if not is_repo_digest(container_image_ref):
+                image_info = self._get_container_image_info(container_image_ref)
+                if image_info.repo_digest:
+                    assert is_repo_digest(image_info.repo_digest), image_info
+                digests[container_image_ref] = image_info
+
+        for entity, container_image_ref in settings.items():
+            if not is_repo_digest(container_image_ref):
+                image_info = digests[container_image_ref]
+                if image_info.repo_digest:
+                    self.set_container_image(entity, image_info.repo_digest)
+
     def set_container_image(self, entity: str, image):
         self.check_mon_command({
             'prefix': 'config set',
@@ -2565,6 +2592,9 @@ To check that the host is reachable:
                         'current_id': dd.container_image_id,
                         'current_version': dd.version,
                     }
+        if self.use_repo_digest:
+            r['target_digest'] = image_info.repo_digest
+
         return json.dumps(r, indent=4, sort_keys=True)
 
     @trivial_completion
index ebddee226cb4095e8168149f1863d28afb2df6b8..083f0f4ea044acd7fa09017ab898510ebd27c72f 100644 (file)
@@ -817,3 +817,30 @@ class TestCephadm(object):
             code, out, err = cephadm_module.registry_login('fail-url', 'fail-user', 'fail-password')
             assert err == 'Host test failed to login to fail-url as fail-user with given password'
             check_registry_credentials('json-url', 'json-user', 'json-pass')
+
+    @mock.patch("cephadm.module.CephadmOrchestrator._run_cephadm", _run_cephadm(json.dumps({
+        'image_id': 'image_id',
+                    'repo_digest': 'image@repo_digest',
+    })))
+    @pytest.mark.parametrize("use_repo_digest",
+                             [
+                                 False,
+                                 True
+                             ])
+    def test_upgrade_run(self, use_repo_digest, cephadm_module: CephadmOrchestrator):
+        with with_host(cephadm_module, 'test'):
+            cephadm_module.set_container_image('global', 'image')
+            if use_repo_digest:
+                cephadm_module.use_repo_digest = True
+
+            cephadm_module.convert_tags_to_repo_digest()
+
+            _, image, _ = cephadm_module.check_mon_command({
+                'prefix': 'config get',
+                'who': 'global',
+                'key': 'container_image',
+            })
+            if use_repo_digest:
+                assert image == 'image@repo_digest'
+            else:
+                assert image == 'image'
index 285fcbc89962cff87fbe4d92cdf73046161de5ac..2b5759514eddda0573526f4bb02a16cba6200bcc 100644 (file)
@@ -2,7 +2,7 @@ import json
 import logging
 import time
 import uuid
-from typing import TYPE_CHECKING, Optional
+from typing import TYPE_CHECKING, Optional, Dict
 
 import orchestrator
 from cephadm.utils import name_to_config_section
@@ -205,6 +205,19 @@ class CephadmUpgrade:
             return
         self.mgr.set_store('upgrade_state', json.dumps(self.upgrade_state.to_json()))
 
+    def get_distinct_container_image_settings(self) -> Dict[str, str]:
+        # get all distinct container_image settings
+        image_settings = {}
+        ret, out, err = self.mgr.check_mon_command({
+            'prefix': 'config dump',
+            'format': 'json',
+        })
+        config = json.loads(out)
+        for opt in config:
+            if opt['name'] == 'container_image':
+                image_settings[opt['section']] = opt['value']
+        return image_settings
+
     def _do_upgrade(self):
         # type: () -> None
         if not self.upgrade_state:
@@ -234,16 +247,7 @@ class CephadmUpgrade:
         logger.info('Upgrade: Target is %s with id %s' % (target_name,
                                                           target_id))
 
-        # get all distinct container_image settings
-        image_settings = {}
-        ret, out, err = self.mgr.check_mon_command({
-            'prefix': 'config dump',
-            'format': 'json',
-        })
-        config = json.loads(out)
-        for opt in config:
-            if opt['name'] == 'container_image':
-                image_settings[opt['section']] = opt['value']
+        image_settings = self.get_distinct_container_image_settings()
 
         daemons = self.mgr.cache.get_daemons()
         done = 0
index ca8bb48c04c587af60acd201ef5f984db48b3ac7..4efcf2f5725117d1ab700a8bb0ff54f9808e10f2 100644 (file)
@@ -103,3 +103,10 @@ def get_cluster_health(mgr: 'CephadmOrchestrator') -> str:
         raise OrchestratorError('failed to parse health status')
 
     return j['status']
+
+
+def is_repo_digest(image_name: str) -> bool:
+    """
+    repo digest are something like "ceph/ceph@sha256:blablabla"
+    """
+    return '@' in image_name