Add some ansible plugin tests

This is not complete, but it's a start.

While doing this it became clear that the upstream csvfile lookup plugin does
not work in python3. Work around that by putting in a simpler version of
the code into our copy.

Change-Id: Ic84d8265e6fd7e15a0e5d66c781409a087d761d7
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/.zuul.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/.zuul.yaml
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/.zuul.yaml
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/README b/tests/fixtures/config/ansible/git/org_plugin-project/README
new file mode 100644
index 0000000..9daeafb
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/README
@@ -0,0 +1 @@
+test
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/cartesian.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/cartesian.yaml
new file mode 100644
index 0000000..a2e92a2
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/cartesian.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  vars:
+    value: "{{ lookup('cartesian', [1, 2], [3, 4]) }}"
+  tasks:
+    - debug: msg="value is {{ value }}"
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/consul_kv.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/consul_kv.yaml
new file mode 100644
index 0000000..8cfee2e
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/consul_kv.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  tasks:
+    - debug: msg='key contains {{item}}'
+      with_consul_kv:
+        - 'key/to/retrieve'
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/credstash.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/credstash.yaml
new file mode 100644
index 0000000..1a59281
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/credstash.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  tasks:
+    - debug: msg='key contains {{item}}'
+      with_credstash:
+        - 'key'
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_bad.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_bad.yaml
new file mode 100644
index 0000000..66e1d84
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_bad.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  vars:
+    value: "{{ lookup('csvfile', 'a file=/etc/passwd') }}"
+  tasks:
+    - debug: msg="value is {{ value }}"
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_good.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_good.yaml
new file mode 100644
index 0000000..74ef51e
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/csvfile_good.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  vars:
+    value: "{{ lookup('csvfile', 'a file=test.csv delimiter=,') }}"
+  tasks:
+    - debug: msg="value is {{ value }}"
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/passwd.yaml b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/passwd.yaml
new file mode 100644
index 0000000..cc74802
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/passwd.yaml
@@ -0,0 +1,5 @@
+- hosts: all
+  vars:
+    value: "{{ lookup('file', '/etc/passwd') }}"
+  tasks:
+    - debug: msg="value is {{ value }}"
diff --git a/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/test.csv b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/test.csv
new file mode 100644
index 0000000..b2ffb02
--- /dev/null
+++ b/tests/fixtures/config/ansible/git/org_plugin-project/playbooks/test.csv
@@ -0,0 +1 @@
+a,b,c
diff --git a/tests/fixtures/config/ansible/main.yaml b/tests/fixtures/config/ansible/main.yaml
index 9ccece9..a4e32c2 100644
--- a/tests/fixtures/config/ansible/main.yaml
+++ b/tests/fixtures/config/ansible/main.yaml
@@ -6,4 +6,5 @@
           - common-config
         untrusted-projects:
           - org/project
+          - org/plugin-project
           - bare-role
diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py
index 8555208..7038471 100755
--- a/tests/unit/test_v3.py
+++ b/tests/unit/test_v3.py
@@ -757,6 +757,54 @@
             self.assertIn(fail.format("timeout", build_timeout.uuid), msg)
             self.assertIn(fail.format("failpost", build_failpost.uuid), msg)
 
+    def _add_job(self, job_name):
+        conf = textwrap.dedent(
+            """
+            - job:
+                name: %s
+
+            - project:
+                name: org/plugin-project
+                check:
+                  jobs:
+                    - %s
+            """ % (job_name, job_name))
+
+        file_dict = {'.zuul.yaml': conf}
+        A = self.fake_gerrit.addFakeChange('org/plugin-project', 'master', 'A',
+                                           files=file_dict)
+        self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
+        self.waitUntilSettled()
+
+    def test_plugins(self):
+        # Keep the jobdir around so we can inspect contents if an
+        # assert fails.
+        self.executor_server.keep_jobdir = True
+        # Output extra ansible info so we might see errors.
+        self.executor_server.verbose = True
+
+        count = 0
+        plugin_tests = [
+            ('passwd', 'FAILURE'),
+            ('cartesian', 'SUCCESS'),
+            ('consul_kv', 'FAILURE'),
+            ('credstash', 'FAILURE'),
+            ('csvfile_good', 'SUCCESS'),
+            ('csvfile_bad', 'FAILURE'),
+        ]
+        for job_name, result in plugin_tests:
+            count += 1
+            self._add_job(job_name)
+
+            job = self.getJobFromHistory(job_name)
+            with self.jobLog(job):
+                self.assertEqual(count, len(self.history))
+                build = self.history[-1]
+                self.assertEqual(build.result, result)
+
+        # TODOv3(jeblair): parse the ansible output and verify we're
+        # getting the exception we expect.
+
 
 class TestPrePlaybooks(AnsibleZuulTestCase):
     # A temporary class to hold new tests while others are disabled
diff --git a/zuul/ansible/lookup/csvfile.py b/zuul/ansible/lookup/csvfile.py
index 6506aa2..f3d543b 100644
--- a/zuul/ansible/lookup/csvfile.py
+++ b/zuul/ansible/lookup/csvfile.py
@@ -13,6 +13,10 @@
 # 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 csv
+
+from ansible.errors import AnsibleError
+from ansible.module_utils._text import to_native
 
 from zuul.ansible import paths
 csvfile = paths._import_ansible_lookup_plugin("csvfile")
@@ -20,6 +24,21 @@
 
 class LookupModule(csvfile.LookupModule):
 
-    def read_csv(self, filename, *args, **kwargs):
+    def read_csv(
+            self, filename, key, delimiter, encoding='utf-8',
+            dflt=None, col=1):
         paths._fail_if_unsafe(filename)
-        return super(LookupModule, self).read_csv(filename, *args, **kwargs)
+
+        # upstream csvfile read_csv does not work with python3 so
+        # carry our own version.
+        try:
+            f = open(filename, 'r')
+            creader = csv.reader(f, dialect=csv.excel, delimiter=delimiter)
+
+            for row in creader:
+                if row[0] == key:
+                    return row[int(col)]
+        except Exception as e:
+            raise AnsibleError("csvfile: %s" % to_native(e))
+
+        return dflt