From d2088e224fa5bc8e0c605237e836306f9eae7e3d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 4 Jul 2022 13:37:58 +0200 Subject: [PATCH 01/13] Rename autoscaling modules (#1294) Rename autoscaling modules SUMMARY Rename the autoscaling modules based on the new naming scheme ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/modules/autoscaling_group.py plugins/modules/autoscaling_group_info.py plugins/modules/autoscaling_instance_refresh.py plugins/modules/autoscaling_instance_refresh_info.py plugins/modules/autoscaling_launch_config.py plugins/modules/autoscaling_launch_config_find.py plugins/modules/autoscaling_launch_config_info.py plugins/modules/autoscaling_lifecycle_hook.py plugins/modules/autoscaling_policy.py plugins/modules/autoscaling_scheduled_action.py plugins/modules/ec2_asg.py plugins/modules/ec2_asg_info.py plugins/modules/ec2_asg_instance_refresh.py plugins/modules/ec2_asg_instance_refresh_info.py plugins/modules/ec2_asg_lifecycle_hook.py plugins/modules/ec2_asg_scheduled_action.py plugins/modules/ec2_lc.py plugins/modules/ec2_lc_find.py plugins/modules/ec2_lc_info.py plugins/modules/ec2_scaling_policy.py ADDITIONAL INFORMATION Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8721cf677afc9692572e634bb1e925584e619921 --- .../modules/autoscaling_instance_refresh.py | 269 +++++++++ .../autoscaling_instance_refresh_info.py | 220 ++++++++ .../autoscaling_instance_refresh/aliases | 2 + .../defaults/main.yml | 16 + .../meta/main.yml | 2 + .../tasks/main.yml | 517 ++++++++++++++++++ .../tasks/refresh_and_cancel_three_times.yml | 29 + 7 files changed, 1055 insertions(+) create mode 100644 plugins/modules/autoscaling_instance_refresh.py create mode 100644 plugins/modules/autoscaling_instance_refresh_info.py create mode 100644 tests/integration/targets/autoscaling_instance_refresh/aliases create mode 100644 tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml create mode 100644 tests/integration/targets/autoscaling_instance_refresh/meta/main.yml create mode 100644 tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml create mode 100644 tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py new file mode 100644 index 00000000000..43ef665e3f3 --- /dev/null +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -0,0 +1,269 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: autoscaling_instance_refresh +version_added: 3.2.0 +short_description: Start or cancel an EC2 Auto Scaling Group (ASG) instance refresh in AWS +description: + - Start or cancel an EC2 Auto Scaling Group instance refresh in AWS. + - Can be used with M(community.aws.autoscaling_instance_refresh_info) to track the subsequent progress. + - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_instance_refresh). + The usage did not change. +author: + - "Dan Khersonsky (@danquixote)" +options: + state: + description: + - Desired state of the ASG. + type: str + required: true + choices: [ 'started', 'cancelled' ] + name: + description: + - The name of the auto scaling group you are searching for. + type: str + required: true + strategy: + description: + - The strategy to use for the instance refresh. The only valid value is C(Rolling). + - A rolling update is an update that is applied to all instances in an Auto Scaling group until all instances have been updated. + - A rolling update can fail due to failed health checks or if instances are on standby or are protected from scale in. + - If the rolling update process fails, any instances that were already replaced are not rolled back to their previous configuration. + type: str + default: 'Rolling' + preferences: + description: + - Set of preferences associated with the instance refresh request. + - If not provided, the default values are used. + - For I(min_healthy_percentage), the default value is C(90). + - For I(instance_warmup), the default is to use the value specified for the health check grace period for the Auto Scaling group. + - Can not be specified when I(state) is set to 'cancelled'. + required: false + suboptions: + min_healthy_percentage: + description: + - Total percent of capacity in ASG that must remain healthy during instance refresh to allow operation to continue. + - It is rounded up to the nearest integer. + type: int + default: 90 + instance_warmup: + description: + - The number of seconds until a newly launched instance is configured and ready to use. + - During this time, Amazon EC2 Auto Scaling does not immediately move on to the next replacement. + - The default is to use the value for the health check grace period defined for the group. + type: int + type: dict +extends_documentation_fragment: + - amazon.aws.aws + - amazon.aws.ec2 +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Start a refresh + community.aws.autoscaling_instance_refresh: + name: some-asg + state: started + +- name: Cancel a refresh + community.aws.autoscaling_instance_refresh: + name: some-asg + state: cancelled + +- name: Start a refresh and pass preferences + community.aws.autoscaling_instance_refresh: + name: some-asg + state: started + preferences: + min_healthy_percentage: 91 + instance_warmup: 60 + +''' + +RETURN = ''' +--- +instance_refresh_id: + description: instance refresh id + returned: success + type: str + sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" +auto_scaling_group_name: + description: Name of autoscaling group + returned: success + type: str + sample: "public-webapp-production-1" +status: + description: + - The current state of the group when DeleteAutoScalingGroup is in progress. + - The following are the possible statuses + - Pending -- The request was created, but the operation has not started. + - InProgress -- The operation is in progress. + - Successful -- The operation completed successfully. + - Failed -- The operation failed to complete. You can troubleshoot using the status reason and the scaling activities. + - Cancelling -- + - An ongoing operation is being cancelled. + - Cancellation does not roll back any replacements that have already been completed, + - but it prevents new replacements from being started. + - Cancelled -- The operation is cancelled. + returned: success + type: str + sample: "Pending" +start_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" +end_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" +percentage_complete: + description: the % of completeness + returned: success + type: int + sample: 100 +instances_to_update: + description: num. of instance to update + returned: success + type: int + sample: 5 +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by AnsibleAWSModule + + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + + +def start_or_cancel_instance_refresh(conn, module): + """ + Args: + conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. + module: AnsibleAWSModule object + + Returns: + { + "instance_refreshes": [ + { + 'auto_scaling_group_name': 'ansible-test-hermes-63642726-asg', + 'instance_refresh_id': '6507a3e5-4950-4503-8978-e9f2636efc09', + 'instances_to_update': 1, + 'percentage_complete': 0, + "preferences": { + "instance_warmup": 60, + "min_healthy_percentage": 90, + "skip_matching": false + }, + 'start_time': '2021-02-04T03:39:40+00:00', + 'status': 'Cancelling', + 'status_reason': 'Replacing instances before cancelling.', + } + ] + } + """ + + asg_state = module.params.get('state') + asg_name = module.params.get('name') + preferences = module.params.get('preferences') + + args = {} + args['AutoScalingGroupName'] = asg_name + if asg_state == 'started': + args['Strategy'] = module.params.get('strategy') + if preferences: + if asg_state == 'cancelled': + module.fail_json(msg='can not pass preferences dict when canceling a refresh') + _prefs = scrub_none_parameters(preferences) + args['Preferences'] = snake_dict_to_camel_dict(_prefs, capitalize_first=True) + cmd_invocations = { + 'cancelled': conn.cancel_instance_refresh, + 'started': conn.start_instance_refresh, + } + try: + if module.check_mode: + if asg_state == 'started': + ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get('InstanceRefreshes', '[]') + if ongoing_refresh: + module.exit_json(changed=False, msg='In check_mode - Instance Refresh is already in progress, can not start new instance refresh.') + else: + module.exit_json(changed=True, msg='Would have started instance refresh if not in check mode.') + elif asg_state == 'cancelled': + ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get('InstanceRefreshes', '[]')[0] + if ongoing_refresh.get('Status', '') in ['Cancelling', 'Cancelled']: + module.exit_json(changed=False, msg='In check_mode - Instance Refresh already cancelled or is pending cancellation.') + elif not ongoing_refresh: + module.exit_json(chaned=False, msg='In check_mode - No active referesh found, nothing to cancel.') + else: + module.exit_json(changed=True, msg='Would have cancelled instance refresh if not in check mode.') + result = cmd_invocations[asg_state](aws_retry=True, **args) + instance_refreshes = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name, InstanceRefreshIds=[result['InstanceRefreshId']]) + result = dict( + instance_refreshes=camel_dict_to_snake_dict(instance_refreshes['InstanceRefreshes'][0]) + ) + return module.exit_json(**result) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws( + e, + msg='Failed to {0} InstanceRefresh'.format( + asg_state.replace('ed', '') + ) + ) + + +def main(): + + argument_spec = dict( + state=dict( + type='str', + required=True, + choices=['started', 'cancelled'], + ), + name=dict(required=True), + strategy=dict( + type='str', + default='Rolling', + required=False + ), + preferences=dict( + type='dict', + required=False, + options=dict( + min_healthy_percentage=dict(type='int', default=90), + instance_warmup=dict(type='int'), + ) + ), + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + autoscaling = module.client( + 'autoscaling', + retry_decorator=AWSRetry.jittered_backoff( + retries=10, + catch_extra_error_codes=['InstanceRefreshInProgress'] + ) + ) + + start_or_cancel_instance_refresh(autoscaling, module) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py new file mode 100644 index 00000000000..4d9cb7e05b7 --- /dev/null +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -0,0 +1,220 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: autoscaling_instance_refresh_info +version_added: 3.2.0 +short_description: Gather information about EC2 Auto Scaling Group (ASG) Instance Refreshes in AWS +description: + - Describes one or more instance refreshes. + - You can determine the status of a request by looking at the I(status) parameter. + - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_instance_refresh_info). + The usage did not change. +author: "Dan Khersonsky (@danquixote)" +options: + name: + description: + - The name of the Auto Scaling group. + type: str + required: true + ids: + description: + - One or more instance refresh IDs. + type: list + elements: str + default: [] + next_token: + description: + - The token for the next set of items to return. (You received this token from a previous call.) + type: str + max_records: + description: + - The maximum number of items to return with this call. The default value is 50 and the maximum value is 100. + type: int + required: false +extends_documentation_fragment: + - amazon.aws.aws + - amazon.aws.ec2 +''' + +EXAMPLES = ''' +# Note: These examples do not set authentication details, see the AWS Guide for details. + +- name: Find an refresh by ASG name + community.aws.autoscaling_instance_refresh_info: + name: somename-asg + +- name: Find an refresh by ASG name and one or more refresh-IDs + community.aws.autoscaling_instance_refresh_info: + name: somename-asg + ids: ['some-id-123'] + register: asgs + +- name: Find an refresh by ASG name and set max_records + community.aws.autoscaling_instance_refresh_info: + name: somename-asg + max_records: 4 + register: asgs + +- name: Find an refresh by ASG name and NextToken, if received from a previous call + community.aws.autoscaling_instance_refresh_info: + name: somename-asg + next_token: 'some-token-123' + register: asgs +''' + +RETURN = ''' +--- +instance_refresh_id: + description: instance refresh id + returned: success + type: str + sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" +auto_scaling_group_name: + description: Name of autoscaling group + returned: success + type: str + sample: "public-webapp-production-1" +status: + description: + - The current state of the group when DeleteAutoScalingGroup is in progress. + - The following are the possible statuses + - C(Pending) - The request was created, but the operation has not started. + - C(InProgress) - The operation is in progress. + - C(Successful) - The operation completed successfully. + - C(Failed) - The operation failed to complete. + You can troubleshoot using the status reason and the scaling activities. + - C(Cancelling) - An ongoing operation is being cancelled. + Cancellation does not roll back any replacements that have already been + completed, but it prevents new replacements from being started. + - C(Cancelled) - The operation is cancelled.' + returned: success + type: str + sample: "Pending" +start_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" +end_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" +percentage_complete: + description: the % of completeness + returned: success + type: int + sample: 100 +instances_to_update: + description: num. of instance to update + returned: success + type: int + sample: 5 +''' + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by AnsibleAWSModule + +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict + + +def find_asg_instance_refreshes(conn, module): + """ + Args: + conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. + module: AnsibleAWSModule object + + Returns: + { + "instance_refreshes": [ + { + 'auto_scaling_group_name': 'ansible-test-hermes-63642726-asg', + 'instance_refresh_id': '6507a3e5-4950-4503-8978-e9f2636efc09', + 'instances_to_update': 1, + 'percentage_complete': 0, + "preferences": { + "instance_warmup": 60, + "min_healthy_percentage": 90, + "skip_matching": false + }, + 'start_time': '2021-02-04T03:39:40+00:00', + 'status': 'Cancelled', + 'status_reason': 'Cancelled due to user request.', + } + ], + 'next_token': 'string' + } + """ + + asg_name = module.params.get('name') + asg_ids = module.params.get('ids') + asg_next_token = module.params.get('next_token') + asg_max_records = module.params.get('max_records') + + args = {} + args['AutoScalingGroupName'] = asg_name + if asg_ids: + args['InstanceRefreshIds'] = asg_ids + if asg_next_token: + args['NextToken'] = asg_next_token + if asg_max_records: + args['MaxRecords'] = asg_max_records + + try: + instance_refreshes_result = {} + response = conn.describe_instance_refreshes(**args) + if 'InstanceRefreshes' in response: + instance_refreshes_dict = dict( + instance_refreshes=response['InstanceRefreshes'], next_token=response.get('next_token', '')) + instance_refreshes_result = camel_dict_to_snake_dict( + instance_refreshes_dict) + + while 'NextToken' in response: + args['NextToken'] = response['NextToken'] + response = conn.describe_instance_refreshes(**args) + if 'InstanceRefreshes' in response: + instance_refreshes_dict = camel_dict_to_snake_dict(dict( + instance_refreshes=response['InstanceRefreshes'], next_token=response.get('next_token', ''))) + instance_refreshes_result.update(instance_refreshes_dict) + + return module.exit_json(**instance_refreshes_result) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg='Failed to describe InstanceRefreshes') + + +def main(): + + argument_spec = dict( + name=dict(required=True, type='str'), + ids=dict(required=False, default=[], elements='str', type='list'), + next_token=dict(required=False, default=None, type='str', no_log=True), + max_records=dict(required=False, type='int'), + ) + + module = AnsibleAWSModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + autoscaling = module.client( + 'autoscaling', + retry_decorator=AWSRetry.jittered_backoff(retries=10) + ) + find_asg_instance_refreshes(autoscaling, module) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/autoscaling_instance_refresh/aliases b/tests/integration/targets/autoscaling_instance_refresh/aliases new file mode 100644 index 00000000000..da172a8ebc1 --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/aliases @@ -0,0 +1,2 @@ +cloud/aws +autoscaling_instance_refresh_info diff --git a/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml b/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml new file mode 100644 index 00000000000..08e57d2558e --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# defaults file for ec2_asg +vpc_seed: '{{ tiny_prefix }}' +subnet_a_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' + +default_resource_name: '{{ resource_prefix }}-asg-refresh' +short_resource_name: '{{ tiny_prefix }}-asg-refresh' + +vpc_name: '{{ default_resource_name }}' +subnet_name: '{{ default_resource_name }}' +route_name: '{{ default_resource_name }}' +sg_name: '{{ default_resource_name }}' +asg_name: '{{ default_resource_name }}' +lc_name_1: '{{ default_resource_name }}-1' +lc_name_2: '{{ default_resource_name }}-2' +load_balancer_name: '{{ short_resource_name }}' diff --git a/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml b/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml new file mode 100644 index 00000000000..1471b11f658 --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_ec2_facts diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml new file mode 100644 index 00000000000..32cfd5378a8 --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -0,0 +1,517 @@ +--- +- name: setup credentials and region + module_defaults: + group/aws: + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token | default(omit) }}" + region: "{{ aws_region }}" + + collections: + - amazon.aws + + block: + # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: '{{ subnet_a_cidr }}' + tenancy: default + register: testing_vpc + + - name: Create internet gateway for use in testing + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: present + register: igw + + - name: Create subnet for use in testing + ec2_vpc_subnet: + state: present + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: '{{ subnet_a_cidr }}' + az: "{{ aws_region }}a" + resource_tags: + Name: "{{ subnet_name }}" + register: testing_subnet + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ route_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + + - name: create a security group with the vpc created in the ec2_setup + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + rules: + - proto: tcp + from_port: 22 + to_port: 22 + cidr_ip: 0.0.0.0/0 + - proto: tcp + from_port: 80 + to_port: 80 + cidr_ip: 0.0.0.0/0 + register: sg + + - name: ensure launch configs exist + ec2_lc: + name: "{{ item }}" + assign_public_ip: true + image_id: "{{ ec2_ami_id }}" + user_data: | + package_upgrade: true + package_update: true + packages: + - httpd + runcmd: + - "service httpd start" + security_groups: "{{ sg.group_id }}" + instance_type: t3.micro + loop: + - "{{ lc_name_1 }}" + - "{{ lc_name_2 }}" + + - name: launch asg and do not wait for instances to be deemed healthy (no ELB) + ec2_asg: + name: "{{ asg_name }}" + launch_config_name: "{{ lc_name_1 }}" + desired_capacity: 1 + min_size: 1 + max_size: 1 + vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" + wait_for_instances: no + state: present + register: output + + - assert: + that: + - "output.viable_instances == 0" + + # ============================================================ + + - name: test invalid cancelation - V1 - (pre-refresh) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + register: result + + - assert: + that: + - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group {{ resource_prefix }}-asg' in result.msg" + + - name: test starting a refresh with a valid ASG name - check_mode + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + check_mode: true + register: output + + - assert: + that: + - output is not failed + - output is changed + - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' + + - name: test starting a refresh with a valid ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test starting a refresh with a valid ASG name - Idempotent + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + ignore_errors: true + register: output + + - assert: + that: + - output is not changed + - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' + + - name: test starting a refresh with a valid ASG name - Idempotent (check_mode) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + ignore_errors: true + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' + + - name: test starting a refresh with a nonexistent ASG name + ec2_asg_instance_refresh: + name: "nonexistentname-asg" + state: "started" + ignore_errors: yes + register: result + + - assert: + that: + - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" + + - name: test canceling a refresh with an ASG name - check_mode + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + check_mode: true + register: output + + - assert: + that: + - output is not failed + - output is changed + - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' + + - name: test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test canceling a refresh with a ASG name - Idempotent + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + register: output + + - assert: + that: + - output is not changed + + - name: test cancelling a refresh with a valid ASG name - Idempotent (check_mode) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: true + check_mode: true + register: output + + - assert: + that: + - output is not changed + - output is not failed + + - name: test starting a refresh with an ASG name and preferences dict + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test valid start - V1 - (with preferences missing instance_warmup) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + ignore_errors: yes + retries: 5 + register: output + until: output is not failed + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test valid start - V2 - (with preferences missing min_healthy_percentage) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + ignore_errors: yes + + - assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + + - name: test invalid cancelation - V2 - (with preferences) + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + ignore_errors: yes + register: result + + - assert: + that: + - "'can not pass preferences dict when canceling a refresh' in result.msg" + + # ============================================================ + + - name: run setup with refresh_and_cancel_three_times.yml + include_tasks: refresh_and_cancel_three_times.yml + loop: "{{ query('sequence', 'start=1 end=3') }}" + + - name: test getting info for an ASG name + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + region: "{{ aws_region }}" + ignore_errors: yes + register: output + + - assert: + that: + - output | community.general.json_query(inst_refresh_id_json_query) | unique | length == 7 + vars: + inst_refresh_id_json_query: instance_refreshes[].instance_refresh_id + + - name: test using fake refresh ID + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 0" + + - name: test using a real refresh ID + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes |length }} == 1" + + - name: test getting info for an ASG name which doesn't exist + ec2_asg_instance_refresh_info: + name: n0n3x1stentname27b + ignore_errors: yes + register: output + + - assert: + that: + - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' == output.msg" + + - name: assert that the correct number of records are returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 7" + + - name: assert that valid message with fake-token is returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "fake-token-123" + ignore_errors: yes + register: output + + - assert: + that: + - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." == output.msg' + + - name: assert that max records=1 returns no more than one record + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} < 2" + + - name: assert that valid message with real-token is returned + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "{{ output.next_token }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 7" + + - name: test using both real nextToken and max_records=1 + ec2_asg_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + next_token: "{{ output.next_token }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 1" + + always: + + - name: kill asg + ec2_asg: + name: "{{ asg_name }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + # Remove the testing dependencies + + - name: remove the load balancer + ec2_elb_lb: + name: "{{ load_balancer_name }}" + state: absent + security_group_ids: + - "{{ sg.group_id }}" + subnets: "{{ testing_subnet.subnet.id }}" + wait: yes + connection_draining_timeout: 60 + listeners: + - protocol: http + load_balancer_port: 80 + instance_port: 80 + health_check: + ping_protocol: tcp + ping_port: 80 + ping_path: "/" + response_timeout: 5 + interval: 10 + unhealthy_threshold: 4 + healthy_threshold: 2 + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove launch configs + ec2_lc: + name: "{{ item }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + loop: + - "{{ lc_name_1 }}" + - "{{ lc_name_2 }}" + + - name: delete launch template + ec2_launch_template: + name: "{{ resource_prefix }}-lt" + state: absent + register: del_lt + retries: 10 + until: del_lt is not failed + ignore_errors: true + + - name: remove the security group + ec2_group: + name: "{{ sg_name }}" + description: a security group for ansible tests + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove routing rules + ec2_vpc_route_table: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ route_name }}" + routes: + - dest: 0.0.0.0/0 + gateway_id: "{{ igw.gateway_id }}" + subnets: + - "{{ testing_subnet.subnet.id }}" + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove internet gateway + ec2_vpc_igw: + vpc_id: "{{ testing_vpc.vpc.id }}" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the subnet + ec2_vpc_subnet: + state: absent + vpc_id: "{{ testing_vpc.vpc.id }}" + cidr: '{{ subnet_a_cidr }}' + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + + - name: remove the VPC + ec2_vpc_net: + name: "{{ vpc_name }}" + cidr_block: '{{ subnet_a_cidr }}' + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml new file mode 100644 index 00000000000..15fa2100c7f --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -0,0 +1,29 @@ +--- + +- name: try to cancel pre-loop + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: yes + +- name: test starting a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "started" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region }}" + ignore_errors: no + retries: 10 + delay: 5 + register: refreshout + until: refreshout is not failed + +- name: test cancelling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region }}" + ignore_errors: yes From 0fa7f2d4198fd15f9a7b89db60b04c55aca81575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A9ri=20Le=20Bouder?= Date: Fri, 16 Sep 2022 15:35:38 -0400 Subject: [PATCH 02/13] tests/integration: ensure the CI gives more time to some slow targets (#1467) tests/integration: ensure the CI gives more time to some slow targets Depends-On: ansible/ansible-zuul-jobs#1625 Depends-On: #1468 Depends-On: #1473 Reviewed-by: Mark Chappell This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/19a24e51e52e9efdb9cd3ae22f0a86f50683d6c5 --- tests/integration/targets/autoscaling_instance_refresh/aliases | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/targets/autoscaling_instance_refresh/aliases b/tests/integration/targets/autoscaling_instance_refresh/aliases index da172a8ebc1..6ce549da4bb 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/aliases +++ b/tests/integration/targets/autoscaling_instance_refresh/aliases @@ -1,2 +1,3 @@ +time=14m cloud/aws autoscaling_instance_refresh_info From 7b37c9fec903c840cba389d96bca45624f2916e3 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Wed, 5 Oct 2022 17:04:40 +0200 Subject: [PATCH 03/13] Update extends_documentation_fragment with amazon.aws.boto3 (#1459) Update extends_documentation_fragment with amazon.aws.boto3 Depends-On: ansible/ansible-zuul-jobs#1654 SUMMARY As per ansible-collections/amazon.aws#985 add amazon.aws.boto3. ISSUE TYPE Docs Pull Request COMPONENT NAME several Reviewed-by: Jill R Reviewed-by: Mark Chappell Reviewed-by: Markus Bergholz This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/bd3c03fcba0848f593b86309740fa73e986a9646 --- plugins/modules/autoscaling_instance_refresh.py | 1 + plugins/modules/autoscaling_instance_refresh_info.py | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 43ef665e3f3..94c2bb38c37 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -63,6 +63,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 + - amazon.aws.boto3 ''' EXAMPLES = ''' diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 4d9cb7e05b7..3037d0b5295 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -43,6 +43,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 + - amazon.aws.boto3 ''' EXAMPLES = ''' From 487871718975feb074e6393d310bd903f23f53c2 Mon Sep 17 00:00:00 2001 From: Bikouo Aubin <79859644+abikouo@users.noreply.github.com> Date: Tue, 10 Jan 2023 19:22:13 +0100 Subject: [PATCH 04/13] Ansible User-Agent identification for community.aws (#1632) Ansible User-Agent identification for community.aws SUMMARY The value will be similar to this APN/1.0 Ansible/2.14.1 community.aws/6.0.0-dev0 ISSUE TYPE Feature Pull Request Reviewed-by: Mark Chappell Reviewed-by: Bikouo Aubin Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a8cbce24071bcc62fe4594c38aff1baf18bd2862 --- plugins/modules/autoscaling_instance_refresh.py | 2 +- plugins/modules/autoscaling_instance_refresh_info.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 94c2bb38c37..73e223af3de 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -145,7 +145,7 @@ pass # caught by AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 3037d0b5295..064e92789b6 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -127,7 +127,7 @@ except ImportError: pass # caught by AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict From 9676c191cbee1adae123910e5b4dc91a0b8d4bf2 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 8 Mar 2023 12:07:26 +0100 Subject: [PATCH 05/13] Cleanup headers and imports (#1738) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cleanup headers and imports SUMMARY Mass update of imports, docs fragments and file headers Many of the amazon.aws module_utils and docs fragments got moved about, update community.aws to reflect this. Consistently apply the comment headers as documented at https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#python-shebang-utf-8-coding ISSUE TYPE Docs Pull Request Feature Pull Request COMPONENT NAME ADDITIONAL INFORMATION Header cleanup based upon: https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#python-shebang-utf-8-coding Begin your Ansible module with #!/usr/bin/python - this “shebang” allows ansible_python_interpreter to work. Follow the shebang immediately with # -*- coding: utf-8 -*- to clarify that the file is UTF-8 encoded. and https://docs.ansible.com/ansible/devel/dev_guide/developing_modules_documenting.html#copyright-and-license After the shebang and UTF-8 coding, add a copyright line with the original copyright holder and a license declaration. The license declaration should be ONLY one line, not the full GPL prefix. ... Additions to the module (for instance, rewrites) are not permitted to add additional copyright lines other than the default copyright statement if missing: Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a4f20bf114bfab19b1c84c4ecf42efd5614ab80c --- .../modules/autoscaling_instance_refresh.py | 34 +++++++++--------- .../autoscaling_instance_refresh_info.py | 36 +++++++++---------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 73e223af3de..7cf82132e64 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -1,12 +1,10 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- + # Copyright: Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function -__metaclass__ = type - - -DOCUMENTATION = ''' +DOCUMENTATION = r""" --- module: autoscaling_instance_refresh version_added: 3.2.0 @@ -61,12 +59,12 @@ type: int type: dict extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 + - amazon.aws.common.modules + - amazon.aws.region.modules - amazon.aws.boto3 -''' +""" -EXAMPLES = ''' +EXAMPLES = r""" # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Start a refresh @@ -87,9 +85,9 @@ min_healthy_percentage: 91 instance_warmup: 60 -''' +""" -RETURN = ''' +RETURN = r""" --- instance_refresh_id: description: instance refresh id @@ -137,19 +135,21 @@ returned: success type: int sample: 5 -''' +""" try: - from botocore.exceptions import BotoCoreError, ClientError + from botocore.exceptions import BotoCoreError + from botocore.exceptions import ClientError except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict +from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict + +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.transformation import scrub_none_parameters from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters -from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict def start_or_cancel_instance_refresh(conn, module): diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 064e92789b6..64581b46829 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -1,14 +1,10 @@ #!/usr/bin/python +# -*- coding: utf-8 -*- + # Copyright: Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -from __future__ import absolute_import, division, print_function - - -__metaclass__ = type - - -DOCUMENTATION = ''' +DOCUMENTATION = r""" --- module: autoscaling_instance_refresh_info version_added: 3.2.0 @@ -18,7 +14,8 @@ - You can determine the status of a request by looking at the I(status) parameter. - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_instance_refresh_info). The usage did not change. -author: "Dan Khersonsky (@danquixote)" +author: + - "Dan Khersonsky (@danquixote)" options: name: description: @@ -41,12 +38,12 @@ type: int required: false extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 + - amazon.aws.common.modules + - amazon.aws.region.modules - amazon.aws.boto3 -''' +""" -EXAMPLES = ''' +EXAMPLES = r""" # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Find an refresh by ASG name @@ -70,9 +67,9 @@ name: somename-asg next_token: 'some-token-123' register: asgs -''' +""" -RETURN = ''' +RETURN = r""" --- instance_refresh_id: description: instance refresh id @@ -120,16 +117,19 @@ returned: success type: int sample: 5 -''' +""" try: - from botocore.exceptions import BotoCoreError, ClientError + from botocore.exceptions import BotoCoreError + from botocore.exceptions import ClientError except ImportError: pass # caught by AnsibleAWSModule +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry + from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict def find_asg_instance_refreshes(conn, module): From f7c403aed76887a2d48a1bb498ea6dff9ece9bb9 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 26 Apr 2023 19:26:07 +0200 Subject: [PATCH 06/13] Big Black PR (#1784) * Black prep * Black * changelog * Fix pylint unused-import in tests * Split SSM connection plugin changes * disable glue tests - bucket's missing * Disable s3_logging and s3_sync tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2c4575c248776c65d66b06cd60fa09b0dae1cd6f --- .../modules/autoscaling_instance_refresh.py | 93 +++++++++---------- .../autoscaling_instance_refresh_info.py | 55 ++++++----- 2 files changed, 71 insertions(+), 77 deletions(-) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 7cf82132e64..5b9855d135d 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -179,75 +179,75 @@ def start_or_cancel_instance_refresh(conn, module): } """ - asg_state = module.params.get('state') - asg_name = module.params.get('name') - preferences = module.params.get('preferences') + asg_state = module.params.get("state") + asg_name = module.params.get("name") + preferences = module.params.get("preferences") args = {} - args['AutoScalingGroupName'] = asg_name - if asg_state == 'started': - args['Strategy'] = module.params.get('strategy') + args["AutoScalingGroupName"] = asg_name + if asg_state == "started": + args["Strategy"] = module.params.get("strategy") if preferences: - if asg_state == 'cancelled': - module.fail_json(msg='can not pass preferences dict when canceling a refresh') + if asg_state == "cancelled": + module.fail_json(msg="can not pass preferences dict when canceling a refresh") _prefs = scrub_none_parameters(preferences) - args['Preferences'] = snake_dict_to_camel_dict(_prefs, capitalize_first=True) + args["Preferences"] = snake_dict_to_camel_dict(_prefs, capitalize_first=True) cmd_invocations = { - 'cancelled': conn.cancel_instance_refresh, - 'started': conn.start_instance_refresh, + "cancelled": conn.cancel_instance_refresh, + "started": conn.start_instance_refresh, } try: if module.check_mode: - if asg_state == 'started': - ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get('InstanceRefreshes', '[]') + if asg_state == "started": + ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get( + "InstanceRefreshes", "[]" + ) if ongoing_refresh: - module.exit_json(changed=False, msg='In check_mode - Instance Refresh is already in progress, can not start new instance refresh.') + module.exit_json( + changed=False, + msg="In check_mode - Instance Refresh is already in progress, can not start new instance refresh.", + ) else: - module.exit_json(changed=True, msg='Would have started instance refresh if not in check mode.') - elif asg_state == 'cancelled': - ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get('InstanceRefreshes', '[]')[0] - if ongoing_refresh.get('Status', '') in ['Cancelling', 'Cancelled']: - module.exit_json(changed=False, msg='In check_mode - Instance Refresh already cancelled or is pending cancellation.') + module.exit_json(changed=True, msg="Would have started instance refresh if not in check mode.") + elif asg_state == "cancelled": + ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get( + "InstanceRefreshes", "[]" + )[0] + if ongoing_refresh.get("Status", "") in ["Cancelling", "Cancelled"]: + module.exit_json( + changed=False, + msg="In check_mode - Instance Refresh already cancelled or is pending cancellation.", + ) elif not ongoing_refresh: - module.exit_json(chaned=False, msg='In check_mode - No active referesh found, nothing to cancel.') + module.exit_json(chaned=False, msg="In check_mode - No active referesh found, nothing to cancel.") else: - module.exit_json(changed=True, msg='Would have cancelled instance refresh if not in check mode.') + module.exit_json(changed=True, msg="Would have cancelled instance refresh if not in check mode.") result = cmd_invocations[asg_state](aws_retry=True, **args) - instance_refreshes = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name, InstanceRefreshIds=[result['InstanceRefreshId']]) - result = dict( - instance_refreshes=camel_dict_to_snake_dict(instance_refreshes['InstanceRefreshes'][0]) + instance_refreshes = conn.describe_instance_refreshes( + AutoScalingGroupName=asg_name, InstanceRefreshIds=[result["InstanceRefreshId"]] ) + result = dict(instance_refreshes=camel_dict_to_snake_dict(instance_refreshes["InstanceRefreshes"][0])) return module.exit_json(**result) except (BotoCoreError, ClientError) as e: - module.fail_json_aws( - e, - msg='Failed to {0} InstanceRefresh'.format( - asg_state.replace('ed', '') - ) - ) + module.fail_json_aws(e, msg="Failed to {0} InstanceRefresh".format(asg_state.replace("ed", ""))) def main(): - argument_spec = dict( state=dict( - type='str', + type="str", required=True, - choices=['started', 'cancelled'], + choices=["started", "cancelled"], ), name=dict(required=True), - strategy=dict( - type='str', - default='Rolling', - required=False - ), + strategy=dict(type="str", default="Rolling", required=False), preferences=dict( - type='dict', + type="dict", required=False, options=dict( - min_healthy_percentage=dict(type='int', default=90), - instance_warmup=dict(type='int'), - ) + min_healthy_percentage=dict(type="int", default=90), + instance_warmup=dict(type="int"), + ), ), ) @@ -256,15 +256,12 @@ def main(): supports_check_mode=True, ) autoscaling = module.client( - 'autoscaling', - retry_decorator=AWSRetry.jittered_backoff( - retries=10, - catch_extra_error_codes=['InstanceRefreshInProgress'] - ) + "autoscaling", + retry_decorator=AWSRetry.jittered_backoff(retries=10, catch_extra_error_codes=["InstanceRefreshInProgress"]), ) start_or_cancel_instance_refresh(autoscaling, module) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 64581b46829..639940b1b77 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -158,51 +158,51 @@ def find_asg_instance_refreshes(conn, module): ], 'next_token': 'string' } - """ + """ - asg_name = module.params.get('name') - asg_ids = module.params.get('ids') - asg_next_token = module.params.get('next_token') - asg_max_records = module.params.get('max_records') + asg_name = module.params.get("name") + asg_ids = module.params.get("ids") + asg_next_token = module.params.get("next_token") + asg_max_records = module.params.get("max_records") args = {} - args['AutoScalingGroupName'] = asg_name + args["AutoScalingGroupName"] = asg_name if asg_ids: - args['InstanceRefreshIds'] = asg_ids + args["InstanceRefreshIds"] = asg_ids if asg_next_token: - args['NextToken'] = asg_next_token + args["NextToken"] = asg_next_token if asg_max_records: - args['MaxRecords'] = asg_max_records + args["MaxRecords"] = asg_max_records try: instance_refreshes_result = {} response = conn.describe_instance_refreshes(**args) - if 'InstanceRefreshes' in response: + if "InstanceRefreshes" in response: instance_refreshes_dict = dict( - instance_refreshes=response['InstanceRefreshes'], next_token=response.get('next_token', '')) - instance_refreshes_result = camel_dict_to_snake_dict( - instance_refreshes_dict) + instance_refreshes=response["InstanceRefreshes"], next_token=response.get("next_token", "") + ) + instance_refreshes_result = camel_dict_to_snake_dict(instance_refreshes_dict) - while 'NextToken' in response: - args['NextToken'] = response['NextToken'] + while "NextToken" in response: + args["NextToken"] = response["NextToken"] response = conn.describe_instance_refreshes(**args) - if 'InstanceRefreshes' in response: - instance_refreshes_dict = camel_dict_to_snake_dict(dict( - instance_refreshes=response['InstanceRefreshes'], next_token=response.get('next_token', ''))) + if "InstanceRefreshes" in response: + instance_refreshes_dict = camel_dict_to_snake_dict( + dict(instance_refreshes=response["InstanceRefreshes"], next_token=response.get("next_token", "")) + ) instance_refreshes_result.update(instance_refreshes_dict) return module.exit_json(**instance_refreshes_result) except (BotoCoreError, ClientError) as e: - module.fail_json_aws(e, msg='Failed to describe InstanceRefreshes') + module.fail_json_aws(e, msg="Failed to describe InstanceRefreshes") def main(): - argument_spec = dict( - name=dict(required=True, type='str'), - ids=dict(required=False, default=[], elements='str', type='list'), - next_token=dict(required=False, default=None, type='str', no_log=True), - max_records=dict(required=False, type='int'), + name=dict(required=True, type="str"), + ids=dict(required=False, default=[], elements="str", type="list"), + next_token=dict(required=False, default=None, type="str", no_log=True), + max_records=dict(required=False, type="int"), ) module = AnsibleAWSModule( @@ -210,12 +210,9 @@ def main(): supports_check_mode=True, ) - autoscaling = module.client( - 'autoscaling', - retry_decorator=AWSRetry.jittered_backoff(retries=10) - ) + autoscaling = module.client("autoscaling", retry_decorator=AWSRetry.jittered_backoff(retries=10)) find_asg_instance_refreshes(autoscaling, module) -if __name__ == '__main__': +if __name__ == "__main__": main() From 72fe75352502fe5e1f2c2dedcbe5514933645e6c Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 8 May 2023 19:21:22 +0200 Subject: [PATCH 07/13] Bulk migration to Python 3.6 f-strings (#1810) Bulk migration to Python 3.6 f-strings SUMMARY We've dropped support for Python <3.6, bulk migrate to fstrings and perform some general string cleanup A combination of black --preview flynt some manual cleanup ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/ tests/ ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/de338210dc1b0bb2eecee1dc16e073163b2d1df7 --- plugins/modules/autoscaling_instance_refresh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 5b9855d135d..86546fac21e 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -229,7 +229,7 @@ def start_or_cancel_instance_refresh(conn, module): result = dict(instance_refreshes=camel_dict_to_snake_dict(instance_refreshes["InstanceRefreshes"][0])) return module.exit_json(**result) except (BotoCoreError, ClientError) as e: - module.fail_json_aws(e, msg="Failed to {0} InstanceRefresh".format(asg_state.replace("ed", ""))) + module.fail_json_aws(e, msg=f"Failed to {asg_state.replace('ed', '')} InstanceRefresh") def main(): From 5a352d9da70c027eea48aca82c25a5a05c2c3155 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 31 Aug 2023 17:58:59 +0200 Subject: [PATCH 08/13] Mass update of docs and tests (credentials/session tokens) (#1921) Mass update of docs and tests (credentials/session tokens) SUMMARY We had a cleanup of credentials/session parameters which included a batch of deprecations and renames. Ensure that all of our tests and docs are using the 'canonical' names ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/modules/batch_compute_environment.py plugins/modules/cloudformation_exports_info.py plugins/modules/ec2_vpc_vpn.py plugins/modules/elasticache.py plugins/modules/elasticache_parameter_group.py plugins/modules/elasticache_snapshot.py plugins/modules/ses_rule_set.py plugins/modules/sts_assume_role.py plugins/modules/sts_session_token.py tests/integration ADDITIONAL INFORMATION See also ansible-collections/amazon.aws#1172 ansible-collections/amazon.aws#1714 Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4a5b50e9b9c0d6ca1a1f802f3b03d4f503c16885 --- .../targets/autoscaling_instance_refresh/tasks/main.yml | 6 +++--- .../tasks/refresh_and_cancel_three_times.yml | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml index 32cfd5378a8..f6780d18b3e 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -2,9 +2,9 @@ - name: setup credentials and region module_defaults: group/aws: - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token | default(omit) }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" + session_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" collections: diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml index 15fa2100c7f..9ec92e4b5fc 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -10,8 +10,8 @@ ec2_asg_instance_refresh: name: "{{ asg_name }}" state: "started" - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" region: "{{ aws_region }}" ignore_errors: no retries: 10 @@ -23,7 +23,7 @@ ec2_asg_instance_refresh: name: "{{ asg_name }}" state: "cancelled" - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" + access_key: "{{ aws_access_key }}" + secret_key: "{{ aws_secret_key }}" region: "{{ aws_region }}" ignore_errors: yes From 8a101296cee01b1f3e7551007a153e5f8c5c0b1e Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 12 Sep 2023 07:37:30 +0200 Subject: [PATCH 09/13] Cleanup after renamed plugins (#1940) Cleanup after renamed plugins SUMMARY Prior to 5.0.0 we renamed various modules. Cleanup the integration tests (and some docs). ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/modules/acm_certificate.py plugins/modules/acm_certificate_info.py plugins/modules/autoscaling_policy.py plugins/modules/codepipeline.py plugins/modules/storagegateway_info.py ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/c2fead07d8bd121a399ebcddc65421a6d81a1c8e --- .../tasks/main.yml | 64 +++++++++---------- .../tasks/refresh_and_cancel_three_times.yml | 6 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml index f6780d18b3e..f19b7c3c24a 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -47,7 +47,7 @@ - "{{ testing_subnet.subnet.id }}" - name: create a security group with the vpc created in the ec2_setup - ec2_group: + ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests vpc_id: "{{ testing_vpc.vpc.id }}" @@ -63,7 +63,7 @@ register: sg - name: ensure launch configs exist - ec2_lc: + autoscaling_launch_config: name: "{{ item }}" assign_public_ip: true image_id: "{{ ec2_ami_id }}" @@ -81,7 +81,7 @@ - "{{ lc_name_2 }}" - name: launch asg and do not wait for instances to be deemed healthy (no ELB) - ec2_asg: + autoscaling_group: name: "{{ asg_name }}" launch_config_name: "{{ lc_name_1 }}" desired_capacity: 1 @@ -99,7 +99,7 @@ # ============================================================ - name: test invalid cancelation - V1 - (pre-refresh) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" ignore_errors: yes @@ -110,7 +110,7 @@ - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group {{ resource_prefix }}-asg' in result.msg" - name: test starting a refresh with a valid ASG name - check_mode - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" check_mode: true @@ -123,7 +123,7 @@ - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' - name: test starting a refresh with a valid ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" register: output @@ -133,7 +133,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: test starting a refresh with a valid ASG name - Idempotent - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" ignore_errors: true @@ -145,7 +145,7 @@ - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' - name: test starting a refresh with a valid ASG name - Idempotent (check_mode) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" ignore_errors: true @@ -159,7 +159,7 @@ - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' - name: test starting a refresh with a nonexistent ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "nonexistentname-asg" state: "started" ignore_errors: yes @@ -170,7 +170,7 @@ - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" - name: test canceling a refresh with an ASG name - check_mode - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" check_mode: true @@ -183,7 +183,7 @@ - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' - name: test canceling a refresh with an ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output @@ -193,7 +193,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: test canceling a refresh with a ASG name - Idempotent - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" ignore_errors: yes @@ -204,7 +204,7 @@ - output is not changed - name: test cancelling a refresh with a valid ASG name - Idempotent (check_mode) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" ignore_errors: true @@ -217,7 +217,7 @@ - output is not failed - name: test starting a refresh with an ASG name and preferences dict - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -232,7 +232,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: re-test canceling a refresh with an ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output @@ -242,7 +242,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: test valid start - V1 - (with preferences missing instance_warmup) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -257,7 +257,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: re-test canceling a refresh with an ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output @@ -267,7 +267,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: test valid start - V2 - (with preferences missing min_healthy_percentage) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -282,7 +282,7 @@ - "'instance_refresh_id' in output.instance_refreshes" - name: test invalid cancelation - V2 - (with preferences) - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" preferences: @@ -302,7 +302,7 @@ loop: "{{ query('sequence', 'start=1 end=3') }}" - name: test getting info for an ASG name - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" region: "{{ aws_region }}" ignore_errors: yes @@ -315,7 +315,7 @@ inst_refresh_id_json_query: instance_refreshes[].instance_refresh_id - name: test using fake refresh ID - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] ignore_errors: yes @@ -326,7 +326,7 @@ - "{{ output.instance_refreshes|length }} == 0" - name: test using a real refresh ID - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] ignore_errors: yes @@ -337,7 +337,7 @@ - "{{ output.instance_refreshes |length }} == 1" - name: test getting info for an ASG name which doesn't exist - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: n0n3x1stentname27b ignore_errors: yes register: output @@ -347,7 +347,7 @@ - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' == output.msg" - name: assert that the correct number of records are returned - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" ignore_errors: yes register: output @@ -357,7 +357,7 @@ - "{{ output.instance_refreshes|length }} == 7" - name: assert that valid message with fake-token is returned - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" next_token: "fake-token-123" ignore_errors: yes @@ -368,7 +368,7 @@ - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." == output.msg' - name: assert that max records=1 returns no more than one record - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" max_records: 1 ignore_errors: yes @@ -379,7 +379,7 @@ - "{{ output.instance_refreshes|length }} < 2" - name: assert that valid message with real-token is returned - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" next_token: "{{ output.next_token }}" ignore_errors: yes @@ -390,7 +390,7 @@ - "{{ output.instance_refreshes|length }} == 7" - name: test using both real nextToken and max_records=1 - ec2_asg_instance_refresh_info: + autoscaling_instance_refresh_info: name: "{{ asg_name }}" max_records: 1 next_token: "{{ output.next_token }}" @@ -404,7 +404,7 @@ always: - name: kill asg - ec2_asg: + autoscaling_group: name: "{{ asg_name }}" state: absent register: removed @@ -414,7 +414,7 @@ # Remove the testing dependencies - name: remove the load balancer - ec2_elb_lb: + elb_classic_lb: name: "{{ load_balancer_name }}" state: absent security_group_ids: @@ -440,7 +440,7 @@ retries: 10 - name: remove launch configs - ec2_lc: + autoscaling_launch_config: name: "{{ item }}" state: absent register: removed @@ -461,7 +461,7 @@ ignore_errors: true - name: remove the security group - ec2_group: + ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests vpc_id: "{{ testing_vpc.vpc.id }}" diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml index 9ec92e4b5fc..9b051a054e0 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -1,13 +1,13 @@ --- - name: try to cancel pre-loop - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" ignore_errors: yes - name: test starting a refresh with an ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" access_key: "{{ aws_access_key }}" @@ -20,7 +20,7 @@ until: refreshout is not failed - name: test cancelling a refresh with an ASG name - ec2_asg_instance_refresh: + autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" access_key: "{{ aws_access_key }}" From 56d1e5840fa33d753982f6585e226976446603b8 Mon Sep 17 00:00:00 2001 From: Markus Bergholz Date: Fri, 1 Dec 2023 13:50:37 +0100 Subject: [PATCH 10/13] fix unsafe asserts (#2013) fix unsafe asserts SUMMARY Closes #2012 ISSUE TYPE Bugfix Pull Request COMPONENT NAME integrationtests Reviewed-by: Mark Chappell Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4bdcecda3d37e1ccd4d568ec641b59d0a745bbca --- .../autoscaling_instance_refresh/tasks/main.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml index f19b7c3c24a..5b754d47d69 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -107,7 +107,7 @@ - assert: that: - - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group {{ resource_prefix }}-asg' in result.msg" + - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group ' ~ resource_prefix ~ '-asg' in result.msg" - name: test starting a refresh with a valid ASG name - check_mode autoscaling_instance_refresh: @@ -323,7 +323,7 @@ - assert: that: - - "{{ output.instance_refreshes|length }} == 0" + - output.instance_refreshes | length == 0 - name: test using a real refresh ID autoscaling_instance_refresh_info: @@ -334,7 +334,7 @@ - assert: that: - - "{{ output.instance_refreshes |length }} == 1" + - output.instance_refreshes | length == 1 - name: test getting info for an ASG name which doesn't exist autoscaling_instance_refresh_info: @@ -354,7 +354,7 @@ - assert: that: - - "{{ output.instance_refreshes|length }} == 7" + - output.instance_refreshes | length == 7 - name: assert that valid message with fake-token is returned autoscaling_instance_refresh_info: @@ -376,7 +376,7 @@ - assert: that: - - "{{ output.instance_refreshes|length }} < 2" + - output.instance_refreshes | length < 2 - name: assert that valid message with real-token is returned autoscaling_instance_refresh_info: @@ -387,7 +387,7 @@ - assert: that: - - "{{ output.instance_refreshes|length }} == 7" + - output.instance_refreshes | length == 7 - name: test using both real nextToken and max_records=1 autoscaling_instance_refresh_info: @@ -399,7 +399,7 @@ - assert: that: - - "{{ output.instance_refreshes|length }} == 1" + - output.instance_refreshes | length == 1 always: From 619cfbbd6b7c5f955818201e1be912f4e55103a7 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 5 Jan 2024 18:42:41 +0100 Subject: [PATCH 11/13] ansible-lint (documentation) cleanup for plugins/ (#2036) ansible-lint (documentation) cleanup for plugins/ SUMMARY Fixes an array of ansible-lint failures in plugins/ Adds ansible-lint plugins/ to tox -m lint ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/ ADDITIONAL INFORMATION docs changes only (no changelog fragment needed) Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6dd4a00b8c18fe3499bad04f90c8ac7832ade8bb --- plugins/modules/autoscaling_instance_refresh.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index 86546fac21e..b301fea9439 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -84,7 +84,6 @@ preferences: min_healthy_percentage: 91 instance_warmup: 60 - """ RETURN = r""" From b0e8b32878ea4d8736868c5534e56a04dbf213f2 Mon Sep 17 00:00:00 2001 From: Bikouo Aubin <79859644+abikouo@users.noreply.github.com> Date: Wed, 25 Sep 2024 18:00:10 +0200 Subject: [PATCH 12/13] autoscaling_instance_refresh - prepare modules for promotion (#2150) SUMMARY Closes #2120 Closes #2019 Closes #2016 Prepare modules autoscaling_instance_refresh and autoscaling_instance_refresh_info for promotion: Refactor modules to use common code from ansible_collections.amazon.aws.plugins.module_utils.autoscaling Add type hinting Update integration tests ISSUE TYPE Feature Pull Request Reviewed-by: GomathiselviS Reviewed-by: Bikouo Aubin Reviewed-by: Alina Buzachis This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/d59fa93cf463cbb194cd10ab1bb0e59763a5b3f0 --- .../modules/autoscaling_instance_refresh.py | 238 +++++++----- .../autoscaling_instance_refresh_info.py | 230 ++++++------ .../tasks/instance_refresh_info.yml | 99 +++++ .../tasks/main.yml | 351 ++---------------- .../tasks/refresh_and_cancel_three_times.yml | 19 +- .../tasks/start_cancel_instance_refresh.yml | 206 ++++++++++ .../test_autoscaling_instance_refresh.py | 28 ++ 7 files changed, 618 insertions(+), 553 deletions(-) create mode 100644 tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml create mode 100644 tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml create mode 100644 tests/unit/plugins/modules/test_autoscaling_instance_refresh.py diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index b301fea9439..b337b5b1f52 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -12,7 +12,7 @@ description: - Start or cancel an EC2 Auto Scaling Group instance refresh in AWS. - Can be used with M(community.aws.autoscaling_instance_refresh_info) to track the subsequent progress. - - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_instance_refresh). + - Prior to release 5.0.0 this module was called M(community.aws.ec2_asg_instance_refresh). The usage did not change. author: - "Dan Khersonsky (@danquixote)" @@ -30,7 +30,7 @@ required: true strategy: description: - - The strategy to use for the instance refresh. The only valid value is C(Rolling). + - The strategy to use for the instance refresh. The only valid value is V(Rolling). - A rolling update is an update that is applied to all instances in an Auto Scaling group until all instances have been updated. - A rolling update can fail due to failed health checks or if instances are on standby or are protected from scale in. - If the rolling update process fails, any instances that were already replaced are not rolled back to their previous configuration. @@ -40,15 +40,16 @@ description: - Set of preferences associated with the instance refresh request. - If not provided, the default values are used. - - For I(min_healthy_percentage), the default value is C(90). - - For I(instance_warmup), the default is to use the value specified for the health check grace period for the Auto Scaling group. - - Can not be specified when I(state) is set to 'cancelled'. + - For O(preferences.min_healthy_percentage), the default value is V(90). + - For O(preferences.instance_warmup), the default is to use the value specified for the health check grace period for the Auto Scaling group. + - Can not be specified when O(state=cancelled). required: false suboptions: min_healthy_percentage: description: - Total percent of capacity in ASG that must remain healthy during instance refresh to allow operation to continue. - It is rounded up to the nearest integer. + - Value range is V(0) to V(100). type: int default: 90 instance_warmup: @@ -57,6 +58,21 @@ - During this time, Amazon EC2 Auto Scaling does not immediately move on to the next replacement. - The default is to use the value for the health check grace period defined for the group. type: int + skip_matching: + description: + - Indicates whether skip matching is enabled. + - If enabled V(true), then Amazon EC2 Auto Scaling skips replacing instances that match the desired configuration. + type: bool + version_added: 9.0.0 + max_healthy_percentage: + description: + - Specifies the maximum percentage of the group that can be in service and healthy, or pending, + to support your workload when replacing instances. + - The value is expressed as a percentage of the desired capacity of the Auto Scaling group. + - Value range is V(100) to V(200). + - When specified, you must also specify O(preferences.min_healthy_percentage), and the difference between them cannot be greater than V(100). + type: int + version_added: 9.0.0 type: dict extends_documentation_fragment: - amazon.aws.common.modules @@ -84,98 +100,117 @@ preferences: min_healthy_percentage: 91 instance_warmup: 60 + skip_matching: true """ RETURN = r""" ---- -instance_refresh_id: - description: instance refresh id - returned: success - type: str - sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" -auto_scaling_group_name: - description: Name of autoscaling group - returned: success - type: str - sample: "public-webapp-production-1" -status: - description: - - The current state of the group when DeleteAutoScalingGroup is in progress. - - The following are the possible statuses - - Pending -- The request was created, but the operation has not started. - - InProgress -- The operation is in progress. - - Successful -- The operation completed successfully. - - Failed -- The operation failed to complete. You can troubleshoot using the status reason and the scaling activities. - - Cancelling -- - - An ongoing operation is being cancelled. - - Cancellation does not roll back any replacements that have already been completed, - - but it prevents new replacements from being started. - - Cancelled -- The operation is cancelled. - returned: success - type: str - sample: "Pending" -start_time: - description: The date and time this ASG was created, in ISO 8601 format. - returned: success - type: str - sample: "2015-11-25T00:05:36.309Z" -end_time: - description: The date and time this ASG was created, in ISO 8601 format. - returned: success - type: str - sample: "2015-11-25T00:05:36.309Z" -percentage_complete: - description: the % of completeness - returned: success - type: int - sample: 100 -instances_to_update: - description: num. of instance to update - returned: success - type: int - sample: 5 +instance_refreshes: + description: Details of the instance refreshes for the Auto Scaling group. + returned: always + type: complex + contains: + instance_refresh_id: + description: Instance refresh id. + returned: success + type: str + sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" + auto_scaling_group_name: + description: Name of autoscaling group. + returned: success + type: str + sample: "public-webapp-production-1" + status: + description: + - The current state of the group when DeleteAutoScalingGroup is in progress. + - The following are the possible statuses + - Pending - The request was created, but the operation has not started. + - InProgress - The operation is in progress. + - Successful - The operation completed successfully. + - Failed - The operation failed to complete. + You can troubleshoot using the status reason and the scaling activities. + - Cancelling - An ongoing operation is being cancelled. + Cancellation does not roll back any replacements that have already been + completed, but it prevents new replacements from being started. + - Cancelled - The operation is cancelled. + returned: success + type: str + sample: "Pending" + preferences: + description: The preferences for an instance refresh. + returned: always + type: dict + sample: { + 'AlarmSpecification': { + 'Alarms': [ + 'my-alarm', + ], + }, + 'AutoRollback': True, + 'InstanceWarmup': 200, + 'MinHealthyPercentage': 90, + 'ScaleInProtectedInstances': 'Ignore', + 'SkipMatching': False, + 'StandbyInstances': 'Ignore', + } + start_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" + end_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" + percentage_complete: + description: the % of completeness. + returned: success + type: int + sample: 100 + instances_to_update: + description: number of instances to update. + returned: success + type: int + sample: 5 """ -try: - from botocore.exceptions import BotoCoreError - from botocore.exceptions import ClientError -except ImportError: - pass # caught by AnsibleAWSModule +from typing import Dict +from typing import Optional +from typing import Union from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import AnsibleAutoScalingError +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import cancel_instance_refresh +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import describe_instance_refreshes +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import start_instance_refresh from ansible_collections.amazon.aws.plugins.module_utils.transformation import scrub_none_parameters from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -def start_or_cancel_instance_refresh(conn, module): +def validate_healthy_percentage(preferences: Dict[str, Union[bool, int]]) -> Optional[str]: + min_healthy_percentage = preferences.get("min_healthy_percentage") + max_healthy_percentage = preferences.get("max_healthy_percentage") + + if min_healthy_percentage is not None and (min_healthy_percentage < 0 or min_healthy_percentage > 100): + return "The value range for the min_healthy_percentage is 0 to 100." + if max_healthy_percentage is not None: + if max_healthy_percentage < 100 or max_healthy_percentage > 200: + return "The value range for the max_healthy_percentage is 100 to 200." + if min_healthy_percentage is None: + return "You must also specify min_healthy_percentage when max_healthy_percentage is specified." + if (max_healthy_percentage - min_healthy_percentage) > 100: + return "The difference between the max_healthy_percentage and min_healthy_percentage cannot be greater than 100." + return None + + +def start_or_cancel_instance_refresh(conn, module: AnsibleAWSModule) -> None: """ Args: conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. module: AnsibleAWSModule object - - Returns: - { - "instance_refreshes": [ - { - 'auto_scaling_group_name': 'ansible-test-hermes-63642726-asg', - 'instance_refresh_id': '6507a3e5-4950-4503-8978-e9f2636efc09', - 'instances_to_update': 1, - 'percentage_complete': 0, - "preferences": { - "instance_warmup": 60, - "min_healthy_percentage": 90, - "skip_matching": false - }, - 'start_time': '2021-02-04T03:39:40+00:00', - 'status': 'Cancelling', - 'status_reason': 'Replacing instances before cancelling.', - } - ] - } """ asg_state = module.params.get("state") @@ -183,24 +218,25 @@ def start_or_cancel_instance_refresh(conn, module): preferences = module.params.get("preferences") args = {} - args["AutoScalingGroupName"] = asg_name if asg_state == "started": args["Strategy"] = module.params.get("strategy") if preferences: if asg_state == "cancelled": module.fail_json(msg="can not pass preferences dict when canceling a refresh") - _prefs = scrub_none_parameters(preferences) - args["Preferences"] = snake_dict_to_camel_dict(_prefs, capitalize_first=True) + error = validate_healthy_percentage(preferences) + if error: + module.fail_json(msg=error) + args["Preferences"] = snake_dict_to_camel_dict(scrub_none_parameters(preferences), capitalize_first=True) cmd_invocations = { - "cancelled": conn.cancel_instance_refresh, - "started": conn.start_instance_refresh, + "cancelled": cancel_instance_refresh, + "started": start_instance_refresh, } try: if module.check_mode: + ongoing_refresh = describe_instance_refreshes(conn, auto_scaling_group_name=asg_name).get( + "InstanceRefreshes", [] + ) if asg_state == "started": - ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get( - "InstanceRefreshes", "[]" - ) if ongoing_refresh: module.exit_json( changed=False, @@ -209,26 +245,23 @@ def start_or_cancel_instance_refresh(conn, module): else: module.exit_json(changed=True, msg="Would have started instance refresh if not in check mode.") elif asg_state == "cancelled": - ongoing_refresh = conn.describe_instance_refreshes(AutoScalingGroupName=asg_name).get( - "InstanceRefreshes", "[]" - )[0] - if ongoing_refresh.get("Status", "") in ["Cancelling", "Cancelled"]: + if ongoing_refresh and ongoing_refresh[0].get("Status", "") in ["Cancelling", "Cancelled"]: module.exit_json( changed=False, msg="In check_mode - Instance Refresh already cancelled or is pending cancellation.", ) elif not ongoing_refresh: - module.exit_json(chaned=False, msg="In check_mode - No active referesh found, nothing to cancel.") + module.exit_json(changed=False, msg="In check_mode - No active referesh found, nothing to cancel.") else: module.exit_json(changed=True, msg="Would have cancelled instance refresh if not in check mode.") - result = cmd_invocations[asg_state](aws_retry=True, **args) - instance_refreshes = conn.describe_instance_refreshes( - AutoScalingGroupName=asg_name, InstanceRefreshIds=[result["InstanceRefreshId"]] + instance_refresh_id = cmd_invocations[asg_state](conn, auto_scaling_group_name=asg_name, **args) + response = describe_instance_refreshes( + conn, auto_scaling_group_name=asg_name, instance_refresh_ids=[instance_refresh_id] ) - result = dict(instance_refreshes=camel_dict_to_snake_dict(instance_refreshes["InstanceRefreshes"][0])) - return module.exit_json(**result) - except (BotoCoreError, ClientError) as e: - module.fail_json_aws(e, msg=f"Failed to {asg_state.replace('ed', '')} InstanceRefresh") + result = dict(instance_refreshes=camel_dict_to_snake_dict(response["InstanceRefreshes"][0])) + module.exit_json(**result) + except AnsibleAutoScalingError as e: + module.fail_json_aws(e, msg=f"Failed to {asg_state.replace('ed', '')} InstanceRefresh: {e}") def main(): @@ -246,6 +279,8 @@ def main(): options=dict( min_healthy_percentage=dict(type="int", default=90), instance_warmup=dict(type="int"), + skip_matching=dict(type="bool"), + max_healthy_percentage=dict(type="int"), ), ), ) @@ -254,10 +289,7 @@ def main(): argument_spec=argument_spec, supports_check_mode=True, ) - autoscaling = module.client( - "autoscaling", - retry_decorator=AWSRetry.jittered_backoff(retries=10, catch_extra_error_codes=["InstanceRefreshInProgress"]), - ) + autoscaling = module.client("autoscaling") start_or_cancel_instance_refresh(autoscaling, module) diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 639940b1b77..1d3bf8fe90c 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -11,8 +11,8 @@ short_description: Gather information about EC2 Auto Scaling Group (ASG) Instance Refreshes in AWS description: - Describes one or more instance refreshes. - - You can determine the status of a request by looking at the I(status) parameter. - - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_instance_refresh_info). + - You can determine the status of a request by looking at the RV(instance_refreshes.status) return value. + - Prior to release 5.0.0 this module was called M(community.aws.ec2_asg_instance_refresh_info). The usage did not change. author: - "Dan Khersonsky (@danquixote)" @@ -34,7 +34,7 @@ type: str max_records: description: - - The maximum number of items to return with this call. The default value is 50 and the maximum value is 100. + - The maximum number of items to return with this call. The default value is V(50) and the maximum value is V(100). type: int required: false extends_documentation_fragment: @@ -70,131 +70,137 @@ """ RETURN = r""" ---- -instance_refresh_id: - description: instance refresh id - returned: success - type: str - sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" -auto_scaling_group_name: - description: Name of autoscaling group - returned: success - type: str - sample: "public-webapp-production-1" -status: - description: - - The current state of the group when DeleteAutoScalingGroup is in progress. - - The following are the possible statuses - - C(Pending) - The request was created, but the operation has not started. - - C(InProgress) - The operation is in progress. - - C(Successful) - The operation completed successfully. - - C(Failed) - The operation failed to complete. - You can troubleshoot using the status reason and the scaling activities. - - C(Cancelling) - An ongoing operation is being cancelled. - Cancellation does not roll back any replacements that have already been - completed, but it prevents new replacements from being started. - - C(Cancelled) - The operation is cancelled.' - returned: success - type: str - sample: "Pending" -start_time: - description: The date and time this ASG was created, in ISO 8601 format. - returned: success - type: str - sample: "2015-11-25T00:05:36.309Z" -end_time: - description: The date and time this ASG was created, in ISO 8601 format. - returned: success - type: str - sample: "2015-11-25T00:05:36.309Z" -percentage_complete: - description: the % of completeness - returned: success - type: int - sample: 100 -instances_to_update: - description: num. of instance to update - returned: success - type: int - sample: 5 +next_token: + description: A string that indicates that the response contains more items than can be returned in a single response. + returned: always + type: str +instance_refreshes: + description: A list of instance refreshes. + returned: always + type: complex + contains: + instance_refresh_id: + description: instance refresh id. + returned: success + type: str + sample: "08b91cf7-8fa6-48af-b6a6-d227f40f1b9b" + auto_scaling_group_name: + description: Name of autoscaling group. + returned: success + type: str + sample: "public-webapp-production-1" + status: + description: + - The current state of the group when DeleteAutoScalingGroup is in progress. + - The following are the possible statuses + - Pending - The request was created, but the operation has not started. + - InProgress - The operation is in progress. + - Successful - The operation completed successfully. + - Failed - The operation failed to complete. + You can troubleshoot using the status reason and the scaling activities. + - Cancelling - An ongoing operation is being cancelled. + Cancellation does not roll back any replacements that have already been + completed, but it prevents new replacements from being started. + - Cancelled - The operation is cancelled. + returned: success + type: str + sample: "Pending" + preferences: + description: The preferences for an instance refresh. + returned: always + type: dict + sample: { + 'AlarmSpecification': { + 'Alarms': [ + 'my-alarm', + ], + }, + 'AutoRollback': True, + 'InstanceWarmup': 200, + 'MinHealthyPercentage': 90, + 'ScaleInProtectedInstances': 'Ignore', + 'SkipMatching': False, + 'StandbyInstances': 'Ignore', + } + start_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" + end_time: + description: The date and time this ASG was created, in ISO 8601 format. + returned: success + type: str + sample: "2015-11-25T00:05:36.309Z" + percentage_complete: + description: the % of completeness + returned: success + type: int + sample: 100 + instances_to_update: + description: number of instances to update. + returned: success + type: int + sample: 5 """ -try: - from botocore.exceptions import BotoCoreError - from botocore.exceptions import ClientError -except ImportError: - pass # caught by AnsibleAWSModule +from typing import Any +from typing import Dict from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict -from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import AnsibleAutoScalingError +from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import describe_instance_refreshes from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule -def find_asg_instance_refreshes(conn, module): +def format_response(response: Dict[str, Any]) -> Dict[str, Any]: + result = {} + if "InstanceRefreshes" in response: + instance_refreshes_dict = { + "instance_refreshes": response["InstanceRefreshes"], + "next_token": response.get("NextToken", ""), + } + result = camel_dict_to_snake_dict(instance_refreshes_dict) + return result + + +def find_asg_instance_refreshes(client, module: AnsibleAWSModule) -> None: """ Args: - conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. + client (boto3.AutoScaling.Client): Valid Boto3 ASG client. module: AnsibleAWSModule object - - Returns: - { - "instance_refreshes": [ - { - 'auto_scaling_group_name': 'ansible-test-hermes-63642726-asg', - 'instance_refresh_id': '6507a3e5-4950-4503-8978-e9f2636efc09', - 'instances_to_update': 1, - 'percentage_complete': 0, - "preferences": { - "instance_warmup": 60, - "min_healthy_percentage": 90, - "skip_matching": false - }, - 'start_time': '2021-02-04T03:39:40+00:00', - 'status': 'Cancelled', - 'status_reason': 'Cancelled due to user request.', - } - ], - 'next_token': 'string' - } """ - asg_name = module.params.get("name") - asg_ids = module.params.get("ids") - asg_next_token = module.params.get("next_token") - asg_max_records = module.params.get("max_records") - - args = {} - args["AutoScalingGroupName"] = asg_name - if asg_ids: - args["InstanceRefreshIds"] = asg_ids - if asg_next_token: - args["NextToken"] = asg_next_token - if asg_max_records: - args["MaxRecords"] = asg_max_records - try: - instance_refreshes_result = {} - response = conn.describe_instance_refreshes(**args) - if "InstanceRefreshes" in response: - instance_refreshes_dict = dict( - instance_refreshes=response["InstanceRefreshes"], next_token=response.get("next_token", "") - ) - instance_refreshes_result = camel_dict_to_snake_dict(instance_refreshes_dict) - - while "NextToken" in response: - args["NextToken"] = response["NextToken"] - response = conn.describe_instance_refreshes(**args) - if "InstanceRefreshes" in response: - instance_refreshes_dict = camel_dict_to_snake_dict( - dict(instance_refreshes=response["InstanceRefreshes"], next_token=response.get("next_token", "")) + max_records = module.params.get("max_records") + response = describe_instance_refreshes( + client, + auto_scaling_group_name=module.params.get("name"), + instance_refresh_ids=module.params.get("ids"), + next_token=module.params.get("next_token"), + max_records=max_records, + ) + instance_refreshes_result = format_response(response) + + if max_records is None: + while "NextToken" in response: + response = describe_instance_refreshes( + client, + auto_scaling_group_name=module.params.get("name"), + instance_refresh_ids=module.params.get("ids"), + next_token=response["NextToken"], + max_records=max_records, ) - instance_refreshes_result.update(instance_refreshes_dict) + f_response = format_response(response) + if "instance_refreshes" in f_response: + instance_refreshes_result["instance_refreshes"].extend(f_response["instance_refreshes"]) + instance_refreshes_result["next_token"] = f_response["next_token"] - return module.exit_json(**instance_refreshes_result) - except (BotoCoreError, ClientError) as e: - module.fail_json_aws(e, msg="Failed to describe InstanceRefreshes") + module.exit_json(changed=False, **instance_refreshes_result) + except AnsibleAutoScalingError as e: + module.fail_json_aws(e, msg=f"Failed to describe InstanceRefreshes: {e}") def main(): @@ -210,7 +216,7 @@ def main(): supports_check_mode=True, ) - autoscaling = module.client("autoscaling", retry_decorator=AWSRetry.jittered_backoff(retries=10)) + autoscaling = module.client("autoscaling") find_asg_instance_refreshes(autoscaling, module) diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml new file mode 100644 index 00000000000..bf95b1668db --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml @@ -0,0 +1,99 @@ +--- +- name: Test getting info for an ASG name + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + register: output + +- name: Assert that the correct number of records are returned + assert: + that: + - output.instance_refreshes | map(attribute='instance_refresh_id') | unique | length == 7 + +- name: Test using fake refresh ID + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] + register: output + +- name: Assert that no record is returned + assert: + that: + - output.instance_refreshes | length == 0 + +- name: Test using a real refresh ID + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] + register: output + +- name: Assert that the correct record is returned + assert: + that: + - output.instance_refreshes | length == 1 + +- name: Test getting info for an ASG name which doesn't exist + autoscaling_instance_refresh_info: + name: n0n3x1stentname27b + ignore_errors: true + register: output + +- name: Assert that module failed to return record + assert: + that: + - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' in output.msg" + +- name: Retrieve instance refresh info + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + register: output + +- name: Assert that the correct number of records are returned + assert: + that: + - output.instance_refreshes | length == 7 + +- name: Retrieve instance refresh info using next_token + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "fake-token-123" + ignore_errors: true + register: output + +- name: Assert that valid message with fake-token is returned + assert: + that: + - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." in output.msg' + +- name: Retrieve instance refresh info using max_records + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + register: output_with_token + +- name: Assert that max records=1 returns no more than one record + assert: + that: + - output_with_token.instance_refreshes | length == 1 + +- name: Retrieve instance refresh using valid token + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + next_token: "{{ output_with_token.next_token }}" + register: output + +- name: Assert that valid message with real-token is returned + assert: + that: + - output.instance_refreshes | length == 6 + +- name: Test using both real nextToken and max_records=1 + autoscaling_instance_refresh_info: + name: "{{ asg_name }}" + max_records: 1 + next_token: "{{ output_with_token.next_token }}" + register: output + +- name: Assert that only one instance refresh is returned + assert: + that: + - output.instance_refreshes | length == 1 diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml index 5b754d47d69..cdfa2e00106 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -35,7 +35,7 @@ Name: "{{ subnet_name }}" register: testing_subnet - - name: create routing rules + - name: Create routing rules ec2_vpc_route_table: vpc_id: "{{ testing_vpc.vpc.id }}" tags: @@ -46,7 +46,7 @@ subnets: - "{{ testing_subnet.subnet.id }}" - - name: create a security group with the vpc created in the ec2_setup + - name: Create a security group with the vpc created in the ec2_setup ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests @@ -62,7 +62,7 @@ cidr_ip: 0.0.0.0/0 register: sg - - name: ensure launch configs exist + - name: Ensure launch configs exist autoscaling_launch_config: name: "{{ item }}" assign_public_ip: true @@ -80,7 +80,7 @@ - "{{ lc_name_1 }}" - "{{ lc_name_2 }}" - - name: launch asg and do not wait for instances to be deemed healthy (no ELB) + - name: Launch asg and do not wait for instances to be deemed healthy (no ELB) autoscaling_group: name: "{{ asg_name }}" launch_config_name: "{{ lc_name_1 }}" @@ -92,335 +92,44 @@ state: present register: output - - assert: + - name: Assert that there is no viable instance + assert: that: - "output.viable_instances == 0" # ============================================================ - - - name: test invalid cancelation - V1 - (pre-refresh) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - ignore_errors: yes - register: result - - - assert: - that: - - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group ' ~ resource_prefix ~ '-asg' in result.msg" - - - name: test starting a refresh with a valid ASG name - check_mode - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - check_mode: true - register: output - - - assert: - that: - - output is not failed - - output is changed - - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' - - - name: test starting a refresh with a valid ASG name - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - register: output - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: test starting a refresh with a valid ASG name - Idempotent - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - ignore_errors: true - register: output - - - assert: - that: - - output is not changed - - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' - - - name: test starting a refresh with a valid ASG name - Idempotent (check_mode) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - ignore_errors: true - check_mode: true - register: output - - - assert: - that: - - output is not changed - - output is not failed - - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' - - - name: test starting a refresh with a nonexistent ASG name - autoscaling_instance_refresh: - name: "nonexistentname-asg" - state: "started" - ignore_errors: yes - register: result - - - assert: - that: - - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" - - - name: test canceling a refresh with an ASG name - check_mode - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - check_mode: true - register: output - - - assert: - that: - - output is not failed - - output is changed - - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' - - - name: test canceling a refresh with an ASG name - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - register: output - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: test canceling a refresh with a ASG name - Idempotent - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - ignore_errors: yes - register: output - - - assert: - that: - - output is not changed - - - name: test cancelling a refresh with a valid ASG name - Idempotent (check_mode) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - ignore_errors: true - check_mode: true - register: output - - - assert: - that: - - output is not changed - - output is not failed - - - name: test starting a refresh with an ASG name and preferences dict - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - preferences: - min_healthy_percentage: 10 - instance_warmup: 10 - retries: 5 - register: output - until: output is not failed - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: re-test canceling a refresh with an ASG name - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - register: output - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: test valid start - V1 - (with preferences missing instance_warmup) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - preferences: - min_healthy_percentage: 10 - ignore_errors: yes - retries: 5 - register: output - until: output is not failed - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: re-test canceling a refresh with an ASG name - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - register: output - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: test valid start - V2 - (with preferences missing min_healthy_percentage) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "started" - preferences: - instance_warmup: 10 - retries: 5 - register: output - until: output is not failed - ignore_errors: yes - - - assert: - that: - - "'instance_refresh_id' in output.instance_refreshes" - - - name: test invalid cancelation - V2 - (with preferences) - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - preferences: - min_healthy_percentage: 10 - instance_warmup: 10 - ignore_errors: yes - register: result - - - assert: - that: - - "'can not pass preferences dict when canceling a refresh' in result.msg" - + - name: Run test with start_cancel_instance_refresh.yml + include_tasks: start_cancel_instance_refresh.yml + # ============================================================ - - name: run setup with refresh_and_cancel_three_times.yml + - name: Run test with refresh_and_cancel_three_times.yml include_tasks: refresh_and_cancel_three_times.yml loop: "{{ query('sequence', 'start=1 end=3') }}" - - name: test getting info for an ASG name - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - region: "{{ aws_region }}" - ignore_errors: yes - register: output - - - assert: - that: - - output | community.general.json_query(inst_refresh_id_json_query) | unique | length == 7 - vars: - inst_refresh_id_json_query: instance_refreshes[].instance_refresh_id - - - name: test using fake refresh ID - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length == 0 - - - name: test using a real refresh ID - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length == 1 - - - name: test getting info for an ASG name which doesn't exist - autoscaling_instance_refresh_info: - name: n0n3x1stentname27b - ignore_errors: yes - register: output - - - assert: - that: - - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' == output.msg" - - - name: assert that the correct number of records are returned - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length == 7 - - - name: assert that valid message with fake-token is returned - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - next_token: "fake-token-123" - ignore_errors: yes - register: output - - - assert: - that: - - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." == output.msg' - - - name: assert that max records=1 returns no more than one record - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - max_records: 1 - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length < 2 - - - name: assert that valid message with real-token is returned - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - next_token: "{{ output.next_token }}" - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length == 7 - - - name: test using both real nextToken and max_records=1 - autoscaling_instance_refresh_info: - name: "{{ asg_name }}" - max_records: 1 - next_token: "{{ output.next_token }}" - ignore_errors: yes - register: output - - - assert: - that: - - output.instance_refreshes | length == 1 + - name: Run test with instance_refresh_info.yml + include_tasks: instance_refresh_info.yml always: - - name: kill asg + - name: Kill asg autoscaling_group: name: "{{ asg_name }}" state: absent register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 # Remove the testing dependencies - - name: remove the load balancer + - name: Remove the load balancer elb_classic_lb: name: "{{ load_balancer_name }}" state: absent security_group_ids: - "{{ sg.group_id }}" subnets: "{{ testing_subnet.subnet.id }}" - wait: yes + wait: true connection_draining_timeout: 60 listeners: - protocol: http @@ -436,22 +145,22 @@ healthy_threshold: 2 register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 - - name: remove launch configs + - name: Remove launch configs autoscaling_launch_config: name: "{{ item }}" state: absent register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 loop: - "{{ lc_name_1 }}" - "{{ lc_name_2 }}" - - name: delete launch template + - name: Delete launch template ec2_launch_template: name: "{{ resource_prefix }}-lt" state: absent @@ -460,7 +169,7 @@ until: del_lt is not failed ignore_errors: true - - name: remove the security group + - name: Remove the security group ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests @@ -468,10 +177,10 @@ state: absent register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 - - name: remove routing rules + - name: Remove routing rules ec2_vpc_route_table: state: absent vpc_id: "{{ testing_vpc.vpc.id }}" @@ -484,34 +193,34 @@ - "{{ testing_subnet.subnet.id }}" register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 - - name: remove internet gateway + - name: Remove internet gateway ec2_vpc_igw: vpc_id: "{{ testing_vpc.vpc.id }}" state: absent register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 - - name: remove the subnet + - name: Remove the subnet ec2_vpc_subnet: state: absent vpc_id: "{{ testing_vpc.vpc.id }}" cidr: '{{ subnet_a_cidr }}' register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 - - name: remove the VPC + - name: Remove the VPC ec2_vpc_net: name: "{{ vpc_name }}" cidr_block: '{{ subnet_a_cidr }}' state: absent register: removed until: removed is not failed - ignore_errors: yes + ignore_errors: true retries: 10 diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml index 9b051a054e0..e2cebe924a9 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -1,29 +1,14 @@ --- - -- name: try to cancel pre-loop - autoscaling_instance_refresh: - name: "{{ asg_name }}" - state: "cancelled" - ignore_errors: yes - -- name: test starting a refresh with an ASG name +- name: Test starting a refresh with an ASG name autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" - region: "{{ aws_region }}" - ignore_errors: no retries: 10 delay: 5 register: refreshout until: refreshout is not failed -- name: test cancelling a refresh with an ASG name +- name: Test cancelling a refresh with an ASG name autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" - access_key: "{{ aws_access_key }}" - secret_key: "{{ aws_secret_key }}" - region: "{{ aws_region }}" - ignore_errors: yes diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml new file mode 100644 index 00000000000..a15e71cc3d3 --- /dev/null +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml @@ -0,0 +1,206 @@ +--- +- name: test invalid cancelation - V1 - (pre-refresh) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + ignore_errors: true + register: result + +- name: Assert that module failed with proper message + assert: + that: + - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group ' ~ resource_prefix ~ '-asg' in result.msg" + +- name: Test starting a refresh with a valid ASG name - check_mode + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + check_mode: true + register: output + +- name: Validate starting in check_mode + assert: + that: + - output is changed + - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' + +- name: Test starting a refresh with a valid ASG name + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + register: output + +- name: Validate start + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Test starting a refresh with a valid ASG name - Idempotent + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + ignore_errors: true + register: output + +- name: Validate starting Idempotency + assert: + that: + - output is not changed + - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' + +- name: Test starting a refresh with a valid ASG name - Idempotent (check_mode) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + check_mode: true + register: output + +- name: Validate starting Idempotency in check_mode + assert: + that: + - output is not changed + - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' + +- name: Test starting a refresh with a nonexistent ASG name + autoscaling_instance_refresh: + name: "nonexistentname-asg" + state: "started" + ignore_errors: true + register: result + +- name: Assert that module failed with proper message + assert: + that: + - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" + +- name: Test canceling a refresh with an ASG name - check_mode + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + check_mode: true + register: output + +- name: Validate cancelation + assert: + that: + - output is not failed + - output is changed + - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' + +- name: Test canceling a refresh with an ASG name + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + +- name: Validate cancelation + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Test canceling a refresh with a ASG name - Idempotent + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + ignore_errors: true + +- name: Validate cancelling Idempotency + assert: + that: + - output is not changed + +- name: Test cancelling a refresh with a valid ASG name - Idempotent (check_mode) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + check_mode: true + register: output + +- name: Validate cancelling Idempotency in check_mode + assert: + that: + - output is not changed + +- name: Test starting a refresh with an ASG name and preferences dict + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + +- name: Assert that module succeed with preferences + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Re-test canceling a refresh with an ASG name + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + +- name: Assert that module returned instance refresh id + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Test valid start - V1 - (with preferences missing instance_warmup) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + min_healthy_percentage: 10 + retries: 5 + register: output + until: output is not failed + +- name: Validate start with preferences missing instance warmup + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Re-test canceling a refresh with an ASG name + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + register: output + +- name: Validate canceling Idempotency + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Test valid start - V2 - (with preferences missing min_healthy_percentage) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "started" + preferences: + instance_warmup: 10 + retries: 5 + register: output + until: output is not failed + +- name: Assert that module did not returned and instance refresh id + assert: + that: + - "'instance_refresh_id' in output.instance_refreshes" + +- name: Test invalid cancelation - V2 - (with preferences) + autoscaling_instance_refresh: + name: "{{ asg_name }}" + state: "cancelled" + preferences: + min_healthy_percentage: 10 + instance_warmup: 10 + ignore_errors: true + register: result + +- name: Assert that module failed with proper message + assert: + that: + - "'can not pass preferences dict when canceling a refresh' in result.msg" diff --git a/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py b/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py new file mode 100644 index 00000000000..590074367a2 --- /dev/null +++ b/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py @@ -0,0 +1,28 @@ +# (c) 2024 Red Hat Inc. +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +import pytest + +from ansible_collections.community.aws.plugins.modules.autoscaling_instance_refresh import validate_healthy_percentage + + +@pytest.mark.parametrize( + "min_healthy, max_healthy, expected_error", + [ + (90, None, None), + (-1, None, "The value range for the min_healthy_percentage is 0 to 100."), + (101, None, "The value range for the min_healthy_percentage is 0 to 100."), + (None, 90, "The value range for the max_healthy_percentage is 100 to 200."), + (None, 201, "The value range for the max_healthy_percentage is 100 to 200."), + (None, 100, "You must also specify min_healthy_percentage when max_healthy_percentage is specified."), + (10, 100, None), + ( + 10, + 150, + "The difference between the max_healthy_percentage and min_healthy_percentage cannot be greater than 100.", + ), + ], +) +def test_validate_healthy_percentage(min_healthy, max_healthy, expected_error): + preferences = dict(min_healthy_percentage=min_healthy, max_healthy_percentage=max_healthy) + assert expected_error == validate_healthy_percentage(preferences) From b2ac534e516c4f53c9039ce06034338c9bb95563 Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 15 Oct 2024 14:36:02 +0200 Subject: [PATCH 13/13] update meta/runtime.yml, modules and tests add changelog file --- ...esh_autoscaling_instance_refresh_info.yaml | 8 +++ meta/runtime.yml | 18 ++++-- .../modules/autoscaling_instance_refresh.py | 12 ++-- .../autoscaling_instance_refresh_info.py | 12 ++-- .../tasks/instance_refresh_info.yml | 36 +++++------ .../tasks/main.yml | 43 ++++++------- .../tasks/refresh_and_cancel_three_times.yml | 4 +- .../tasks/start_cancel_instance_refresh.yml | 64 +++++++++---------- .../test_autoscaling_instance_refresh.py | 2 +- 9 files changed, 106 insertions(+), 93 deletions(-) create mode 100644 changelogs/fragments/migrate_autoscaling_instance_refresh_autoscaling_instance_refresh_info.yaml diff --git a/changelogs/fragments/migrate_autoscaling_instance_refresh_autoscaling_instance_refresh_info.yaml b/changelogs/fragments/migrate_autoscaling_instance_refresh_autoscaling_instance_refresh_info.yaml new file mode 100644 index 00000000000..64604207795 --- /dev/null +++ b/changelogs/fragments/migrate_autoscaling_instance_refresh_autoscaling_instance_refresh_info.yaml @@ -0,0 +1,8 @@ +--- +major_changes: + - autoscaling_instance_refresh - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.autoscaling_instance_refresh`` (https://github.com/ansible-collections/amazon.aws/pull/2338). + - autoscaling_instance_refresh_info - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.autoscaling_instance_refresh_info`` (https://github.com/ansible-collections/amazon.aws/pull/2338). diff --git a/meta/runtime.yml b/meta/runtime.yml index b62a89aae8d..6fc1faddce3 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -4,6 +4,8 @@ action_groups: aws: - autoscaling_group - autoscaling_group_info + - autoscaling_instance_refresh + - autoscaling_instance_refresh_info - aws_az_info - aws_caller_info - aws_region_info @@ -146,6 +148,12 @@ plugin_routing: ec2_asg_info: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: amazon.aws.autoscaling_group_info + ec2_asg_instance_refresh: + # Deprecation for this alias should not *start* prior to 2024-09-01 + redirect: amazon.aws.autoscaling_instance_refresh + ec2_asg_instance_refresh_info: + # Deprecation for this alias should not *start* prior to 2024-09-01 + redirect: amazon.aws.autoscaling_instance_refresh_info ec2_elb_lb: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: amazon.aws.elb_classic_lb @@ -164,14 +172,14 @@ plugin_routing: rds_param_group: redirect: amazon.aws.rds_instance_param_group deprecation: - removal_version: 10.0.0 - warning_text: >- - rds_param_group has been renamed to rds_instance_param_group. - Please update your tasks. + removal_version: 10.0.0 + warning_text: >- + rds_param_group has been renamed to rds_instance_param_group. + Please update your tasks. lookup: aws_ssm: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: amazon.aws.ssm_parameter aws_secret: # Deprecation for this alias should not *start* prior to 2024-09-01 - redirect: amazon.aws.secretsmanager_secret + redirect: amazon.aws.secretsmanager_secret \ No newline at end of file diff --git a/plugins/modules/autoscaling_instance_refresh.py b/plugins/modules/autoscaling_instance_refresh.py index b337b5b1f52..e1685b96219 100644 --- a/plugins/modules/autoscaling_instance_refresh.py +++ b/plugins/modules/autoscaling_instance_refresh.py @@ -8,10 +8,11 @@ --- module: autoscaling_instance_refresh version_added: 3.2.0 +version_added_collection: community.aws short_description: Start or cancel an EC2 Auto Scaling Group (ASG) instance refresh in AWS description: - Start or cancel an EC2 Auto Scaling Group instance refresh in AWS. - - Can be used with M(community.aws.autoscaling_instance_refresh_info) to track the subsequent progress. + - Can be used with M(amazon.aws.autoscaling_instance_refresh_info) to track the subsequent progress. - Prior to release 5.0.0 this module was called M(community.aws.ec2_asg_instance_refresh). The usage did not change. author: @@ -84,17 +85,17 @@ # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Start a refresh - community.aws.autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: some-asg state: started - name: Cancel a refresh - community.aws.autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: some-asg state: cancelled - name: Start a refresh and pass preferences - community.aws.autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: some-asg state: started preferences: @@ -185,10 +186,9 @@ from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import cancel_instance_refresh from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import describe_instance_refreshes from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import start_instance_refresh +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.transformation import scrub_none_parameters -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule - def validate_healthy_percentage(preferences: Dict[str, Union[bool, int]]) -> Optional[str]: min_healthy_percentage = preferences.get("min_healthy_percentage") diff --git a/plugins/modules/autoscaling_instance_refresh_info.py b/plugins/modules/autoscaling_instance_refresh_info.py index 1d3bf8fe90c..06a96d151ea 100644 --- a/plugins/modules/autoscaling_instance_refresh_info.py +++ b/plugins/modules/autoscaling_instance_refresh_info.py @@ -8,6 +8,7 @@ --- module: autoscaling_instance_refresh_info version_added: 3.2.0 +version_added_collection: community.aws short_description: Gather information about EC2 Auto Scaling Group (ASG) Instance Refreshes in AWS description: - Describes one or more instance refreshes. @@ -47,23 +48,23 @@ # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Find an refresh by ASG name - community.aws.autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: somename-asg - name: Find an refresh by ASG name and one or more refresh-IDs - community.aws.autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: somename-asg ids: ['some-id-123'] register: asgs - name: Find an refresh by ASG name and set max_records - community.aws.autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: somename-asg max_records: 4 register: asgs - name: Find an refresh by ASG name and NextToken, if received from a previous call - community.aws.autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: somename-asg next_token: 'some-token-123' register: asgs @@ -151,8 +152,7 @@ from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import AnsibleAutoScalingError from ansible_collections.amazon.aws.plugins.module_utils.autoscaling import describe_instance_refreshes - -from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.modules import AnsibleAWSModule def format_response(response: Dict[str, Any]) -> Dict[str, Any]: diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml index bf95b1668db..b3590867899 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/instance_refresh_info.yml @@ -1,99 +1,99 @@ --- - name: Test getting info for an ASG name - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" register: output - name: Assert that the correct number of records are returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | map(attribute='instance_refresh_id') | unique | length == 7 - name: Test using fake refresh ID - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" ids: ['0e367f58-blabla-bla-bla-ca870dc5dbfe'] register: output - name: Assert that no record is returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | length == 0 - name: Test using a real refresh ID - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" ids: [ '{{ refreshout.instance_refreshes.instance_refresh_id }}' ] register: output - name: Assert that the correct record is returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | length == 1 - name: Test getting info for an ASG name which doesn't exist - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: n0n3x1stentname27b ignore_errors: true register: output - name: Assert that module failed to return record - assert: + ansible.builtin.assert: that: - "'Failed to describe InstanceRefreshes: An error occurred (ValidationError) when calling the DescribeInstanceRefreshes operation: AutoScalingGroup name not found - AutoScalingGroup n0n3x1stentname27b not found' in output.msg" - name: Retrieve instance refresh info - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" register: output - name: Assert that the correct number of records are returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | length == 7 - name: Retrieve instance refresh info using next_token - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" next_token: "fake-token-123" ignore_errors: true register: output - name: Assert that valid message with fake-token is returned - assert: + ansible.builtin.assert: that: - '"Failed to describe InstanceRefreshes: An error occurred (InvalidNextToken) when calling the DescribeInstanceRefreshes operation: The token ''********'' is invalid." in output.msg' - name: Retrieve instance refresh info using max_records - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" max_records: 1 register: output_with_token - name: Assert that max records=1 returns no more than one record - assert: + ansible.builtin.assert: that: - output_with_token.instance_refreshes | length == 1 - name: Retrieve instance refresh using valid token - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" next_token: "{{ output_with_token.next_token }}" register: output - name: Assert that valid message with real-token is returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | length == 6 - name: Test using both real nextToken and max_records=1 - autoscaling_instance_refresh_info: + amazon.aws.autoscaling_instance_refresh_info: name: "{{ asg_name }}" max_records: 1 next_token: "{{ output_with_token.next_token }}" register: output - name: Assert that only one instance refresh is returned - assert: + ansible.builtin.assert: that: - output.instance_refreshes | length == 1 diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml index cdfa2e00106..46e343ec29d 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/main.yml @@ -7,26 +7,23 @@ session_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" - collections: - - amazon.aws - block: # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations - name: Create VPC for use in testing - ec2_vpc_net: + amazon.aws.ec2_vpc_net: name: "{{ vpc_name }}" cidr_block: '{{ subnet_a_cidr }}' tenancy: default register: testing_vpc - name: Create internet gateway for use in testing - ec2_vpc_igw: + amazon.aws.ec2_vpc_igw: vpc_id: "{{ testing_vpc.vpc.id }}" state: present register: igw - name: Create subnet for use in testing - ec2_vpc_subnet: + amazon.aws.ec2_vpc_subnet: state: present vpc_id: "{{ testing_vpc.vpc.id }}" cidr: '{{ subnet_a_cidr }}' @@ -36,7 +33,7 @@ register: testing_subnet - name: Create routing rules - ec2_vpc_route_table: + amazon.aws.ec2_vpc_route_table: vpc_id: "{{ testing_vpc.vpc.id }}" tags: created: "{{ route_name }}" @@ -47,7 +44,7 @@ - "{{ testing_subnet.subnet.id }}" - name: Create a security group with the vpc created in the ec2_setup - ec2_security_group: + amazon.aws.ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests vpc_id: "{{ testing_vpc.vpc.id }}" @@ -63,7 +60,7 @@ register: sg - name: Ensure launch configs exist - autoscaling_launch_config: + community.aws.autoscaling_launch_config: name: "{{ item }}" assign_public_ip: true image_id: "{{ ec2_ami_id }}" @@ -81,7 +78,7 @@ - "{{ lc_name_2 }}" - name: Launch asg and do not wait for instances to be deemed healthy (no ELB) - autoscaling_group: + amazon.aws.autoscaling_group: name: "{{ asg_name }}" launch_config_name: "{{ lc_name_1 }}" desired_capacity: 1 @@ -93,27 +90,27 @@ register: output - name: Assert that there is no viable instance - assert: + ansible.builtin.assert: that: - "output.viable_instances == 0" # ============================================================ - name: Run test with start_cancel_instance_refresh.yml - include_tasks: start_cancel_instance_refresh.yml + ansible.builtin.include_tasks: start_cancel_instance_refresh.yml # ============================================================ - name: Run test with refresh_and_cancel_three_times.yml - include_tasks: refresh_and_cancel_three_times.yml + ansible.builtin.include_tasks: refresh_and_cancel_three_times.yml loop: "{{ query('sequence', 'start=1 end=3') }}" - name: Run test with instance_refresh_info.yml - include_tasks: instance_refresh_info.yml + ansible.builtin.include_tasks: instance_refresh_info.yml always: - name: Kill asg - autoscaling_group: + amazon.aws.autoscaling_group: name: "{{ asg_name }}" state: absent register: removed @@ -123,7 +120,7 @@ # Remove the testing dependencies - name: Remove the load balancer - elb_classic_lb: + amazon.aws.elb_classic_lb: name: "{{ load_balancer_name }}" state: absent security_group_ids: @@ -149,7 +146,7 @@ retries: 10 - name: Remove launch configs - autoscaling_launch_config: + community.aws.autoscaling_launch_config: name: "{{ item }}" state: absent register: removed @@ -161,7 +158,7 @@ - "{{ lc_name_2 }}" - name: Delete launch template - ec2_launch_template: + community.aws.ec2_launch_template: name: "{{ resource_prefix }}-lt" state: absent register: del_lt @@ -170,7 +167,7 @@ ignore_errors: true - name: Remove the security group - ec2_security_group: + amazon.aws.ec2_security_group: name: "{{ sg_name }}" description: a security group for ansible tests vpc_id: "{{ testing_vpc.vpc.id }}" @@ -181,7 +178,7 @@ retries: 10 - name: Remove routing rules - ec2_vpc_route_table: + amazon.aws.ec2_vpc_route_table: state: absent vpc_id: "{{ testing_vpc.vpc.id }}" tags: @@ -197,7 +194,7 @@ retries: 10 - name: Remove internet gateway - ec2_vpc_igw: + amazon.aws.ec2_vpc_igw: vpc_id: "{{ testing_vpc.vpc.id }}" state: absent register: removed @@ -206,7 +203,7 @@ retries: 10 - name: Remove the subnet - ec2_vpc_subnet: + amazon.aws.ec2_vpc_subnet: state: absent vpc_id: "{{ testing_vpc.vpc.id }}" cidr: '{{ subnet_a_cidr }}' @@ -216,7 +213,7 @@ retries: 10 - name: Remove the VPC - ec2_vpc_net: + amazon.aws.ec2_vpc_net: name: "{{ vpc_name }}" cidr_block: '{{ subnet_a_cidr }}' state: absent diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml index e2cebe924a9..64f6e7752bb 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -1,6 +1,6 @@ --- - name: Test starting a refresh with an ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" retries: 10 @@ -9,6 +9,6 @@ until: refreshout is not failed - name: Test cancelling a refresh with an ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" diff --git a/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml b/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml index a15e71cc3d3..24d6b9d67ab 100644 --- a/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml +++ b/tests/integration/targets/autoscaling_instance_refresh/tasks/start_cancel_instance_refresh.yml @@ -1,129 +1,129 @@ --- - name: test invalid cancelation - V1 - (pre-refresh) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" ignore_errors: true register: result - name: Assert that module failed with proper message - assert: + ansible.builtin.assert: that: - "'An error occurred (ActiveInstanceRefreshNotFound) when calling the CancelInstanceRefresh operation: No in progress or pending Instance Refresh found for Auto Scaling group ' ~ resource_prefix ~ '-asg' in result.msg" - name: Test starting a refresh with a valid ASG name - check_mode - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" check_mode: true register: output - name: Validate starting in check_mode - assert: + ansible.builtin.assert: that: - output is changed - '"autoscaling:StartInstanceRefresh" not in output.resource_actions' - name: Test starting a refresh with a valid ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" register: output - name: Validate start - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Test starting a refresh with a valid ASG name - Idempotent - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" ignore_errors: true register: output - name: Validate starting Idempotency - assert: + ansible.builtin.assert: that: - output is not changed - '"Failed to start InstanceRefresh: An error occurred (InstanceRefreshInProgress) when calling the StartInstanceRefresh operation: An Instance Refresh is already in progress and blocks the execution of this Instance Refresh." in output.msg' - name: Test starting a refresh with a valid ASG name - Idempotent (check_mode) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" check_mode: true register: output - name: Validate starting Idempotency in check_mode - assert: + ansible.builtin.assert: that: - output is not changed - '"In check_mode - Instance Refresh is already in progress, can not start new instance refresh." in output.msg' - name: Test starting a refresh with a nonexistent ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "nonexistentname-asg" state: "started" ignore_errors: true register: result - name: Assert that module failed with proper message - assert: + ansible.builtin.assert: that: - "'Failed to start InstanceRefresh: An error occurred (ValidationError) when calling the StartInstanceRefresh operation: AutoScalingGroup name not found' in result.msg" - name: Test canceling a refresh with an ASG name - check_mode - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" check_mode: true register: output - name: Validate cancelation - assert: + ansible.builtin.assert: that: - output is not failed - output is changed - '"autoscaling:CancelInstanceRefresh" not in output.resource_actions' - name: Test canceling a refresh with an ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output - name: Validate cancelation - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Test canceling a refresh with a ASG name - Idempotent - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output ignore_errors: true - name: Validate cancelling Idempotency - assert: + ansible.builtin.assert: that: - output is not changed - name: Test cancelling a refresh with a valid ASG name - Idempotent (check_mode) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" check_mode: true register: output - name: Validate cancelling Idempotency in check_mode - assert: + ansible.builtin.assert: that: - output is not changed - name: Test starting a refresh with an ASG name and preferences dict - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -134,23 +134,23 @@ until: output is not failed - name: Assert that module succeed with preferences - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Re-test canceling a refresh with an ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output - name: Assert that module returned instance refresh id - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Test valid start - V1 - (with preferences missing instance_warmup) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -160,23 +160,23 @@ until: output is not failed - name: Validate start with preferences missing instance warmup - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Re-test canceling a refresh with an ASG name - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" register: output - name: Validate canceling Idempotency - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Test valid start - V2 - (with preferences missing min_healthy_percentage) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "started" preferences: @@ -186,12 +186,12 @@ until: output is not failed - name: Assert that module did not returned and instance refresh id - assert: + ansible.builtin.assert: that: - "'instance_refresh_id' in output.instance_refreshes" - name: Test invalid cancelation - V2 - (with preferences) - autoscaling_instance_refresh: + amazon.aws.autoscaling_instance_refresh: name: "{{ asg_name }}" state: "cancelled" preferences: @@ -201,6 +201,6 @@ register: result - name: Assert that module failed with proper message - assert: + ansible.builtin.assert: that: - "'can not pass preferences dict when canceling a refresh' in result.msg" diff --git a/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py b/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py index 590074367a2..aa371166077 100644 --- a/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py +++ b/tests/unit/plugins/modules/test_autoscaling_instance_refresh.py @@ -3,7 +3,7 @@ import pytest -from ansible_collections.community.aws.plugins.modules.autoscaling_instance_refresh import validate_healthy_percentage +from ansible_collections.amazon.aws.plugins.modules.autoscaling_instance_refresh import validate_healthy_percentage @pytest.mark.parametrize(