mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-06 07:11:13 +02:00
Merge pull request #2983 from yueguobin/fix/docker-image-digest-translation
fix(docker): accept the pinned image id when the two daemons report different ids for the same content
This commit is contained in:
8 files changed
+259
-19
No files matched your search
@@ -66,6 +66,11 @@ class Docker(BaseManager):
|
||||
self._session = None
|
||||
self._api_version = DOCKER_MINIMUM_API_VERSION
|
||||
self._host_checked = False
|
||||
# Image name -> (controller digest, local id) for the images synced from
|
||||
# the controller. The controller pins the id its own daemon reports,
|
||||
# which is not comparable with the id this daemon reports for the same
|
||||
# content (see load_image()).
|
||||
self._image_id_translations = {}
|
||||
|
||||
def _select_node_class(self, **kwargs):
|
||||
"""
|
||||
@@ -419,11 +424,16 @@ class Docker(BaseManager):
|
||||
progress_callback(f"Success pulling image {image}")
|
||||
|
||||
@locking
|
||||
async def load_image(self, stream, progress_callback=None):
|
||||
async def load_image(self, stream, image=None, digest=None, progress_callback=None):
|
||||
"""
|
||||
Load a Docker image into the Docker daemon from a docker save tar stream
|
||||
|
||||
:param stream: An async iterable of bytes (the tar produced by docker save)
|
||||
:param image: Tag of the image in the archive. Together with digest, the
|
||||
(controller digest, local id) pair is remembered so that node creation
|
||||
can accept the digest the controller pinned for this image
|
||||
:param digest: Image id as reported by the Docker daemon on the controller
|
||||
host (the value the controller pins in the node create payload)
|
||||
:param progress_callback: A function that receive a log message about image load progress
|
||||
"""
|
||||
|
||||
@@ -470,6 +480,39 @@ class Docker(BaseManager):
|
||||
if progress_callback:
|
||||
progress_callback("Docker image loaded")
|
||||
|
||||
if image and digest:
|
||||
await self._remember_image_id_translation(image, digest)
|
||||
|
||||
async def _remember_image_id_translation(self, image, controller_digest):
|
||||
"""
|
||||
Remember how the local daemon identifies an image that was just loaded
|
||||
from the controller.
|
||||
|
||||
Docker ids are not comparable across image stores: for the same content
|
||||
a classic store reports the config digest, while a containerd store (or
|
||||
an image loaded from an OCI archive) reports the manifest digest. The
|
||||
controller pins the id of its own daemon, so recording the pair lets
|
||||
DockerVM accept that pin for the image it actually received instead of
|
||||
asking for another full sync on every node creation.
|
||||
"""
|
||||
|
||||
try:
|
||||
image_infos = await self.query("GET", f"images/{image}/json")
|
||||
except DockerError as e:
|
||||
log.warning(f"Image '{image}' has been loaded but cannot be inspected: {e}")
|
||||
return
|
||||
local_id = image_infos.get("Id")
|
||||
if local_id:
|
||||
self._image_id_translations[image] = (controller_digest, local_id)
|
||||
|
||||
def image_id_translation(self, image):
|
||||
"""
|
||||
:returns: the (controller digest, local id) pair recorded the last time
|
||||
this image was loaded from the controller, None otherwise
|
||||
"""
|
||||
|
||||
return self._image_id_translations.get(image)
|
||||
|
||||
async def list_images(self):
|
||||
"""
|
||||
Gets Docker image list.
|
||||
|
||||
@@ -591,15 +591,20 @@ class DockerVM(DockerKernelDatapathMixin, BaseNode):
|
||||
if image_infos is None:
|
||||
raise DockerError(f"Cannot get information for image '{self._image}', please try again.")
|
||||
|
||||
if self._image_digest and image_infos.get("Id") != self._image_digest:
|
||||
local_id = image_infos.get("Id")
|
||||
if self._image_digest and local_id != self._image_digest:
|
||||
# the tag exists but points to a different image (e.g. a moved :latest):
|
||||
# report it as missing so the controller re-syncs the expected version
|
||||
local_id = image_infos.get("Id")
|
||||
log.info(
|
||||
f"Image '{self._image}' version mismatch on this compute: "
|
||||
f"local id '{local_id}' != expected '{self._image_digest}'"
|
||||
)
|
||||
raise ImageMissingError(self._image)
|
||||
# report it as missing so the controller re-syncs the expected version.
|
||||
# Image ids are not comparable across image stores (a classic store
|
||||
# reports the config digest where a containerd store, or an image
|
||||
# loaded from an OCI archive, reports the manifest digest), so accept
|
||||
# the pin for the image this compute received from the controller.
|
||||
if self.manager.image_id_translation(self._image) != (self._image_digest, local_id):
|
||||
log.info(
|
||||
f"Image '{self._image}' version mismatch on this compute: "
|
||||
f"local id '{local_id}' != expected '{self._image_digest}'"
|
||||
)
|
||||
raise ImageMissingError(self._image)
|
||||
|
||||
available_cpus = psutil.cpu_count(logical=True)
|
||||
if self._cpus > available_cpus:
|
||||
|
||||
Reference in new issue
Block a user