diff options
Diffstat (limited to 'ansible_collections/amazon/aws/tests')
12 files changed, 948 insertions, 15 deletions
diff --git a/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml b/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml index a9289b3c1..267e52abb 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml +++ b/ansible_collections/amazon/aws/tests/integration/targets/ec2_ami/tasks/main.yml @@ -708,8 +708,8 @@ tags: Name: "{{ ec2_ami_name }}_permissions" launch_permissions: - org_arns: [arn:aws:organizations::123456789012:organization/o-123ab4cdef] - org_unit_arns: [arn:aws:organizations::123456789012:ou/o-123example/ou-1234-5exampld] + org_arns: ["arn:aws:organizations::123456789012:organization/o-123ab4cdef"] + org_unit_arns: ["arn:aws:organizations::123456789012:ou/o-123example/ou-1234-5exampld"] register: permissions_update_result - name: Get ami info diff --git a/ansible_collections/amazon/aws/tests/integration/targets/ec2_security_group/tasks/multi_nested_target.yml b/ansible_collections/amazon/aws/tests/integration/targets/ec2_security_group/tasks/multi_nested_target.yml index dcb7ac7bb..02057003a 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/ec2_security_group/tasks/multi_nested_target.yml +++ b/ansible_collections/amazon/aws/tests/integration/targets/ec2_security_group/tasks/multi_nested_target.yml @@ -12,7 +12,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -38,7 +38,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -66,7 +66,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -92,7 +92,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -117,7 +117,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -142,7 +142,7 @@ to_port: 8182 cidr_ipv6: - 64:ff9b::/96 - - [2620::/32] + - ["2620::/32"] - proto: tcp ports: 5665 cidr_ip: @@ -167,7 +167,7 @@ from_port: 8182 to_port: 8182 cidr_ipv6: - - [2620::/32, 64:ff9b::/96] + - ["2620::/32", "64:ff9b::/96"] - proto: tcp ports: 5665 cidr_ip: @@ -190,8 +190,8 @@ from_port: 8182 to_port: 8182 cidr_ipv6: - - [2620::/32, 64:ff9b::/96] - - [2001:DB8:A0B:12F0::1/64] + - ["2620::/32", "64:ff9b::/96"] + - ["2001:DB8:A0B:12F0::1/64"] - proto: tcp ports: 5665 cidr_ip: diff --git a/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml index 232911d24..282ca43ee 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml +++ b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/playbooks/create_inventory_config.yml @@ -9,3 +9,8 @@ ansible.builtin.copy: dest: ../test.aws_ec2.yml content: "{{ lookup('template', template_name) }}" + + - name: write ini configuration + ansible.builtin.copy: + dest: ../config.ini + content: "{{ lookup('template', '../templates/config.ini.j2') }}" diff --git a/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/config.ini.j2 b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/config.ini.j2 new file mode 100644 index 000000000..f7320a7fb --- /dev/null +++ b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/config.ini.j2 @@ -0,0 +1,3 @@ +[ansible-test] + +region = {{ aws_region }}
\ No newline at end of file diff --git a/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_template.yml.j2 b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_template.yml.j2 index 44a132c1c..dee7422a9 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_template.yml.j2 +++ b/ansible_collections/amazon/aws/tests/integration/targets/inventory_aws_ec2/templates/inventory_with_template.yml.j2 @@ -5,7 +5,7 @@ secret_key: '{{ aws_secret_key }}' session_token: '{{ security_token }}' {% endif %} regions: -- '{{ aws_region }}' +- '{{ '{{ lookup("ansible.builtin.ini", "region", section="ansible-test", file="config.ini") }}' }}' filters: tag:Name: - '{{ resource_prefix }}' diff --git a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/aliases b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/aliases index d34fac48d..2a1c5ccb6 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/aliases +++ b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/aliases @@ -1,3 +1,4 @@ cloud/aws aws_s3 s3_object_info +time=12m diff --git a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/copy_object.yml b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/copy_object.yml index 9ae36b952..994733d81 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/copy_object.yml +++ b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/copy_object.yml @@ -1,5 +1,12 @@ --- -- block: +- vars: + withmeta_data: + something: exists + version: "1.0.2" + metacopy_data: + name: metacopy + version: "1.0.3" + block: - name: define bucket name used for tests ansible.builtin.set_fact: copy_bucket: @@ -142,6 +149,68 @@ - result is not changed - result.msg == "Key this_key_does_not_exist.txt does not exist in bucket "+copy_bucket.src+"." + # Copy with metadata + - name: Set fact for bucket name + ansible.builtin.set_fact: + bucket_name: "{{ copy_bucket.dst }}" + + - name: Create test bucket + amazon.aws.s3_bucket: + name: "{{ bucket_name }}" + state: present + + - name: Create test object + amazon.aws.s3_object: + bucket: "{{ bucket_name }}" + object: nometa + mode: put + content: "some content" + + - name: Copy and add metadata + amazon.aws.s3_object: + bucket: "{{ bucket_name }}" + object: metacopy + mode: copy + copy_src: + bucket: "{{ bucket_name }}" + object: nometa + metadata: "{{ metacopy_data }}" + + - name: Create test object with metadata + amazon.aws.s3_object: + bucket: "{{ bucket_name }}" + object: withmeta + mode: put + content: "another content" + metadata: "{{ withmeta_data }}" + + - name: Copy and preserve metadata + amazon.aws.s3_object: + bucket: "{{ bucket_name }}" + object: copywithmeta + mode: copy + copy_src: + bucket: "{{ bucket_name }}" + object: withmeta + + - name: Get objects info + amazon.aws.s3_object_info: + bucket_name: "{{ bucket_name }}" + object_name: "{{ item }}" + loop: + - nometa + - metacopy + - withmeta + - copywithmeta + register: obj_info + + - assert: + that: + - obj_info.results | selectattr('item', 'equalto', 'nometa') | map(attribute='object_info.0.object_data.metadata') | first == {} + - obj_info.results | selectattr('item', 'equalto', 'withmeta') | map(attribute='object_info.0.object_data.metadata') | first == withmeta_data + - obj_info.results | selectattr('item', 'equalto', 'metacopy') | map(attribute='object_info.0.object_data.metadata') | first == metacopy_data + - obj_info.results | selectattr('item', 'equalto', 'copywithmeta') | map(attribute='object_info.0.object_data.metadata') | first == withmeta_data + always: - ansible.builtin.include_tasks: delete_bucket.yml with_items: diff --git a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/main.yml b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/main.yml index ed65fe31f..7a8a585de 100644 --- a/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/main.yml +++ b/ansible_collections/amazon/aws/tests/integration/targets/s3_object/tasks/main.yml @@ -837,8 +837,6 @@ that: - binary_files.results[0].stat.checksum == binary_files.results[1].stat.checksum - - ansible.builtin.include_tasks: copy_object.yml - - ansible.builtin.include_tasks: copy_object_acl_disabled_bucket.yml - name: Run tagging tests block: # ============================================================ @@ -1074,6 +1072,8 @@ - (result.tags | length) == 0 - ansible.builtin.include_tasks: copy_recursively.yml + - ansible.builtin.include_tasks: copy_object.yml + - ansible.builtin.include_tasks: copy_object_acl_disabled_bucket.yml always: - name: delete temporary files file: diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py b/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py new file mode 100644 index 000000000..28090f993 --- /dev/null +++ b/ansible_collections/amazon/aws/tests/unit/module_utils/iam/test_iam_resource_transforms.py @@ -0,0 +1,583 @@ +# -*- coding: utf-8 -*- + +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +import dateutil + +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_access_key +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_access_keys +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_group +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_instance_profile +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_mfa_device +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_mfa_devices +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_policy +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_role +from ansible_collections.amazon.aws.plugins.module_utils.iam import normalize_iam_user + +# The various normalize_ functions are based upon ..transformation.boto3_resource_to_ansible_dict +# As such these tests will be relatively light touch. + +example_date1_txt = "2020-12-30T00:00:00.000Z" +example_date2_txt = "2021-04-26T01:23:58.000Z" +example_date1_iso = "2020-12-30T00:00:00+00:00" +example_date2_iso = "2021-04-26T01:23:58+00:00" +example_date1 = dateutil.parser.parse(example_date1_txt) +example_date2 = dateutil.parser.parse(example_date2_txt) + + +class TestIamResourceToAnsibleDict: + def setup_method(self): + pass + + def test_normalize_iam_mfa_device(self): + INPUT = { + "UserName": "ExampleUser", + "SerialNumber": "arn:aws:iam::123456789012:mfa/ExampleUser", + "EnableDate": example_date1, + } + OUTPUT = { + "user_name": "ExampleUser", + "serial_number": "arn:aws:iam::123456789012:mfa/ExampleUser", + "enable_date": example_date1_iso, + "tags": {}, + } + + assert OUTPUT == normalize_iam_mfa_device(INPUT) + + def test_normalize_iam_mfa_devices(self): + INPUT = [ + { + "UserName": "ExampleUser", + "SerialNumber": "arn:aws:iam::123456789012:mfa/ExampleUser", + "EnableDate": example_date1, + } + ] + OUTPUT = [ + { + "user_name": "ExampleUser", + "serial_number": "arn:aws:iam::123456789012:mfa/ExampleUser", + "enable_date": example_date1_iso, + "tags": {}, + } + ] + + assert OUTPUT == normalize_iam_mfa_devices(INPUT) + + def test_normalize_iam_user(self): + INPUT = { + "Path": "/MyPath/", + "UserName": "ExampleUser", + "UserId": "AIDU12345EXAMPLE12345", + "Arn": "arn:aws:iam::123456789012:user/MyPath/ExampleUser", + "CreateDate": example_date1, + "PasswordLastUsed": example_date2, + "PermissionsBoundary": { + "PermissionsBoundaryType": "PermissionsBoundaryPolicy", + "PermissionsBoundaryArn": "arn:aws:iam::123456789012:policy/ExamplePolicy", + }, + "Tags": [ + {"Key": "MyKey", "Value": "Example Value"}, + ], + } + + OUTPUT = { + "path": "/MyPath/", + "user_name": "ExampleUser", + "user_id": "AIDU12345EXAMPLE12345", + "arn": "arn:aws:iam::123456789012:user/MyPath/ExampleUser", + "create_date": example_date1_iso, + "password_last_used": example_date2_iso, + "permissions_boundary": { + "permissions_boundary_type": "PermissionsBoundaryPolicy", + "permissions_boundary_arn": "arn:aws:iam::123456789012:policy/ExamplePolicy", + }, + "tags": {"MyKey": "Example Value"}, + } + + assert OUTPUT == normalize_iam_user(INPUT) + + def test_normalize_iam_policy(self): + INPUT = { + "PolicyName": "AnsibleIntegratation-CI-ApplicationServices", + "PolicyId": "ANPA12345EXAMPLE12345", + "Arn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-ApplicationServices", + "Path": "/examples/", + "DefaultVersionId": "v6", + "AttachmentCount": 2, + "PermissionsBoundaryUsageCount": 0, + "IsAttachable": True, + "CreateDate": example_date1, + "UpdateDate": example_date2, + "Tags": [ + {"Key": "MyKey", "Value": "Example Value"}, + ], + } + + OUTPUT = { + "policy_name": "AnsibleIntegratation-CI-ApplicationServices", + "policy_id": "ANPA12345EXAMPLE12345", + "arn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-ApplicationServices", + "path": "/examples/", + "default_version_id": "v6", + "attachment_count": 2, + "permissions_boundary_usage_count": 0, + "is_attachable": True, + "create_date": example_date1_iso, + "update_date": example_date2_iso, + "tags": {"MyKey": "Example Value"}, + } + + assert OUTPUT == normalize_iam_policy(INPUT) + + def test_normalize_iam_group(self): + INPUT = { + "Users": [ + { + "Path": "/", + "UserName": "ansible_test", + "UserId": "AIDA12345EXAMPLE12345", + "Arn": "arn:aws:iam::123456789012:user/ansible_test", + "CreateDate": example_date1, + "PasswordLastUsed": example_date2, + } + ], + "Group": { + "Path": "/", + "GroupName": "ansible-integration-ci", + "GroupId": "AGPA01234EXAMPLE01234", + "Arn": "arn:aws:iam::123456789012:group/ansible-integration-ci", + "CreateDate": example_date1, + }, + "AttachedPolicies": [ + { + "PolicyName": "AnsibleIntegratation-CI-Paas", + "PolicyArn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-Paas", + }, + { + "PolicyName": "AnsibleIntegratation-CI-ApplicationServices", + "PolicyArn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-ApplicationServices", + }, + ], + } + + OUTPUT = { + "users": [ + { + "path": "/", + "user_name": "ansible_test", + "user_id": "AIDA12345EXAMPLE12345", + "arn": "arn:aws:iam::123456789012:user/ansible_test", + "create_date": example_date1_iso, + "password_last_used": example_date2_iso, + } + ], + "group": { + "path": "/", + "group_name": "ansible-integration-ci", + "group_id": "AGPA01234EXAMPLE01234", + "arn": "arn:aws:iam::123456789012:group/ansible-integration-ci", + "create_date": example_date1_iso, + }, + "attached_policies": [ + { + "policy_name": "AnsibleIntegratation-CI-Paas", + "policy_arn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-Paas", + }, + { + "policy_name": "AnsibleIntegratation-CI-ApplicationServices", + "policy_arn": "arn:aws:iam::123456789012:policy/AnsibleIntegratation-CI-ApplicationServices", + }, + ], + } + + assert OUTPUT == normalize_iam_group(INPUT) + + def test_normalize_access_key(self): + INPUT = { + "UserName": "ansible_test", + "AccessKeyId": "AKIA12345EXAMPLE1234", + "Status": "Active", + "CreateDate": example_date1, + } + + OUTPUT = { + "user_name": "ansible_test", + "access_key_id": "AKIA12345EXAMPLE1234", + "status": "Active", + "create_date": example_date1_iso, + } + + assert OUTPUT == normalize_iam_access_key(INPUT) + + def test_normalize_access_keys(self): + INPUT = [ + { + "UserName": "ansible_test", + "AccessKeyId": "AKIA12345EXAMPLE1234", + "Status": "Active", + "CreateDate": example_date1, + }, + { + "UserName": "ansible_test", + "AccessKeyId": "AKIA01234EXAMPLE4321", + "Status": "Active", + "CreateDate": example_date2, + }, + ] + + OUTPUT = [ + { + "access_key_id": "AKIA12345EXAMPLE1234", + "create_date": example_date1_iso, + "status": "Active", + "user_name": "ansible_test", + }, + { + "access_key_id": "AKIA01234EXAMPLE4321", + "create_date": example_date2_iso, + "status": "Active", + "user_name": "ansible_test", + }, + ] + + assert OUTPUT == normalize_iam_access_keys(INPUT) + + # Switch order to test that they're sorted by Creation Date + INPUT = [ + { + "UserName": "ansible_test", + "AccessKeyId": "AKIA12345EXAMPLE1234", + "Status": "Active", + "CreateDate": example_date2, + }, + { + "UserName": "ansible_test", + "AccessKeyId": "AKIA01234EXAMPLE4321", + "Status": "Active", + "CreateDate": example_date1, + }, + ] + + OUTPUT = [ + { + "access_key_id": "AKIA01234EXAMPLE4321", + "create_date": example_date1_iso, + "status": "Active", + "user_name": "ansible_test", + }, + { + "access_key_id": "AKIA12345EXAMPLE1234", + "create_date": example_date2_iso, + "status": "Active", + "user_name": "ansible_test", + }, + ] + + assert OUTPUT == normalize_iam_access_keys(INPUT) + + def test_normalize_role(self): + INPUT = { + "Arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "AssumeRolePolicyDocument": { + "Statement": [ + {"Action": "sts:AssumeRole", "Effect": "Deny", "Principal": {"Service": "ec2.amazonaws.com"}} + ], + "Version": "2012-10-17", + }, + "CreateDate": example_date1, + "Description": "Ansible Test Role (updated) ansible-test-76640355", + "InlinePolicies": ["inline-policy-a", "inline-policy-b"], + "InstanceProfiles": [ + { + "Arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-76640355", + "CreateDate": example_date2, + "InstanceProfileId": "AIPA12345EXAMPLE12345", + "InstanceProfileName": "ansible-test-76640355", + "Path": "/", + "Roles": [ + { + "Arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "CreateDate": example_date1, + "Path": "/", + "RoleId": "AROA12345EXAMPLE12345", + "RoleName": "ansible-test-76640355", + # XXX Bug in iam_role_info - Tags should have been in here. + "Tags": [{"Key": "TagB", "Value": "ValueB"}], + } + ], + "Tags": [{"Key": "TagA", "Value": "Value A"}], + } + ], + "ManagedPolicies": [ + { + "PolicyArn": "arn:aws:iam::123456789012:policy/ansible-test-76640355", + "PolicyName": "ansible-test-76640355", + } + ], + "MaxSessionDuration": 43200, + "Path": "/", + "RoleId": "AROA12345EXAMPLE12345", + "RoleLastUsed": {}, + "RoleName": "ansible-test-76640355", + "Tags": [{"Key": "TagB", "Value": "ValueB"}], + } + + OUTPUT = { + "arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "assume_role_policy_document": { + "Statement": [ + {"Action": "sts:AssumeRole", "Effect": "Deny", "Principal": {"Service": "ec2.amazonaws.com"}} + ], + "Version": "2012-10-17", + }, + "create_date": example_date1_iso, + "description": "Ansible Test Role (updated) ansible-test-76640355", + "inline_policies": ["inline-policy-a", "inline-policy-b"], + "instance_profiles": [ + { + "arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-76640355", + "create_date": example_date2_iso, + "instance_profile_id": "AIPA12345EXAMPLE12345", + "instance_profile_name": "ansible-test-76640355", + "path": "/", + "roles": [ + { + "arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "assume_role_policy_document": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "create_date": example_date1_iso, + "path": "/", + "role_id": "AROA12345EXAMPLE12345", + "role_name": "ansible-test-76640355", + "tags": {"TagB": "ValueB"}, + } + ], + "tags": {"TagA": "Value A"}, + } + ], + "managed_policies": [ + { + "policy_arn": "arn:aws:iam::123456789012:policy/ansible-test-76640355", + "policy_name": "ansible-test-76640355", + } + ], + "max_session_duration": 43200, + "path": "/", + "role_id": "AROA12345EXAMPLE12345", + "role_last_used": {}, + "role_name": "ansible-test-76640355", + "tags": {"TagB": "ValueB"}, + } + + assert OUTPUT == normalize_iam_role(INPUT) + + def test_normalize_role_compat(self): + INPUT = { + "Arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "AssumeRolePolicyDocument": { + "Statement": [ + {"Action": "sts:AssumeRole", "Effect": "Deny", "Principal": {"Service": "ec2.amazonaws.com"}} + ], + "Version": "2012-10-17", + }, + "CreateDate": example_date1, + "Description": "Ansible Test Role (updated) ansible-test-76640355", + "InlinePolicies": ["inline-policy-a", "inline-policy-b"], + "InstanceProfiles": [ + { + "Arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-76640355", + "CreateDate": example_date2, + "InstanceProfileId": "AIPA12345EXAMPLE12345", + "InstanceProfileName": "ansible-test-76640355", + "Path": "/", + "Roles": [ + { + "Arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "CreateDate": example_date1, + "Path": "/", + "RoleId": "AROA12345EXAMPLE12345", + "RoleName": "ansible-test-76640355", + # XXX Bug in iam_role_info - Tags should have been in here. + "Tags": [{"Key": "TagB", "Value": "ValueB"}], + } + ], + "Tags": [{"Key": "TagA", "Value": "Value A"}], + } + ], + "ManagedPolicies": [ + { + "PolicyArn": "arn:aws:iam::123456789012:policy/ansible-test-76640355", + "PolicyName": "ansible-test-76640355", + } + ], + "MaxSessionDuration": 43200, + "Path": "/", + "RoleId": "AROA12345EXAMPLE12345", + "RoleLastUsed": {}, + "RoleName": "ansible-test-76640355", + "Tags": [{"Key": "TagB", "Value": "ValueB"}], + } + + OUTPUT = { + "arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "assume_role_policy_document": { + "statement": [ + {"action": "sts:AssumeRole", "effect": "Deny", "principal": {"service": "ec2.amazonaws.com"}} + ], + "version": "2012-10-17", + }, + "assume_role_policy_document_raw": { + "Statement": [ + {"Action": "sts:AssumeRole", "Effect": "Deny", "Principal": {"Service": "ec2.amazonaws.com"}} + ], + "Version": "2012-10-17", + }, + "create_date": example_date1_iso, + "description": "Ansible Test Role (updated) ansible-test-76640355", + "inline_policies": ["inline-policy-a", "inline-policy-b"], + "instance_profiles": [ + { + "arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-76640355", + "create_date": example_date2_iso, + "instance_profile_id": "AIPA12345EXAMPLE12345", + "instance_profile_name": "ansible-test-76640355", + "path": "/", + "roles": [ + { + "arn": "arn:aws:iam::123456789012:role/ansible-test-76640355", + "assume_role_policy_document": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "create_date": example_date1_iso, + "path": "/", + "role_id": "AROA12345EXAMPLE12345", + "role_name": "ansible-test-76640355", + "tags": {"TagB": "ValueB"}, + } + ], + "tags": {"TagA": "Value A"}, + } + ], + "managed_policies": [ + { + "policy_arn": "arn:aws:iam::123456789012:policy/ansible-test-76640355", + "policy_name": "ansible-test-76640355", + } + ], + "max_session_duration": 43200, + "path": "/", + "role_id": "AROA12345EXAMPLE12345", + "role_last_used": {}, + "role_name": "ansible-test-76640355", + "tags": {"TagB": "ValueB"}, + } + + assert OUTPUT == normalize_iam_role(INPUT, _v7_compat=True) + + def test_normalize_instance_profile(self): + INPUT = { + "Arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-40050922/ansible-test-40050922", + "CreateDate": example_date1, + "InstanceProfileId": "AIPA12345EXAMPLE12345", + "InstanceProfileName": "ansible-test-40050922", + "Path": "/ansible-test-40050922/", + "Roles": [ + { + "Arn": "arn:aws:iam::123456789012:role/ansible-test-40050922/ansible-test-40050922", + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "CreateDate": example_date2, + "Path": "/ansible-test-40050922/", + "RoleId": "AROA12345EXAMPLE12345", + "RoleName": "ansible-test-40050922", + "Tags": [{"Key": "TagC", "Value": "ValueC"}], + } + ], + "Tags": [ + {"Key": "Key with Spaces", "Value": "Value with spaces"}, + {"Key": "snake_case_key", "Value": "snake_case_value"}, + {"Key": "CamelCaseKey", "Value": "CamelCaseValue"}, + {"Key": "pascalCaseKey", "Value": "pascalCaseValue"}, + ], + } + + OUTPUT = { + "arn": "arn:aws:iam::123456789012:instance-profile/ansible-test-40050922/ansible-test-40050922", + "create_date": "2020-12-30T00:00:00+00:00", + "instance_profile_id": "AIPA12345EXAMPLE12345", + "instance_profile_name": "ansible-test-40050922", + "path": "/ansible-test-40050922/", + "roles": [ + { + "arn": "arn:aws:iam::123456789012:role/ansible-test-40050922/ansible-test-40050922", + "assume_role_policy_document": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Deny", + "Principal": {"Service": "ec2.amazonaws.com"}, + } + ], + "Version": "2012-10-17", + }, + "create_date": "2021-04-26T01:23:58+00:00", + "path": "/ansible-test-40050922/", + "role_id": "AROA12345EXAMPLE12345", + "role_name": "ansible-test-40050922", + "tags": {"TagC": "ValueC"}, + } + ], + "tags": { + "CamelCaseKey": "CamelCaseValue", + "Key with Spaces": "Value with spaces", + "pascalCaseKey": "pascalCaseValue", + "snake_case_key": "snake_case_value", + }, + } + + assert OUTPUT == normalize_iam_instance_profile(INPUT) diff --git a/ansible_collections/amazon/aws/tests/unit/module_utils/transformation/test_boto3_resource_to_ansible_dict.py b/ansible_collections/amazon/aws/tests/unit/module_utils/transformation/test_boto3_resource_to_ansible_dict.py new file mode 100644 index 000000000..89a0a837c --- /dev/null +++ b/ansible_collections/amazon/aws/tests/unit/module_utils/transformation/test_boto3_resource_to_ansible_dict.py @@ -0,0 +1,140 @@ +# (c) 2017 Red Hat Inc. +# +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from copy import deepcopy +from unittest.mock import sentinel + +import dateutil +import pytest + +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.transformation import boto3_resource_to_ansible_dict + +example_date_txt = "2020-12-30T00:00:00.000Z" +example_date_iso = "2020-12-30T00:00:00+00:00" +example_date = dateutil.parser.parse(example_date_txt) + +EXAMPLE_BOTO3 = [ + None, + {}, + {"ExampleDate": example_date}, + {"ExampleTxtDate": example_date_txt}, + {"Tags": [{"Key": "MyKey", "Value": "MyValue"}, {"Key": "Normal case", "Value": "Normal Value"}]}, + { + "Name": "ExampleResource", + "ExampleDate": example_date, + "Tags": [{"Key": "MyKey", "Value": "MyValue"}, {"Key": "Normal case", "Value": "Normal Value"}], + }, + {"ExampleNested": {"ExampleKey": "Example Value"}}, +] + +EXAMPLE_DICT = [ + None, + {}, + {"example_date": example_date_iso, "tags": {}}, + {"example_txt_date": example_date_txt, "tags": {}}, + {"tags": {"MyKey": "MyValue", "Normal case": "Normal Value"}}, + { + "name": "ExampleResource", + "example_date": example_date_iso, + "tags": {"MyKey": "MyValue", "Normal case": "Normal Value"}, + }, + {"example_nested": {"example_key": "Example Value"}, "tags": {}}, +] + +TEST_DATA = zip(EXAMPLE_BOTO3, EXAMPLE_DICT) + +NESTED_DATA = {"sentinal": sentinel.MY_VALUE} + + +def do_transform_nested(resource): + return {"sentinal": sentinel.MY_VALUE} + + +class TestBoto3ResourceToAnsibleDict: + def setup_method(self): + pass + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_default_conversion(self, input_params, output_params): + # Test default behaviour + assert boto3_resource_to_ansible_dict(input_params) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_normalize(self, input_params, output_params): + # Test with normalize explicitly enabled + assert boto3_resource_to_ansible_dict(input_params, normalize=True) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_no_normalize(self, input_params, output_params): + # Test with normalize explicitly disabled + expected_value = deepcopy(output_params) + if input_params and "ExampleDate" in input_params: + expected_value["example_date"] = example_date + assert expected_value == boto3_resource_to_ansible_dict(input_params, normalize=False) + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_no_skip(self, input_params, output_params): + # Test with ignore_list explicitly set to [] + assert boto3_resource_to_ansible_dict(input_params, ignore_list=[]) == output_params + assert boto3_resource_to_ansible_dict(input_params, ignore_list=["NotUsed"]) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_skip(self, input_params, output_params): + # Test with ignore_list explicitly set + expected_value = deepcopy(output_params) + if input_params and "ExampleNested" in input_params: + expected_value["example_nested"] = input_params["ExampleNested"] + assert expected_value == boto3_resource_to_ansible_dict(input_params, ignore_list=["ExampleNested"]) + assert expected_value == boto3_resource_to_ansible_dict(input_params, ignore_list=["NotUsed", "ExampleNested"]) + assert expected_value == boto3_resource_to_ansible_dict(input_params, ignore_list=["ExampleNested", "NotUsed"]) + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_tags(self, input_params, output_params): + # Test with transform_tags explicitly enabled + assert boto3_resource_to_ansible_dict(input_params, transform_tags=True) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_no_tags(self, input_params, output_params): + # Test with transform_tags explicitly disabled + expected_value = deepcopy(output_params) + if input_params and "Tags" in input_params: + camel_tags = camel_dict_to_snake_dict({"tags": input_params["Tags"]}) + expected_value.update(camel_tags) + assert expected_value == boto3_resource_to_ansible_dict(input_params, transform_tags=False) + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_no_nested(self, input_params, output_params): + # Test with transform_nested explicitly set to an empty dictionary + assert boto3_resource_to_ansible_dict(input_params, nested_transforms={}) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_nested(self, input_params, output_params): + # Test with a custom transformation of nested resources + transform_map = {"ExampleNested": do_transform_nested} + expected_value = deepcopy(output_params) + + actual_value = boto3_resource_to_ansible_dict(input_params, nested_transforms=transform_map) + + if input_params and "ExampleNested" in input_params: + assert actual_value["example_nested"] == NESTED_DATA + del actual_value["example_nested"] + del expected_value["example_nested"] + + assert expected_value == actual_value + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_force_tags(self, input_params, output_params): + # Test with force_tags explicitly enabled + assert boto3_resource_to_ansible_dict(input_params, force_tags=True) == output_params + + @pytest.mark.parametrize("input_params, output_params", deepcopy(TEST_DATA)) + def test_no_force_tags(self, input_params, output_params): + # Test with force_tags explicitly enabled + expected_value = deepcopy(output_params) + if input_params and "Tags" not in input_params: + del expected_value["tags"] + assert boto3_resource_to_ansible_dict(input_params, force_tags=False) == expected_value diff --git a/ansible_collections/amazon/aws/tests/unit/plugin_utils/inventory/test_inventory_base.py b/ansible_collections/amazon/aws/tests/unit/plugin_utils/inventory/test_inventory_base.py index 32eb3f7ab..4da5792a8 100644 --- a/ansible_collections/amazon/aws/tests/unit/plugin_utils/inventory/test_inventory_base.py +++ b/ansible_collections/amazon/aws/tests/unit/plugin_utils/inventory/test_inventory_base.py @@ -3,6 +3,7 @@ # This file is part of Ansible # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +import re from unittest.mock import MagicMock from unittest.mock import call from unittest.mock import patch @@ -11,6 +12,8 @@ from unittest.mock import sentinel import pytest import ansible.plugins.inventory as base_inventory +from ansible.errors import AnsibleError +from ansible.module_utils.six import string_types import ansible_collections.amazon.aws.plugins.plugin_utils.inventory as utils_inventory @@ -65,3 +68,131 @@ def test_inventory_verify_file(monkeypatch, filename, result): assert inventory_plugin.verify_file(filename) is result base_verify.return_value = False assert inventory_plugin.verify_file(filename) is False + + +class AwsUnitTestTemplar: + def __init__(self, config): + self.config = config + + def is_template_string(self, key): + m = re.findall("{{([ ]*[a-zA-Z0-9_]*[ ]*)}}", key) + return bool(m) + + def is_template(self, data): + if isinstance(data, string_types): + return self.is_template_string(data) + elif isinstance(data, (list, tuple)): + for v in data: + if self.is_template(v): + return True + elif isinstance(data, dict): + for k in data: + if self.is_template(k) or self.is_template(data[k]): + return True + return False + + def template(self, variable, disable_lookups): + for k, v in self.config.items(): + variable = re.sub("{{([ ]*%s[ ]*)}}" % k, v, variable) + if self.is_template_string(variable): + m = re.findall("{{([ ]*[a-zA-Z0-9_]*[ ]*)}}", variable) + raise AnsibleError(f"Missing variables: {','.join([k.replace(' ', '') for k in m])}") + return variable + + +@pytest.fixture +def aws_inventory_base(): + inventory = utils_inventory.AWSInventoryBase() + inventory._options = {} + inventory.templar = None + return inventory + + +@pytest.mark.parametrize( + "option,value", + [ + ("access_key", "amazon_ansible_access_key_001"), + ("secret_key", "amazon_ansible_secret_key_890"), + ("session_token", None), + ("use_ssm_inventory", False), + ("This_field_is_undefined", None), + ("assume_role_arn", "arn:aws:iam::123456789012:role/ansible-test-inventory"), + ("region", "us-east-2"), + ], +) +def test_inventory_get_options_without_templar(aws_inventory_base, mocker, option, value): + inventory_options = { + "access_key": "amazon_ansible_access_key_001", + "secret_key": "amazon_ansible_secret_key_890", + "endpoint": "http//ansible.amazon.com", + "assume_role_arn": "arn:aws:iam::123456789012:role/ansible-test-inventory", + "region": "us-east-2", + "use_ssm_inventory": False, + } + aws_inventory_base._options = inventory_options + + super_get_options_patch = mocker.patch( + "ansible_collections.amazon.aws.plugins.plugin_utils.inventory.BaseInventoryPlugin.get_options" + ) + super_get_options_patch.return_value = aws_inventory_base._options + + options = aws_inventory_base.get_options() + assert value == options.get(option) + + +@pytest.mark.parametrize( + "option,value,error", + [ + ("access_key", "amazon_ansible_access_key_001", None), + ("session_token", None, None), + ("use_ssm_inventory", "{{ aws_inventory_use_ssm }}", None), + ("This_field_is_undefined", None, None), + ("region", "us-east-1", None), + ("profile", None, "Missing variables: ansible_version"), + ], +) +def test_inventory_get_options_with_templar(aws_inventory_base, mocker, option, value, error): + inventory_options = { + "access_key": "amazon_ansible_access_key_001", + "profile": "ansbile_{{ ansible_os }}_{{ ansible_version }}", + "endpoint": "{{ aws_endpoint }}", + "region": "{{ aws_region_country }}-east-{{ aws_region_id }}", + "use_ssm_inventory": "{{ aws_inventory_use_ssm }}", + } + aws_inventory_base._options = inventory_options + templar_config = { + "ansible_os": "RedHat", + "aws_region_country": "us", + "aws_region_id": "1", + "aws_endpoint": "http//ansible.amazon.com", + } + aws_inventory_base.templar = AwsUnitTestTemplar(templar_config) + + super_get_options_patch = mocker.patch( + "ansible_collections.amazon.aws.plugins.plugin_utils.inventory.BaseInventoryPlugin.get_options" + ) + super_get_options_patch.return_value = aws_inventory_base._options + + super_get_option_patch = mocker.patch( + "ansible_collections.amazon.aws.plugins.plugin_utils.inventory.BaseInventoryPlugin.get_option" + ) + super_get_option_patch.side_effect = lambda x, hostvars=None: aws_inventory_base._options.get(x) + + if error: + # test using get_options() + with pytest.raises(AnsibleError) as exc: + options = aws_inventory_base.get_options() + options.get(option) + assert error == str(exc.value) + + # test using get_option() + with pytest.raises(AnsibleError) as exc: + aws_inventory_base.get_option(option) + assert error == str(exc.value) + else: + # test using get_options() + options = aws_inventory_base.get_options() + assert value == options.get(option) + + # test using get_option() + assert value == aws_inventory_base.get_option(option) diff --git a/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py b/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py index 8cced1662..e33b78c51 100644 --- a/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py +++ b/ansible_collections/amazon/aws/tests/unit/plugins/inventory/test_aws_ec2.py @@ -240,6 +240,7 @@ def test_get_tag_hostname(preference, instance, expected): ) def test_inventory_build_include_filters(inventory, _options, expected): inventory._options = _options + inventory.templar = None assert inventory.build_include_filters() == expected |