From 3bb4c199817c8aa4ecd4ae684bd28f84933fb14d Mon Sep 17 00:00:00 2001
From: Victor Coutellier <victor.coutellier@gmail.com>
Date: Sun, 10 Mar 2019 19:07:00 +0100
Subject: [PATCH] Fix kolla-docker possible undefined variable

It is possible to reference undefined variable in kolla-docker module if
DockerWorker object initialization fail, so the current behaviour will
crash the playbook with the unwanted error message :

UnboundLocalError: local variable 'dw' referenced before assignment

Change-Id: Ic8d26b11f93255220888b5406f8ab4a6f81736c2
Closes-Bug: #1819361
---
 ansible/library/kolla_docker.py |  3 ++-
 tests/test_kolla_docker.py      | 38 +++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 1 deletion(-)

diff --git a/ansible/library/kolla_docker.py b/ansible/library/kolla_docker.py
index 38c0900771..063e240914 100644
--- a/ansible/library/kolla_docker.py
+++ b/ansible/library/kolla_docker.py
@@ -974,6 +974,7 @@ def generate_module():
 def main():
     module = generate_module()
 
+    dw = None
     try:
         dw = DockerWorker(module)
         # TODO(inc0): We keep it bool to have ansible deal with consistent
@@ -983,7 +984,7 @@ def main():
         module.exit_json(changed=dw.changed, result=result, **dw.result)
     except Exception:
         module.fail_json(changed=True, msg=repr(traceback.format_exc()),
-                         **dw.result)
+                         **getattr(dw, 'result', {}))
 
 
 if __name__ == '__main__':
diff --git a/tests/test_kolla_docker.py b/tests/test_kolla_docker.py
index 1a7d943529..1aab4617c1 100644
--- a/tests/test_kolla_docker.py
+++ b/tests/test_kolla_docker.py
@@ -113,6 +113,7 @@ class ModuleArgsTest(base.BaseTestCase):
             bypass_checks=False
         )
 
+
 FAKE_DATA = {
 
     'params': {
@@ -192,6 +193,43 @@ def get_DockerWorker(mod_param, mock_dclient):
     return dw
 
 
+class TestMainModule(base.BaseTestCase):
+
+    def setUp(self):
+        super(TestMainModule, self).setUp()
+        self.fake_data = copy.deepcopy(FAKE_DATA)
+
+    @mock.patch("kolla_docker.traceback.format_exc")
+    @mock.patch("kolla_docker.get_docker_client")
+    @mock.patch("kolla_docker.generate_module")
+    def test_docker_client_exception(self, mock_generate_module, mock_dclient,
+                                     mock_traceback):
+        module_mock = mock.MagicMock()
+        mock_generate_module.return_value = module_mock
+        mock_dclient.side_effect = AttributeError()
+        mock_traceback.return_value = "Some very ugly traceback"
+        kd.main()
+        module_mock.fail_json.assert_called_once_with(
+            changed=True, msg=repr("Some very ugly traceback"))
+
+    @mock.patch("kolla_docker.DockerWorker")
+    @mock.patch("kolla_docker.generate_module")
+    def test_execute_module(self, mock_generate_module, mock_dw):
+        mock_dw.return_value.check_image.return_value = False
+        mock_dw.return_value.changed = False
+        mock_dw.return_value.result = {"some_key": "some_value"}
+        module_mock = mock.MagicMock()
+        module_mock.params = self.fake_data['params']
+        module_mock.params["action"] = "check_image"
+        mock_generate_module.return_value = module_mock
+        kd.main()
+        mock_dw.assert_called_once_with(module_mock)
+        mock_dw.return_value.check_image.assert_called_once_with()
+        module_mock.exit_json.assert_called_once_with(changed=False,
+                                                      result=False,
+                                                      some_key="some_value")
+
+
 class TestContainer(base.BaseTestCase):
 
     def setUp(self):