diff --git a/meta/runtime.yml b/meta/runtime.yml index ab5bb4e0bb8..5c77e361f8d 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -65,6 +65,8 @@ action_groups: - ec2_ami_copy - ec2_asg - ec2_asg_info + - ec2_asg_instance_refresh + - ec2_asg_instance_refreshes_info - ec2_asg_scheduled_action - ec2_asg_lifecycle_hook - ec2_customer_gateway diff --git a/plugins/modules/ec2_asg_instance_refresh.py b/plugins/modules/ec2_asg_instance_refresh.py new file mode 100644 index 00000000000..e984b408d4a --- /dev/null +++ b/plugins/modules/ec2_asg_instance_refresh.py @@ -0,0 +1,197 @@ +#!/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: ec2_asg_instance_refresh +version_added: 1.0.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 ec2_asg_instance_refreshes_info to track the subsequent progress +requirements: [ boto3 ] +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 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 InstanceWarmup, the default is to use the value specified for the health check grace period for the Auto Scaling group. + required: false + suboptions: + min_healthy_percentage: + description: + - The amount of capacity in the Auto Scaling group that must remain healthy during an instance refresh to allow the operation to continue, + - as a percentage of the desired capacity of the Auto Scaling group (rounded up to the nearest integer). + - The default is 90. + type: int + 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.ec2_asg_instance_refresh: + name: some-asg + state: started + +- name: Cancel a refresh + community.aws.ec2_asg_instance_refresh: + name: some-asg + state: cancelled + +- name: Start a refresh and pass preferences + community.aws.ec2_asg_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" +''' + +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.core import scrub_none_parameters +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +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. + name (str): Mandatory name of the ASG you are looking for. + state (str): Start or Cancel a refresh + + Returns: + Dict + { + 'instance_refresh_id': 'string' + } + """ + + 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'] = 'Rolling' + 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: + result = cmd_invocations[asg_state](aws_retry=True, **args) + result = dict( + instance_refresh_id=result['InstanceRefreshId'] + ) + 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'), + instance_warmup=dict(type='int'), + ) + ), + ) + + module = AnsibleAWSModule(argument_spec=argument_spec) + autoscaling = module.client( + 'autoscaling', + retry_decorator=AWSRetry.jittered_backoff( + retries=10, + catch_extra_error_codes=['InstanceRefreshInProgress'] + ) + ) + results = start_or_cancel_instance_refresh( + autoscaling, + module, + ) + return results + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/ec2_asg_instance_refreshes_info.py b/plugins/modules/ec2_asg_instance_refreshes_info.py new file mode 100644 index 00000000000..b143a53af95 --- /dev/null +++ b/plugins/modules/ec2_asg_instance_refreshes_info.py @@ -0,0 +1,219 @@ +#!/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: ec2_asg_instance_refreshes_info +version_added: 1.0.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. +requirements: [ boto3 ] +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.ec2_asg_instance_refreshes_info: + name: somename-asg + +- name: Find an refresh by ASG name and one or more refresh-IDs + community.aws.ec2_asg_instance_refreshes_info: + name: somename-asg + ids: ['some-id-123'] + register: asgs + +- name: Find an refresh by ASG name and set max_records + community.aws.ec2_asg_instance_refreshes_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.ec2_asg_instance_refreshes_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 + - 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 camel_dict_to_snake_dict +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + + +def find_asg_instance_refreshes(conn, module): + """ + Args: + conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. + name (str): Mandatory name of the ASG you are looking for. + ids (dict): Optional list of ASG Instace Refresh IDs + max_records (int): Optional number of max records to return + next_token (str): Optional NextToken from previous call + + Basic Usage: + >>> name = 'some-asg' + >>> conn = boto3.client('autoscaling', region_name='us-west-2') + >>> results = find_asg_instance_refreshes(name, conn) + + Returns: + Dict + { + 'instance_refreshes': [ + { + 'instance_refresh_id': '6507a3e5-4950-4503-8978-e9f2636efc09', + 'auto_scaling_group_name': 'ansible-test-hermes-63642726-asg', + 'status': 'Cancelled', + 'status_reason': 'Cancelled due to user request.', + 'start_time': '2021-02-04T03:39:40+00:00', + 'end_time': '2021-02-04T03:41:18+00:00', + 'percentage_complete': 0, + 'instances_to_update': 1 + } + ], + '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: + asgs = conn.describe_instance_refreshes(aws_retry=True, **args) + asgs = camel_dict_to_snake_dict(asgs) + result = dict( + instance_refreshes=asgs['instance_refreshes'], + next_token=asgs.get('next_token', ''), + ) + return module.exit_json(**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, + ) + + autoscaling = module.client( + 'autoscaling', + retry_decorator=AWSRetry.jittered_backoff(retries=10) + ) + results = find_asg_instance_refreshes( + autoscaling, + module, + ) + + return results + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/ec2_asg_instance_refresh/aliases b/tests/integration/targets/ec2_asg_instance_refresh/aliases new file mode 100644 index 00000000000..5160941f4f1 --- /dev/null +++ b/tests/integration/targets/ec2_asg_instance_refresh/aliases @@ -0,0 +1,2 @@ +cloud/aws +ec2_asg_instance_refreshes_info diff --git a/tests/integration/targets/ec2_asg_instance_refresh/defaults/main.yml b/tests/integration/targets/ec2_asg_instance_refresh/defaults/main.yml new file mode 100644 index 00000000000..fe574617c0d --- /dev/null +++ b/tests/integration/targets/ec2_asg_instance_refresh/defaults/main.yml @@ -0,0 +1,6 @@ +--- +# defaults file for ec2_asg +# Amazon Linux 2 AMI 2019.06.12 (HVM), GP2 Volume Type +vpc_seed: '{{ resource_prefix }}' +ec2_ami_name: 'amzn2-ami-hvm-2.*-x86_64-gp2' +subnet_a_cidr: '10.{{ 256 | random(seed=vpc_seed) }}.32.0/24' diff --git a/tests/integration/targets/ec2_asg_instance_refresh/tasks/main.yml b/tests/integration/targets/ec2_asg_instance_refresh/tasks/main.yml new file mode 100644 index 00000000000..bef31a9b9f8 --- /dev/null +++ b/tests/integration/targets/ec2_asg_instance_refresh/tasks/main.yml @@ -0,0 +1,471 @@ +--- +- 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: + + #NOTE: entire ASG setup is 'borrowed' from ec2_asg + - name: Find AMI to use + ec2_ami_info: + owners: 'amazon' + filters: + name: '{{ ec2_ami_name }}' + register: ec2_amis + - set_fact: + ec2_ami_image: '{{ ec2_amis.images[0].image_id }}' + + - name: load balancer name has to be less than 32 characters + set_fact: + load_balancer_name: "{{ item }}-lb" + loop: "{{ resource_prefix | regex_findall('.{8}$') }}" + + # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + 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: "{{ resource_prefix }}-subnet" + register: testing_subnet + + - name: create routing rules + ec2_vpc_route_table: + vpc_id: "{{ testing_vpc.vpc.id }}" + tags: + created: "{{ resource_prefix }}-route" + 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: "{{ resource_prefix }}-sg" + 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_image }}" + user_data: | + package_upgrade: true + package_update: true + packages: + - httpd + runcmd: + - "service httpd start" + security_groups: "{{ sg.group_id }}" + instance_type: t3.micro + loop: + - "{{ resource_prefix }}-lc" + - "{{ resource_prefix }}-lc-2" + + - name: launch asg and do not wait for instances to be deemed healthy (no ELB) + ec2_asg: + name: "{{ resource_prefix }}-asg" + launch_config_name: "{{ resource_prefix }}-lc" + 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: "{{ resource_prefix }}-asg" + 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" + ignore_errors: no + + - name: test starting a refresh with a valid ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "started" + register: output + + - assert: + that: + - "'instance_refresh_id' in output" + ignore_errors: no + + - 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" + ignore_errors: no + + - name: test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output" + ignore_errors: no + + - name: test starting a refresh with an ASG name and preferences dict + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + 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" + ignore_errors: no + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output" + ignore_errors: no + + - name: test valid start - V1 - (with preferences missing instance_warmup) + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + 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" + ignore_errors: no + + - name: re-test canceling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "cancelled" + register: output + + - assert: + that: + - "'instance_refresh_id' in output" + ignore_errors: no + + - name: test valid start - V2 - (with preferences missing min_healthy_percentage) + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + 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" + ignore_errors: no + + - name: test invalid cancelation - V2 - (with preferences) + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + 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" + ignore_errors: no + + # ============================================================ + # ============================================================ + + - 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + ids: [ '{{ refreshout.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_refreshes_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_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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_refreshes_info: + name: "{{ resource_prefix }}-asg" + next_token: "{{ output.next_token }}" + ignore_errors: yes + register: output + + - assert: + that: + - "{{ output.instance_refreshes|length }} == 6 " + + - name: test using both real nextToken and max_records=1 + ec2_asg_instance_refreshes_info: + name: "{{ resource_prefix }}-asg" + 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: "{{ resource_prefix }}-asg" + 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: "{{ resource_prefix }}-lc" + state: absent + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + loop: + - "{{ resource_prefix }}-lc" + - "{{ resource_prefix }}-lc-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: "{{ resource_prefix }}-sg" + 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: "{{ resource_prefix }}-route" + 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: "{{ resource_prefix }}-vpc" + 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/ec2_asg_instance_refresh/tasks/main.yml.mini b/tests/integration/targets/ec2_asg_instance_refresh/tasks/main.yml.mini new file mode 100644 index 00000000000..c3398dcc5f8 --- /dev/null +++ b/tests/integration/targets/ec2_asg_instance_refresh/tasks/main.yml.mini @@ -0,0 +1,37 @@ +- name: Run ec2_asg integration tests. + + 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: + + - name: test getting info for an ASG name + ec2_asg_instance_refreshes_info: + name: "{{ resource_prefix }}-asg" + ignore_errors: yes + register: output + ignore_errors: no + - debug: var=output + + - name: Find AMI to use + ec2_ami_info: + owners: 'amazon' + filters: + name: '{{ ec2_ami_name }}' + register: ec2_amis + - set_fact: + ec2_ami_image: '{{ ec2_amis.images[0].image_id }}' + + - name: load balancer name has to be less than 32 characters + # the 8 digit identifier at the end of resource_prefix helps determine during which test something + # was created + set_fact: + load_balancer_name: "{{ item }}-lb" + loop: "{{ resource_prefix | regex_findall('.{8}$') }}" diff --git a/tests/integration/targets/ec2_asg_instance_refresh/tasks/refresh_and_cancel_three_times.yml b/tests/integration/targets/ec2_asg_instance_refresh/tasks/refresh_and_cancel_three_times.yml new file mode 100644 index 00000000000..e4391657a32 --- /dev/null +++ b/tests/integration/targets/ec2_asg_instance_refresh/tasks/refresh_and_cancel_three_times.yml @@ -0,0 +1,30 @@ +--- + +- name: try to cancel pre-loop + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "cancelled" + ignore_errors: yes + +- name: test starting a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + 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 +- debug: var=refreshout + +- name: test cancelling a refresh with an ASG name + ec2_asg_instance_refresh: + name: "{{ resource_prefix }}-asg" + state: "cancelled" + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + region: "{{ aws_region }}" + ignore_errors: yes diff --git a/tests/integration/targets/ec2_asg_instance_refresh/vars/main.yml b/tests/integration/targets/ec2_asg_instance_refresh/vars/main.yml new file mode 100644 index 00000000000..e69de29bb2d