Skip to content

Commit

Permalink
Add docker_image_export module.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfontein committed Jan 21, 2024
1 parent 901c1c4 commit 7c27159
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ If you use the Ansible package and do not update collections independently, use
- community.docker.docker_host_info: retrieve information on the Docker daemon
- community.docker.docker_image: manage Docker images
- community.docker.docker_image_build: build Docker images using Docker buildx
- community.docker.docker_image_export: export (archive) Docker images
- community.docker.docker_image_info: retrieve information on Docker images
- community.docker.docker_image_load: load Docker images from archives
- community.docker.docker_image_pull: pull Docker images from registries
Expand Down
3 changes: 3 additions & 0 deletions docs/docsite/rst/scenario_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ For working with a plain Docker daemon, that is without Swarm, there are connect
docker_image_build
The :ansplugin:`community.docker.docker_image_build module <community.docker.docker_image_build#module>` allows you to build a Docker image using Docker buildx.

docker_image_export module
The :ansplugin:`community.docker.docker_image_export module <community.docker.docker_image_export#module>` allows you to export (archive) images.

docker_image_info module
The :ansplugin:`community.docker.docker_image_info module <community.docker.docker_image_info#module>` allows you to list and inspect images.

Expand Down
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ action_groups:
- docker_host_info
- docker_image
- docker_image_build
- docker_image_export
- docker_image_info
- docker_image_load
- docker_image_pull
Expand Down
123 changes: 77 additions & 46 deletions plugins/module_utils/image_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, image_id, repo_tags):
:param image_id: File name portion of Config entry, e.g. abcde12345 from abcde12345.json
:type image_id: str
:param repo_tags Docker image names, e.g. ["hello-world:latest"]
:type repo_tags: list
:type repo_tags: list[str]
'''

self.image_id = image_id
Expand Down Expand Up @@ -60,13 +60,13 @@ def api_image_id(archive_image_id):
return 'sha256:%s' % archive_image_id


def archived_image_manifest(archive_path):
def load_archived_image_manifest(archive_path):
'''
Attempts to get Image.Id and image name from metadata stored in the image
Attempts to get image IDs and image names from metadata stored in the image
archive tar file.
The tar should contain a file "manifest.json" with an array with a single entry,
and the entry should have a Config field with the image ID in its file name, as
The tar should contain a file "manifest.json" with an array with one or more entries,
and every entry should have a Config field with the image ID in its file name, as
well as a RepoTags list, which typically has only one entry.
:raises:
Expand All @@ -75,7 +75,7 @@ def archived_image_manifest(archive_path):
:param archive_path: Tar file to read
:type archive_path: str
:return: None, if no file at archive_path, or the extracted image ID, which will not have a sha256: prefix.
:return: None, if no file at archive_path, or a list of ImageArchiveManifestSummary objects.
:rtype: ImageArchiveManifestSummary
'''

Expand All @@ -100,50 +100,51 @@ def archived_image_manifest(archive_path):
# In Python 2.6, this does not have __exit__
ef.close()

if len(manifest) != 1:
if len(manifest) == 0:
raise ImageArchiveInvalidException(
"Expected to have one entry in manifest.json but found %s" % len(manifest),
"Expected to have at least one entry in manifest.json but found none",
None
)

m0 = manifest[0]

try:
config_file = m0['Config']
except KeyError as exc:
raise ImageArchiveInvalidException(
"Failed to get Config entry from manifest.json: %s" % to_native(exc),
exc
)

# Extracts hash without 'sha256:' prefix
try:
# Strip off .json filename extension, leaving just the hash.
image_id = os.path.splitext(config_file)[0]
except Exception as exc:
raise ImageArchiveInvalidException(
"Failed to extract image id from config file name %s: %s" % (config_file, to_native(exc)),
exc
)

for prefix in (
'blobs/sha256/', # Moby 25.0.0, Docker API 1.44
):
if image_id.startswith(prefix):
image_id = image_id[len(prefix):]

try:
repo_tags = m0['RepoTags']
except KeyError as exc:
raise ImageArchiveInvalidException(
"Failed to get RepoTags entry from manifest.json: %s" % to_native(exc),
exc
)

return ImageArchiveManifestSummary(
image_id=image_id,
repo_tags=repo_tags
)
result = []
for index, meta in enumerate(manifest):
try:
config_file = meta['Config']
except KeyError as exc:
raise ImageArchiveInvalidException(
"Failed to get Config entry from {0}th manifest in manifest.json: {1}".format(index + 1, to_native(exc)),
exc
)

# Extracts hash without 'sha256:' prefix
try:
# Strip off .json filename extension, leaving just the hash.
image_id = os.path.splitext(config_file)[0]
except Exception as exc:
raise ImageArchiveInvalidException(
"Failed to extract image id from config file name %s: %s" % (config_file, to_native(exc)),
exc
)

for prefix in (
'blobs/sha256/', # Moby 25.0.0, Docker API 1.44
):
if image_id.startswith(prefix):
image_id = image_id[len(prefix):]

try:
repo_tags = meta['RepoTags']
except KeyError as exc:
raise ImageArchiveInvalidException(
"Failed to get RepoTags entry from {0}th manifest in manifest.json: {1}".format(index + 1, to_native(exc)),
exc
)

result.append(ImageArchiveManifestSummary(
image_id=image_id,
repo_tags=repo_tags
))
return result

except ImageArchiveInvalidException:
raise
Expand All @@ -161,3 +162,33 @@ def archived_image_manifest(archive_path):
raise
except Exception as exc:
raise ImageArchiveInvalidException("Failed to open tar file %s: %s" % (archive_path, to_native(exc)), exc)


def archived_image_manifest(archive_path):
'''
Attempts to get Image.Id and image name from metadata stored in the image
archive tar file.
The tar should contain a file "manifest.json" with an array with a single entry,
and the entry should have a Config field with the image ID in its file name, as
well as a RepoTags list, which typically has only one entry.
:raises:
ImageArchiveInvalidException: A file already exists at archive_path, but could not extract an image ID from it.
:param archive_path: Tar file to read
:type archive_path: str
:return: None, if no file at archive_path, or the extracted image ID, which will not have a sha256: prefix.
:rtype: ImageArchiveManifestSummary
'''

results = load_archived_image_manifest(archive_path)
if results is None:
return None
if len(results) == 1:
return results[0]
raise ImageArchiveInvalidException(
"Expected to have one entry in manifest.json but found %s" % len(results),
None
)
Loading

0 comments on commit 7c27159

Please sign in to comment.