diff --git a/awx/main/tests/functional/test_bulk.py b/awx/main/tests/functional/test_bulk.py index f33ac5a99a1b..a2a6413368fc 100644 --- a/awx/main/tests/functional/test_bulk.py +++ b/awx/main/tests/functional/test_bulk.py @@ -6,23 +6,15 @@ import json from contextlib import contextmanager -from django.test.utils import CaptureQueriesContext from django.db import connections -from awx.main.models.jobs import JobTemplate -from awx.main.models import Organization, Inventory - -@contextmanager -def withAssertNumQueriesLessThan(num_queries): - with CaptureQueriesContext(connections['default']) as context: - yield - fail_msg = f"\r\n{json.dumps(context.captured_queries, indent=4)}" - assert len(context.captured_queries) < num_queries, fail_msg +from awx.main.models.jobs import JobTemplate +from awx.main.models import Organization, Inventory, WorkflowJob @pytest.mark.django_db @pytest.mark.parametrize('num_hosts, num_queries', [(9, 15), (99, 20), (999, 30)]) -def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries): +def test_bulk_host_create_num_queries(organization, inventory, post, get, user, num_hosts, num_queries, django_assert_max_num_queries): ''' If I am a... org admin @@ -45,7 +37,7 @@ def test_bulk_host_create_num_queries(organization, inventory, post, get, user, for u in [org_admin, inventory_admin, org_inv_admin, superuser]: hosts = [{'name': uuid4()} for i in range(num_hosts)] - with withAssertNumQueriesLessThan(num_queries): + with django_assert_max_num_queries(num_queries): bulk_host_create_response = post(reverse('api:bulk_host_create'), {'inventory': inventory.id, 'hosts': hosts}, u, expect=201).data assert len(bulk_host_create_response['hosts']) == len(hosts), f"unexpected number of hosts created for user {u}" @@ -89,20 +81,36 @@ def test_bulk_host_create_rbac(organization, inventory, post, get, user): @pytest.mark.django_db -def test_bulk_job_launch(job_template, organization, inventory, project, credential, post, get, user): +@pytest.mark.parametrize('num_jobs, num_queries', [(9, 30), (99, 35)]) +def test_bulk_job_launch_queries(job_template, organization, inventory, project, post, get, user, num_jobs, num_queries, django_assert_max_num_queries): ''' if I have access to the unified job template ... I can launch the bulk job + ... and the number of queries should NOT scale with the number of jobs ''' normal_user = user('normal_user', False) - jt = JobTemplate.objects.create(name='my-jt', inventory=inventory, project=project, playbook='helloworld.yml') - jt.save() + org_admin = user('org_admin', False) + jt = JobTemplate.objects.create(name='my-jt', ask_inventory_on_launch=True, project=project, playbook='helloworld.yml') organization.member_role.members.add(normal_user) + organization.admin_role.members.add(org_admin) jt.execute_role.members.add(normal_user) - bulk_job_launch_response = post( - reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}]}, normal_user, expect=201 - ).data - assert bulk_job_launch_response['id'] == 1 + inventory.use_role.members.add(normal_user) + jt.save() + inventory.save() + jobs = [{'unified_job_template': jt.id, 'inventory': inventory.id} for _ in range(num_jobs)] + + # This is not working, we need to figure that out if we want to include tests for more jobs + # with mock.patch('awx.api.serializers.settings.BULK_JOB_MAX_LAUNCH', num_jobs + 1): + with django_assert_max_num_queries(num_queries): + bulk_job_launch_response = post(reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': jobs}, normal_user, expect=201).data + + for u in (org_admin,): # TODO normal_user not working because launched_by not getting set in the tests...does happen in real request + bulk_job = get(bulk_job_launch_response['url'], u, expect=200).data + assert organization.id == bulk_job['summary_fields']['organization']['id'] + resp = get(bulk_job_launch_response['related']['workflow_nodes'], u) + assert resp.data['count'] == num_jobs + for item in resp.data['results']: + assert item["unified_job_template"] == jt.id @pytest.mark.django_db @@ -167,7 +175,9 @@ def test_bulk_job_launch_specific_org(job_template, organization, inventory, pro bulk_job_launch_response = post( reverse('api:bulk_job_launch'), {'name': 'Bulk Job Launch', 'jobs': [{'unified_job_template': jt.id}], 'organization': org1.id}, normal_user, expect=201 ).data - assert bulk_job_launch_response['id'] == 1 + bulk_job_id = bulk_job_launch_response['id'] + bulk_job_obj = WorkflowJob.objects.filter(id=bulk_job_id, is_bulk_job=True).first() + assert org1.id == bulk_job_obj.organization.id @pytest.mark.django_db diff --git a/awx_collection/README.md b/awx_collection/README.md index 2be1ad624a7b..ea9e85e11a9d 100644 --- a/awx_collection/README.md +++ b/awx_collection/README.md @@ -68,6 +68,7 @@ Notable releases of the `awx.awx` collection: - 7.0.0 is intended to be identical to the content prior to the migration, aside from changes necessary to function as a collection. - 11.0.0 has no non-deprecated modules that depend on the deprecated `tower-cli` [PyPI](https://pypi.org/project/ansible-tower-cli/). - 19.2.1 large renaming purged "tower" names (like options and module names), adding redirects for old names + - 21.11.0 "tower" modules deprecated and symlinks removed. - 0.0.1-devel is the version you should see if installing from source, which is intended for development and expected to be unstable. The following notes are changes that may require changes to playbooks: diff --git a/awx_collection/plugins/modules/bulk_host_create.py b/awx_collection/plugins/modules/bulk_host_create.py index d848d999b5d1..f20a20636b4e 100644 --- a/awx_collection/plugins/modules/bulk_host_create.py +++ b/awx_collection/plugins/modules/bulk_host_create.py @@ -29,7 +29,7 @@ description: - The name to use for the host. type: str - require: True + required: True description: description: - The description to use for the host. @@ -70,7 +70,7 @@ def main(): # Any additional arguments that are not fields of the item can be added here argument_spec = dict( - hosts=dict(required=True, type='list'), + hosts=dict(required=True, type='list', elements='dict'), inventory=dict(required=True, type='int'), ) @@ -82,8 +82,8 @@ def main(): hosts = module.params.get('hosts') for h in hosts: - if 'variables' in h: - h['variables'] = json.dumps(h['variables']) + if 'variables' in h: + h['variables'] = json.dumps(h['variables']) # Launch the jobs result = module.post_endpoint("bulk/host_create", data={"inventory": inventory, "hosts": hosts}) diff --git a/awx_collection/plugins/modules/bulk_job_launch.py b/awx_collection/plugins/modules/bulk_job_launch.py index 9b7545605e2c..b38b662d5fa2 100644 --- a/awx_collection/plugins/modules/bulk_job_launch.py +++ b/awx_collection/plugins/modules/bulk_job_launch.py @@ -205,7 +205,7 @@ def main(): # Any additional arguments that are not fields of the item can be added here argument_spec = dict( - jobs=dict(required=True, type='list'), + jobs=dict(required=True, type='list', elements='dict'), name=dict(), description=dict(), organization=dict(type='int'), @@ -217,7 +217,6 @@ def main(): skip_tags=dict(), wait=dict(required=False, default=True, type='bool'), interval=dict(required=False, default=2.0, type='float'), - timeout=dict(required=False, default=None, type='int'), ) # Create a module for ourselves diff --git a/awx_collection/test/awx/test_bulk.py b/awx_collection/test/awx/test_bulk.py index 842e1a997f13..a0862a04733f 100644 --- a/awx_collection/test/awx/test_bulk.py +++ b/awx_collection/test/awx/test_bulk.py @@ -6,6 +6,7 @@ from awx.main.models import WorkflowJob + @pytest.mark.django_db def test_bulk_job_launch(run_module, admin_user, job_template): jobs = [dict(unified_job_template=job_template.id)] @@ -39,4 +40,4 @@ def test_bulk_host_create(run_module, admin_user, inventory): ) resp_hosts = inventory.hosts.all().values_list('name', flat=True) for h in hosts: - assert h['name'] in resp_hosts \ No newline at end of file + assert h['name'] in resp_hosts