From fffdc6b3e5c8db44e43558b697ba6c5cc54dade9 Mon Sep 17 00:00:00 2001
From: Eduardo Gonzalez <dabarren@gmail.com>
Date: Thu, 9 Aug 2018 17:05:38 +0200
Subject: [PATCH] Remove_images in kolla_docker module

Add support to remove images from kolla_docker
ansible module.

Change-Id: Ib6bcb98b5b295a2d590df3013188913d1f7f3584
---
 ansible/library/kolla_docker.py | 32 ++++++++++++++++--
 tests/test_kolla_docker.py      | 58 ++++++++++++++++++++++++++++++++-
 2 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/ansible/library/kolla_docker.py b/ansible/library/kolla_docker.py
index 00946302a..c177c0086 100644
--- a/ansible/library/kolla_docker.py
+++ b/ansible/library/kolla_docker.py
@@ -40,6 +40,7 @@ options:
       - get_container_state
       - pull_image
       - remove_container
+      - remove_image
       - remove_volume
       - recreate_or_restart_container
       - restart_container
@@ -199,6 +200,10 @@ EXAMPLES = '''
       kolla_docker:
         action: remove_volume
         name: name_of_volume
+    - name: Remove image
+      kolla_docker:
+        action: remove_image
+        image: name_of_image
 '''
 
 import json
@@ -767,6 +772,26 @@ class DockerWorker(object):
                     )
                 raise
 
+    def remove_image(self):
+        if self.check_image():
+            self.changed = True
+            try:
+                self.dc.remove_image(image=self.params.get('image'))
+            except docker.errors.APIError as e:
+                if e.response.status_code == 409:
+                    self.module.fail_json(
+                        failed=True,
+                        msg="Image '{}' is currently in-use".format(
+                            self.params.get('image')
+                        )
+                    )
+                elif e.response.status_code == 500:
+                    self.module.fail_json(
+                        failed=True,
+                        msg="Server error"
+                    )
+                raise
+
 
 def generate_module():
     # NOTE(jeffrey4l): add empty string '' to choices let us use
@@ -778,9 +803,9 @@ def generate_module():
                              'create_volume', 'get_container_env',
                              'get_container_state', 'pull_image',
                              'recreate_or_restart_container',
-                             'remove_container', 'remove_volume',
-                             'restart_container', 'start_container',
-                             'stop_container']),
+                             'remove_container', 'remove_image',
+                             'remove_volume', 'restart_container',
+                             'start_container', 'stop_container']),
         api_version=dict(required=False, type='str', default='auto'),
         auth_email=dict(required=False, type='str'),
         auth_password=dict(required=False, type='str', no_log=True),
@@ -830,6 +855,7 @@ def generate_module():
         ['action', 'get_container_state', ['name']],
         ['action', 'recreate_or_restart_container', ['name']],
         ['action', 'remove_container', ['name']],
+        ['action', 'remove_image', ['image']],
         ['action', 'remove_volume', ['name']],
         ['action', 'restart_container', ['name']],
         ['action', 'stop_container', ['name']]
diff --git a/tests/test_kolla_docker.py b/tests/test_kolla_docker.py
index d2e7a9de7..aab7a1b34 100644
--- a/tests/test_kolla_docker.py
+++ b/tests/test_kolla_docker.py
@@ -44,7 +44,7 @@ class ModuleArgsTest(base.BaseTestCase):
                 choices=['compare_container', 'compare_image', 'create_volume',
                          'get_container_env', 'get_container_state',
                          'pull_image', 'recreate_or_restart_container',
-                         'remove_container', 'remove_volume',
+                         'remove_container', 'remove_image', 'remove_volume',
                          'restart_container', 'start_container',
                          'stop_container']),
             api_version=dict(required=False, type='str', default='auto'),
@@ -96,6 +96,7 @@ class ModuleArgsTest(base.BaseTestCase):
             ['action', 'get_container_state', ['name']],
             ['action', 'recreate_or_restart_container', ['name']],
             ['action', 'remove_container', ['name']],
+            ['action', 'remove_image', ['image']],
             ['action', 'remove_volume', ['name']],
             ['action', 'restart_container', ['name']],
             ['action', 'stop_container', ['name']]
@@ -606,6 +607,61 @@ class TestImage(base.BaseTestCase):
             msg="Unknown error message: unexpected error",
             failed=True)
 
+    def test_remove_image(self):
+        self.dw = get_DockerWorker(
+            {'image': 'myregistrydomain.com:5000/ubuntu:16.04',
+             'action': 'remove_image'})
+        self.dw.dc.images.return_value = self.fake_data['images']
+
+        self.dw.remove_image()
+        self.assertTrue(self.dw.changed)
+        self.dw.dc.remove_image.assert_called_once_with(
+            image='myregistrydomain.com:5000/ubuntu:16.04')
+
+    def test_remove_image_not_exists(self):
+        self.dw = get_DockerWorker(
+            {'image': 'myregistrydomain.com:5000/non_existing:16.04',
+             'action': 'remove_image'})
+        self.dw.dc.images.return_value = self.fake_data['images']
+
+        self.dw.remove_image()
+        self.assertFalse(self.dw.changed)
+
+    def test_remove_image_exception_409(self):
+        resp = mock.MagicMock()
+        resp.status_code = 409
+        docker_except = docker_error.APIError('test error', resp)
+        self.dw = get_DockerWorker(
+            {'image': 'myregistrydomain.com:5000/ubuntu:16.04',
+             'action': 'remove_image'})
+        self.dw.dc.images.return_value = self.fake_data['images']
+        self.dw.dc.remove_image.side_effect = docker_except
+
+        self.assertRaises(docker_error.APIError, self.dw.remove_image)
+        self.assertTrue(self.dw.changed)
+        self.dw.module.fail_json.assert_called_once_with(
+            failed=True,
+            msg=("Image 'myregistrydomain.com:5000/ubuntu:16.04' "
+                 "is currently in-use")
+        )
+
+    def test_remove_image_exception_500(self):
+        resp = mock.MagicMock()
+        resp.status_code = 500
+        docker_except = docker_error.APIError('test error', resp)
+        self.dw = get_DockerWorker(
+            {'image': 'myregistrydomain.com:5000/ubuntu:16.04',
+             'action': 'remove_image'})
+        self.dw.dc.images.return_value = self.fake_data['images']
+        self.dw.dc.remove_image.side_effect = docker_except
+
+        self.assertRaises(docker_error.APIError, self.dw.remove_image)
+        self.assertTrue(self.dw.changed)
+        self.dw.module.fail_json.assert_called_once_with(
+            failed=True,
+            msg=("Server error")
+        )
+
 
 class TestVolume(base.BaseTestCase):
 
-- 
GitLab