Merge "Fix infinite recursion on action module import" into feature/zuulv3
diff --git a/zuul/ansible/action/normal.py b/zuul/ansible/action/normal.py
index a50125c..b18cb51 100644
--- a/zuul/ansible/action/normal.py
+++ b/zuul/ansible/action/normal.py
@@ -13,12 +13,8 @@
 # You should have received a copy of the GNU General Public License
 # along with this software.  If not, see <http://www.gnu.org/licenses/>.
 
-# TODO(mordred) Figure out how to not need to do this to get the base class
-import ansible.plugins.action
-import imp
-normal = imp.load_module(
-    'ansible.plugins.action.normal',
-    *imp.find_module('normal', ansible.plugins.action.__path__))
+from zuul.ansible import paths
+normal = paths._import_ansible_action_plugin('normal')
 
 
 class ActionModule(normal.ActionModule):
diff --git a/zuul/ansible/paths.py b/zuul/ansible/paths.py
index 2bd0181..cde43f1 100644
--- a/zuul/ansible/paths.py
+++ b/zuul/ansible/paths.py
@@ -13,8 +13,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this software.  If not, see <http://www.gnu.org/licenses/>.
 
+import imp
 import os
 
+import ansible.plugins.action
+
 
 def _is_safe_path(path):
     if os.path.isabs(path):
@@ -31,3 +34,21 @@
         path=path,
         msg="{prefix} outside the working dir is prohibited".format(
             prefix=prefix))
+
+
+def _import_ansible_action_plugin(name):
+    # Ansible forces the import of our action plugins
+    # (zuul.ansible.action.foo) as ansible.plugins.action.foo, which
+    # is the import path of the ansible implementation.  Our
+    # implementations need to subclass that, but if we try to import
+    # it with that name, we will get our own module.  This bypasses
+    # Python's module namespace to load the actual ansible modules.
+    # We need to give it a name, however.  If we load it with its
+    # actual name, we will end up overwriting our module in Python's
+    # namespace, causing infinite recursion.  So we supply an
+    # otherwise unused name for the module:
+    # zuul.ansible.protected.action.foo.
+
+    return imp.load_module(
+        'zuul.ansible.protected.action.' + name,
+        *imp.find_module(name, ansible.plugins.action.__path__))