mirror of
https://github.com/ansible/awx.git
synced 2026-04-09 20:19:21 -02:30
Merge pull request #5163 from AlanCoding/custom_venv
Add custom virtualenv param to various modules Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
@@ -72,6 +72,12 @@ options:
|
|||||||
change the output of the openstack.py script. It has to be YAML or
|
change the output of the openstack.py script. It has to be YAML or
|
||||||
JSON.
|
JSON.
|
||||||
type: str
|
type: str
|
||||||
|
custom_virtualenv:
|
||||||
|
version_added: "2.9"
|
||||||
|
description:
|
||||||
|
- Local absolute file path containing a custom Python virtualenv to use.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
timeout:
|
timeout:
|
||||||
description:
|
description:
|
||||||
- Number in seconds after which the Tower API methods will time out.
|
- Number in seconds after which the Tower API methods will time out.
|
||||||
@@ -227,6 +233,7 @@ def main():
|
|||||||
source_script=dict(required=False),
|
source_script=dict(required=False),
|
||||||
overwrite=dict(type='bool', required=False),
|
overwrite=dict(type='bool', required=False),
|
||||||
overwrite_vars=dict(type='bool', required=False),
|
overwrite_vars=dict(type='bool', required=False),
|
||||||
|
custom_virtualenv=dict(type='str', required=False),
|
||||||
update_on_launch=dict(type='bool', required=False),
|
update_on_launch=dict(type='bool', required=False),
|
||||||
update_cache_timeout=dict(type='int', required=False),
|
update_cache_timeout=dict(type='int', required=False),
|
||||||
organization=dict(type='str'),
|
organization=dict(type='str'),
|
||||||
@@ -318,7 +325,7 @@ def main():
|
|||||||
changed=False
|
changed=False
|
||||||
)
|
)
|
||||||
|
|
||||||
for key in ('source_vars', 'timeout', 'source_path',
|
for key in ('source_vars', 'custom_virtualenv', 'timeout', 'source_path',
|
||||||
'update_on_project_update', 'source_regions',
|
'update_on_project_update', 'source_regions',
|
||||||
'instance_filters', 'group_by', 'overwrite',
|
'instance_filters', 'group_by', 'overwrite',
|
||||||
'overwrite_vars', 'update_on_launch',
|
'overwrite_vars', 'update_on_launch',
|
||||||
|
|||||||
@@ -191,6 +191,13 @@ options:
|
|||||||
description:
|
description:
|
||||||
- Maximum time in seconds to wait for a job to finish (server-side).
|
- Maximum time in seconds to wait for a job to finish (server-side).
|
||||||
type: int
|
type: int
|
||||||
|
custom_virtualenv:
|
||||||
|
version_added: "2.9"
|
||||||
|
description:
|
||||||
|
- Local absolute file path containing a custom Python virtualenv to use.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
default: ''
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Desired state of the resource.
|
- Desired state of the resource.
|
||||||
@@ -218,6 +225,7 @@ EXAMPLES = '''
|
|||||||
tower_config_file: "~/tower_cli.cfg"
|
tower_config_file: "~/tower_cli.cfg"
|
||||||
survey_enabled: yes
|
survey_enabled: yes
|
||||||
survey_spec: "{{ lookup('file', 'my_survey.json') }}"
|
survey_spec: "{{ lookup('file', 'my_survey.json') }}"
|
||||||
|
custom_virtualenv: "/var/lib/awx/venv/custom-venv/"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode
|
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode
|
||||||
@@ -298,6 +306,7 @@ def main():
|
|||||||
playbook=dict(required=True),
|
playbook=dict(required=True),
|
||||||
credential=dict(default=''),
|
credential=dict(default=''),
|
||||||
vault_credential=dict(default=''),
|
vault_credential=dict(default=''),
|
||||||
|
custom_virtualenv=dict(type='str', required=False),
|
||||||
forks=dict(type='int'),
|
forks=dict(type='int'),
|
||||||
limit=dict(default=''),
|
limit=dict(default=''),
|
||||||
verbosity=dict(type='int', choices=[0, 1, 2, 3, 4], default=0),
|
verbosity=dict(type='int', choices=[0, 1, 2, 3, 4], default=0),
|
||||||
|
|||||||
@@ -32,6 +32,13 @@ options:
|
|||||||
description:
|
description:
|
||||||
- The description to use for the organization.
|
- The description to use for the organization.
|
||||||
type: str
|
type: str
|
||||||
|
custom_virtualenv:
|
||||||
|
version_added: "2.9"
|
||||||
|
description:
|
||||||
|
- Local absolute file path containing a custom Python virtualenv to use.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
default: ''
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Desired state of the resource.
|
- Desired state of the resource.
|
||||||
@@ -49,6 +56,14 @@ EXAMPLES = '''
|
|||||||
description: "Foo bar organization"
|
description: "Foo bar organization"
|
||||||
state: present
|
state: present
|
||||||
tower_config_file: "~/tower_cli.cfg"
|
tower_config_file: "~/tower_cli.cfg"
|
||||||
|
|
||||||
|
- name: Create tower organization using 'foo-venv' as default Python virtualenv
|
||||||
|
tower_organization:
|
||||||
|
name: "Foo"
|
||||||
|
description: "Foo bar organization using foo-venv"
|
||||||
|
custom_virtualenv: "/var/lib/awx/venv/foo-venv/"
|
||||||
|
state: present
|
||||||
|
tower_config_file: "~/tower_cli.cfg"
|
||||||
'''
|
'''
|
||||||
|
|
||||||
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode
|
from ..module_utils.ansible_tower import TowerModule, tower_auth_config, tower_check_mode
|
||||||
@@ -66,6 +81,7 @@ def main():
|
|||||||
argument_spec = dict(
|
argument_spec = dict(
|
||||||
name=dict(required=True),
|
name=dict(required=True),
|
||||||
description=dict(),
|
description=dict(),
|
||||||
|
custom_virtualenv=dict(type='str', required=False),
|
||||||
state=dict(choices=['present', 'absent'], default='present'),
|
state=dict(choices=['present', 'absent'], default='present'),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,6 +89,7 @@ def main():
|
|||||||
|
|
||||||
name = module.params.get('name')
|
name = module.params.get('name')
|
||||||
description = module.params.get('description')
|
description = module.params.get('description')
|
||||||
|
custom_virtualenv = module.params.get('custom_virtualenv')
|
||||||
state = module.params.get('state')
|
state = module.params.get('state')
|
||||||
|
|
||||||
json_output = {'organization': name, 'state': state}
|
json_output = {'organization': name, 'state': state}
|
||||||
@@ -83,7 +100,7 @@ def main():
|
|||||||
organization = tower_cli.get_resource('organization')
|
organization = tower_cli.get_resource('organization')
|
||||||
try:
|
try:
|
||||||
if state == 'present':
|
if state == 'present':
|
||||||
result = organization.modify(name=name, description=description, create_on_missing=True)
|
result = organization.modify(name=name, description=description, custom_virtualenv=custom_virtualenv, create_on_missing=True)
|
||||||
json_output['id'] = result['id']
|
json_output['id'] = result['id']
|
||||||
elif state == 'absent':
|
elif state == 'absent':
|
||||||
result = organization.delete(name=name)
|
result = organization.delete(name=name)
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ options:
|
|||||||
description:
|
description:
|
||||||
- Local absolute file path containing a custom Python virtualenv to use
|
- Local absolute file path containing a custom Python virtualenv to use
|
||||||
type: str
|
type: str
|
||||||
|
required: False
|
||||||
|
default: ''
|
||||||
organization:
|
organization:
|
||||||
description:
|
description:
|
||||||
- Primary key of organization for project.
|
- Primary key of organization for project.
|
||||||
@@ -156,7 +158,7 @@ def main():
|
|||||||
scm_update_on_launch=dict(type='bool', default=False),
|
scm_update_on_launch=dict(type='bool', default=False),
|
||||||
scm_update_cache_timeout=dict(type='int'),
|
scm_update_cache_timeout=dict(type='int'),
|
||||||
job_timeout=dict(type='int', default=0),
|
job_timeout=dict(type='int', default=0),
|
||||||
custom_virtualenv=dict(),
|
custom_virtualenv=dict(type='str', required=False),
|
||||||
local_path=dict(),
|
local_path=dict(),
|
||||||
state=dict(choices=['present', 'absent'], default='present'),
|
state=dict(choices=['present', 'absent'], default='present'),
|
||||||
wait=dict(type='bool', default=True),
|
wait=dict(type='bool', default=True),
|
||||||
|
|||||||
@@ -1,6 +1,40 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from awx.main.models import Organization, Inventory, InventorySource
|
from awx.main.models import Organization, Inventory, InventorySource, Project
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def base_inventory():
|
||||||
|
org = Organization.objects.create(name='test-org')
|
||||||
|
inv = Inventory.objects.create(name='test-inv', organization=org)
|
||||||
|
Project.objects.create(
|
||||||
|
name='test-proj',
|
||||||
|
organization=org,
|
||||||
|
scm_type='git',
|
||||||
|
scm_url='https://github.com/ansible/test-playbooks.git',
|
||||||
|
)
|
||||||
|
return inv
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_inventory_source_create(run_module, admin_user, base_inventory):
|
||||||
|
result = run_module('tower_inventory_source', dict(
|
||||||
|
name='foo',
|
||||||
|
inventory='test-inv',
|
||||||
|
state='present',
|
||||||
|
source='scm',
|
||||||
|
source_project='test-proj'
|
||||||
|
), admin_user)
|
||||||
|
assert result.pop('changed', None), result
|
||||||
|
|
||||||
|
inv_src = InventorySource.objects.get(name='foo')
|
||||||
|
assert inv_src.inventory == base_inventory
|
||||||
|
result.pop('invocation')
|
||||||
|
assert result == {
|
||||||
|
'id': inv_src.id,
|
||||||
|
'inventory_source': 'foo',
|
||||||
|
'state': 'present'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -54,3 +88,53 @@ def test_create_inventory_source_multiple_orgs(run_module, admin_user):
|
|||||||
"state": "present",
|
"state": "present",
|
||||||
"id": inv_src.id,
|
"id": inv_src.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_create_inventory_source_with_venv(run_module, admin_user, base_inventory, mocker):
|
||||||
|
path = '/var/lib/awx/venv/custom-venv/foobar13489435/'
|
||||||
|
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=[path]):
|
||||||
|
result = run_module('tower_inventory_source', dict(
|
||||||
|
name='foo',
|
||||||
|
inventory='test-inv',
|
||||||
|
state='present',
|
||||||
|
source='scm',
|
||||||
|
source_project='test-proj',
|
||||||
|
custom_virtualenv=path
|
||||||
|
), admin_user)
|
||||||
|
assert result.pop('changed'), result
|
||||||
|
|
||||||
|
inv_src = InventorySource.objects.get(name='foo')
|
||||||
|
assert inv_src.inventory == base_inventory
|
||||||
|
result.pop('invocation')
|
||||||
|
|
||||||
|
assert inv_src.custom_virtualenv == path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_custom_venv_no_op(run_module, admin_user, base_inventory, mocker):
|
||||||
|
"""If the inventory source is modified, then it should not blank fields
|
||||||
|
unrelated to the params that the user passed.
|
||||||
|
This enforces assumptions about the behavior of the AnsibleModule
|
||||||
|
default argument_spec behavior.
|
||||||
|
"""
|
||||||
|
inv_src = InventorySource.objects.create(
|
||||||
|
name='foo',
|
||||||
|
inventory=base_inventory,
|
||||||
|
source_project=Project.objects.get(name='test-proj'),
|
||||||
|
source='scm',
|
||||||
|
custom_virtualenv='/venv/foobar/'
|
||||||
|
)
|
||||||
|
# mock needed due to API behavior, not incorrect client behavior
|
||||||
|
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=['/venv/foobar/']):
|
||||||
|
result = run_module('tower_inventory_source', dict(
|
||||||
|
name='foo',
|
||||||
|
description='this is the changed description',
|
||||||
|
inventory='test-inv',
|
||||||
|
source='scm', # is required, but behavior is arguable
|
||||||
|
state='present'
|
||||||
|
), admin_user)
|
||||||
|
assert result.pop('changed', None), result
|
||||||
|
inv_src.refresh_from_db()
|
||||||
|
assert inv_src.custom_virtualenv == '/venv/foobar/'
|
||||||
|
assert inv_src.description == 'this is the changed description'
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ def test_create_organization(run_module, admin_user):
|
|||||||
module_args = {'name': 'foo', 'description': 'barfoo', 'state': 'present'}
|
module_args = {'name': 'foo', 'description': 'barfoo', 'state': 'present'}
|
||||||
|
|
||||||
result = run_module('tower_organization', module_args, admin_user)
|
result = run_module('tower_organization', module_args, admin_user)
|
||||||
|
assert result.get('changed'), result
|
||||||
|
|
||||||
org = Organization.objects.get(name='foo')
|
org = Organization.objects.get(name='foo')
|
||||||
|
|
||||||
@@ -26,3 +27,26 @@ def test_create_organization(run_module, admin_user):
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert org.description == 'barfoo'
|
assert org.description == 'barfoo'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_create_organization_with_venv(run_module, admin_user, mocker):
|
||||||
|
path = '/var/lib/awx/venv/custom-venv/foobar13489435/'
|
||||||
|
with mocker.patch('awx.main.models.mixins.get_custom_venv_choices', return_value=[path]):
|
||||||
|
result = run_module('tower_organization', {
|
||||||
|
'name': 'foo',
|
||||||
|
'custom_virtualenv': path,
|
||||||
|
'state': 'present'
|
||||||
|
}, admin_user)
|
||||||
|
assert result.pop('changed'), result
|
||||||
|
|
||||||
|
org = Organization.objects.get(name='foo')
|
||||||
|
result.pop('invocation')
|
||||||
|
|
||||||
|
assert result == {
|
||||||
|
"organization": "foo",
|
||||||
|
"state": "present",
|
||||||
|
"id": org.id
|
||||||
|
}
|
||||||
|
|
||||||
|
assert org.custom_virtualenv == path
|
||||||
|
|||||||
Reference in New Issue
Block a user