From 08bbd16c52f943b34b0d8e7ca6b747935ad70007 Mon Sep 17 00:00:00 2001
From: Ian Wienand <iwienand@redhat.com>
Date: Mon, 27 Apr 2020 14:57:12 +1000
Subject: [PATCH] Add ensure-virtualenv

This is part of the efforts to remove pip-and-virtualenv from our base
images [1].

There are some users who specifically require the virtualenv command
(perhaps, like dib, they have some code that uses the activate_this.py
mechanisms it provides wich venv does not).

This installs the virtualenv package for the currently running
distribution.

One of the main maintenance issues of pip-and-virtualenv is that tried
to ensure that "virtualenv" created a Python 2 environment always by
default.  Now that we have Python 3 only distributions like current
Fedora, this is not something we can continue to do (even if we wanted
to, which we don't).

What owns virtualenv and what it produces in our heterogeneous
environment is messy, and I think the best we can do is document it as
done here.

[1] https://docs.opendev.org/opendev/infra-specs/latest/specs/cleanup-test-node-python.html

Change-Id: I97d8bfb970ed2b5aaa02a0813899014c94994066
---
 doc/source/python-roles.rst                 |  1 +
 roles/ensure-virtualenv/README.rst          | 28 +++++++++++++++++++++
 roles/ensure-virtualenv/tasks/CentOS-7.yaml |  6 +++++
 roles/ensure-virtualenv/tasks/Debian.yaml   |  5 ++++
 roles/ensure-virtualenv/tasks/Gentoo.yaml   |  5 ++++
 roles/ensure-virtualenv/tasks/RedHat.yaml   |  6 +++++
 roles/ensure-virtualenv/tasks/Suse.yaml     |  4 +++
 roles/ensure-virtualenv/tasks/default.yaml  |  3 +++
 roles/ensure-virtualenv/tasks/main.yaml     | 18 +++++++++++++
 test-playbooks/ensure-pip.yaml              | 27 +++++++++++++++++---
 zuul-tests.d/python-jobs.yaml               |  1 +
 11 files changed, 101 insertions(+), 3 deletions(-)
 create mode 100644 roles/ensure-virtualenv/README.rst
 create mode 100644 roles/ensure-virtualenv/tasks/CentOS-7.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/Debian.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/Gentoo.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/RedHat.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/Suse.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/default.yaml
 create mode 100644 roles/ensure-virtualenv/tasks/main.yaml

diff --git a/doc/source/python-roles.rst b/doc/source/python-roles.rst
index b7d871e26..8089c2b2d 100644
--- a/doc/source/python-roles.rst
+++ b/doc/source/python-roles.rst
@@ -10,6 +10,7 @@ Python Roles
 .. zuul:autorole:: ensure-sphinx
 .. zuul:autorole:: ensure-tox
 .. zuul:autorole:: ensure-twine
+.. zuul:autorole:: ensure-virtualenv
 .. zuul:autorole:: fetch-coverage-output
 .. zuul:autorole:: fetch-python-sdist-output
 .. zuul:autorole:: fetch-sphinx-output
diff --git a/roles/ensure-virtualenv/README.rst b/roles/ensure-virtualenv/README.rst
new file mode 100644
index 000000000..f3a64b0ae
--- /dev/null
+++ b/roles/ensure-virtualenv/README.rst
@@ -0,0 +1,28 @@
+Ensure virtualenv is available
+
+This role installs the requirements for the ``virtualenv`` command
+on the current distribution.
+
+Users should be aware of some portability issues when using
+``virtualenv``:
+
+* Distributions differ on the interpreter that ``virtualenv`` is
+  provided by, so by calling ``virtualenv`` with no other arguments
+  means that on some platforms you will get a Python 2 environment and
+  others a Python 3 environment.
+* If you wish to call ``virtualenv`` as a module (e.g. ``python -m
+  virtualenv``) you will need to know which interpreter owns the
+  ``virtualenv`` package for your distribution; e.g. on some, such as
+  Bionic, ``virtualenv`` is provided by ``python3-virtualenv`` but
+  ``python`` refers to Python 2, so ``python -m virtualenv`` is not a
+  portable way to call ``virtualenv``.
+* ``virtualenv -p python3`` is likely the most portable way to
+  consistently get a Python 3 environment.  ``virtualenv -p python2``
+  may not work on some platforms without Python 2.
+* If you use Python 3 and do not require the specific features of
+  ``virtualenv``, it is likely easier to use Python's inbuilt
+  ``python3 -m venv`` module to create an isolated environment.  If
+  you are using ``pip:`` in your Ansible roles and require an
+  environment, see the documentation for :zuul:role:`ensure-pip`.
+
+
diff --git a/roles/ensure-virtualenv/tasks/CentOS-7.yaml b/roles/ensure-virtualenv/tasks/CentOS-7.yaml
new file mode 100644
index 000000000..602c2ce25
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/CentOS-7.yaml
@@ -0,0 +1,6 @@
+- name: Install virtualenv
+  package:
+    name:
+      - python-virtualenv
+  become: yes
+
diff --git a/roles/ensure-virtualenv/tasks/Debian.yaml b/roles/ensure-virtualenv/tasks/Debian.yaml
new file mode 100644
index 000000000..b2d498b0b
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/Debian.yaml
@@ -0,0 +1,5 @@
+- name: Install virtualenv
+  package:
+    name:
+      - virtualenv
+  become: yes
diff --git a/roles/ensure-virtualenv/tasks/Gentoo.yaml b/roles/ensure-virtualenv/tasks/Gentoo.yaml
new file mode 100644
index 000000000..8ff8c8945
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/Gentoo.yaml
@@ -0,0 +1,5 @@
+- name: Install virtualenv
+  package:
+    name: dev-python/virtualenv
+  become: yes
+
diff --git a/roles/ensure-virtualenv/tasks/RedHat.yaml b/roles/ensure-virtualenv/tasks/RedHat.yaml
new file mode 100644
index 000000000..98f7a071b
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/RedHat.yaml
@@ -0,0 +1,6 @@
+- name: Install virtualenv
+  package:
+    name:
+      - python3-virtualenv
+  become: yes
+
diff --git a/roles/ensure-virtualenv/tasks/Suse.yaml b/roles/ensure-virtualenv/tasks/Suse.yaml
new file mode 100644
index 000000000..8e129ce2b
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/Suse.yaml
@@ -0,0 +1,4 @@
+- name: Install virtualenv
+  package:
+    name: python3-virtualenv
+  become: yes
diff --git a/roles/ensure-virtualenv/tasks/default.yaml b/roles/ensure-virtualenv/tasks/default.yaml
new file mode 100644
index 000000000..fd6bb8fbb
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/default.yaml
@@ -0,0 +1,3 @@
+- name: Unsupported platform
+  fail:
+    msg: 'This platform is currently unsupported'
diff --git a/roles/ensure-virtualenv/tasks/main.yaml b/roles/ensure-virtualenv/tasks/main.yaml
new file mode 100644
index 000000000..ede55216f
--- /dev/null
+++ b/roles/ensure-virtualenv/tasks/main.yaml
@@ -0,0 +1,18 @@
+- name: Check if virtualenv is installed
+  shell: |
+    command -v virtualenv || exit 1
+  args:
+    executable: /bin/bash
+  register: virtualenv_preinstalled
+  failed_when: false
+
+- name: Install virtualenv package
+  include: "{{ item }}"
+  with_first_found:
+    - "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yaml"
+    - "{{ ansible_distribution_release }}.yaml"
+    - "{{ ansible_distribution }}.yaml"
+    - "{{ ansible_os_family }}.yaml"
+    - "default.yaml"
+  when:
+    - virtualenv_preinstalled.rc != 0
diff --git a/test-playbooks/ensure-pip.yaml b/test-playbooks/ensure-pip.yaml
index 8fb39a02c..498dbe4ab 100644
--- a/test-playbooks/ensure-pip.yaml
+++ b/test-playbooks/ensure-pip.yaml
@@ -1,7 +1,9 @@
 - hosts: all
-  roles:
-    - ensure-pip
   tasks:
+    - name: Include ensure-pip
+      include_role:
+        name: ensure-pip
+
     - name: Sanity check provided virtualenv command works
       shell: |
         tmp_venv=$(mktemp -d -t venv-XXXXXXXXXX)
@@ -11,12 +13,31 @@
       failed_when: false
       register: _venv_sanity
 
-    - name: Assert sanity check
+    - name: Assert pip venv sanity check
       fail:
         msg: 'The virtualenv_command: "{{ ensure_pip_virtualenv_command }}" does not appear to work!'
       when:
         - _venv_sanity.rc != 0
 
+    - name: Include ensure-virtualenv
+      include_role:
+        name: ensure-virtualenv
+
+    - name: Sanity check virtualenv command works
+      shell: |
+        tmp_venv=$(mktemp -d -t venv-XXXXXXXXXX)
+        trap "rm -rf $tmp_venv" EXIT
+        virtualenv $tmp_venv
+        $tmp_venv/bin/pip install tox
+      failed_when: false
+      register: _virtualenv_sanity
+
+    - name: Assert sanity check
+      fail:
+        msg: 'The virtualenv command does not appear to work!'
+      when:
+        - _virtualenv_sanity.rc != 0
+
 # NOTE(ianw) : this does not play nicely with pip-and-virtualenv which
 # has already installed from source.  We might be able to test this
 # once it's gone...
diff --git a/zuul-tests.d/python-jobs.yaml b/zuul-tests.d/python-jobs.yaml
index eb213c82e..79eb4d6eb 100644
--- a/zuul-tests.d/python-jobs.yaml
+++ b/zuul-tests.d/python-jobs.yaml
@@ -3,6 +3,7 @@
     description: Test the ensure-pip role
     files:
       - roles/ensure-pip/.*
+      - roles/ensure-virtualenv/.*
     run: test-playbooks/ensure-pip.yaml
     tags: all-platforms