From 33f510b3409a55fd7a4e9050853e14cbd853d18e Mon Sep 17 00:00:00 2001
From: Jesus Tinoco <jesus.rodriguez.tinoco@gmail.com>
Date: Sat, 11 Jun 2016 01:12:02 +0200
Subject: [PATCH] 3501 - Add a new command option (images)

Signed-off-by: Jesus Rodriguez Tinoco <jesus.rodriguez.tinoco@gmail.com>
---
 compose/cli/main.py                    | 38 ++++++++++++++++++++++++++
 contrib/completion/bash/docker-compose | 10 +++++++
 tests/acceptance/cli_test.py           | 27 ++++++++++++++++++
 3 files changed, 75 insertions(+)

diff --git a/compose/cli/main.py b/compose/cli/main.py
index a7aec945e..ba053e233 100644
--- a/compose/cli/main.py
+++ b/compose/cli/main.py
@@ -175,6 +175,7 @@ class TopLevelCommand(object):
       events             Receive real time events from containers
       exec               Execute a command in a running container
       help               Get help on a command
+      images             List images
       kill               Kill containers
       logs               View output from containers
       pause              Pause services
@@ -481,6 +482,43 @@ class TopLevelCommand(object):
 
         print(getdoc(subject))
 
+    def images(self, options):
+        """
+        List images.
+        Usage: images [options] [SERVICE...]
+
+        Options:
+        -q     Only display IDs
+        """
+        containers = sorted(
+            self.project.containers(service_names=options['SERVICE'], stopped=True) +
+            self.project.containers(service_names=options['SERVICE'], one_off=OneOffFilter.only),
+            key=attrgetter('name'))
+
+        if options['-q']:
+            for container in containers:
+                print(str.split(str(container.image), ':')[1])
+        else:
+            headers = [
+                'Repository',
+                'Tag',
+                'Image Id',
+                'Size'
+            ]
+            rows = []
+            for container in containers:
+                image_config = container.image_config
+                repo_tags = str.split(str(image_config['RepoTags'][0]), ':')
+                image_id = str.split(str(container.image), ':')[1][0:12]
+                size = round(int(image_config['Size'])/float(1 << 20), 1)
+                rows.append([
+                    repo_tags[0],
+                    repo_tags[1],
+                    image_id,
+                    size
+                ])
+            print(Formatter().table(headers, rows))
+
     def kill(self, options):
         """
         Force stop service containers.
diff --git a/contrib/completion/bash/docker-compose b/contrib/completion/bash/docker-compose
index f4b9342f3..fa099eac4 100644
--- a/contrib/completion/bash/docker-compose
+++ b/contrib/completion/bash/docker-compose
@@ -220,6 +220,16 @@ _docker_compose_help() {
 	COMPREPLY=( $( compgen -W "${commands[*]}" -- "$cur" ) )
 }
 
+_docker_compose_images() {
+    case "$cur" in
+	-*)
+	    COMPREPLY=( $( compgen -W "--help -q" -- "$cur" ) )
+	    ;;
+	*)
+	    __docker_compose_services_all
+	    ;;
+    esac
+}
 
 _docker_compose_kill() {
 	case "$prev" in
diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py
index cc7bc5dfe..26baf3377 100644
--- a/tests/acceptance/cli_test.py
+++ b/tests/acceptance/cli_test.py
@@ -1986,3 +1986,30 @@ class CLITestCase(DockerClientTestCase):
         result = wait_on_process(proc, returncode=1)
 
         assert 'exitcodefrom_another_1 exited with code 1' in result.stdout
+
+    def test_images(self):
+        self.project.get_service('simple').create_container()
+        result = self.dispatch(['images'])
+        assert 'simplecomposefile_simple' in result.stdout
+
+    def test_images_default_composefile(self):
+        self.base_dir = 'tests/fixtures/multiple-composefiles'
+        self.dispatch(['up', '-d'])
+        result = self.dispatch(['images'])
+
+        self.assertIn('multiplecomposefiles_simple', result.stdout)
+        self.assertIn('multiplecomposefiles_another', result.stdout)
+        self.assertNotIn('multiplecomposefiles_yetanother', result.stdout)
+
+    def test_images_alternate_composefile(self):
+        config_path = os.path.abspath(
+            'tests/fixtures/multiple-composefiles/compose2.yml')
+        self._project = get_project(self.base_dir, [config_path])
+
+        self.base_dir = 'tests/fixtures/multiple-composefiles'
+        self.dispatch(['-f', 'compose2.yml', 'up', '-d'])
+        result = self.dispatch(['-f', 'compose2.yml', 'images'])
+
+        self.assertNotIn('multiplecomposefiles_simple', result.stdout)
+        self.assertNotIn('multiplecomposefiles_another', result.stdout)
+        self.assertIn('multiplecomposefiles_yetanother', result.stdout)