From 5651de586565f0d400fe5058d3581e84ce8958cd Mon Sep 17 00:00:00 2001 From: Markus Falb Date: Tue, 16 Jan 2024 14:55:46 +0100 Subject: [PATCH] work on mafalb.ansible.virtualenv --- plugins/action/virtualenv.py | 91 ++++++------ plugins/filter/ansible.py | 2 +- roles/virtualenv/meta/argument_specs.yml | 2 +- .../targets/action_virtualenv/tasks/main.yml | 129 +++++++++++------- .../unit/plugins/filter/test_best_version.py | 2 + 5 files changed, 130 insertions(+), 96 deletions(-) diff --git a/plugins/action/virtualenv.py b/plugins/action/virtualenv.py index ab464fc..e1587ec 100644 --- a/plugins/action/virtualenv.py +++ b/plugins/action/virtualenv.py @@ -5,6 +5,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import os from ansible.plugins.action import ActionBase from ansible.plugins.filter.core import to_json from ansible_collections.mafalb.ansible.plugins.filter.ansible import best_version @@ -32,68 +33,62 @@ def run(self, tmp=None, task_vars=None): self._supports_check_mode = True super(ActionModule, self).run(tmp, task_vars) module_args = self._task.args.copy() - module_return = { - 'results': [ - {'item': 'python_info1'}, - {'item': 'create_virtualenv'}, - {'item': 'python_info2'}, - {'item': 'install_packages'} - ] - } - display.debug("Variables %s" % to_json(task_vars)) + # display.debug("Variables %s" % to_json(task_vars)) ansible_python_executable = task_vars.get('ansible_python')['executable'] - python_executable = self._task.args.get('executable', ansible_python_executable) - # Get info about the targeted python interpreter # Note that the virtualenv may exist already and could have been created with - # another interpreter. However, We do not recreate virtualenvs. + # another interpreter. If the virtualenv already exists override + # python_executable # - module_return['results'][0] = self.python_info(executable=python_executable, - task_vars=task_vars) + virtualenv = self._task.args.get('virtualenv') + if (os.path.exists(os.path.join(virtualenv, 'bin', 'activate')) and + os.path.exists(os.path.join(virtualenv, 'bin', 'python'))): + python_executable = os.path.join(virtualenv, 'bin', 'python') + else: + python_executable = self._task.args.get('virtualenv_python', ansible_python_executable) - python_version = self._task.args.get('python_version') - packages = self._task.args.get('name') - fixed_packages = fix_package_list(packages, python_version) - best_ansible_version = best_version(packages, python_version) + # Get info about the targeted python interpreter + ret0 = self.python_info(executable=python_executable, task_vars=task_vars) + ret0['item'] = 'python_info1' - display.debug("Python interpreter %s" % to_json(python_version)) - display.debug("Python interpreter %s" % to_json(packages)) - display.debug("Python interpreter %s" % to_json(fixed_packages)) - display.debug("Python interpreter %s" % to_json(best_ansible_version)) - display.v("Virtualenv %s" % to_json(self._task.args.get('virtualenv'))) + display.v("python_info1 %s" % to_json(ret0)) # create the virtualenv + display.v("Virtualenv %s" % to_json(self._task.args.get('virtualenv'))) + module_args['virtualenv_command'] = python_executable + " -m venv" module_args_copy = dict(module_args) module_args_copy['name'] = ['wheel'] - module_return['results'][1] = self._execute_module(module_name='pip', - module_args=module_args_copy, - task_vars=task_vars) + ret1 = self._execute_module(module_name='pip', module_args=module_args_copy, task_vars=task_vars) + ret1['item'] = 'create_virtualenv' del module_args_copy - if 'failed' not in module_return['results'][1]: - module_return['results'][1]['failed'] = False + if 'failed' not in ret1: + ret1['failed'] = False + display.v("Create virtualenv %s" % to_json(ret1)) - # if the virtualenv already existed, the virtualenv interpreter could - # be different from python_version - # - if module_return['results'][1]['changed']: - if self._play_context.check_mode: - virtualenv = self._task.args.get('virtualenv') - else: - # get absolute path (get rid of shellism like ~) - virtualenv = module_return['results'][1]['virtualenv'] - module_return['results'][2] = self.python_info(executable=virtualenv + '/bin/python', - task_vars=task_vars) - module_return['results'][2] = {} + packages = self._task.args.get('name') + display.v("Packages %s" % to_json(packages)) + + python_version = ret0['version']['majmin'] + display.v("Python version %s" % to_json(python_version)) + + fixed_packages = fix_package_list(packages, python_version) + display.v("Fixed packages %s" % to_json(fixed_packages)) + module_args['name'] = fixed_packages + + best_ansible_version = best_version(packages, python_version) + display.v("Best ansible version %s" % to_json(best_ansible_version)) # install the packages - module_return['results'][3] = self._execute_module(module_name='pip', - module_args=module_args, - task_vars=task_vars) - if 'failed' not in module_return['results'][3]: - module_return['results'][3]['failed'] = False - - module_return['failed'] = False - module_return['changed'] = module_return['results'][3]['changed'] + ret2 = self._execute_module(module_name='pip', module_args=module_args, task_vars=task_vars) + ret2['item'] = 'install_packages' + if 'failed' not in ret2: + ret2['failed'] = False + + module_return = {} + module_return['failed'] = ret2['failed'] + if 'changed' in ret2: + module_return['changed'] = ret2['changed'] + module_return['results'] = [ret0, ret1, ret2] return dict(module_return) diff --git a/plugins/filter/ansible.py b/plugins/filter/ansible.py index b7ec76f..4452820 100644 --- a/plugins/filter/ansible.py +++ b/plugins/filter/ansible.py @@ -159,7 +159,7 @@ def best_version(arg_packages, python_version=None): break name, spec, specstr, req_contains = parse_requirement(s) if name != 'ansible': - raise AnsibleFilterError("not '_ansible': {str}".format(str=name)) + raise AnsibleFilterError("There is no '_ansible' in package list: {str}".format(str=name)) # exact version is requested, expand to full version if necessary # it could be that that's not compatible with requested python version diff --git a/roles/virtualenv/meta/argument_specs.yml b/roles/virtualenv/meta/argument_specs.yml index 659ab7a..23e299b 100644 --- a/roles/virtualenv/meta/argument_specs.yml +++ b/roles/virtualenv/meta/argument_specs.yml @@ -43,6 +43,6 @@ argument_specs: type: path default: ~/.virtualenvs/ansible - virtualenv_interpreter: + virtualenv_python: description: The path to the python interpreter type: path diff --git a/tests/integration/targets/action_virtualenv/tasks/main.yml b/tests/integration/targets/action_virtualenv/tasks/main.yml index 2616553..c575855 100644 --- a/tests/integration/targets/action_virtualenv/tasks/main.yml +++ b/tests/integration/targets/action_virtualenv/tasks/main.yml @@ -3,50 +3,87 @@ # see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt --- -- name: Virtualenv virtualenv-test1 is present | check mode - check_mode: true - register: _output - mafalb.ansible.virtualenv: - virtualenv: ~/.virtualenvs/virtualenv-test1 - virtualenv_command: python3 -m venv - name: - - ansible - -- name: Assertions | check mode - ansible.builtin.assert: - that: - - not _output.failed - - _output.changed - - not _output.results[0].changed - - _output.results[1].changed - - _output.results[2] == {} - - _output.results[3].changed - - not _output.results[0].failed - - not _output.results[1].failed - - not _output.results[3].failed - -- name: Virtualenv virtualenv-test1 is present - mafalb.ansible.virtualenv: - virtualenv: ~/.virtualenvs/virtualenv-test1 - virtualenv_command: python3 -m venv - name: - - ansible - register: _output - -- name: Debug _output - ansible.builtin.debug: - var: _output - -- name: Assertions - ansible.builtin.assert: - that: - - _output.changed - - not _output.failed - - not _output.results[0].changed - - _output.results[1].changed - - _output.results[2] == {} - - _output.results[3].changed - - not _output.results[0].failed - - not _output.results[1].failed - - not _output.results[3].failed +- name: Test creation of mafalb.ansible.virtualenv + # unset PYTHONPATH, it contains ansible-core + # The contents would be visible in virtualenv otherwise + # + environment: + PYTHONPATH: '' + block: + + - name: Virtualenv virtualenv-test1 is present | check mode + check_mode: true + register: _result + mafalb.ansible.virtualenv: + virtualenv: ~/.virtualenvs/virtualenv-test1 + name: + - _ansible + - selinux + + - name: Assertions | check mode + ansible.builtin.assert: + that: + - not _result.failed + - _result.changed + - not _result.results[0].changed + - _result.results[1].changed + - _result.results[2].changed + - not _result.results[0].failed + - not _result.results[1].failed + - not _result.results[2].failed + + - name: Virtualenv virtualenv-test1 is present + register: _result + mafalb.ansible.virtualenv: + virtualenv: ~/.virtualenvs/virtualenv-test1 + name: + - _ansible + - selinux + + - name: Debug _result + ansible.builtin.debug: + var: _result + + - name: Assertions + ansible.builtin.assert: + that: + - _result.changed + - not _result.failed + - not _result.results[0].changed + - _result.results[1].changed + - _result.results[2].changed + - not _result.results[0].failed + - not _result.results[1].failed + - not _result.results[2].failed + + - name: Virtualenv virtualenv-test1 is present | Idempotence + register: _result + mafalb.ansible.virtualenv: + virtualenv: ~/.virtualenvs/virtualenv-test1 + name: + - _ansible + - selinux + + - name: Assertions | Idempotence + ansible.builtin.assert: + that: + - not _result.changed + - not _result.failed + - not _result.results[0].changed + - not _result.results[1].changed + - not _result.results[2].changed + - not _result.results[0].failed + - not _result.results[1].failed + - not _result.results[2].failed + + - name: Get ansible version + ansible.builtin.command: + cmd: ~/.virtualenvs/virtualenv-test1/bin/ansible --version + changed_when: false + register: _result + + - name: Debug ansible + ansible.builtin.debug: + var: _result + ... diff --git a/tests/unit/plugins/filter/test_best_version.py b/tests/unit/plugins/filter/test_best_version.py index b1b6789..9c430e5 100644 --- a/tests/unit/plugins/filter/test_best_version.py +++ b/tests/unit/plugins/filter/test_best_version.py @@ -64,11 +64,13 @@ (['_ansible'], '3.7', data['latest_ansible_version']['2.11']), (['_ansible~=2.11.6', '_ansible_test'], '3.8', data['latest_ansible_version']['2.11']), + (['ansible'], '3.12', data['latest_ansible_version']['2.16']), ) FAIL_CASES = ( (['_ansible=="2.9"'], AnsibleFilterError), (['ansible=="2.9"'], AnsibleFilterError), + (['ansible-core', 'selinux'], AnsibleFilterError), )