docker_image: do not crash in load_image for docker-py < 2.5.0. (#73)

* Avoid crash for docker-py < 2.5.0.

* Add warnings when load_image does not return a generator.

* Add test.

* Update plugins/modules/docker_image.py

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>

Co-authored-by: Andrew Klychkov <aaklychkov@mail.ru>
This commit is contained in:
Felix Fontein 2021-01-25 15:17:04 +01:00 committed by GitHub
parent c1e6c2e699
commit 8702713ac3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 20 deletions

View File

@ -0,0 +1,2 @@
bugfixes:
- "docker_image - fix crash on loading images with versions of Docker SDK for Python before 2.5.0 (https://github.com/ansible-collections/community.docker/issues/72, https://github.com/ansible-collections/community.docker/pull/73)."

View File

@ -684,13 +684,39 @@ class ImageManager(DockerBaseClass):
''' '''
# Load image(s) from file # Load image(s) from file
load_output = [] load_output = []
has_output = False
try: try:
self.log("Opening image %s" % self.load_path) self.log("Opening image %s" % self.load_path)
with open(self.load_path, 'rb') as image_tar: with open(self.load_path, 'rb') as image_tar:
self.log("Loading image from %s" % self.load_path) self.log("Loading image from %s" % self.load_path)
for line in self.client.load_image(image_tar): output = self.client.load_image(image_tar)
self.log(line, pretty_print=True) if output is not None:
self._extract_output_line(line, load_output) # Old versions of Docker SDK of Python (before version 2.5.0) do not return anything.
# (See https://github.com/docker/docker-py/commit/7139e2d8f1ea82340417add02090bfaf7794f159)
# Note that before that commit, something else than None was returned, but that was also
# only introduced in a commit that first appeared in 2.5.0 (see
# https://github.com/docker/docker-py/commit/9e793806ff79559c3bc591d8c52a3bbe3cdb7350).
# So the above check works for every released version of Docker SDK for Python.
has_output = True
for line in output:
self.log(line, pretty_print=True)
self._extract_output_line(line, load_output)
else:
if LooseVersion(docker_version) < LooseVersion('2.5.0'):
self.client.module.warn(
'The installed version of the Docker SDK for Python does not return the loading results'
' from the Docker daemon. Therefore, we cannot verify whether the expected image was'
' loaded, whether multiple images where loaded, or whether the load actually succeeded.'
' If you are not stuck with Python 2.6, *please* upgrade to a version newer than 2.5.0'
' (2.5.0 was released in August 2017).'
)
else:
self.client.module.warn(
'The API version of your Docker daemon is < 1.23, which does not return the image'
' loading result from the Docker daemon. Therefore, we cannot verify whether the'
' expected image was loaded, whether multiple images where loaded, or whether the load'
' actually succeeded. You should consider upgrading your Docker daemon.'
)
except EnvironmentError as exc: except EnvironmentError as exc:
if exc.errno == errno.ENOENT: if exc.errno == errno.ENOENT:
self.client.fail("Error opening image %s - %s" % (self.load_path, str(exc))) self.client.fail("Error opening image %s - %s" % (self.load_path, str(exc)))
@ -699,26 +725,28 @@ class ImageManager(DockerBaseClass):
self.client.fail("Error loading image %s - %s" % (self.name, str(exc)), stdout='\n'.join(load_output)) self.client.fail("Error loading image %s - %s" % (self.name, str(exc)), stdout='\n'.join(load_output))
# Collect loaded images # Collect loaded images
loaded_images = set() if has_output:
for line in load_output: # We can only do this when we actually got some output from Docker daemon
if line.startswith('Loaded image:'): loaded_images = set()
loaded_images.add(line[len('Loaded image:'):].strip()) for line in load_output:
if line.startswith('Loaded image:'):
loaded_images.add(line[len('Loaded image:'):].strip())
if not loaded_images: if not loaded_images:
self.client.fail("Detected no loaded images. Archive potentially corrupt?", stdout='\n'.join(load_output)) self.client.fail("Detected no loaded images. Archive potentially corrupt?", stdout='\n'.join(load_output))
expected_image = '%s:%s' % (self.name, self.tag) expected_image = '%s:%s' % (self.name, self.tag)
if expected_image not in loaded_images: if expected_image not in loaded_images:
self.client.fail( self.client.fail(
"The archive did not contain image '%s'. Instead, found %s." % ( "The archive did not contain image '%s'. Instead, found %s." % (
expected_image, ', '.join(["'%s'" % image for image in sorted(loaded_images)])), expected_image, ', '.join(["'%s'" % image for image in sorted(loaded_images)])),
stdout='\n'.join(load_output)) stdout='\n'.join(load_output))
loaded_images.remove(expected_image) loaded_images.remove(expected_image)
if loaded_images: if loaded_images:
self.client.module.warn( self.client.module.warn(
"The archive contained more images than specified: %s" % ( "The archive contained more images than specified: %s" % (
', '.join(["'%s'" % image for image in sorted(loaded_images)]), )) ', '.join(["'%s'" % image for image in sorted(loaded_images)]), ))
return self.client.find_image(self.name, self.tag) return self.client.find_image(self.name, self.tag)

View File

@ -282,6 +282,14 @@
register: load_image_3 register: load_image_3
ignore_errors: true ignore_errors: true
- name: load image (invalid image, old API version)
docker_image:
name: foo:bar
load_path: "{{ output_dir }}/image-invalid.tar"
source: load
api_version: "1.22"
register: load_image_4
- assert: - assert:
that: that:
- load_image is changed - load_image is changed
@ -292,6 +300,8 @@
"The archive did not contain image 'foo:bar'. Instead, found '" ~ docker_test_image_hello_world ~ "'." == load_image_2.msg "The archive did not contain image 'foo:bar'. Instead, found '" ~ docker_test_image_hello_world ~ "'." == load_image_2.msg
- load_image_3 is failed - load_image_3 is failed
- '"Detected no loaded images. Archive potentially corrupt?" == load_image_3.msg' - '"Detected no loaded images. Archive potentially corrupt?" == load_image_3.msg'
- load_image_4 is changed
- "'The API version of your Docker daemon is < 1.23, which does not return the image loading result from the Docker daemon. Therefore, we cannot verify whether the expected image was loaded, whether multiple images where loaded, or whether the load actually succeeded. You should consider upgrading your Docker daemon.' in load_image_4.warnings"
#################################################################### ####################################################################
## build.path ###################################################### ## build.path ######################################################