From 6fcfea443dcf95d8ac92ed612a4e3d35adff1db9 Mon Sep 17 00:00:00 2001 From: yuqi-zhang Date: Thu, 28 Jul 2016 21:17:16 +0000 Subject: [PATCH] images: add more sub-commands to images Add the following sub-commands to "atomic images list" --all: show all images, including intermediate images --filter: filter output based on given filters --quiet: only display image IDs and corresponding bash auto-complete, tests, and documentation. Closes: #502 Approved by: rhatdan --- Atomic/atomic.py | 62 ++++++++++++++++----- atomic | 12 ++++ bash/atomic | 28 +++++++++- docs/atomic-images.1.md | 11 ++++ tests/integration/test_system_containers.sh | 12 ++++ 5 files changed, 110 insertions(+), 15 deletions(-) diff --git a/Atomic/atomic.py b/Atomic/atomic.py index 4449c5e..263dae0 100644 --- a/Atomic/atomic.py +++ b/Atomic/atomic.py @@ -115,6 +115,7 @@ class Atomic(object): self._images = [] self.containers = False self.images_cache = [] + self.images_all_cache = [] self.active_containers = [] self.docker_cmd = None self.debug = False @@ -667,6 +668,25 @@ class Atomic(object): return True return False + def _filter_include_image(self, image_info): + filterables = ["repo", "tag", "id", "created", "size", "type"] + for i in self.args.filter: + var, value = str(i).split("=") + var = var.lower() + if var == "repository": + var = "repo" + + if var == "image": + var = "id" + + if var not in filterables: # Default to allowing all images through for non-existing filterable + continue + + if value not in image_info[var].lower(): + return False + + return True + def images(self): def split_repo_tags(_images): sub_list = [item.split(":") for sublist in _images for item @@ -694,7 +714,7 @@ class Atomic(object): else: return 1, 1 - _images = self.get_images() + _images = self.get_images(get_all=self.args.all) used_image_ids = [x['ImageID'] for x in self.get_containers()] @@ -709,7 +729,7 @@ class Atomic(object): col_out = "{0:2} {1:" + str(_max_repo) + "} {2:" + str(_max_tag) + \ "} {3:" + str(_max_id) + "} {4:18} {5:14} {6:10}" - if self.args.heading: + if self.args.heading and not self.args.quiet: util.write_out(col_out.format(" ", "REPOSITORY", "TAG", @@ -717,7 +737,7 @@ class Atomic(object): "CREATED", "VIRTUAL SIZE", "TYPE")) - for image in self.get_images(): + for image in _images: repo, tag = image["RepoTags"][0].rsplit(":", 1) if "Created" in image: created = time.strftime("%F %H:%M", time.localtime(image["Created"])) @@ -741,11 +761,20 @@ class Atomic(object): image_id = image["Id"][:12] if self.args.truncate else image["Id"] image_type = image['ImageType'] - util.write_out(col_out.format(indicator, repo, - tag, image_id, - created, - virtual_size, - image_type)) + if self.args.filter: + image_info = {"repo" : repo, "tag" : tag, "id" : image_id, + "created" : created, "size" : virtual_size, "type" : image_type} + if not self._filter_include_image(image_info): + continue + + if self.args.quiet: + util.write_out(image_id) + else: + util.write_out(col_out.format(indicator, repo, + tag, image_id, + created, + virtual_size, + image_type)) util.write_out("") return @@ -1014,23 +1043,28 @@ class Atomic(object): raise DockerObjectNotFound(identifier) - def _get_docker_images(self): + def _get_docker_images(self, get_all=False): try: - images = self.d.images() + images = self.d.images(all=get_all) for i in images: i["ImageType"] = "Docker" except requests.exceptions.ConnectionError: raise NoDockerDaemon() return images - def get_images(self): + def get_images(self, get_all=False): ''' Wrapper function that should be used instead of querying docker multiple times for a list of images. ''' - if len(self.images_cache) == 0: - self.images_cache = self._get_docker_images() + self.syscontainers.get_system_images() - return self.images_cache + if get_all: + if len(self.images_all_cache) == 0: + self.images_all_cache = self._get_docker_images(get_all=True) + self.syscontainers.get_system_images() + return self.images_all_cache + else: + if len(self.images_cache) == 0: + self.images_cache = self._get_docker_images() + self.syscontainers.get_system_images() + return self.images_cache def get_containers(self): ''' diff --git a/atomic b/atomic index cc047df..e560dc3 100755 --- a/atomic +++ b/atomic @@ -319,6 +319,14 @@ def create_parser(help_text): "container images on your system.") list_parser.set_defaults(func='images') + list_parser.add_argument("-a", "--all", dest="all", default=False, + action="store_true", + help=_("Show all images, including intermediate images")) + + list_parser.add_argument("-f", "--filter", dest="filter", metavar="FILTER", + action="append", + help=_("Filter output based on VARIABLE=VALUE format")) + list_parser.add_argument("-n", "--noheading", dest="heading", default=True, action="store_false", help=_("do not print heading when listing the images")) @@ -327,6 +335,10 @@ def create_parser(help_text): action="store_false", help=_("Don't truncate output")) + list_parser.add_argument("-q", "--quiet", dest="quiet", default=False, + action="store_true", + help=_("Only display image IDs")) + # atomic images delete delete_parser = images_subparser.add_parser("delete", help=_("mark image for deletion"), diff --git a/bash/atomic b/bash/atomic index 73648b8..40a9c2d 100644 --- a/bash/atomic +++ b/bash/atomic @@ -497,12 +497,38 @@ _atomic_images() { } _atomic_images_list() { - local all_options=" + local options_with_args=" + --filter -f + " + + local all_options="$options_with_args + --all -a --help -h --noheading -n --no-trunc + --quiet -q " + local filterables=" + repo + tag + id + image + created + size + type + " + + local options_with_args_glob=$(__atomic_to_extglob "$options_with_args") + + case "$prev" in + $options_with_args_glob ) + compopt -o nospace + COMPREPLY=( $( compgen -S\= -W "$filterables" "$cur" ) ) + return 0 + ;; + esac + case "$cur" in -*) COMPREPLY=( $( compgen -W "$all_options" -- "$cur" ) ) diff --git a/docs/atomic-images.1.md b/docs/atomic-images.1.md index 74924e3..7630bd0 100644 --- a/docs/atomic-images.1.md +++ b/docs/atomic-images.1.md @@ -28,12 +28,22 @@ also removing the older version of the image. [**-h|--help**] Print usage statement +[**-a|--all**] + Show all images, including intermediate images + +[**-f|--filter**] + Filter output based on given filters, example usage: '--filter repo=foo' +will list all images that has "foo" as part of their repository name. + [**-n|--noheading**] Do not print heading when listing the images [**--no-trunc**] Do not truncate output +[**-q|--quiet] + Only display image IDs + **delete IMAGES...** @@ -57,3 +67,4 @@ July 2015, Originally compiled by Daniel Walsh (dwalsh at redhat dot com) July 2016, Updated to reflect images sub-command changes (jhonce at redhat dot com) +July 2016, Added sub-commands all, filter and quiet to list (jerzhang at redhat dot com) diff --git a/tests/integration/test_system_containers.sh b/tests/integration/test_system_containers.sh index eeb47b9..3e1629f 100755 --- a/tests/integration/test_system_containers.sh +++ b/tests/integration/test_system_containers.sh @@ -36,6 +36,18 @@ grep -q "ociimage/atomic-test-system-latest" refs ${ATOMIC} images list > ${WORK_DIR}/images grep -q "atomic-test-system" ${WORK_DIR}/images +${ATOMIC} images list -a > ${WORK_DIR}/images +grep -q "atomic-test-system" ${WORK_DIR}/images +${ATOMIC} images list -f repo=atomic-test-system > ${WORK_DIR}/images +grep -q "atomic-test-system" ${WORK_DIR}/images +${ATOMIC} images list -f repo=non-existing-repo > ${WORK_DIR}/images +if grep -q "atomic-test-system" ${WORK_DIR}/images; then + exit 1 +fi +${ATOMIC} images list -q > ${WORK_DIR}/images +if grep -q "atomic-test-system" ${WORK_DIR}/images; then + exit 1 +fi export NAME="test-system-container-$$"