diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 36b358a1bd..7d6c350274 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -7,6 +7,7 @@ import time import logging import re import copy +import json import os.path from urllib.parse import urljoin import yaml @@ -1157,6 +1158,20 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, CustomVirtualE raise ValidationError(_("Cannot set source_path if not SCM type.")) return self.source_path + def clean_source_vars(self): + injector = self.injectors.get(self.source) + source_vars = dict(self.source_vars_dict) # make a copy + if injector and self.source_vars_dict.get('plugin', '') != injector.get_proper_name(): + source_vars['plugin'] = injector.get_proper_name() + elif not injector: + source_vars = dict(self.source_vars_dict) # make a copy + collection_pattern = re.compile("^(.+)\.(.+)\.(.+)$") # noqa + if 'plugin' not in source_vars: + raise ValidationError(_("plugin: must be present and of the form namespace.collection.inv_plugin")) + elif not bool(collection_pattern.match(source_vars['plugin'])): + raise ValidationError(_("plugin: must be of the form namespace.collection.inv_plugin")) + return json.dumps(source_vars) + ''' RelatedJobsMixin ''' @@ -1344,6 +1359,12 @@ class PluginFileInjector(object): # This is InventoryOptions instance, could be source or inventory update self.ansible_version = ansible_version + @classmethod + def get_proper_name(cls): + if cls.plugin_name is None: + return None + return f'{cls.namespace}.{cls.collection}.{cls.plugin_name}' + @property def filename(self): """Inventory filename for using the inventory plugin diff --git a/awx/main/tests/data/inventory/plugins/azure_rm/files/azure_rm.yml b/awx/main/tests/data/inventory/plugins/azure_rm/files/azure_rm.yml deleted file mode 100644 index 8d6c1dbfa7..0000000000 --- a/awx/main/tests/data/inventory/plugins/azure_rm/files/azure_rm.yml +++ /dev/null @@ -1,43 +0,0 @@ -conditional_groups: - azure: true -default_host_filters: [] -exclude_host_filters: -- resource_group not in ['foo_resources', 'bar_resources'] -- '"Creator" not in tags.keys()' -- tags["Creator"] != "jmarshall" -- '"peanutbutter" not in tags.keys()' -- tags["peanutbutter"] != "jelly" -- location not in ['southcentralus', 'westus'] -fail_on_template_errors: false -hostvar_expressions: - ansible_host: private_ipv4_addresses[0] - computer_name: name - private_ip: private_ipv4_addresses[0] if private_ipv4_addresses else None - provisioning_state: provisioning_state | title - public_ip: public_ipv4_addresses[0] if public_ipv4_addresses else None - public_ip_id: public_ip_id if public_ip_id is defined else None - public_ip_name: public_ip_name if public_ip_name is defined else None - tags: tags if tags else None - type: resource_type -keyed_groups: -- key: location - prefix: '' - separator: '' -- key: tags.keys() | list if tags else [] - prefix: '' - separator: '' -- key: security_group - prefix: '' - separator: '' -- key: resource_group - prefix: '' - separator: '' -- key: os_disk.operating_system_type - prefix: '' - separator: '' -- key: dict(tags.keys() | map("regex_replace", "^(.*)$", "\1_") | list | zip(tags.values() | list)) if tags else [] - prefix: '' - separator: '' -plain_host_names: true -plugin: azure.azcollection.azure_rm -use_contrib_script_compatible_sanitization: true diff --git a/awx/main/tests/data/inventory/plugins/ec2/files/aws_ec2.yml b/awx/main/tests/data/inventory/plugins/ec2/files/aws_ec2.yml deleted file mode 100644 index 8984d4cb56..0000000000 --- a/awx/main/tests/data/inventory/plugins/ec2/files/aws_ec2.yml +++ /dev/null @@ -1,81 +0,0 @@ -boto_profile: /tmp/my_boto_stuff -compose: - ansible_host: public_dns_name - ec2_account_id: owner_id - ec2_ami_launch_index: ami_launch_index | string - ec2_architecture: architecture - ec2_block_devices: dict(block_device_mappings | map(attribute='device_name') | list | zip(block_device_mappings | map(attribute='ebs.volume_id') | list)) - ec2_client_token: client_token - ec2_dns_name: public_dns_name - ec2_ebs_optimized: ebs_optimized - ec2_eventsSet: events | default("") - ec2_group_name: placement.group_name - ec2_hypervisor: hypervisor - ec2_id: instance_id - ec2_image_id: image_id - ec2_instance_profile: iam_instance_profile | default("") - ec2_instance_type: instance_type - ec2_ip_address: public_ip_address - ec2_kernel: kernel_id | default("") - ec2_key_name: key_name - ec2_launch_time: launch_time | regex_replace(" ", "T") | regex_replace("(\+)(\d\d):(\d)(\d)$", ".\g<2>\g<3>Z") - ec2_monitored: monitoring.state in ['enabled', 'pending'] - ec2_monitoring_state: monitoring.state - ec2_persistent: persistent | default(false) - ec2_placement: placement.availability_zone - ec2_platform: platform | default("") - ec2_private_dns_name: private_dns_name - ec2_private_ip_address: private_ip_address - ec2_public_dns_name: public_dns_name - ec2_ramdisk: ramdisk_id | default("") - ec2_reason: state_transition_reason - ec2_region: placement.region - ec2_requester_id: requester_id | default("") - ec2_root_device_name: root_device_name - ec2_root_device_type: root_device_type - ec2_security_group_ids: security_groups | map(attribute='group_id') | list | join(',') - ec2_security_group_names: security_groups | map(attribute='group_name') | list | join(',') - ec2_sourceDestCheck: source_dest_check | default(false) | lower | string - ec2_spot_instance_request_id: spot_instance_request_id | default("") - ec2_state: state.name - ec2_state_code: state.code - ec2_state_reason: state_reason.message if state_reason is defined else "" - ec2_subnet_id: subnet_id | default("") - ec2_tag_Name: tags.Name - ec2_virtualization_type: virtualization_type - ec2_vpc_id: vpc_id | default("") -filters: - instance-state-name: - - running -groups: - ec2: true -hostnames: -- dns-name -iam_role_arn: arn:aws:iam::123456789012:role/test-role -keyed_groups: -- key: placement.availability_zone - parent_group: zones - prefix: '' - separator: '' -- key: instance_type | regex_replace("[^A-Za-z0-9\_]", "_") - parent_group: types - prefix: type -- key: placement.region - parent_group: regions - prefix: '' - separator: '' -- key: dict(tags.keys() | map("regex_replace", "[^A-Za-z0-9\_]", "_") | list | zip(tags.values() | map("regex_replace", "[^A-Za-z0-9\_]", "_") | list)) - parent_group: tags - prefix: tag -- key: tags.keys() | map("regex_replace", "[^A-Za-z0-9\_]", "_") | list - parent_group: tags - prefix: tag -- key: placement.availability_zone - parent_group: '{{ placement.region }}' - prefix: '' - separator: '' -plugin: amazon.aws.aws_ec2 -regions: -- us-east-2 -- ap-south-1 -use_contrib_script_compatible_sanitization: true diff --git a/awx/main/tests/data/inventory/plugins/gce/files/gcp_compute.yml b/awx/main/tests/data/inventory/plugins/gce/files/gcp_compute.yml deleted file mode 100644 index 63f8a44f64..0000000000 --- a/awx/main/tests/data/inventory/plugins/gce/files/gcp_compute.yml +++ /dev/null @@ -1,50 +0,0 @@ -auth_kind: serviceaccount -compose: - ansible_ssh_host: networkInterfaces[0].accessConfigs[0].natIP | default(networkInterfaces[0].networkIP) - gce_description: description if description else None - gce_id: id - gce_image: image - gce_machine_type: machineType - gce_metadata: metadata.get("items", []) | items2dict(key_name="key", value_name="value") - gce_name: name - gce_network: networkInterfaces[0].network.name - gce_private_ip: networkInterfaces[0].networkIP - gce_public_ip: networkInterfaces[0].accessConfigs[0].natIP | default(None) - gce_status: status - gce_subnetwork: networkInterfaces[0].subnetwork.name - gce_tags: tags.get("items", []) - gce_zone: zone -hostnames: -- name -- public_ip -- private_ip -keyed_groups: -- key: gce_subnetwork - prefix: network -- key: gce_private_ip - prefix: '' - separator: '' -- key: gce_public_ip - prefix: '' - separator: '' -- key: machineType - prefix: '' - separator: '' -- key: zone - prefix: '' - separator: '' -- key: gce_tags - prefix: tag -- key: status | lower - prefix: status -- key: image - prefix: '' - separator: '' -plugin: google.cloud.gcp_compute -projects: -- fooo -retrieve_image_info: true -use_contrib_script_compatible_sanitization: true -zones: -- us-east4-a -- us-west1-b diff --git a/awx/main/tests/data/inventory/plugins/openstack/files/file_reference b/awx/main/tests/data/inventory/plugins/openstack/files/file_reference index 895a1eb8a8..c578942ca1 100644 --- a/awx/main/tests/data/inventory/plugins/openstack/files/file_reference +++ b/awx/main/tests/data/inventory/plugins/openstack/files/file_reference @@ -1,7 +1,3 @@ -ansible: - expand_hostvars: true - fail_on_errors: true - use_hostnames: false clouds: devstack: auth: @@ -11,5 +7,5 @@ clouds: project_domain_name: fooo project_name: fooo username: fooo - private: false + private: true verify: false diff --git a/awx/main/tests/data/inventory/plugins/openstack/files/openstack.yml b/awx/main/tests/data/inventory/plugins/openstack/files/openstack.yml deleted file mode 100644 index 36e9024b54..0000000000 --- a/awx/main/tests/data/inventory/plugins/openstack/files/openstack.yml +++ /dev/null @@ -1,4 +0,0 @@ -expand_hostvars: true -fail_on_errors: true -inventory_hostname: uuid -plugin: openstack.cloud.openstack diff --git a/awx/main/tests/data/inventory/plugins/rhv/files/ovirt.yml b/awx/main/tests/data/inventory/plugins/rhv/files/ovirt.yml deleted file mode 100644 index 67a94ae6de..0000000000 --- a/awx/main/tests/data/inventory/plugins/rhv/files/ovirt.yml +++ /dev/null @@ -1,20 +0,0 @@ -base_source_var: value_of_var -compose: - ansible_host: (devices.values() | list)[0][0] if devices else None -groups: - dev: '"dev" in tags' -keyed_groups: -- key: cluster - prefix: cluster - separator: _ -- key: status - prefix: status - separator: _ -- key: tags - prefix: tag - separator: _ -ovirt_hostname_preference: -- name -- fqdn -ovirt_insecure: false -plugin: ovirt.ovirt.ovirt diff --git a/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml b/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml deleted file mode 100644 index fcad2586f6..0000000000 --- a/awx/main/tests/data/inventory/plugins/satellite6/files/foreman.yml +++ /dev/null @@ -1,30 +0,0 @@ -base_source_var: value_of_var -compose: - ansible_ssh_host: foreman['ip6'] | default(foreman['ip'], true) -group_prefix: foo_group_prefix -keyed_groups: -- key: foreman['environment_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9_]', '_') | regex_replace('none', '') - prefix: foo_group_prefixenvironment_ - separator: '' -- key: foreman['location_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9_]', '_') - prefix: foo_group_prefixlocation_ - separator: '' -- key: foreman['organization_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9_]', '_') - prefix: foo_group_prefixorganization_ - separator: '' -- key: foreman['content_facet_attributes']['lifecycle_environment_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9_]', '_') - prefix: foo_group_prefixlifecycle_environment_ - separator: '' -- key: foreman['content_facet_attributes']['content_view_name'] | lower | regex_replace(' ', '') | regex_replace('[^A-Za-z0-9_]', '_') - prefix: foo_group_prefixcontent_view_ - separator: '' -- key: '"%s-%s-%s" | format(app, tier, color)' - separator: '' -- key: '"%s-%s" | format(app, color)' - separator: '' -legacy_hostvars: true -plugin: theforeman.foreman.foreman -validate_certs: false -want_facts: true -want_hostcollections: true -want_params: true diff --git a/awx/main/tests/data/inventory/plugins/tower/files/tower.yml b/awx/main/tests/data/inventory/plugins/tower/files/tower.yml deleted file mode 100644 index 2c41f1b55d..0000000000 --- a/awx/main/tests/data/inventory/plugins/tower/files/tower.yml +++ /dev/null @@ -1,3 +0,0 @@ -include_metadata: true -inventory_id: 42 -plugin: awx.awx.tower diff --git a/awx/main/tests/data/inventory/plugins/vmware/files/vmware_vm_inventory.yml b/awx/main/tests/data/inventory/plugins/vmware/files/vmware_vm_inventory.yml deleted file mode 100644 index ac1db9f4cf..0000000000 --- a/awx/main/tests/data/inventory/plugins/vmware/files/vmware_vm_inventory.yml +++ /dev/null @@ -1,55 +0,0 @@ -compose: - ansible_host: guest.ipAddress - ansible_ssh_host: guest.ipAddress - ansible_uuid: 99999999 | random | to_uuid - availablefield: availableField - configissue: configIssue - configstatus: configStatus - customvalue: customValue - effectiverole: effectiveRole - guestheartbeatstatus: guestHeartbeatStatus - layoutex: layoutEx - overallstatus: overallStatus - parentvapp: parentVApp - recenttask: recentTask - resourcepool: resourcePool - rootsnapshot: rootSnapshot - triggeredalarmstate: triggeredAlarmState -filters: -- config.zoo == "DC0_H0_VM0" -hostnames: -- config.foo -keyed_groups: -- key: config.asdf - prefix: '' - separator: '' -plugin: community.vmware.vmware_vm_inventory -properties: -- availableField -- configIssue -- configStatus -- customValue -- datastore -- effectiveRole -- guestHeartbeatStatus -- layout -- layoutEx -- name -- network -- overallStatus -- parentVApp -- permission -- recentTask -- resourcePool -- rootSnapshot -- snapshot -- triggeredAlarmState -- value -- capability -- config -- guest -- runtime -- storage -- summary -strict: false -with_nested_properties: true diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index e63286f7e8..8eda9cddd0 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import pytest +import json from unittest import mock from django.core.exceptions import ValidationError @@ -8,8 +9,6 @@ from awx.api.versioning import reverse from awx.main.models import InventorySource, Inventory, ActivityStream -import json - @pytest.fixture def scm_inventory(inventory, project): @@ -457,6 +456,56 @@ def test_inventory_source_vars_prohibition(post, inventory, admin_user): assert 'FOOBAR' in r.data['source_vars'][0] +@pytest.mark.django_db +@pytest.mark.parametrize('source,source_var_actual,source_var_expected,description', [ + ('ec2', {'plugin': 'blah'}, {'plugin': 'amazon.aws.aws_ec2'}, 'source plugin mismatch'), + ('ec2', {'plugin': 'amazon.aws.aws_ec2'}, {'plugin': 'amazon.aws.aws_ec2'}, 'valid plugin'), +]) +def test_inventory_source_vars_source_plugin_ok(post, inventory, admin_user, source, source_var_actual, source_var_expected, description): + r = post(reverse('api:inventory_source_list'), + {'name': 'new inv src', 'source_vars': json.dumps(source_var_actual), 'inventory': inventory.pk, 'source': source}, + admin_user, expect=201) + + assert r.data['source_vars'] == json.dumps(source_var_expected) + + +@pytest.mark.django_db +@pytest.mark.parametrize('source_var_actual,description', [ + ({'plugin': 'namespace.collection.script'}, 'valid scm user plugin'), +]) +def test_inventory_source_vars_source_plugin_scm_ok(post, inventory, admin_user, project, source_var_actual, description): + r = post(reverse('api:inventory_source_list'), + {'name': 'new inv src', + 'source_vars': json.dumps(source_var_actual), + 'inventory': inventory.pk, + 'source': 'scm', + 'source_project': project.id,}, + admin_user, expect=201) + + assert r.data['source_vars'] == json.dumps(source_var_actual) + + +@pytest.mark.django_db +@pytest.mark.parametrize('source_var_actual,err_msg,description', [ + ({'foo': 'bar'}, 'plugin: must be present and of the form namespace.collection.inv_plugin', 'no plugin line'), + ({'plugin': ''}, 'plugin: must be of the form namespace.collection.inv_plugin', 'blank plugin line'), + ({'plugin': '.'}, 'plugin: must be of the form namespace.collection.inv_plugin', 'missing namespace, collection name, and inventory plugin'), + ({'plugin': 'a.'}, 'plugin: must be of the form namespace.collection.inv_plugin', 'missing collection name and inventory plugin'), + ({'plugin': 'a.b'}, 'plugin: must be of the form namespace.collection.inv_plugin', 'missing inventory plugin'), + ({'plugin': 'a.b.'}, 'plugin: must be of the form namespace.collection.inv_plugin', 'missing inventory plugin'), +]) +def test_inventory_source_vars_source_plugin_scm_invalid(post, inventory, admin_user, project, source_var_actual, err_msg, description): + r = post(reverse('api:inventory_source_list'), + {'name': 'new inv src', + 'source_vars': json.dumps(source_var_actual), + 'inventory': inventory.pk, + 'source': 'scm', + 'source_project': project.id,}, + admin_user, expect=400) + + assert err_msg in r.data['source_vars'][0] + + @pytest.mark.django_db @pytest.mark.parametrize('role,expect', [ ('admin_role', 200), @@ -522,7 +571,8 @@ class TestInventorySourceCredential: data={ 'inventory': inventory.pk, 'name': 'fobar', 'source': 'scm', 'source_project': project.pk, 'source_path': '', - 'credential': vault_credential.pk + 'credential': vault_credential.pk, + 'source_vars': 'plugin: a.b.c', }, expect=400, user=admin_user @@ -561,7 +611,7 @@ class TestInventorySourceCredential: data={ 'inventory': inventory.pk, 'name': 'fobar', 'source': 'scm', 'source_project': project.pk, 'source_path': '', - 'credential': os_cred.pk + 'credential': os_cred.pk, 'source_vars': 'plugin: a.b.c', }, expect=201, user=admin_user @@ -636,8 +686,14 @@ class TestControlledBySCM: assert scm_inventory.inventory_sources.count() == 0 def test_adding_inv_src_ok(self, post, scm_inventory, project, admin_user): - post(reverse('api:inventory_inventory_sources_list', kwargs={'pk': scm_inventory.id}), - {'name': 'new inv src', 'source_project': project.pk, 'update_on_project_update': False, 'source': 'scm', 'overwrite_vars': True}, + post(reverse('api:inventory_inventory_sources_list', + kwargs={'pk': scm_inventory.id}), + {'name': 'new inv src', + 'source_project': project.pk, + 'update_on_project_update': False, + 'source': 'scm', + 'overwrite_vars': True, + 'source_vars': 'plugin: a.b.c'}, admin_user, expect=201) def test_adding_inv_src_prohibited(self, post, scm_inventory, project, admin_user): @@ -657,7 +713,7 @@ class TestControlledBySCM: def test_adding_inv_src_without_proj_access_prohibited(self, post, project, inventory, rando): inventory.admin_role.members.add(rando) post(reverse('api:inventory_inventory_sources_list', kwargs={'pk': inventory.id}), - {'name': 'new inv src', 'source_project': project.pk, 'source': 'scm', 'overwrite_vars': True}, + {'name': 'new inv src', 'source_project': project.pk, 'source': 'scm', 'overwrite_vars': True, 'source_vars': 'plugin: a.b.c'}, rando, expect=403) diff --git a/awx/main/tests/functional/models/test_inventory.py b/awx/main/tests/functional/models/test_inventory.py index 6765f0e73b..2b3c747868 100644 --- a/awx/main/tests/functional/models/test_inventory.py +++ b/awx/main/tests/functional/models/test_inventory.py @@ -2,7 +2,6 @@ import pytest from unittest import mock -import json from django.core.exceptions import ValidationError @@ -259,30 +258,19 @@ class TestInventorySourceInjectors: injector = InventorySource.injectors[source]('2.7.7') assert injector.filename == filename - def test_group_by_azure(self): - injector = InventorySource.injectors['azure_rm']('2.9') - inv_src = InventorySource( - name='azure source', source='azure_rm', - source_vars={'group_by_os_family': True} - ) - group_by_on = injector.inventory_as_dict(inv_src, '/tmp/foo') - # suspicious, yes, that is just what the script did - expected_groups = 6 - assert len(group_by_on['keyed_groups']) == expected_groups - inv_src.source_vars = json.dumps({'group_by_os_family': False}) - group_by_off = injector.inventory_as_dict(inv_src, '/tmp/foo') - # much better, everyone should turn off the flag and live in the future - assert len(group_by_off['keyed_groups']) == expected_groups - 1 - - def test_tower_plugin_named_url(self): - injector = InventorySource.injectors['tower']('2.9') - inv_src = InventorySource( - name='my tower source', source='tower', - # named URL pattern "inventory++organization" - instance_filters='Designer hair 읰++Cosmetic_products䵆' - ) - result = injector.inventory_as_dict(inv_src, '/tmp/foo') - assert result['inventory_id'] == 'Designer%20hair%20%EC%9D%B0++Cosmetic_products%E4%B5%86' + @pytest.mark.parametrize('source,proper_name', [ + ('ec2', 'amazon.aws.aws_ec2'), + ('openstack', 'openstack.cloud.openstack'), + ('gce', 'google.cloud.gcp_compute'), + ('azure_rm', 'azure.azcollection.azure_rm'), + ('vmware', 'community.vmware.vmware_vm_inventory'), + ('rhv', 'ovirt.ovirt.ovirt'), + ('satellite6', 'theforeman.foreman.foreman'), + ('tower', 'awx.awx.tower'), + ]) + def test_plugin_proper_names(self, source, proper_name): + injector = InventorySource.injectors[source]('2.9') + assert injector.get_proper_name() == proper_name @pytest.mark.django_db diff --git a/awx/main/tests/functional/test_inventory_source_injectors.py b/awx/main/tests/functional/test_inventory_source_injectors.py index 392abf8535..994c2b4c11 100644 --- a/awx/main/tests/functional/test_inventory_source_injectors.py +++ b/awx/main/tests/functional/test_inventory_source_injectors.py @@ -14,69 +14,6 @@ from django.conf import settings DATA = os.path.join(os.path.dirname(data.__file__), 'inventory') -TEST_SOURCE_FIELDS = { - 'vmware': { - 'instance_filters': '{{ config.name == "only_my_server" }},{{ somevar == "bar"}}', - 'group_by': 'fouo' - }, - 'ec2': { - 'instance_filters': 'foobaa', - # group_by selected to capture some non-trivial cross-interactions - 'group_by': 'availability_zone,instance_type,tag_keys,region', - 'source_regions': 'us-east-2,ap-south-1' - }, - 'gce': { - 'source_regions': 'us-east4-a,us-west1-b' # surfaced as env var - }, - 'azure_rm': { - 'source_regions': 'southcentralus,westus' - }, - 'tower': { - 'instance_filters': '42' - } -} - -INI_TEST_VARS = { - 'ec2': { - 'boto_profile': '/tmp/my_boto_stuff', - 'iam_role_arn': 'arn:aws:iam::123456789012:role/test-role', - 'hostname_variable': 'public_dns_name', - 'destination_variable': 'public_dns_name' - }, - 'gce': {}, - 'openstack': { - 'private': False, - 'use_hostnames': False, - 'expand_hostvars': True, - 'fail_on_errors': True - }, - 'tower': {}, # there are none - 'vmware': { - 'alias_pattern': "{{ config.foo }}", - 'host_filters': '{{ config.zoo == "DC0_H0_VM0" }}', - 'groupby_patterns': "{{ config.asdf }}", - # setting VMWARE_VALIDATE_CERTS is duplicated with env var - }, - 'azure_rm': { - 'use_private_ip': True, - 'resource_groups': 'foo_resources,bar_resources', - 'tags': 'Creator:jmarshall, peanutbutter:jelly' - }, - 'satellite6': { - 'satellite6_group_patterns': '["{app}-{tier}-{color}", "{app}-{color}"]', - 'satellite6_group_prefix': 'foo_group_prefix', - 'satellite6_want_hostcollections': True, - 'satellite6_want_ansible_ssh_host': True, - 'satellite6_want_facts': True - }, - 'rhv': { # options specific to the plugin - 'ovirt_insecure': False, - 'groups': { - 'dev': '"dev" in tags' - } - } -} - def generate_fake_var(element): """Given a credential type field element, makes up something acceptable. @@ -245,25 +182,21 @@ def create_reference_data(source_dir, env, content): @pytest.mark.django_db @pytest.mark.parametrize('this_kind', CLOUD_PROVIDERS) def test_inventory_update_injected_content(this_kind, inventory, fake_credential_factory): + injector = InventorySource.injectors[this_kind] + if injector.plugin_name is None: + pytest.skip('Use of inventory plugin is not enabled for this source') + src_vars = dict(base_source_var='value_of_var') - if this_kind in INI_TEST_VARS: - src_vars.update(INI_TEST_VARS[this_kind]) - extra_kwargs = {} - if this_kind in TEST_SOURCE_FIELDS: - extra_kwargs.update(TEST_SOURCE_FIELDS[this_kind]) + src_vars['plugin'] = injector.get_proper_name() inventory_source = InventorySource.objects.create( inventory=inventory, source=this_kind, source_vars=src_vars, - **extra_kwargs ) inventory_source.credentials.add(fake_credential_factory(this_kind)) inventory_update = inventory_source.create_unified_job() task = RunInventoryUpdate() - if InventorySource.injectors[this_kind].plugin_name is None: - pytest.skip('Use of inventory plugin is not enabled for this source') - def substitute_run(envvars=None, **_kw): """This method will replace run_pexpect instead of running, it will read the private data directory contents @@ -274,6 +207,12 @@ def test_inventory_update_injected_content(this_kind, inventory, fake_credential assert envvars.pop('ANSIBLE_INVENTORY_ENABLED') == 'auto' set_files = bool(os.getenv("MAKE_INVENTORY_REFERENCE_FILES", 'false').lower()[0] not in ['f', '0']) env, content = read_content(private_data_dir, envvars, inventory_update) + + # Assert inventory plugin inventory file is in private_data_dir + inventory_filename = InventorySource.injectors[inventory_update.source]('2.9').filename + assert len([True for k in content.keys() if k.endswith(inventory_filename)]) > 0, \ + f"'{inventory_filename}' file not found in inventory update runtime files {content.keys()}" + env.pop('ANSIBLE_COLLECTIONS_PATHS', None) # collection paths not relevant to this test base_dir = os.path.join(DATA, 'plugins') if not os.path.exists(base_dir): @@ -283,6 +222,8 @@ def test_inventory_update_injected_content(this_kind, inventory, fake_credential create_reference_data(source_dir, env, content) pytest.skip('You set MAKE_INVENTORY_REFERENCE_FILES, so this created files, unset to run actual test.') else: + source_dir = os.path.join(base_dir, this_kind) # this_kind is a global + if not os.path.exists(source_dir): raise FileNotFoundError( 'Maybe you never made reference files? ' @@ -292,9 +233,6 @@ def test_inventory_update_injected_content(this_kind, inventory, fake_credential expected_file_list = os.listdir(files_dir) except FileNotFoundError: expected_file_list = [] - assert set(expected_file_list) == set(content.keys()), ( - 'Inventory update runtime environment does not have expected files' - ) for f_name in expected_file_list: with open(os.path.join(files_dir, f_name), 'r') as f: ref_content = f.read() diff --git a/awx/main/tests/unit/models/test_inventory.py b/awx/main/tests/unit/models/test_inventory.py index dc6af0e828..26ef5e1fa9 100644 --- a/awx/main/tests/unit/models/test_inventory.py +++ b/awx/main/tests/unit/models/test_inventory.py @@ -72,23 +72,6 @@ def test_invalid_kind_clean_insights_credential(): assert json.dumps(str(e.value)) == json.dumps(str([u'Assignment not allowed for Smart Inventory'])) -@pytest.mark.parametrize('source_vars,validate_certs', [ - ({'ssl_verify': True}, True), - ({'ssl_verify': False}, False), - ({'validate_certs': True}, True), - ({'validate_certs': False}, False)]) -def test_satellite_plugin_backwards_support_for_ssl_verify(source_vars, validate_certs): - injector = InventorySource.injectors['satellite6']('2.9') - inv_src = InventorySource( - name='satellite source', source='satellite6', - source_vars=source_vars - ) - - ret = injector.inventory_as_dict(inv_src, '/tmp/foo') - assert 'validate_certs' in ret - assert ret['validate_certs'] in (validate_certs, str(validate_certs)) - - class TestControlledBySCM(): def test_clean_source_path_valid(self): inv_src = InventorySource(source_path='/not_real/', diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index cb7dc49e46..93e373e401 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -8,8 +8,6 @@ from datetime import timedelta # global settings from django.conf import global_settings -# ugettext lazy -from django.utils.translation import ugettext_lazy as _ # Update this module's local settings from the global settings module. this_module = sys.modules[__name__]