diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst index b18ddab5d599759d617e59e862b8e4977ebe0efc..4c03089af14e139901693cde8cbfc931dbf18107 100644 --- a/docs/releasenotes.rst +++ b/docs/releasenotes.rst @@ -39,6 +39,11 @@ New features/improvements: check. E.g. mails are sent out only in case some of the configured certificates will expire within next 30 days. +* ``wsgi_website`` role + + * Support for specifying Python version for Python virtual + environment. + 2.0.0 ----- diff --git a/docs/rolereference.rst b/docs/rolereference.rst index ccd94bd0068ea43c3f230414e1515229d1b3c357..7ce18ec09981b14fff4ddc8323099ab8c8424b1e 100644 --- a/docs/rolereference.rst +++ b/docs/rolereference.rst @@ -1645,9 +1645,10 @@ The role implements the following: * Adds website administrator to website's group, so administrator could manage the code and data. * Installs additional packages required for running the role (as configured). -* Sets-up a dedicated Python virtual environment for website. +* Sets-up a dedicated Python virtual environment for website. Python + version can be specified (default is Python 2). * Install ``futures`` package in Python virtual environment (required for - Gunicorn in combination withg Python 2.7). + Gunicorn in combination with Python 2.7). * Install Gunicorn in Python virtual environment. * Installs additional packages required for running the role in Python virtual environment (as configured). @@ -1758,11 +1759,11 @@ Parameters for calculating the user/group name for dedicated website user, as well as home directory of the website user (where data/code should be stored at). -**futures_version** (string, optional, ``3.1.1``) +**futures_version** (string, optional, ``3.2.0``) Version of ``futures`` package to deploy in virtual environment. Required by Gunicorn when using Python 2.7. Default version is tested with the test site. -**gunicorn_version** (string, optional, ``19.7.1``) +**gunicorn_version** (string, optional, ``19.9.0``) Version of Gunicorn to deploy in virtual environment for running the WSGI application. Default version is tested with the test site. @@ -1786,6 +1787,9 @@ Parameters forget to surround them by another set of quotes for YAML syntax, for example ``"\"\""`` or ``'""'``). +**python_version** (string, optional, ``2``) + Python version to use when setting-up the Python virtual environment. + **rewrites** (list, optional, ``[]``) A list of rewrite rules that are applied to incoming requests. Each element of the list should be a string value compatible with the format of ``nginx`` diff --git a/roles/wsgi_website/defaults/main.yml b/roles/wsgi_website/defaults/main.yml index e6d8898bd847f20f414bccf22e173e755fdfec82..27fe4ec967bcfcb691e3ab8abf945de37ed9ecb5 100644 --- a/roles/wsgi_website/defaults/main.yml +++ b/roles/wsgi_website/defaults/main.yml @@ -10,14 +10,16 @@ virtualenv_packages: [] environment_variables: {} https_tls_certificate: "{{ lookup('file', tls_certificate_dir + '/' + fqdn + '_https.pem') }}" https_tls_key: "{{ lookup('file', tls_private_key_dir + '/' + fqdn + '_https.key') }}" -gunicorn_version: "19.7.1" -futures_version: "3.1.1" +gunicorn_version: "19.9.0" +futures_version: "3.2.0" website_mail_recipients: "root" environment_indicator: null proxy_headers: {} +python_version: 2 wsgi_requirements: [] # Internal parameters. admin: "admin-{{ fqdn | replace('.', '_') }}" user: "web-{{ fqdn | replace('.', '_') }}" home: "/var/www/{{ fqdn }}" +python_interpreter: "/usr/bin/python{{ python_version }}" diff --git a/roles/wsgi_website/molecule/default/playbook.yml b/roles/wsgi_website/molecule/default/playbook.yml index 05ee8867ba63c15c189bcb14d3b212537eda4fa4..e183f522738307ea147c0f6a7abf6592b37674c1 100644 --- a/roles/wsgi_website/molecule/default/playbook.yml +++ b/roles/wsgi_website/molecule/default/playbook.yml @@ -57,6 +57,7 @@ website_mail_recipients: user wsgi_application: testapp:application wsgi_requirements: [] + python_version: 3 - role: wsgi_website fqdn: parameters-paste-req diff --git a/roles/wsgi_website/molecule/default/tests/data/python/paste/testapp.py b/roles/wsgi_website/molecule/default/tests/data/python/paste/testapp.py index 155506151fd3f73518ab4208339128fa2a767132..50396bb6534f6090cf1b2ff58225cf9733cdfa35 100644 --- a/roles/wsgi_website/molecule/default/tests/data/python/paste/testapp.py +++ b/roles/wsgi_website/molecule/default/tests/data/python/paste/testapp.py @@ -1,4 +1,6 @@ import os +import sys + import flask from flask import Flask @@ -20,6 +22,7 @@ def index(path):

