From d169906c6865a70ce2670495b5fbb16bfa0a7494 Mon Sep 17 00:00:00 2001 From: Jesse Pretorius Date: Fri, 27 Apr 2018 15:15:40 +0100 Subject: [PATCH] Revise the role to properly cater to use-cases 1. Variables have been renamed to make it easier to understand their purpose. 2. Unnecessary variables have been removed. 3. The role no longer caters to installing pip packages on the host. This should never be necessary - if it is, then something should do so beforehand. 4. The expected versions of pip/virtualenv are documented and a check has been added to ensure that they exist. 5. The handler has been named to make debug logs less confusing. 6. The default storage path for venvs/wheels is no longer opinionated. If paths based on distro/architecture are required then different paths should be provided to the role. Change-Id: I9eb96e9db22f918b00456af943d81f66050107ce --- README.rst | 19 +++++ defaults/main.yml | 75 +++++++---------- handlers/main.yml | 4 +- tasks/main.yml | 21 ++--- tasks/python_venv_build.yml | 144 ++++++++++++++++++-------------- tasks/python_venv_install.yml | 57 +++++++++---- tasks/python_venv_preflight.yml | 79 ++++++++++-------- tests/inventory | 10 --- tests/test-functional.yml | 135 +++++++++++++++--------------- tests/test.yml | 40 +++++++-- tox.ini | 2 + vars/main.yml | 19 +++++ 12 files changed, 347 insertions(+), 258 deletions(-) create mode 100644 vars/main.yml diff --git a/README.rst b/README.rst index b49fb27..a1b7055 100644 --- a/README.rst +++ b/README.rst @@ -13,6 +13,25 @@ OpenStack-Ansible python_venv_build This Ansible role prepares a python venv for use in OpenStack-Ansible. +The role requires the following to be present prior to execution: + +* pip >= 7.1 (to support using the constraints option) +* virtualenv >= 13.0.0 (to support using the no-pip, no-setuptools, no-wheels + options) + +Use-cases +~~~~~~~~~ + +This role is built to cater to the following use-cases: + +# Execute a build against a build host, then serve the venvs from a web + server. +# Execute a build against the first host in a group, then serving the + venvs from the deployment host. + +References +~~~~~~~~~~ + Documentation for the project can be found at: https://docs.openstack.org/openstack-ansible-python_venv_build/latest/ diff --git a/defaults/main.yml b/defaults/main.yml index 2c453e3..9b26b01 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -19,7 +19,7 @@ # The path where venvs are extracted to # on the target host during an install, for example: -# venv_destination_path: "/openstack/venvs/myvenv" +# venv_install_destination_path: "/openstack/venvs/myvenv" # # Optional variables @@ -27,70 +27,59 @@ # Distribution packages which must be installed # on the host for the purpose of building the venv. -distro_package_list: [] +venv_build_distro_package_list: [] + +# Distribution packages which must be installed +# on the host when installing the venv. +venv_install_distro_package_list: [] # Set the package install state for packages # Options are 'present' and 'latest' -distro_package_state: "latest" -pip_package_state: "latest" +venv_distro_package_state: "latest" +venv_pip_package_state: "latest" # The time in seconds that the distribution package # cache is valid for. This is only used by the apt # and zypper package managers. -distro_cache_valid_time: 600 - -# Python packages which must be installed -# on to the host for the purpose of building -# the venv. -host_pip_packages: [] - -# Arguments to pass to the installation -# of pip packages on the host. -host_pip_install_args: "" +venv_distro_cache_valid_time: 600 # Python packages which must be installed # into the venv. venv_pip_packages: [] -# Arguments to pass to the venv build +# Arguments to pass to pip when installing into the venv venv_pip_install_args: "" -# Enable the reuse of venvs across multiple hosts. -# This sets the build process to copy the venv to -# the deployment host once it's built, then to -# re-use the venv in subsequent deployments. -venv_reuse_enable: yes +# The python executable to use for creating the venv +venv_python_executable: "python2" # Enable the ability to *only* do the build, so that -# the deploy host can be pre-populated with the venvs. -venv_reuse_build_only: no +# the build host can be pre-populated with the venvs. +venv_build_only: no + +# Enable the packaging of venvs, so that they can +# be used for installation. +venv_build_package: yes # Enable the use of wheels to speed up the venv # build process, especially when building multiple # venvs. -venv_reuse_build_wheels: yes +venv_build_wheels: yes -# The path where a built venv is stored on the By default, -# a path on the deployment host is used which ensures that -# the location separates venvs per distribution and architecture -# to prevent re-use of venvs between them. If the path set -# begins with http(s):// then the install process will -# recognise this and deploy from a URL instead of the -# deployment host. -venv_reuse_download_venv_path: "{{ lookup('env', 'HOME') | default('/opt', true) }}/cache/venvs/{{ venv_reuse_download_subfolder }}" - -# NOTE(hwoarang): ansible_distribution may return a string with spaces -# such as "openSUSE Leap" so we need to replace the space with underscore -# in order to create a more sensible repo name for the distro. -venv_reuse_download_subfolder: "{{ (ansible_distribution | lower) | replace(' ', '_') }}-{{ ansible_distribution_version.split('.')[:2] | join('.') }}-{{ ansible_architecture | lower }}" - -# The owner of the venv_reuse_download_venv_path if it is -# housed on the deployment host. -venv_reuse_download_path_owner: "{{ lookup('env', 'USER') | default('root', true) }}" - -# The path where the wheels are cached on the target host +# The path where the wheels are cached on the build host # for speeding up the build process. -venv_wheel_destination_path: "/var/cache/python_wheels" +venv_build_wheel_path: "{{ lookup('env', 'HOME') }}/archive/wheels" + +# The path where the venvs are archived on the build host +# to be reused for installations. +venv_build_archive_path: "{{ lookup('env', 'HOME') }}/archive/venvs" + +# The path where a built venv is sourced from when +# installing. If the path set begins with https(s):// +# then the install process will recognise the source +# is a web server. Otherwise the path is assumed to +# be on the deployment host. +venv_install_source_path: "{{ lookup('env', 'HOME') }}/archive/venvs" # The facts to set when the venv changes during a # build, or the installation of a venv. diff --git a/handlers/main.yml b/handlers/main.yml index eec181a..8a88c3c 100644 --- a/handlers/main.yml +++ b/handlers/main.yml @@ -13,6 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -- meta: noop - listen: venv changed +- name: venv changed + meta: noop when: false diff --git a/tasks/main.yml b/tasks/main.yml index ffe3d93..d3268cd 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -20,27 +20,28 @@ # To ensure that only the first host in every distro/architecture # group does the build, and the others make use of that build, # without forcing the use of serialisation, we use a dynamic -# group based on the venv_reuse_download_subfolder which combines +# group based on the venv_distro_arch_grouping which combines # the distribution/architecture and is therefore unique for mixed # environments. - name: Add hosts to dynamic inventory group to ensure build/install serialization group_by: - key: "{{ venv_reuse_download_subfolder }}" + key: "{{ venv_distro_arch_grouping }}" + tags: + - always - include_tasks: "python_venv_build.yml" when: - - (not _src_venv_present.stat.exists | bool) or - (not venv_reuse_enable | bool) - - inventory_hostname == groups[venv_reuse_download_subfolder][0] + - (venv_build_only | bool) or + ((_venv_source == 'file') and + (not _src_venv_present.stat.exists | bool)) + - inventory_hostname == groups[venv_distro_arch_grouping][0] - include_tasks: "python_venv_install.yml" when: - - venv_reuse_enable | bool - - not venv_reuse_build_only | bool - - (_src_venv_present.stat.exists | bool) or - inventory_hostname != groups[venv_reuse_download_subfolder][0] + - not venv_build_only | bool + - inventory_hostname != groups[venv_distro_arch_grouping][0] - include_tasks: "python_venv_set_facts.yml" when: - - not venv_reuse_build_only | bool + - not venv_build_only | bool - venv_facts_when_changed != [] diff --git a/tasks/python_venv_build.yml b/tasks/python_venv_build.yml index 1d11338..809e936 100644 --- a/tasks/python_venv_build.yml +++ b/tasks/python_venv_build.yml @@ -15,44 +15,44 @@ - name: Install distro packages for venv build package: - name: "{{ distro_package_list }}" - state: "{{ distro_package_state }}" + name: "{{ venv_build_distro_package_list + venv_install_distro_package_list }}" + state: "{{ venv_distro_package_state }}" update_cache: "{{ (ansible_pkg_mgr in ['apt', 'zypper']) | ternary('yes', omit) }}" cache_valid_time: "{{ (ansible_pkg_mgr == 'apt') | ternary(distro_cache_valid_time, omit) }}" - when: distro_package_list | length > 0 + when: + - (venv_build_distro_package_list | length > 0) or + (venv_install_distro_package_list | length > 0) register: _install_distro_packages until: _install_distro_packages | success retries: 5 delay: 2 -- name: Install pip packages on the host for venv build - pip: - name: "{{ host_pip_packages }}" - state: "{{ pip_package_state }}" - extra_args: "{{ host_pip_install_args }}" - when: host_pip_packages | length > 0 - register: _install_host_pip_packages - until: _install_host_pip_packages | success - retries: 5 - delay: 2 +- name: Ensure a fresh venv_install_destination_path if venv_build_only is enabled + file: + path: "{{ venv_install_destination_path }}" + state: absent + when: + - venv_build_only | bool - name: Create wheel/venv directories on the target host file: path: "{{ item }}" state: directory with_items: - - "{{ venv_destination_path }}" - - "{{ venv_wheel_destination_path }}" + - "{{ venv_build_archive_path }}" + - "{{ venv_build_wheel_path }}" + - "{{ venv_install_destination_path }}" - name: Build wheels for the packages to be installed into the venv command: >- pip wheel - --wheel-dir {{ venv_wheel_destination_path }}/ - --find-links {{ venv_wheel_destination_path }}/ + --wheel-dir {{ venv_build_wheel_path }}/ + --find-links {{ venv_build_wheel_path }}/ --log /var/log/python_wheel_build.log {{ venv_pip_install_args }} {{ venv_pip_packages | join(' ') }} - when: venv_reuse_build_wheels | bool + when: + - venv_build_wheels | bool #TODO(odyssey4me): # Split the venv build into multiple parts: @@ -64,11 +64,12 @@ - name: Build venv pip: name: "{{ venv_pip_packages }}" - state: "{{ pip_package_state }}" - virtualenv: "{{ venv_destination_path }}" + state: "{{ venv_pip_package_state }}" + virtualenv: "{{ venv_install_destination_path }}" virtualenv_site_packages: "no" + virtualenv_python: "{{ venv_python_executable }}" extra_args: >- - --find-links {{ venv_wheel_destination_path }}/ + --find-links {{ venv_build_wheel_path }}/ --log /var/log/python_venv_build.log {{ venv_pip_install_args }} register: _install_venv_pip_packages @@ -78,53 +79,66 @@ notify: - venv changed -- name: Package the venv when venv_reuse_enable is enabled +- name: Package the venv when venv_build_package is enabled when: - - venv_reuse_enable | bool + - venv_build_package | bool block: - - name: Clean up the virtualenv before packaging - shell: | - find {{ venv_destination_path }}/bin -type f -name '*.pyc' -delete - when: - - _install_venv_pip_packages is mapping - - _install_venv_pip_packages | changed + - name: Clean up the virtualenv before packaging + shell: | + find {{ venv_install_destination_path }}/bin -type f -name '*.pyc' -delete + when: + - _install_venv_pip_packages is mapping + - _install_venv_pip_packages | changed - # Note(odyssey4me): - # We purposefully use shell instead of the archive module - # here. The archive module's output is far too verbose to - # be practical when debugging. - - name: Package venv - command: | - tar czf '{{ venv_destination_path }}.tgz' -C '{{ venv_destination_path }}' . - args: - chdir: "{{ venv_destination_path }}" - warn: no - register: _venv_package_build - when: - - _install_venv_pip_packages is mapping - - _install_venv_pip_packages | changed + # Note(odyssey4me): + # We purposefully use shell instead of the archive module + # here. The archive module's output is far too verbose to + # be practical when debugging. + - name: Package venv + command: | + tar czf '{{ venv_build_archive_path }}/{{ venv_install_destination_path | basename }}.tgz' -C '{{ venv_install_destination_path }}' . + args: + chdir: "{{ venv_install_destination_path }}" + warn: no + register: _venv_package_build + when: + - _install_venv_pip_packages is mapping + - _install_venv_pip_packages | changed - - name: Prepare checksum for packaged venv - shell: | - sha1sum '{{ venv_destination_path }}.tgz' | awk '{print $1}' > '{{ venv_destination_path }}.checksum' - args: - executable: /bin/bash - when: - - _venv_package_build is mapping - - _venv_package_build | changed + - name: Prepare checksum for packaged venv + shell: | + sha1sum '{{ venv_build_archive_path }}/{{ venv_install_destination_path | basename }}.tgz' | awk '{print $1}' > '{{ venv_build_archive_path }}/{{ venv_install_destination_path | basename }}.checksum' + args: + executable: /bin/bash + when: + - _venv_package_build is mapping + - _venv_package_build | changed - - name: Copy the packaged venv and checksum file to the deployment host - fetch: - src: "{{ item.src }}" - dest: "{{ item.dest }}" - flat: yes - with_items: - - src: "{{ venv_destination_path }}.tgz" - dest: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.tgz" - - src: "{{ venv_destination_path }}.checksum" - dest: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.checksum" - when: - - _venv_source == 'file' - - _venv_package_build is mapping - - _venv_package_build | changed + - name: Ensure that venv_install_source_path exists on the deployment host + file: + path: "{{ venv_install_source_path }}" + state: directory + owner: "{{ lookup('env', 'USER') }}" + recurse: yes + delegate_to: localhost + run_once: yes + when: + - _venv_source == 'file' + - inventory_hostname != 'localhost' + + - name: Copy the packaged venv and checksum file to the deployment host + fetch: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + flat: yes + with_items: + - src: "{{ venv_build_archive_path }}/{{ venv_install_destination_path | basename }}.tgz" + dest: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.tgz" + - src: "{{ venv_build_archive_path }}/{{ venv_install_destination_path | basename }}.checksum" + dest: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.checksum" + when: + - _venv_source == 'file' + - _venv_package_build is mapping + - _venv_package_build | changed + - inventory_hostname != 'localhost' diff --git a/tasks/python_venv_install.yml b/tasks/python_venv_install.yml index f4c8961..13b4ae0 100644 --- a/tasks/python_venv_install.yml +++ b/tasks/python_venv_install.yml @@ -13,34 +13,59 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Copy the venv checksum file to the target host +- name: Install distro packages for venv install + package: + name: "{{ venv_install_distro_package_list }}" + state: "{{ venv_distro_package_state }}" + update_cache: "{{ (ansible_pkg_mgr in ['apt', 'zypper']) | ternary('yes', omit) }}" + cache_valid_time: "{{ (ansible_pkg_mgr == 'apt') | ternary(distro_cache_valid_time, omit) }}" + when: + - venv_install_distro_package_list | length > 0 + register: _install_distro_packages + until: _install_distro_packages | success + retries: 5 + delay: 2 + +- name: Create venv parent directories on the target host + file: + path: "{{ venv_install_destination_path | dirname }}" + state: directory + +- name: Copy the venv archive checksum copy: - src: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.checksum" - dest: "{{ venv_destination_path | dirname }}/" + src: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.checksum" + dest: "{{ venv_install_destination_path | dirname }}/" register: _venv_checksum_copy when: - _venv_source == 'file' -- name: Retrieve checksum for venv download +- name: Copy the venv archive + copy: + src: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.tgz" + dest: "{{ venv_install_destination_path | dirname }}/" + when: + - _venv_source == 'file' + +- name: Download the venv archive checksum uri: - url: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.checksum" - dest: "{{ venv_destination_path | dirname }}/" + url: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.checksum" + dest: "{{ venv_install_destination_path | dirname }}/" return_content: yes register: _venv_checksum_download when: - _venv_source == 'url' -- name: Attempt venv download +- name: Download the venv archive get_url: - url: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.tgz" - dest: "{{ venv_destination_path | dirname }}/" + url: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.tgz" + dest: "{{ venv_install_destination_path | dirname }}/" checksum: "sha1:{{ _venv_checksum_download.content | trim }}" when: - _venv_source == 'url' - name: Remove existing venv on target host if it is changing file: - path: "{{ venv_destination_path }}" + path: "{{ venv_install_destination_path }}" state: absent when: - (_venv_checksum_copy is mapping and _venv_checksum_copy | changed) or @@ -48,15 +73,15 @@ - name: Create venv directory on the target host file: - path: "{{ venv_destination_path }}" + path: "{{ venv_install_destination_path }}" state: directory register: _venv_dir_create - name: Unarchive pre-built venv unarchive: - src: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.tgz" - dest: "{{ venv_destination_path }}" - remote_src: no + src: "{{ venv_install_destination_path }}.tgz" + dest: "{{ venv_install_destination_path }}" + remote_src: yes when: - (_venv_checksum_copy is mapping and _venv_checksum_copy | changed) or (_venv_checksum_download is mapping and _venv_checksum_download | changed) or @@ -73,8 +98,8 @@ # to https://github.com/pypa/virtualenv/issues/565 - name: Update virtualenv python and paths shell: | - sed -si '1s/^.*python.*$/#!{{ (venv_destination_path ~ '/bin') | replace ('/','\/') }}\/python/' {{ venv_destination_path }}/bin/* - virtualenv {{ venv_destination_path }} \ + sed -si '1s/^.*python.*$/#!{{ (venv_install_destination_path ~ '/bin') | replace ('/','\/') }}\/python/' {{ venv_install_destination_path }}/bin/* + virtualenv {{ venv_install_destination_path }} \ {{ (ansible_pkg_mgr == 'apt') | ternary('--always-copy', '') }} \ --no-pip \ --no-setuptools \ diff --git a/tasks/python_venv_preflight.yml b/tasks/python_venv_preflight.yml index 7c04d9d..259d4de 100644 --- a/tasks/python_venv_preflight.yml +++ b/tasks/python_venv_preflight.yml @@ -13,43 +13,59 @@ # See the License for the specific language governing permissions and # limitations under the License. -- name: Verify that venv_destination_path has been provided +- name: Verify that venv_install_destination_path has been provided fail: msg: | - The variable venv_destination_path is required and - has not been set + The variable venv_install_destination_path is required and + has not been set. when: - - venv_destination_path is not defined + - venv_install_destination_path is not defined -- name: Verify that venv_reuse_enable and venv_reuse_build_only are not interfering with each other +- name: Collect the version of virtualenv + shell: | + virtualenv --version 2>/dev/null || echo 'none' + args: + executable: /bin/bash + changed_when: false + failed_when: false + register: _virtualenv_version + +- name: Collect the version of pip + shell: | + pip --version 2>/dev/null | awk '{print $2}' || echo 'none' + args: + executable: /bin/bash + changed_when: false + failed_when: false + register: _pip_version + +- name: Fail when required virtualenv version is not present fail: - msg: | - If venv_reuse_build_only is enabled, then venv_reuse_enable must also - be enabled. - The variable venv_reuse_enable is set to {{ venv_reuse_enable }}. - The variable venv_reuse_build_only is set to {{ venv_reuse_build_only }}. + msg: >- + The required virtualenv version is not present. + The minimum version of 13.0.0 is required, but + {{ _virtualenv_version.stdout }} is installed. when: - - venv_reuse_build_only | bool - - not venv_reuse_enable | bool + - ((_virtualenv_version.stdout | trim) == 'none') or + ((_virtualenv_version.stdout | trim) is version_compare('13.0.0', '<')) -- name: Check whether the venv_reuse_download_venv_path is a URL or a file path +- name: Fail when required pip version is not present + fail: + msg: >- + The required virtualenv version is not present. + The minimum version of 7.1 is required, but + {{ _pip_version.stdout }} is installed. + when: + - ((_pip_version.stdout | trim) == 'none') or + ((_pip_version.stdout | trim) is version_compare('7.1', '<')) + +- name: Check whether the venv_install_source_path is a URL or a file path set_fact: - _venv_source: "{{ (venv_reuse_download_venv_path is match('^https?://.*')) | ternary('url', 'file') }}" - -- name: Ensure that venv_reuse_download_path exists on the deployment host - file: - path: "{{ venv_reuse_download_venv_path }}" - state: directory - owner: "{{ venv_reuse_download_path_owner }}" - delegate_to: localhost - run_once: yes - when: - - _venv_source == 'file' - - venv_reuse_enable | bool + _venv_source: "{{ ((venv_install_source_path | trim) is match('^https?://.*')) | ternary('url', 'file') }}" - name: Check if venv tgz is present on the deployment host stat: - path: "{{ venv_reuse_download_venv_path }}/{{ venv_destination_path | basename }}.tgz" + path: "{{ venv_install_source_path }}/{{ venv_install_destination_path | basename }}.tgz" get_attributes: no get_checksum: no get_md5: no @@ -59,14 +75,3 @@ run_once: yes when: - _venv_source == 'file' - - venv_reuse_enable | bool - -- name: Ensure that virtualenv is installed on the destination host - pip: - name: virtualenv - state: "{{ pip_package_state }}" - extra_args: "{{ host_pip_install_args }}" - register: _install_host_pip_virtualenv - until: _install_host_pip_virtualenv | success - retries: 5 - delay: 2 diff --git a/tests/inventory b/tests/inventory index adb0bbb..47621f2 100644 --- a/tests/inventory +++ b/tests/inventory @@ -3,18 +3,8 @@ localhost container1 container2 container3 -container4 [all_containers] container1 container2 container3 -container4 - -[test1] -container1 -container2 - -[test2] -container3 -container4 diff --git a/tests/test-functional.yml b/tests/test-functional.yml index 811e15e..6994101 100644 --- a/tests/test-functional.yml +++ b/tests/test-functional.yml @@ -14,7 +14,7 @@ # limitations under the License. - name: Clean up from previous tests - hosts: "{{ target_host_group_name }}:localhost" + hosts: "{{ build_host }}:{{ install_hosts }}" become: yes any_errors_fatal: yes tasks: @@ -22,42 +22,87 @@ file: path: "{{ item.path }}" state: absent - when: item.condition | default(omit) with_items: - - path: "{{ lookup('env', 'HOME') | default('/opt', true) }}/venvs" - - path: "{{ lookup('env', 'HOME') | default('/opt', true) }}/cache/venvs" - - path: "{{ lookup('env', 'HOME') | default('/opt', true) }}/cache/wheels" - condition: "{{ target_host_group_name == 'test1' }}" - - path: "/var/cache/python_wheels" + - path: "{{ lookup('env', 'HOME') }}/archive" + - path: "{{ lookup('env', 'HOME') }}/venvs" - name: Clean up facts from previous tests ini_file: path: "/etc/ansible/facts.d/openstack_ansible.fact" - section: "{{ target_host_group_name }}" + section: "{{ item }}" state: absent + with_items: "{{ build_host.split(':') + install_hosts.split(':') }}" - name: Refresh the inventory to clear the added groups meta: refresh_inventory -- name: Execute role test and verify target host content - hosts: "{{ target_host_group_name }}" + - name: Set venv_build_archive_path and venv_install_source_path + set_fact: + venv_build_archive_path: >- + {%- if build_host == "container1" %} + {%- if ansible_distribution == "Ubuntu" %} + {%- set _path = "/var/www/html" %} + {%- elif ansible_distribution == "CentOS" %} + {%- set _path = "/usr/share/nginx/html" %} + {%- else %} + {%- set _path = "/srv/www/htdocs" %} + {%- endif %} + {%- else -%} + {%- set _path = lookup('env', 'HOME') ~ "/archive/venvs" -%} + {%- endif -%} + {{- _path }} + venv_install_source_path: >- + {%- if build_host == "container1" -%} + http://{{ hostvars['container1'].ansible_default_ipv4.address }} + {%- else -%} + {{- lookup('env', 'HOME') }}/archive/venvs + {%- endif -%} + + - name: Install distro packages + package: + name: "nginx" + update_cache: "{{ (ansible_pkg_mgr in ['apt', 'zypper']) | ternary('yes', omit) }}" + register: install + until: install | success + retries: 5 + delay: 2 + when: + - inventory_hostname == 'container1' + + - name: Enable and start nginx + service: + name: nginx + enabled: yes + daemon_reload: yes + state: restarted + when: + - inventory_hostname == 'container1' + +- name: Execute build + hosts: "{{ build_host }}" become: yes any_errors_fatal: yes - serial: "{{ target_serial }}" - vars: - base_directory: "{{ lookup('env', 'HOME') | default('/opt', true) }}" tasks: - - - name: Execute role + - name: Execute venv build + include_role: + name: "python_venv_build" + private: yes + vars: + venv_build_only: yes + +- name: Execute install + hosts: "{{ install_hosts }}" + become: yes + any_errors_fatal: yes + tasks: + + - name: Execute venv install include_role: name: "python_venv_build" private: yes vars: - venv_pip_packages: - - "Jinja2==2.10" - venv_destination_path: "{{ base_directory }}/venvs/{{ target_host_group_name }}" venv_facts_when_changed: - - section: "{{ target_host_group_name }}" + - section: "{{ inventory_hostname }}" option: "test" value: True @@ -73,7 +118,7 @@ - name: Verify that the facts were set assert: that: - - ansible_local['openstack_ansible'][target_host_group_name]['test'] | bool + - ansible_local['openstack_ansible'][inventory_hostname]['test'] | bool - name: Find files/folders on targets find: @@ -81,17 +126,12 @@ get_checksum: no recurse: no paths: - - "{{ base_directory }}/venvs" + - "{{ venv_install_destination_path | dirname }}" register: _target_folders - name: Compile the folder list from the targets set_fact: - _target_folder_list: >- - {%- set folder_list = [] %} - {%- for item in _target_folders['files'] %} - {%- set _ = folder_list.append(item['path']) %} - {%- endfor %} - {{ folder_list }} + _target_folder_list: "{{ _target_folders['files'] | map(attribute='path') | list }}" - name: Show the files/folder from the targets debug: @@ -100,43 +140,4 @@ - name: Verify the folder list from the targets assert: that: - - "{{ base_directory ~ '/venvs/' ~ target_host_group_name in _target_folder_list }}" - -- name: Verify deploy host content - hosts: localhost - connection: local - become: yes - any_errors_fatal: yes - vars: - base_directory: "{{ lookup('env', 'HOME') | default('/opt', true) }}" - sub_directory: "{{ (ansible_distribution | lower) | replace(' ', '_') }}-{{ ansible_distribution_version.split('.')[:2] | join('.') }}-{{ ansible_architecture | lower }}" - package_file_path: "{{ base_directory }}/cache/venvs/{{ sub_directory }}/{{ target_host_group_name }}" - tasks: - - - name: Find files/folders on deploy host - find: - file_type: any - get_checksum: no - recurse: yes - paths: - - "{{ base_directory }}/cache" - register: _localhost_folders - - - name: Compile the folder list from the deploy host - set_fact: - _localhost_folder_list: >- - {%- set folder_list = [] %} - {%- for item in _localhost_folders['files'] %} - {%- set _ = folder_list.append(item['path']) %} - {%- endfor %} - {{ folder_list }} - - - name: Show the files/folders from the deploy host - debug: - var: _localhost_folder_list - - - name: Verify the folder list from the deploy host - assert: - that: - - "{{ package_file_path ~ '.tgz' in _localhost_folder_list }}" - - "{{ package_file_path ~ '.checksum' in _localhost_folder_list }}" + - "{{ venv_install_destination_path in _target_folder_list }}" diff --git a/tests/test.yml b/tests/test.yml index 5a46348..9c0200c 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -16,14 +16,38 @@ - name: Prepare the host/containers include: common/test-setup-host.yml -- name: Verify that the role works with a serial playbook - include: test-functional.yml +- name: Verify building on a web server, and installing using a URL + import_playbook: test-functional.yml vars: - target_host_group_name: test1 - target_serial: 1 + build_host: container1 + install_hosts: "container2:container3" + venv_pip_packages: + - "Jinja2==2.10" + venv_install_destination_path: "{{ lookup('env', 'HOME') }}/venvs/test-venv" -- name: Verify that the role works with a parallel playbook - include: test-functional.yml +- name: Verify building on localhost, and installing using a copy + import_playbook: test-functional.yml vars: - target_host_group_name: test2 - target_serial: "100%" + build_host: localhost + install_hosts: "container1:container2:container3" + venv_pip_packages: + - "Jinja2==2.10" + venv_install_destination_path: "{{ lookup('env', 'HOME') }}/venvs/test-venv" + +- name: Verify building on a build host, and installing using a copy + import_playbook: test-functional.yml + vars: + build_host: container2 + install_hosts: "container1:container3" + venv_pip_packages: + - "Jinja2==2.10" + venv_install_destination_path: "{{ lookup('env', 'HOME') }}/venvs/test-venv" + +- name: Verify setting multiple build hosts behaves correctly + import_playbook: test-functional.yml + vars: + build_host: "localhost:container1" + install_hosts: "container2:container3" + venv_pip_packages: + - "Jinja2==2.10" + venv_install_destination_path: "{{ lookup('env', 'HOME') }}/venvs/test-venv" diff --git a/tox.ini b/tox.ini index 19dcf89..9dfc784 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,8 @@ setenv = ROLE_NAME=python_venv_build VIRTUAL_ENV={envdir} WORKING_DIR={toxinidir} + # TODO(odyssey4me): remove after debugging is completed + ANSIBLE_PARAMETERS=-v [testenv:docs] diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..02b28e6 --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,19 @@ +--- +# Copyright 2018, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# NOTE(hwoarang): ansible_distribution may return a string with spaces +# such as "openSUSE Leap" so we need to replace the space with underscore +# in order to create a more sensible repo name for the distro. +venv_distro_arch_grouping: "{{ (ansible_distribution | lower) | replace(' ', '_') }}-{{ ansible_distribution_version.split('.')[:2] | join('.') }}-{{ ansible_architecture | lower }}"