From 51f6d129cbb30f42c445f7e2fecba68fe02d6f85 Mon Sep 17 00:00:00 2001 From: Sloane Hertel Date: Tue, 9 Jun 2020 18:38:57 -0400 Subject: [PATCH] support hard coded module_defaults.yml groups for collections (#69919) * Only allow groups which were hardcoded in module_defaults.yml only load action groups from the collection if module_defaults contains a potential group for the action * Fix tests using modules that override those whitelisted in lib/ansible/config/module_defaults.yml Third party modules should not be using group/ - use the action name instead * add externalized module_defaults tests add the missing group and collections ci_complete Co-authored-by: Matt Davis * changelog ci_complete * Fix import in tests ci_complete * Update with requested changes ci_complete * don't traceback since we don't validate the contents of module_defaults ci_complete Co-authored-by: Matt Davis --- ...919-module_defaults-groups-collections.yml | 3 + lib/ansible/config/manager.py | 14 - lib/ansible/config/module_defaults.yml | 1545 ----------------- lib/ansible/executor/module_common.py | 43 +- lib/ansible/executor/task_executor.py | 4 +- lib/ansible/parsing/mod_args.py | 24 +- lib/ansible/playbook/task.py | 14 + lib/ansible/plugins/action/gather_facts.py | 2 +- lib/ansible/plugins/action/package.py | 4 +- lib/ansible/plugins/action/service.py | 4 +- lib/ansible/plugins/loader.py | 4 + .../collection_loader/_collection_finder.py | 9 + .../tasks/main.yml | 12 +- .../incidental_cloudformation/tasks/main.yml | 23 +- .../targets/incidental_ec2_instance/main.yml | 2 +- .../roles/ec2_instance/tasks/env_cleanup.yml | 18 + .../roles/ec2_instance/tasks/env_setup.yml | 16 + .../roles/ec2_instance/tasks/main.yml | 20 +- .../plugins/action/other_echoaction.py | 8 + .../othercoll/plugins/modules/other_echo1.py | 13 + .../testns/testcoll/meta/runtime.yml | 9 + .../testcoll/plugins/action/echoaction.py | 19 + .../plugins/module_utils/echo_impl.py | 15 + .../testns/testcoll/plugins/modules/echo1.py | 13 + .../testns/testcoll/plugins/modules/echo2.py | 13 + .../targets/module_defaults/runme.sh | 5 + .../targets/module_defaults/tasks/main.yml | 27 - .../targets/module_defaults/test_defaults.yml | 60 + .../ansible_test/_internal/cloud/vcenter.py | 2 +- test/sanity/ignore.txt | 1 - .../units/plugins/action/test_gather_facts.py | 8 +- 31 files changed, 341 insertions(+), 1613 deletions(-) create mode 100644 changelogs/fragments/69919-module_defaults-groups-collections.yml delete mode 100644 lib/ansible/config/module_defaults.yml create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py create mode 100755 test/integration/targets/module_defaults/runme.sh create mode 100644 test/integration/targets/module_defaults/test_defaults.yml diff --git a/changelogs/fragments/69919-module_defaults-groups-collections.yml b/changelogs/fragments/69919-module_defaults-groups-collections.yml new file mode 100644 index 00000000000..e3feda80f87 --- /dev/null +++ b/changelogs/fragments/69919-module_defaults-groups-collections.yml @@ -0,0 +1,3 @@ +bugfixes: + - module_defaults - support short group names for content relocated to collections + - module_defaults - support candidate action names for relocated content diff --git a/lib/ansible/config/manager.py b/lib/ansible/config/manager.py index 18d2d11c955..3cbfb38d448 100644 --- a/lib/ansible/config/manager.py +++ b/lib/ansible/config/manager.py @@ -289,12 +289,6 @@ class ConfigManager(object): # update constants self.update_config_data() - try: - self.update_module_defaults_groups() - except Exception as e: - # Since this is a 2.7 preview feature, we want to have it fail as gracefully as possible when there are issues. - sys.stderr.write('Could not load module_defaults_groups: %s: %s\n\n' % (type(e).__name__, e)) - self.module_defaults_groups = {} def _read_config_yaml_file(self, yml_file): # TODO: handle relative paths as relative to the directory containing the current playbook instead of CWD @@ -525,14 +519,6 @@ class ConfigManager(object): self._plugins[plugin_type][name] = defs - def update_module_defaults_groups(self): - defaults_config = self._read_config_yaml_file( - '%s/module_defaults.yml' % os.path.join(os.path.dirname(__file__)) - ) - if defaults_config.get('version') not in ('1', '1.0', 1, 1.0): - raise AnsibleError('module_defaults.yml has an invalid version "%s" for configuration. Could be a bad install.' % defaults_config.get('version')) - self.module_defaults_groups = defaults_config.get('groupings', {}) - def update_config_data(self, defs=None, configfile=None): ''' really: update constants ''' diff --git a/lib/ansible/config/module_defaults.yml b/lib/ansible/config/module_defaults.yml deleted file mode 100644 index aff04a7668c..00000000000 --- a/lib/ansible/config/module_defaults.yml +++ /dev/null @@ -1,1545 +0,0 @@ ---- -version: '1.0' -groupings: - acme_account: - - acme - acme_account_info: - - acme - acme_account_facts: - - acme - acme_certificate: - - acme - acme_certificate_revoke: - - acme - acme_inspect: - - acme - aws_acm: - - aws - aws_acm_facts: - - aws - aws_acm_info: - - aws - aws_api_gateway: - - aws - aws_application_scaling_policy: - - aws - aws_az_facts: - - aws - aws_az_info: - - aws - aws_batch_compute_environment: - - aws - aws_batch_job_definition: - - aws - aws_batch_job_queue: - - aws - aws_caller_facts: - - aws - aws_caller_info: - - aws - aws_codebuild: - - aws - aws_codecommit: - - aws - aws_codepipeline: - - aws - aws_config_aggregation_authorization: - - aws - aws_config_aggregator: - - aws - aws_config_delivery_channel: - - aws - aws_config_recorder: - - aws - aws_config_rule: - - aws - aws_direct_connect_connection: - - aws - aws_direct_connect_gateway: - - aws - aws_direct_connect_link_aggregation_group: - - aws - aws_direct_connect_virtual_interface: - - aws - aws_eks_cluster: - - aws - aws_elasticbeanstalk_app: - - aws - aws_glue_connection: - - aws - aws_glue_job: - - aws - aws_inspector_target: - - aws - aws_kms: - - aws - aws_kms_facts: - - aws - aws_kms_info: - - aws - aws_region_facts: - - aws - aws_region_info: - - aws - aws_s3: - - aws - aws_s3_bucket_facts: - - aws - aws_s3_bucket_info: - - aws - aws_s3_cors: - - aws - aws_secret: - - aws - aws_ses_identity: - - aws - aws_ses_identity_policy: - - aws - aws_ses_rule_set: - - aws - aws_sgw_facts: - - aws - aws_sgw_info: - - aws - aws_ssm_parameter_store: - - aws - aws_step_functions_state_machine: - - aws - aws_step_functions_state_machine_execution: - - aws - aws_waf_condition: - - aws - aws_waf_facts: - - aws - aws_waf_info: - - aws - aws_waf_rule: - - aws - aws_waf_web_acl: - - aws - cloudformation: - - aws - cloudformation_exports_info: - - aws - cloudformation_facts: - - aws - cloudformation_info: - - aws - cloudformation_stack_set: - - aws - cloudfront_distribution: - - aws - cloudfront_facts: - - aws - cloudfront_info: - - aws - cloudfront_invalidation: - - aws - cloudfront_origin_access_identity: - - aws - cloudtrail: - - aws - cloudwatchevent_rule: - - aws - cloudwatchlogs_log_group: - - aws - cloudwatchlogs_log_group_facts: - - aws - cloudwatchlogs_log_group_info: - - aws - cloudwatchlogs_log_group_metric_filter: - - aws - cpm_plugconfig: - - cpm - cpm_plugcontrol: - - cpm - cpm_serial_port_config: - - cpm - cpm_serial_port_info: - - cpm - cpm_user: - - cpm - data_pipeline: - - aws - dms_endpoint: - - aws - dms_replication_subnet_group: - - aws - docker_compose: - - docker - docker_config: - - docker - docker_container_info: - - docker - docker_container: - - docker - docker_host_info: - - docker - docker_image_facts: - - docker - docker_image_info: - - docker - docker_image: - - docker - docker_login: - - docker - docker_network_info: - - docker - docker_network: - - docker - docker_node_info: - - docker - docker_node: - - docker - docker_prune: - - docker - docker_secret: - - docker - docker_service: - - docker - docker_swarm_info: - - docker - docker_swarm: - - docker - docker_swarm_service_info: - - docker - docker_swarm_service: - - docker - docker_volume_info: - - docker - docker_volume: - - docker - dynamodb_table: - - aws - dynamodb_ttl: - - aws - ec2: - - aws - ec2_ami: - - aws - ec2_ami_copy: - - aws - ec2_ami_facts: - - aws - ec2_ami_info: - - aws - ec2_asg: - - aws - ec2_asg_facts: - - aws - ec2_asg_info: - - aws - ec2_asg_lifecycle_hook: - - aws - ec2_customer_gateway: - - aws - ec2_customer_gateway_facts: - - aws - ec2_customer_gateway_info: - - aws - ec2_eip: - - aws - ec2_eip_facts: - - aws - ec2_eip_info: - - aws - ec2_elb: - - aws - ec2_elb_facts: - - aws - ec2_elb_info: - - aws - ec2_elb_lb: - - aws - ec2_eni: - - aws - ec2_eni_facts: - - aws - ec2_eni_info: - - aws - ec2_group: - - aws - ec2_group_facts: - - aws - ec2_group_info: - - aws - ec2_instance: - - aws - ec2_instance_facts: - - aws - ec2_instance_info: - - aws - ec2_key: - - aws - ec2_launch_template: - - aws - ec2_lc: - - aws - ec2_lc_facts: - - aws - ec2_lc_info: - - aws - ec2_lc_find: - - aws - ec2_metric_alarm: - - aws - ec2_placement_group: - - aws - ec2_placement_group_facts: - - aws - ec2_placement_group_info: - - aws - ec2_scaling_policy: - - aws - ec2_snapshot: - - aws - ec2_snapshot_copy: - - aws - ec2_snapshot_facts: - - aws - ec2_snapshot_info: - - aws - ec2_tag: - - aws - ec2_tag_info: - - aws - ec2_transit_gateway: - - aws - ec2_transit_gateway_info: - - aws - ec2_vol: - - aws - ec2_vol_facts: - - aws - ec2_vol_info: - - aws - ec2_vpc_dhcp_option: - - aws - ec2_vpc_dhcp_option_facts: - - aws - ec2_vpc_dhcp_option_info: - - aws - ec2_vpc_egress_igw: - - aws - ec2_vpc_endpoint: - - aws - ec2_vpc_endpoint_facts: - - aws - ec2_vpc_endpoint_info: - - aws - ec2_vpc_igw: - - aws - ec2_vpc_igw_facts: - - aws - ec2_vpc_igw_info: - - aws - ec2_vpc_nacl: - - aws - ec2_vpc_nacl_facts: - - aws - ec2_vpc_nacl_info: - - aws - ec2_vpc_nat_gateway: - - aws - ec2_vpc_nat_gateway_facts: - - aws - ec2_vpc_nat_gateway_info: - - aws - ec2_vpc_net: - - aws - ec2_vpc_net_facts: - - aws - ec2_vpc_net_info: - - aws - ec2_vpc_peer: - - aws - ec2_vpc_peering_facts: - - aws - ec2_vpc_peering_info: - - aws - ec2_vpc_route_table: - - aws - ec2_vpc_route_table_facts: - - aws - ec2_vpc_route_table_info: - - aws - ec2_vpc_subnet: - - aws - ec2_vpc_subnet_facts: - - aws - ec2_vpc_subnet_info: - - aws - ec2_vpc_vgw: - - aws - ec2_vpc_vgw_facts: - - aws - ec2_vpc_vgw_info: - - aws - ec2_vpc_vpn: - - aws - ec2_vpc_vpn_facts: - - aws - ec2_vpc_vpn_info: - - aws - ec2_win_password: - - aws - ecs_attribute: - - aws - ecs_cluster: - - aws - ecs_ecr: - - aws - ecs_service: - - aws - ecs_service_facts: - - aws - ecs_service_info: - - aws - ecs_tag: - - aws - ecs_task: - - aws - ecs_taskdefinition: - - aws - ecs_taskdefinition_facts: - - aws - ecs_taskdefinition_info: - - aws - efs: - - aws - efs_facts: - - aws - efs_info: - - aws - elasticache: - - aws - elasticache_facts: - - aws - elasticache_info: - - aws - elasticache_parameter_group: - - aws - elasticache_snapshot: - - aws - elasticache_subnet_group: - - aws - elb_application_lb: - - aws - elb_application_lb_facts: - - aws - elb_application_lb_info: - - aws - elb_classic_lb: - - aws - elb_classic_lb_facts: - - aws - elb_classic_lb_info: - - aws - elb_instance: - - aws - elb_network_lb: - - aws - elb_target: - - aws - elb_target_facts: - - aws - elb_target_group: - - aws - elb_target_group_facts: - - aws - elb_target_group_info: - - aws - elb_target_info: - - aws - execute_lambda: - - aws - iam: - - aws - iam_cert: - - aws - iam_group: - - aws - iam_managed_policy: - - aws - iam_mfa_device_facts: - - aws - iam_mfa_device_info: - - aws - iam_password_policy: - - aws - iam_policy: - - aws - iam_policy_info: - - aws - iam_role: - - aws - iam_role_facts: - - aws - iam_role_info: - - aws - iam_server_certificate_facts: - - aws - iam_saml_federation: - - aws - iam_server_certificate_info: - - aws - iam_user: - - aws - iam_user_info: - - aws - kinesis_stream: - - aws - lambda: - - aws - lambda_alias: - - aws - lambda_event: - - aws - lambda_facts: - - aws - lambda_info: - - aws - lambda_policy: - - aws - lightsail: - - aws - lightsail_keypair: - - aws - rds: - - aws - rds_instance: - - aws - rds_instance_facts: - - aws - rds_instance_info: - - aws - rds_param_group: - - aws - rds_snapshot_facts: - - aws - rds_snapshot_info: - - aws - rds_subnet_group: - - aws - redshift: - - aws - redshift_cross_region_snapshots: - - aws - redshift_facts: - - aws - redshift_info: - - aws - redshift_subnet_group: - - aws - route53: - - aws - route53_facts: - - aws - route53_info: - - aws - route53_health_check: - - aws - route53_zone: - - aws - s3_bucket: - - aws - s3_bucket_notification: - - aws - s3_lifecycle: - - aws - s3_logging: - - aws - s3_sync: - - aws - s3_website: - - aws - sns: - - aws - sns_topic: - - aws - sqs_queue: - - aws - sts_assume_role: - - aws - sts_session_token: - - aws - gcp_appengine_firewall_rule: - - gcp - gcp_appengine_firewall_rule_info: - - gcp - gcp_bigquery_dataset: - - gcp - gcp_bigquery_dataset_info: - - gcp - gcp_bigquery_table: - - gcp - gcp_bigquery_table_info: - - gcp - gcp_cloudbuild_trigger: - - gcp - gcp_cloudbuild_trigger_info: - - gcp - gcp_cloudfunctions_cloud_function: - - gcp - gcp_cloudfunctions_cloud_function_info: - - gcp - gcp_cloudscheduler_job: - - gcp - gcp_cloudscheduler_job_info: - - gcp - gcp_cloudtasks_queue: - - gcp - gcp_cloudtasks_queue_info: - - gcp - gcp_compute_address: - - gcp - gcp_compute_address_info: - - gcp - gcp_compute_autoscaler: - - gcp - gcp_compute_autoscaler_info: - - gcp - gcp_compute_backend_bucket: - - gcp - gcp_compute_backend_bucket_info: - - gcp - gcp_compute_backend_service: - - gcp - gcp_compute_backend_service_info: - - gcp - gcp_compute_disk: - - gcp - gcp_compute_disk_info: - - gcp - gcp_compute_firewall: - - gcp - gcp_compute_firewall_info: - - gcp - gcp_compute_forwarding_rule: - - gcp - gcp_compute_forwarding_rule_info: - - gcp - gcp_compute_global_address: - - gcp - gcp_compute_global_address_info: - - gcp - gcp_compute_global_forwarding_rule: - - gcp - gcp_compute_global_forwarding_rule_info: - - gcp - gcp_compute_health_check: - - gcp - gcp_compute_health_check_info: - - gcp - gcp_compute_http_health_check: - - gcp - gcp_compute_http_health_check_info: - - gcp - gcp_compute_https_health_check: - - gcp - gcp_compute_https_health_check_info: - - gcp - gcp_compute_image: - - gcp - gcp_compute_image_info: - - gcp - gcp_compute_instance: - - gcp - gcp_compute_instance_info: - - gcp - gcp_compute_instance_group: - - gcp - gcp_compute_instance_group_info: - - gcp - gcp_compute_instance_group_manager: - - gcp - gcp_compute_instance_group_manager_info: - - gcp - gcp_compute_instance_template: - - gcp - gcp_compute_instance_template_info: - - gcp - gcp_compute_interconnect_attachment: - - gcp - gcp_compute_interconnect_attachment_info: - - gcp - gcp_compute_network: - - gcp - gcp_compute_network_info: - - gcp - gcp_compute_network_endpoint_group: - - gcp - gcp_compute_network_endpoint_group_info: - - gcp - gcp_compute_node_group: - - gcp - gcp_compute_node_group_info: - - gcp - gcp_compute_node_template: - - gcp - gcp_compute_node_template_info: - - gcp - gcp_compute_region_backend_service: - - gcp - gcp_compute_region_backend_service_info: - - gcp - gcp_compute_region_disk: - - gcp - gcp_compute_region_disk_info: - - gcp - gcp_compute_reservation: - - gcp - gcp_compute_reservation_info: - - gcp - gcp_compute_route: - - gcp - gcp_compute_route_info: - - gcp - gcp_compute_router: - - gcp - gcp_compute_router_info: - - gcp - gcp_compute_snapshot: - - gcp - gcp_compute_snapshot_info: - - gcp - gcp_compute_ssl_certificate: - - gcp - gcp_compute_ssl_certificate_info: - - gcp - gcp_compute_ssl_policy: - - gcp - gcp_compute_ssl_policy_info: - - gcp - gcp_compute_subnetwork: - - gcp - gcp_compute_subnetwork_info: - - gcp - gcp_compute_target_http_proxy: - - gcp - gcp_compute_target_http_proxy_info: - - gcp - gcp_compute_target_https_proxy: - - gcp - gcp_compute_target_https_proxy_info: - - gcp - gcp_compute_target_instance: - - gcp - gcp_compute_target_instance_info: - - gcp - gcp_compute_target_pool: - - gcp - gcp_compute_target_pool_info: - - gcp - gcp_compute_target_ssl_proxy: - - gcp - gcp_compute_target_ssl_proxy_info: - - gcp - gcp_compute_target_tcp_proxy: - - gcp - gcp_compute_target_tcp_proxy_info: - - gcp - gcp_compute_target_vpn_gateway: - - gcp - gcp_compute_target_vpn_gateway_info: - - gcp - gcp_compute_url_map: - - gcp - gcp_compute_url_map_info: - - gcp - gcp_compute_vpn_tunnel: - - gcp - gcp_compute_vpn_tunnel_info: - - gcp - gcp_container_cluster: - - gcp - gcp_container_cluster_info: - - gcp - gcp_container_node_pool: - - gcp - gcp_container_node_pool_info: - - gcp - gcp_dns_managed_zone: - - gcp - gcp_dns_managed_zone_info: - - gcp - gcp_dns_resource_record_set: - - gcp - gcp_dns_resource_record_set_info: - - gcp - gcp_filestore_instance: - - gcp - gcp_filestore_instance_info: - - gcp - gcp_iam_role: - - gcp - gcp_iam_role_info: - - gcp - gcp_iam_service_account: - - gcp - gcp_iam_service_account_info: - - gcp - gcp_iam_service_account_key: - - gcp - gcp_kms_crypto_key: - - gcp - gcp_kms_crypto_key_info: - - gcp - gcp_kms_key_ring: - - gcp - gcp_kms_key_ring_info: - - gcp - gcp_logging_metric: - - gcp - gcp_logging_metric_info: - - gcp - gcp_mlengine_model: - - gcp - gcp_mlengine_model_info: - - gcp - gcp_mlengine_version: - - gcp - gcp_mlengine_version_info: - - gcp - gcp_pubsub_subscription: - - gcp - gcp_pubsub_subscription_info: - - gcp - gcp_pubsub_topic: - - gcp - gcp_pubsub_topic_info: - - gcp - gcp_redis_instance: - - gcp - gcp_redis_instance_info: - - gcp - gcp_resourcemanager_project: - - gcp - gcp_resourcemanager_project_info: - - gcp - gcp_runtimeconfig_config: - - gcp - gcp_runtimeconfig_config_info: - - gcp - gcp_runtimeconfig_variable: - - gcp - gcp_runtimeconfig_variable_info: - - gcp - gcp_serviceusage_service: - - gcp - gcp_serviceusage_service_info: - - gcp - gcp_sourcerepo_repository: - - gcp - gcp_sourcerepo_repository_info: - - gcp - gcp_spanner_database: - - gcp - gcp_spanner_database_info: - - gcp - gcp_spanner_instance: - - gcp - gcp_spanner_instance_info: - - gcp - gcp_sql_database: - - gcp - gcp_sql_database_info: - - gcp - gcp_sql_instance: - - gcp - gcp_sql_instance_info: - - gcp - gcp_sql_user: - - gcp - gcp_sql_user_info: - - gcp - gcp_storage_bucket: - - gcp - gcp_storage_bucket_access_control: - - gcp - gcp_storage_object: - - gcp - gcp_tpu_node: - - gcp - gcp_tpu_node_info: - - gcp - azure_rm_acs: - - azure - azure_rm_aks: - - azure - azure_rm_aks_facts: - - azure - azure_rm_appserviceplan: - - azure - azure_rm_appserviceplan_facts: - - azure - azure_rm_availabilityset: - - azure - azure_rm_availabilityset_facts: - - azure - azure_rm_containerinstance: - - azure - azure_rm_containerregistry: - - azure - azure_rm_deployment: - - azure - azure_rm_dnsrecordset: - - azure - azure_rm_dnsrecordset_facts: - - azure - azure_rm_dnszone: - - azure - azure_rm_dnszone_facts: - - azure - azure_rm_functionapp: - - azure - azure_rm_functionapp_facts: - - azure - azure_rm_image: - - azure - azure_rm_keyvault: - - azure - azure_rm_keyvaultkey: - - azure - azure_rm_keyvaultsecret: - - azure - azure_rm_loadbalancer: - - azure - azure_rm_loadbalancer_facts: - - azure - azure_rm_managed_disk: - - azure - azure_rm_manageddisk: - - azure - azure_rm_managed_disk_facts: - - azure - azure_rm_manageddisk_facts: - - azure - azure_rm_mysqldatabase: - - azure - azure_rm_mysqldatabase_facts: - - azure - azure_rm_mysqlserver: - - azure - azure_rm_mysqlserver_facts: - - azure - azure_rm_networkinterface: - - azure - azure_rm_networkinterface_facts: - - azure - azure_rm_postgresqldatabase: - - azure - azure_rm_postgresqldatabase_facts: - - azure - azure_rm_postgresqlserver: - - azure - azure_rm_publicipaddress: - - azure - azure_rm_publicipaddress_facts: - - azure - azure_rm_resource: - - azure - azure_rm_resource_facts: - - azure - azure_rm_resourcegroup: - - azure - azure_rm_resourcegroup_facts: - - azure - azure_rm_resourcegroup_info: - - azure - azure_rm_securitygroup: - - azure - azure_rm_securitygroup_facts: - - azure - azure_rm_sqldatabase: - - azure - azure_rm_sqlserver: - - azure - azure_rm_sqlserver_facts: - - azure - azure_rm_storageaccount: - - azure - azure_rm_storageaccount_facts: - - azure - azure_rm_storageblob: - - azure - azure_rm_subnet: - - azure - azure_rm_virtualmachine: - - azure - azure_rm_virtualmachine_extension: - - azure - azure_rm_virtualmachine_facts: - - azure - azure_rm_virtualmachineimage_facts: - - azure - azure_rm_virtualmachine_scaleset: - - azure - azure_rm_virtualmachine_scaleset_facts: - - azure - azure_rm_virtualnetwork: - - azure - azure_rm_virtualnetwork_facts: - - azure - azure_rm_webapp: - - azure - k8s: - - k8s - k8s_auth: - - k8s - k8s_facts: - - k8s - k8s_info: - - k8s - k8s_service: - - k8s - k8s_scale: - - k8s - kubevirt_cdi_upload: - - k8s - kubevirt_preset: - - k8s - kubevirt_pvc: - - k8s - kubevirt_rs: - - k8s - kubevirt_template: - - k8s - kubevirt_vm: - - k8s - os_auth: - - os - os_client_config: - - os - os_coe_cluster: - - os - os_coe_cluster_template: - - os - os_flavor_facts: - - os - os_flavor_info: - - os - os_floating_ip: - - os - os_group: - - os - os_image: - - os - os_image_facts: - - os - os_image_info: - - os - os_ironic: - - os - os_ironic_inspect: - - os - os_ironic_node: - - os - os_keypair: - - os - os_keystone_domain: - - os - os_keystone_domain_facts: - - os - os_keystone_domain_info: - - os - os_keystone_endpoint: - - os - os_keystone_role: - - os - os_keystone_service: - - os - os_listener: - - os - os_loadbalancer: - - os - os_member: - - os - os_network: - - os - os_networks_facts: - - os - os_networks_info: - - os - os_nova_flavor: - - os - os_nova_host_aggregate: - - os - os_object: - - os - os_pool: - - os - os_port: - - os - os_port_facts: - - os - os_port_info: - - os - os_project: - - os - os_project_access: - - os - os_project_facts: - - os - os_project_info: - - os - os_quota: - - os - os_recordset: - - os - os_router: - - os - os_security_group: - - os - os_security_group_rule: - - os - os_server: - - os - os_server_action: - - os - os_server_facts: - - os - os_server_info: - - os - os_server_group: - - os - os_server_metadata: - - os - os_server_volume: - - os - os_stack: - - os - os_subnet: - - os - os_subnets_facts: - - os - os_subnets_info: - - os - os_user: - - os - os_user_facts: - - os - os_user_info: - - os - os_user_group: - - os - os_user_role: - - os - os_volume: - - os - os_volume_snapshot: - - os - os_zone: - - os - ovirt_affinity_group: - - ovirt - ovirt_affinity_label_facts: - - ovirt - ovirt_affinity_label_info: - - ovirt - ovirt_affinity_label: - - ovirt - ovirt_api_facts: - - ovirt - ovirt_api_info: - - ovirt - ovirt_auth: - - ovirt - ovirt_cluster_facts: - - ovirt - ovirt_cluster_info: - - ovirt - ovirt_cluster: - - ovirt - ovirt_datacenter_facts: - - ovirt - ovirt_datacenter_info: - - ovirt - ovirt_datacenter: - - ovirt - ovirt_disk_facts: - - ovirt - ovirt_disk_info: - - ovirt - ovirt_disk: - - ovirt - ovirt_event_facts: - - ovirt - ovirt_event_info: - - ovirt - ovirt_event: - - ovirt - ovirt_external_provider_facts: - - ovirt - ovirt_external_provider_info: - - ovirt - ovirt_external_provider: - - ovirt - ovirt_group_facts: - - ovirt - ovirt_group_info: - - ovirt - ovirt_group: - - ovirt - ovirt_host_facts: - - ovirt - ovirt_host_info: - - ovirt - ovirt_host_network: - - ovirt - ovirt_host_pm: - - ovirt - ovirt_host: - - ovirt - ovirt_host_storage_facts: - - ovirt - ovirt_host_storage_info: - - ovirt - ovirt_instance_type: - - ovirt - ovirt_job: - - ovirt - ovirt_mac_pool: - - ovirt - ovirt_network_facts: - - ovirt - ovirt_network_info: - - ovirt - ovirt_network: - - ovirt - ovirt_nic_facts: - - ovirt - ovirt_nic_info: - - ovirt - ovirt_nic: - - ovirt - ovirt_permission_facts: - - ovirt - ovirt_permission_info: - - ovirt - ovirt_permission: - - ovirt - ovirt_quota_facts: - - ovirt - ovirt_quota_info: - - ovirt - ovirt_quota: - - ovirt - ovirt_role: - - ovirt - ovirt_scheduling_policy_facts: - - ovirt - ovirt_scheduling_policy_info: - - ovirt - ovirt_snapshot_facts: - - ovirt - ovirt_snapshot_info: - - ovirt - ovirt_snapshot: - - ovirt - ovirt_storage_connection: - - ovirt - ovirt_storage_domain_facts: - - ovirt - ovirt_storage_domain_info: - - ovirt - ovirt_storage_domain: - - ovirt - ovirt_storage_template_facts: - - ovirt - ovirt_storage_template_info: - - ovirt - ovirt_storage_vm_facts: - - ovirt - ovirt_storage_vm_info: - - ovirt - ovirt_tag_facts: - - ovirt - ovirt_tag_info: - - ovirt - ovirt_tag: - - ovirt - ovirt_template_facts: - - ovirt - ovirt_template_info: - - ovirt - ovirt_template: - - ovirt - ovirt_user_facts: - - ovirt - ovirt_user_info: - - ovirt - ovirt_user: - - ovirt - ovirt_vm_facts: - - ovirt - ovirt_vm_info: - - ovirt - ovirt_vmpool_facts: - - ovirt - ovirt_vmpool_info: - - ovirt - ovirt_vmpool: - - ovirt - ovirt_vm: - - ovirt - ovirt_vnic_profile_info: - - ovirt - ovirt_vnic_profile: - - ovirt - test_module_defaults: - - test - vcenter_extension: - - vmware - vcenter_extension_info: - - vmware - vcenter_folder: - - vmware - vcenter_license: - - vmware - vmware_about_info: - - vmware - vmware_category: - - vmware - vmware_category_info: - - vmware - vmware_cfg_backup: - - vmware - vmware_cluster: - - vmware - vmware_cluster_drs: - - vmware - vmware_cluster_ha: - - vmware - vmware_cluster_info: - - vmware - vmware_cluster_vsan: - - vmware - vmware_content_deploy_template: - - vmware - vmware_content_library_info: - - vmware - vmware_content_library_manager: - - vmware - vmware_datacenter: - - vmware - vmware_datastore_cluster: - - vmware - vmware_datastore_info: - - vmware - vmware_datastore_maintenancemode: - - vmware - vmware_deploy_ovf: - - vmware - vmware_dns_config: - - vmware - vmware_drs_group: - - vmware - vmware_drs_group_info: - - vmware - vmware_drs_rule_info: - - vmware - vmware_dvs_host: - - vmware - vmware_dvs_portgroup: - - vmware - vmware_dvs_portgroup_find: - - vmware - vmware_dvs_portgroup_info: - - vmware - vmware_dvswitch: - - vmware - vmware_dvswitch_lacp: - - vmware - vmware_dvswitch_nioc: - - vmware - vmware_dvswitch_pvlans: - - vmware - vmware_dvswitch_uplink_pg: - - vmware - vmware_evc_mode: - - vmware - vmware_export_ovf: - - vmware - vmware_folder_info: - - vmware - vmware_guest: - - vmware - vmware_guest_boot_info: - - vmware - vmware_guest_boot_manager: - - vmware - vmware_guest_controller: - - vmware - vmware_guest_custom_attribute_defs: - - vmware - vmware_guest_custom_attributes: - - vmware - vmware_guest_customization_info: - - vmware - vmware_guest_disk: - - vmware - vmware_guest_disk_info: - - vmware - vmware_guest_file_operation: - - vmware - vmware_guest_find: - - vmware - vmware_guest_info: - - vmware - vmware_guest_move: - - vmware - vmware_guest_network: - - vmware - vmware_guest_powerstate: - - vmware - vmware_guest_register_operation: - - vmware - vmware_guest_screenshot: - - vmware - vmware_guest_sendkey: - - vmware - vmware_guest_serial_port: - - vmware - vmware_guest_snapshot: - - vmware - vmware_guest_snapshot_info: - - vmware - vmware_guest_tools_info: - - vmware - vmware_guest_tools_upgrade: - - vmware - vmware_guest_tools_wait: - - vmware - vmware_guest_video: - - vmware - vmware_guest_vnc: - - vmware - vmware_host: - - vmware - vmware_host_acceptance: - - vmware - vmware_host_active_directory: - - vmware - vmware_host_auto_start: - - vmware - vmware_host_capability_info: - - vmware - vmware_host_config_info: - - vmware - vmware_host_config_manager: - - vmware - vmware_host_datastore: - - vmware - vmware_host_dns: - - vmware - vmware_host_dns_info: - - vmware - vmware_host_facts: - - vmware - vmware_host_feature_info: - - vmware - vmware_host_firewall_info: - - vmware - vmware_host_firewall_manager: - - vmware - vmware_host_hyperthreading: - - vmware - vmware_host_ipv6: - - vmware - vmware_host_kernel_manager: - - vmware - vmware_host_lockdown: - - vmware - vmware_host_ntp: - - vmware - vmware_host_ntp_info: - - vmware - vmware_host_package_info: - - vmware - vmware_host_powermgmt_policy: - - vmware - vmware_host_powerstate: - - vmware - vmware_host_scanhba: - - vmware - vmware_host_service_info: - - vmware - vmware_host_service_manager: - - vmware - vmware_host_snmp: - - vmware - vmware_host_ssl_info: - - vmware - vmware_host_vmhba_info: - - vmware - vmware_host_vmnic_info: - - vmware - vmware_local_role_info: - - vmware - vmware_local_role_manager: - - vmware - vmware_local_user_info: - - vmware - vmware_local_user_manager: - - vmware - vmware_maintenancemode: - - vmware - vmware_migrate_vmk: - - vmware - vmware_object_role_permission: - - vmware - vmware_portgroup: - - vmware - vmware_portgroup_info: - - vmware - vmware_resource_pool: - - vmware - vmware_resource_pool_info: - - vmware - vmware_tag: - - vmware - vmware_tag_info: - - vmware - vmware_tag_manager: - - vmware - vmware_target_canonical_info: - - vmware - vmware_vcenter_settings: - - vmware - vmware_vcenter_statistics: - - vmware - vmware_vm_host_drs_rule: - - vmware - vmware_vm_info: - - vmware - vmware_vm_shell: - - vmware - vmware_vm_storage_policy_info: - - vmware - vmware_vm_vm_drs_rule: - - vmware - vmware_vm_vss_dvs_migrate: - - vmware - vmware_vmkernel: - - vmware - vmware_vmkernel_info: - - vmware - vmware_vmkernel_ip_config: - - vmware - vmware_vmotion: - - vmware - vmware_vsan_cluster: - - vmware - vmware_vsan_health_info: - - vmware - vmware_vspan_session: - - vmware - vmware_vswitch: - - vmware - vmware_vswitch_info: - - vmware - vsphere_copy: - - vmware - vsphere_file: - - vmware diff --git a/lib/ansible/executor/module_common.py b/lib/ansible/executor/module_common.py index 05b1e8ab2f6..429bfbb10e9 100644 --- a/lib/ansible/executor/module_common.py +++ b/lib/ansible/executor/module_common.py @@ -38,7 +38,7 @@ from ansible.executor.interpreter_discovery import InterpreterDiscoveryRequiredE from ansible.executor.powershell import module_manifest as ps_manifest from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native from ansible.plugins.loader import module_utils_loader -from ansible.utils.collection_loader._collection_finder import _get_collection_metadata +from ansible.utils.collection_loader._collection_finder import _get_collection_metadata, AnsibleCollectionRef # Must import strategy and use write_locks from there # If we import write_locks directly then we end up binding a @@ -1319,7 +1319,23 @@ def modify_module(module_name, module_path, module_args, templar, task_vars=None return (b_module_data, module_style, shebang) -def get_action_args_with_defaults(action, args, defaults, templar): +def get_action_args_with_defaults(action, args, defaults, templar, redirected_names=None): + group_collection_map = { + 'acme': ['community.crypto'], + 'aws': ['amazon.aws', 'community.aws'], + 'azure': ['azure.azcollection'], + 'cpm': ['wti.remote'], + 'docker': ['community.general'], + 'gcp': ['google.cloud'], + 'k8s': ['community.kubernetes', 'community.general'], + 'os': ['openstack.cloud'], + 'ovirt': ['ovirt.ovirt', 'community.general'], + 'vmware': ['community.vmware'], + 'testgroup': ['testns.testcoll', 'testns.othercoll', 'testns.boguscoll'] + } + + if not redirected_names: + redirected_names = [action] tmp_args = {} module_defaults = {} @@ -1334,13 +1350,26 @@ def get_action_args_with_defaults(action, args, defaults, templar): module_defaults = templar.template(module_defaults) # deal with configured group defaults first - if action in C.config.module_defaults_groups: - for group in C.config.module_defaults_groups.get(action, []): - tmp_args.update((module_defaults.get('group/{0}'.format(group)) or {}).copy()) + for default in module_defaults: + if not default.startswith('group/'): + continue + + group_name = default.split('group/')[-1] + + for collection_name in group_collection_map.get(group_name, []): + try: + action_group = _get_collection_metadata(collection_name).get('action_groups', {}) + except ValueError: + # The collection may not be installed + continue + + if any(name for name in redirected_names if name in action_group): + tmp_args.update((module_defaults.get('group/%s' % group_name) or {}).copy()) # handle specific action defaults - if action in module_defaults: - tmp_args.update(module_defaults[action].copy()) + for action in redirected_names: + if action in module_defaults: + tmp_args.update(module_defaults[action].copy()) # direct args override all tmp_args.update(args) diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 22f758e9cef..4bb9ea31414 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -631,7 +631,9 @@ class TaskExecutor: self._handler = self._get_action_handler(connection=self._connection, templar=templar) # Apply default params for action/module, if present - self._task.args = get_action_args_with_defaults(self._task.action, self._task.args, self._task.module_defaults, templar) + self._task.args = get_action_args_with_defaults( + self._task.action, self._task.args, self._task.module_defaults, templar, self._task._ansible_internal_redirect_list + ) # And filter out any fields which were set to default(omit), and got the omit token value omit_token = variables.get('omit') diff --git a/lib/ansible/parsing/mod_args.py b/lib/ansible/parsing/mod_args.py index 929d1add9e1..a6906b909c7 100644 --- a/lib/ansible/parsing/mod_args.py +++ b/lib/ansible/parsing/mod_args.py @@ -119,6 +119,8 @@ class ModuleArgsParser: self._task_attrs.update(['local_action', 'static']) self._task_attrs = frozenset(self._task_attrs) + self.internal_redirect_list = [] + def _split_module_string(self, module_string): ''' when module names are expressed like: @@ -266,6 +268,8 @@ class ModuleArgsParser: delegate_to = self._task_ds.get('delegate_to', Sentinel) args = dict() + self.internal_redirect_list = [] + # This is the standard YAML form for command-type modules. We grab # the args and pass them in as additional arguments, which can/will # be overwritten via dict updates from the other arg sources below @@ -294,8 +298,24 @@ class ModuleArgsParser: # walk the filtered input dictionary to see if we recognize a module name for item, value in iteritems(non_task_ds): - if item in BUILTIN_TASKS or skip_action_validation or action_loader.has_plugin(item, collection_list=self._collection_list) or \ - module_loader.has_plugin(item, collection_list=self._collection_list): + is_action_candidate = False + if item in BUILTIN_TASKS: + is_action_candidate = True + elif skip_action_validation: + is_action_candidate = True + else: + # If the plugin is resolved and redirected smuggle the list of candidate names via the task attribute 'internal_redirect_list' + context = action_loader.find_plugin_with_context(item, collection_list=self._collection_list) + if not context.resolved: + context = module_loader.find_plugin_with_context(item, collection_list=self._collection_list) + if context.resolved and context.redirect_list: + self.internal_redirect_list = context.redirect_list + elif context.redirect_list: + self.internal_redirect_list = context.redirect_list + + is_action_candidate = bool(self.internal_redirect_list) + + if is_action_candidate: # finding more than one module name is a problem if action is not None: raise AnsibleParserError("conflicting action statements: %s, %s" % (action, item), obj=self._task_ds) diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 405316d3f8a..80b7fd353fe 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -91,6 +91,10 @@ class Task(Base, Conditional, Taggable, CollectionSearch): def __init__(self, block=None, role=None, task_include=None): ''' constructors a task, without the Task.load classmethod, it will be pretty blank ''' + # This is a reference of all the candidate action names for transparent execution of module_defaults with redirected content + # This isn't a FieldAttribute to prevent it from being set via the playbook + self._ansible_internal_redirect_list = [] + self._role = role self._parent = None @@ -220,6 +224,8 @@ class Task(Base, Conditional, Taggable, CollectionSearch): raise # But if it wasn't, we can add the yaml object now to get more detail raise AnsibleParserError(to_native(e), obj=ds, orig_exc=e) + else: + self._ansible_internal_redirect_list = args_parser.internal_redirect_list[:] # the command/shell/script modules used to support the `cmd` arg, # which corresponds to what we now call _raw_params, so move that @@ -394,6 +400,9 @@ class Task(Base, Conditional, Taggable, CollectionSearch): def copy(self, exclude_parent=False, exclude_tasks=False): new_me = super(Task, self).copy() + # if the task has an associated list of candidate names, copy it to the new object too + new_me._ansible_internal_redirect_list = self._ansible_internal_redirect_list[:] + new_me._parent = None if self._parent and not exclude_parent: new_me._parent = self._parent.copy(exclude_tasks=exclude_tasks) @@ -415,6 +424,9 @@ class Task(Base, Conditional, Taggable, CollectionSearch): if self._role: data['role'] = self._role.serialize() + if self._ansible_internal_redirect_list: + data['_ansible_internal_redirect_list'] = self._ansible_internal_redirect_list[:] + return data def deserialize(self, data): @@ -443,6 +455,8 @@ class Task(Base, Conditional, Taggable, CollectionSearch): self._role = r del data['role'] + self._ansible_internal_redirect_list = data.get('_ansible_internal_redirect_list', []) + super(Task, self).deserialize(data) def set_loader(self, loader): diff --git a/lib/ansible/plugins/action/gather_facts.py b/lib/ansible/plugins/action/gather_facts.py index d58e9dd6bca..cd181203b54 100644 --- a/lib/ansible/plugins/action/gather_facts.py +++ b/lib/ansible/plugins/action/gather_facts.py @@ -42,7 +42,7 @@ class ActionModule(ActionBase): mod_args = dict((k, v) for k, v in mod_args.items() if v is not None) # handle module defaults - mod_args = get_action_args_with_defaults(fact_module, mod_args, self._task.module_defaults, self._templar) + mod_args = get_action_args_with_defaults(fact_module, mod_args, self._task.module_defaults, self._templar, self._task._ansible_internal_redirect_list) return mod_args diff --git a/lib/ansible/plugins/action/package.py b/lib/ansible/plugins/action/package.py index 932acccb04b..c4349ca4d70 100644 --- a/lib/ansible/plugins/action/package.py +++ b/lib/ansible/plugins/action/package.py @@ -66,7 +66,9 @@ class ActionModule(ActionBase): del new_module_args['use'] # get defaults for specific module - new_module_args = get_action_args_with_defaults(module, new_module_args, self._task.module_defaults, self._templar) + new_module_args = get_action_args_with_defaults( + module, new_module_args, self._task.module_defaults, self._templar, self._task._ansible_internal_redirect_list + ) display.vvvv("Running %s" % module) result.update(self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)) diff --git a/lib/ansible/plugins/action/service.py b/lib/ansible/plugins/action/service.py index 3ebd0ae17dc..c6af6248e61 100644 --- a/lib/ansible/plugins/action/service.py +++ b/lib/ansible/plugins/action/service.py @@ -73,7 +73,9 @@ class ActionModule(ActionBase): self._display.warning('Ignoring "%s" as it is not used in "%s"' % (unused, module)) # get defaults for specific module - new_module_args = get_action_args_with_defaults(module, new_module_args, self._task.module_defaults, self._templar) + new_module_args = get_action_args_with_defaults( + module, new_module_args, self._task.module_defaults, self._templar, self._task._ansible_internal_redirect_list + ) self._display.vvvv("Running %s" % module) result.update(self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)) diff --git a/lib/ansible/plugins/loader.py b/lib/ansible/plugins/loader.py index 466dac066fe..249aebd7dd3 100644 --- a/lib/ansible/plugins/loader.py +++ b/lib/ansible/plugins/loader.py @@ -587,6 +587,10 @@ class PluginLoader: else: # 'ansible.builtin' should be handled here. This means only internal, or builtin, paths are searched. plugin_load_context = self._find_fq_plugin(candidate_name, suffix, plugin_load_context=plugin_load_context) + + if candidate_name != plugin_load_context.original_name and candidate_name not in plugin_load_context.redirect_list: + plugin_load_context.redirect_list.append(candidate_name) + if plugin_load_context.resolved or plugin_load_context.pending_redirect: # if we got an answer or need to chase down a redirect, return return plugin_load_context except (AnsiblePluginRemovedError, AnsiblePluginCircularRedirect, AnsibleCollectionUnsupportedVersionError): diff --git a/lib/ansible/utils/collection_loader/_collection_finder.py b/lib/ansible/utils/collection_loader/_collection_finder.py index 47b5782997c..6aa3ca5ab43 100644 --- a/lib/ansible/utils/collection_loader/_collection_finder.py +++ b/lib/ansible/utils/collection_loader/_collection_finder.py @@ -531,6 +531,15 @@ class _AnsibleCollectionPkgLoader(_AnsibleCollectionPkgLoaderBase): # if redirect.startswith('..'): # redirect = redirect[2:] + action_groups = meta_dict.pop('action_groups', {}) + meta_dict['action_groups'] = {} + for group_name in action_groups: + for action_name in action_groups[group_name]: + if action_name in meta_dict['action_groups']: + meta_dict['action_groups'][action_name].append(group_name) + else: + meta_dict['action_groups'][action_name] = [group_name] + return meta_dict diff --git a/test/integration/targets/incidental_aws_step_functions_state_machine/tasks/main.yml b/test/integration/targets/incidental_aws_step_functions_state_machine/tasks/main.yml index 1eb63de130c..23e71dcebf0 100644 --- a/test/integration/targets/incidental_aws_step_functions_state_machine/tasks/main.yml +++ b/test/integration/targets/incidental_aws_step_functions_state_machine/tasks/main.yml @@ -2,7 +2,17 @@ - name: Integration test for AWS Step Function state machine module module_defaults: - group/aws: + iam_role: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + aws_step_functions_state_machine: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + aws_step_functions_state_machine_execution: aws_access_key: "{{ aws_access_key }}" aws_secret_key: "{{ aws_secret_key }}" security_token: "{{ security_token | default(omit) }}" diff --git a/test/integration/targets/incidental_cloudformation/tasks/main.yml b/test/integration/targets/incidental_cloudformation/tasks/main.yml index 9b89722b20f..10924bcd531 100644 --- a/test/integration/targets/incidental_cloudformation/tasks/main.yml +++ b/test/integration/targets/incidental_cloudformation/tasks/main.yml @@ -1,17 +1,25 @@ --- +- name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key | default(omit) }}" + aws_secret_key: "{{ aws_secret_key | default(omit) }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region | default(omit) }}" + no_log: yes - module_defaults: - group/aws: - aws_access_key: '{{ aws_access_key | default(omit) }}' - aws_secret_key: '{{ aws_secret_key | default(omit) }}' - security_token: '{{ security_token | default(omit) }}' - region: '{{ aws_region | default(omit) }}' + cloudformation: + <<: *aws_connection_info + cloudformation_info: + <<: *aws_connection_info block: # ==== Env setup ========================================================== - name: list available AZs aws_az_info: + <<: *aws_connection_info register: region_azs - name: pick an AZ for testing @@ -24,6 +32,7 @@ cidr_block: "{{ vpc_cidr }}" tags: Name: Cloudformation testing + <<: *aws_connection_info register: testing_vpc - name: Create a test subnet @@ -31,6 +40,7 @@ vpc_id: "{{ testing_vpc.vpc.id }}" cidr: "{{ subnet_cidr }}" az: "{{ availability_zone }}" + <<: *aws_connection_info register: testing_subnet - name: Find AMI to use @@ -38,6 +48,7 @@ owners: 'amazon' filters: name: '{{ ec2_ami_name }}' + <<: *aws_connection_info register: ec2_amis - name: Set fact with latest AMI @@ -453,6 +464,7 @@ vpc_id: "{{ testing_vpc.vpc.id }}" cidr: "{{ subnet_cidr }}" state: absent + <<: *aws_connection_info ignore_errors: yes - name: Delete test VPC @@ -460,4 +472,5 @@ name: "{{ vpc_name }}" cidr_block: "{{ vpc_cidr }}" state: absent + <<: *aws_connection_info ignore_errors: yes diff --git a/test/integration/targets/incidental_ec2_instance/main.yml b/test/integration/targets/incidental_ec2_instance/main.yml index 7695f7bcb92..788c2243a0e 100644 --- a/test/integration/targets/incidental_ec2_instance/main.yml +++ b/test/integration/targets/incidental_ec2_instance/main.yml @@ -9,7 +9,7 @@ gather_facts: no tasks: - module_defaults: - group/aws: + ec2_ami_info: aws_access_key: "{{ aws_access_key }}" aws_secret_key: "{{ aws_secret_key }}" security_token: "{{ security_token | default(omit) }}" diff --git a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_cleanup.yml b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_cleanup.yml index 1b6c79e0d95..7ab668de17b 100644 --- a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_cleanup.yml +++ b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_cleanup.yml @@ -1,9 +1,19 @@ +- name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + no_log: yes + - name: "remove Instances" ec2_instance: state: absent filters: vpc-id: "{{ testing_vpc.vpc.id }}" wait: yes + <<: *aws_connection_info ignore_errors: yes retries: 10 @@ -11,12 +21,14 @@ ec2_eni_info: filters: vpc-id: "{{ testing_vpc.vpc.id }}" + <<: *aws_connection_info register: enis - name: "delete all ENIs" ec2_eni: state: absent eni_id: "{{ item.id }}" + <<: *aws_connection_info until: removed is not failed with_items: "{{ enis.network_interfaces }}" ignore_errors: yes @@ -28,6 +40,7 @@ name: "{{ resource_prefix }}-sg" description: a security group for ansible tests vpc_id: "{{ testing_vpc.vpc.id }}" + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -45,6 +58,7 @@ subnets: - "{{ testing_subnet_a.subnet.id }}" - "{{ testing_subnet_b.subnet.id }}" + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -54,6 +68,7 @@ ec2_vpc_igw: state: absent vpc_id: "{{ testing_vpc.vpc.id }}" + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -64,6 +79,7 @@ state: absent vpc_id: "{{ testing_vpc.vpc.id }}" cidr: "{{ subnet_a_cidr }}" + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -74,6 +90,7 @@ state: absent vpc_id: "{{ testing_vpc.vpc.id }}" cidr: "{{ subnet_b_cidr }}" + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -87,6 +104,7 @@ tags: Name: Ansible Testing VPC tenancy: default + <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes diff --git a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_setup.yml b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_setup.yml index 6c76b7bf79f..1f2f3f0adad 100644 --- a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_setup.yml +++ b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/env_setup.yml @@ -1,7 +1,17 @@ +- name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + no_log: yes + - run_once: '{{ setup_run_once | default("no") | bool }}' block: - name: "fetch AZ availability" aws_az_info: + <<: *aws_connection_info register: az_info - name: "Assert that we have multiple AZs available to us" assert: @@ -20,12 +30,14 @@ tags: Name: Ansible ec2_instance Testing VPC tenancy: default + <<: *aws_connection_info register: testing_vpc - name: "Create internet gateway for use in testing" ec2_vpc_igw: state: present vpc_id: "{{ testing_vpc.vpc.id }}" + <<: *aws_connection_info register: igw - name: "Create default subnet in zone A" @@ -36,6 +48,7 @@ az: "{{ subnet_a_az }}" resource_tags: Name: "{{ resource_prefix }}-subnet-a" + <<: *aws_connection_info register: testing_subnet_a - name: "Create secondary subnet in zone B" @@ -46,6 +59,7 @@ az: "{{ subnet_b_az }}" resource_tags: Name: "{{ resource_prefix }}-subnet-b" + <<: *aws_connection_info register: testing_subnet_b - name: "create routing rules" @@ -60,6 +74,7 @@ subnets: - "{{ testing_subnet_a.subnet.id }}" - "{{ testing_subnet_b.subnet.id }}" + <<: *aws_connection_info - name: "create a security group with the vpc" ec2_group: @@ -76,4 +91,5 @@ from_port: 80 to_port: 80 cidr_ip: 0.0.0.0/0 + <<: *aws_connection_info register: sg diff --git a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/main.yml b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/main.yml index e10aebcefe2..c67f81b4e1d 100644 --- a/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/main.yml +++ b/test/integration/targets/incidental_ec2_instance/roles/ec2_instance/tasks/main.yml @@ -17,13 +17,27 @@ # - EC2_REGION -> AWS_REGION # -- name: "Wrap up all tests and setup AWS credentials" - module_defaults: - group/aws: +- name: set up aws connection info + set_fact: + aws_connection_info: &aws_connection_info aws_access_key: "{{ aws_access_key }}" aws_secret_key: "{{ aws_secret_key }}" security_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" + no_log: yes + +- name: "Wrap up all tests and setup AWS credentials" + module_defaults: + ec2_instance: + <<: *aws_connection_info + ec2_instance_info: + <<: *aws_connection_info + ec2_key: + <<: *aws_connection_info + ec2_eni: + <<: *aws_connection_info + iam_role: + <<: *aws_connection_info block: - debug: msg: "{{ inventory_hostname }} start: {{ lookup('pipe','date') }}" diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py new file mode 100644 index 00000000000..f7777b8ae2b --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py @@ -0,0 +1,8 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.action.echoaction import ActionModule as BaseAM + + +class ActionModule(BaseAM): + pass diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py new file mode 100644 index 00000000000..771395f2328 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml new file mode 100644 index 00000000000..62695fbc953 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml @@ -0,0 +1,9 @@ +action_groups: + testgroup: + - testns.testcoll.echo1 + - testns.testcoll.echo2 +# note we can define defaults for an action + - testns.testcoll.echoaction +# note we can define defaults in this group for actions/modules in another collection + - testns.othercoll.other_echoaction + - testns.othercoll.other_echo1 diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py new file mode 100644 index 00000000000..2fa097b29f1 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py @@ -0,0 +1,19 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + TRANSFERS_FILES = False + _VALID_ARGS = frozenset() + + def run(self, tmp=None, task_vars=None): + if task_vars is None: + task_vars = dict() + + result = super(ActionModule, self).run(None, task_vars) + + result = dict(changed=False, args_in=self._task.args) + + return result diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py new file mode 100644 index 00000000000..f5c5d737be7 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py @@ -0,0 +1,15 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +from ansible.module_utils import basic +from ansible.module_utils.basic import _load_params, AnsibleModule + + +def do_echo(): + p = _load_params() + d = json.loads(basic._ANSIBLE_ARGS) + d['ANSIBLE_MODULE_ARGS'] = {} + basic._ANSIBLE_ARGS = json.dumps(d).encode('utf-8') + module = AnsibleModule(argument_spec={}) + module.exit_json(args_in=p) diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py new file mode 100644 index 00000000000..771395f2328 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py new file mode 100644 index 00000000000..771395f2328 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/runme.sh b/test/integration/targets/module_defaults/runme.sh new file mode 100755 index 00000000000..c19e607bbb9 --- /dev/null +++ b/test/integration/targets/module_defaults/runme.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eux + +ansible-playbook test_defaults.yml "$@" diff --git a/test/integration/targets/module_defaults/tasks/main.yml b/test/integration/targets/module_defaults/tasks/main.yml index b84e33f430e..3ed960d3b50 100644 --- a/test/integration/targets/module_defaults/tasks/main.yml +++ b/test/integration/targets/module_defaults/tasks/main.yml @@ -87,30 +87,3 @@ - assert: that: foo.msg == "Hello world!" -- name: Module group defaults block - module_defaults: - group/test: - arg1: "test1" - arg2: "test2" - block: - - test_module_defaults: - register: result - - assert: - that: - - "result.test_module_defaults.arg1 == 'test1'" - - "result.test_module_defaults.arg2 == 'test2'" - - "result.test_module_defaults.arg3 == 'default3'" -- name: Module group defaults block - module_defaults: - group/test: - arg1: "test1" - arg2: "test2" - arg3: "test3" - block: - - test_module_defaults: - register: result - - assert: - that: - - "result.test_module_defaults.arg1 == 'test1'" - - "result.test_module_defaults.arg2 == 'test2'" - - "result.test_module_defaults.arg3 == 'test3'" diff --git a/test/integration/targets/module_defaults/test_defaults.yml b/test/integration/targets/module_defaults/test_defaults.yml new file mode 100644 index 00000000000..15b66362b26 --- /dev/null +++ b/test/integration/targets/module_defaults/test_defaults.yml @@ -0,0 +1,60 @@ +- hosts: localhost + gather_facts: no + collections: + - testns.testcoll + - testns.othercoll + module_defaults: + testns.testcoll.echoaction: + explicit_module_default: from playbook + testns.testcoll.echo1: + explicit_module_default: from playbook + group/testgroup: + group_module_default: from playbook + tasks: + - testns.testcoll.echoaction: + task_arg: from task + register: echoaction_fq + - echoaction: + task_arg: from task + register: echoaction_unq + - testns.testcoll.echo1: + task_arg: from task + register: echo1_fq + - echo1: + task_arg: from task + register: echo1_unq + - testns.testcoll.echo2: + task_arg: from task + register: echo2_fq + - echo2: + task_arg: from task + register: echo2_unq + - testns.othercoll.other_echoaction: + task_arg: from task + register: other_echoaction_fq + - other_echoaction: + task_arg: from task + register: other_echoaction_unq + - testns.othercoll.other_echo1: + task_arg: from task + register: other_echo1_fq + - other_echo1: + task_arg: from task + register: other_echo1_unq + + - debug: var=echo1_fq + + - assert: + that: + - "echoaction_fq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echoaction_unq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo1_fq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo1_unq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo2_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "echo2_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echoaction_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echoaction_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echo1_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echo1_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + + - include_tasks: tasks/main.yml diff --git a/test/lib/ansible_test/_internal/cloud/vcenter.py b/test/lib/ansible_test/_internal/cloud/vcenter.py index 37cfa6d5e6b..59b9fe05bf7 100644 --- a/test/lib/ansible_test/_internal/cloud/vcenter.py +++ b/test/lib/ansible_test/_internal/cloud/vcenter.py @@ -220,7 +220,7 @@ class VcenterEnvironment(CloudEnvironment): env_vars=env_vars, ansible_vars=ansible_vars, module_defaults={ - 'group/vmware': { + 'vmware_guest': { 'hostname': ansible_vars['vcenter_hostname'], 'username': ansible_vars['vcenter_username'], 'password': ansible_vars['vcenter_password'], diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 33560007c54..51cc7256606 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -57,7 +57,6 @@ lib/ansible/cli/console.py pylint:blacklisted-name lib/ansible/cli/scripts/ansible_cli_stub.py shebang lib/ansible/cli/scripts/ansible_connection_cli_stub.py shebang lib/ansible/config/base.yml no-unwanted-files -lib/ansible/config/module_defaults.yml no-unwanted-files lib/ansible/executor/playbook_executor.py pylint:blacklisted-name lib/ansible/executor/powershell/async_watchdog.ps1 pslint:PSCustomUseLiteralPath lib/ansible/executor/powershell/async_wrapper.ps1 pslint:PSCustomUseLiteralPath diff --git a/test/units/plugins/action/test_gather_facts.py b/test/units/plugins/action/test_gather_facts.py index b550917e4a9..6d81200014c 100644 --- a/test/units/plugins/action/test_gather_facts.py +++ b/test/units/plugins/action/test_gather_facts.py @@ -19,12 +19,13 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type from units.compat import unittest -from units.compat.mock import MagicMock +from units.compat.mock import MagicMock, patch from ansible import constants as C from ansible.plugins.action.gather_facts import ActionModule from ansible.playbook.task import Task from ansible.template import Templar +import ansible.executor.module_common as module_common from units.mock.loader import DictDataLoader @@ -48,6 +49,7 @@ class TestNetworkFacts(unittest.TestCase): self.task_vars = {'ansible_network_os': 'ios'} self.task.action = 'gather_facts' self.task.async_val = False + self.task._ansible_internal_redirect_list = [] self.task.args = {'gather_subset': 'min'} self.task.module_defaults = [{'ios_facts': {'gather_subset': 'min'}}] @@ -63,9 +65,11 @@ class TestNetworkFacts(unittest.TestCase): facts_modules = C.config.get_config_value('FACTS_MODULES', variables=self.task_vars) self.assertEqual(facts_modules, ['ios_facts']) - def test_network_gather_facts_fqcn(self): + @patch.object(module_common, '_get_collection_metadata', return_value={}) + def test_network_gather_facts_fqcn(self, mock_collection_metadata): self.fqcn_task_vars = {'ansible_network_os': 'cisco.ios.ios'} self.task.action = 'gather_facts' + self.task._ansible_internal_redirect_list = ['cisco.ios.ios_facts'] self.task.async_val = False self.task.args = {'gather_subset': 'min'} self.task.module_defaults = [{'cisco.ios.ios_facts': {'gather_subset': 'min'}}]