Requested URL was: {scheme}://{host}{script}{path}

MY_ENV_VAR: {my_env_var}

Accept-Encoding: {accept_encoding}

+

Python version: {python_version}

""" @@ -33,6 +36,7 @@ def index(path): parameters['path'] = environ['PATH_INFO'] parameters['my_env_var'] = os.environ.get('MY_ENV_VAR', None) parameters['accept_encoding'] = environ.get('HTTP_ACCEPT_ENCODING') + parameters['python_version'] = "%s.%s.%s" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) output = template.format(**parameters) diff --git a/roles/wsgi_website/molecule/default/tests/data/python/wsgi/testapp.py b/roles/wsgi_website/molecule/default/tests/data/python/wsgi/testapp.py index 27467133ac3512f8f9845179f4d034fba81ce524..42adc83d4de9f0da5d6821f01245a98914d0ba61 100644 --- a/roles/wsgi_website/molecule/default/tests/data/python/wsgi/testapp.py +++ b/roles/wsgi_website/molecule/default/tests/data/python/wsgi/testapp.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import os +import sys def application(environ, start_response): @@ -17,6 +18,7 @@ def application(environ, start_response):

Requested URL was: {scheme}://{host}{script}{path}

MY_ENV_VAR: {my_env_var}

Accept-Encoding: {accept_encoding}

+

Python version: {python_version}

""" @@ -28,6 +30,7 @@ def application(environ, start_response): parameters['path'] = environ['PATH_INFO'] parameters['my_env_var'] = os.environ.get('MY_ENV_VAR', None) parameters['accept_encoding'] = environ.get('HTTP_ACCEPT_ENCODING') + parameters['python_version'] = "%s.%s.%s" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) output = template.format(**parameters) @@ -35,4 +38,4 @@ def application(environ, start_response): ('Content-Length', str(len(output)))] start_response(status, response_headers) - return [output] + return [output.encode('utf-8')] diff --git a/roles/wsgi_website/molecule/default/tests/test_default.py b/roles/wsgi_website/molecule/default/tests/test_default.py index f319638d6cb6eacf2c26e4622f5e54d40a83301d..357569217ae4dbd258f2a05cfb0a078a48aaf316 100644 --- a/roles/wsgi_website/molecule/default/tests/test_default.py +++ b/roles/wsgi_website/molecule/default/tests/test_default.py @@ -298,13 +298,12 @@ def test_python_virtualenv_wrapper_script(host, wrapper_script, expected_owner, @pytest.mark.parametrize("admin_user, pip_path, expected_packages", [ ('admin-parameters-mandatory', '/var/www/parameters-mandatory/virtualenv/bin/pip', [ "argparse==1.2.1", - "futures==3.1.1", - "gunicorn==19.7.1", + "futures==3.2.0", + "gunicorn==19.9.0", "wsgiref==0.1.2" ]), ('admin-parameters-optional_local', '/var/www/parameters-optional.local/virtualenv/bin/pip', [ "Pygments==2.2.0", - "argparse==1.2.1", "dnspython==1.15.0", "docopt==0.6.2", "futures==3.1.0", @@ -315,7 +314,6 @@ def test_python_virtualenv_wrapper_script(host, wrapper_script, expected_owner, "ptpython==0.41", "six==1.11.0", "wcwidth==0.1.7", - "wsgiref==0.1.2" ]), ('admin-parameters-paste-req', '/var/www/parameters-paste-req/virtualenv/bin/pip', [ "Flask==0.12.2", @@ -538,3 +536,28 @@ def test_website_enabled(host, config_file, expected_content): assert config.is_symlink assert config.linked_to == expected_content + + +@pytest.mark.parametrize("python_path, expected_version_startswith", [ + ("/var/www/parameters-mandatory/virtualenv/bin/python", "Python 2"), + ("/var/www/parameters-optional.local/virtualenv/bin/python", "Python 3"), + ("/var/www/parameters-paste-req/virtualenv/bin/python", "Python 2"), +]) +def test_python_version_in_virtualenv(host, python_path, expected_version_startswith): + """ + Tests if correct Python version is used inside of virtual + environment. + """ + + with host.sudo(): + + python_version = host.run(python_path + " --version") + + # Python 2 outputs version to stderr, Python 3 outputs it to + # stdout. Go figure. + python_version_output = python_version.stdout + python_version.stderr + + assert python_version.rc == 0 + + # Python binary prints out version to stderr. + assert python_version_output.startswith(expected_version_startswith) diff --git a/roles/wsgi_website/molecule/default/tests/test_parameters_mandatory.py b/roles/wsgi_website/molecule/default/tests/test_parameters_mandatory.py index 630ce76d1a58890ff49eefbb521d9b05cba054d5..dfa82735c415c428d9a939f91dd267feb960bea3 100644 --- a/roles/wsgi_website/molecule/default/tests/test_parameters_mandatory.py +++ b/roles/wsgi_website/molecule/default/tests/test_parameters_mandatory.py @@ -42,6 +42,7 @@ def test_index_page(host): assert "Requested URL was: https://parameters-mandatory/" in page.stdout assert "MY_ENV_VAR: None" in page.stdout assert "Accept-Encoding: plain" in page.stdout + assert "Python version: 2." in page.stdout def test_static_file_serving(host): diff --git a/roles/wsgi_website/molecule/default/tests/test_parameters_optional.py b/roles/wsgi_website/molecule/default/tests/test_parameters_optional.py index 28cc038c7ca18d4c8cd1384b9a2f4e4c05f91802..4f467c8c4ac180878b31ab0b0113f38dc6257948 100644 --- a/roles/wsgi_website/molecule/default/tests/test_parameters_optional.py +++ b/roles/wsgi_website/molecule/default/tests/test_parameters_optional.py @@ -63,6 +63,7 @@ def test_index_page(host): assert "Requested URL was: https://parameters-optional.local/" in page.stdout assert "MY_ENV_VAR: My environment variable" in page.stdout assert "Accept-Encoding: None" in page.stdout + assert "Python version: 3." in page.stdout def test_static_file_serving(host): diff --git a/roles/wsgi_website/molecule/default/tests/test_parameters_paste_req.py b/roles/wsgi_website/molecule/default/tests/test_parameters_paste_req.py index 4d888cc2311af6b8b653eca602d59bc686c65364..36b6c9c20c1a2cca9398a8b906290d18e9272216 100644 --- a/roles/wsgi_website/molecule/default/tests/test_parameters_paste_req.py +++ b/roles/wsgi_website/molecule/default/tests/test_parameters_paste_req.py @@ -68,6 +68,7 @@ def test_index_page(host): assert "Requested URL was: https://parameters-paste-req/" in page.stdout assert "MY_ENV_VAR: None" in page.stdout assert "Accept-Encoding: plain" in page.stdout + assert "Python version: 2." in page.stdout def test_static_file_serving(host): diff --git a/roles/wsgi_website/tasks/main.yml b/roles/wsgi_website/tasks/main.yml index f0ba29149c1f0b93945d7aea3577aa4b2ba1bcd8..30f1938fdc03e36a0fd7947ad15c4b9b0dc018e4 100644 --- a/roles/wsgi_website/tasks/main.yml +++ b/roles/wsgi_website/tasks/main.yml @@ -94,7 +94,7 @@ mode: 02750 - name: Create Python virtual environment - command: '/usr/bin/virtualenv --prompt "({{ fqdn }})" "{{ home }}/virtualenv"' + command: '/usr/bin/virtualenv --python "{{ python_interpreter }}" --prompt "({{ fqdn }})" "{{ home }}/virtualenv"' args: creates: "{{ home }}/virtualenv/bin/activate" become: true