From 55962ff21023ffdc6ecc1f985503c63852b3b19b Mon Sep 17 00:00:00 2001 From: Mark Woolley Date: Fri, 4 Feb 2022 16:08:05 +0000 Subject: [PATCH] Add deregistration_connection_termination to elb_target_group (#913) Add deregistration_connection_termination to elb_target_group SUMMARY Adding support for the deregistration_connection_termination param in the elb_target_group module. Along with this I've enabled and fixed up the integration tests. ISSUE TYPE Feature Pull Request COMPONENT NAME elb_target_group ADDITIONAL INFORMATION The API param is deregistration_delay.connection_termination.enabled https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html#ElasticLoadBalancingv2.Client.describe_target_group_attributes Reviewed-by: Mark Woolley Reviewed-by: Markus Bergholz Reviewed-by: Alina Buzachis --- .../fragments/913-tg-dereg-conn-param.yml | 2 + plugins/modules/elb_target_group.py | 22 +- tests/integration/targets/elb_target/aliases | 4 +- .../targets/elb_target/defaults/main.yml | 5 +- .../targets/elb_target/meta/main.yml | 3 + .../targets/elb_target/tasks/ec2_target.yml | 205 ++++++------------ .../elb_target/tasks/lambda_target.yml | 47 ++-- .../targets/elb_target/tasks/main.yml | 5 +- 8 files changed, 128 insertions(+), 165 deletions(-) create mode 100644 changelogs/fragments/913-tg-dereg-conn-param.yml create mode 100644 tests/integration/targets/elb_target/meta/main.yml diff --git a/changelogs/fragments/913-tg-dereg-conn-param.yml b/changelogs/fragments/913-tg-dereg-conn-param.yml new file mode 100644 index 00000000000..d4526ebd703 --- /dev/null +++ b/changelogs/fragments/913-tg-dereg-conn-param.yml @@ -0,0 +1,2 @@ +minor_changes: + - elb_target_group - add support for parameter ``deregistration_connection_termination`` (https://github.com/ansible-collections/community.aws/pull/913). diff --git a/plugins/modules/elb_target_group.py b/plugins/modules/elb_target_group.py index 9a740422293..20e9c2b19da 100644 --- a/plugins/modules/elb_target_group.py +++ b/plugins/modules/elb_target_group.py @@ -22,6 +22,13 @@ - The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused. The range is 0-3600 seconds. type: int + deregistration_connection_termination: + description: + - Indicates whether the load balancer terminates connections at the end of the deregistration timeout. + type: bool + default: false + required: false + version_added: 3.1.0 health_check_protocol: description: - The protocol the load balancer uses when performing health checks on targets. @@ -305,6 +312,11 @@ returned: when state present type: int sample: 300 +deregistration_connection_termination: + description: Indicates whether the load balancer terminates connections at the end of the deregistration timeout. + returned: when state present + type: bool + sample: True health_check_interval_seconds: description: The approximate amount of time, in seconds, between health checks of an individual target. returned: when state present @@ -425,7 +437,7 @@ def get_tg_attributes(connection, module, tg_arn): except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get target group attributes") - # Replace '.' with '_' in attribute key names to make it more Ansibley + # Replace '.' with '_' in attribute key names to make it more Ansible friendly return dict((k.replace('.', '_'), v) for k, v in tg_attributes.items()) @@ -486,6 +498,7 @@ def create_or_update_target_group(connection, module): tags = module.params.get("tags") purge_tags = module.params.get("purge_tags") deregistration_delay_timeout = module.params.get("deregistration_delay_timeout") + deregistration_connection_termination = module.params.get("deregistration_connection_termination") stickiness_enabled = module.params.get("stickiness_enabled") stickiness_lb_cookie_duration = module.params.get("stickiness_lb_cookie_duration") stickiness_type = module.params.get("stickiness_type") @@ -767,6 +780,9 @@ def create_or_update_target_group(connection, module): if deregistration_delay_timeout is not None: if str(deregistration_delay_timeout) != current_tg_attributes['deregistration_delay_timeout_seconds']: update_attributes.append({'Key': 'deregistration_delay.timeout_seconds', 'Value': str(deregistration_delay_timeout)}) + if deregistration_connection_termination is not None: + if deregistration_connection_termination and current_tg_attributes.get('deregistration_delay_connection_termination_enabled') != "true": + update_attributes.append({'Key': 'deregistration_delay.connection_termination.enabled', 'Value': 'true'}) if stickiness_enabled is not None: if stickiness_enabled and current_tg_attributes['stickiness_enabled'] != "true": update_attributes.append({'Key': 'stickiness.enabled', 'Value': 'true'}) @@ -855,6 +871,7 @@ def main(): 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP'] argument_spec = dict( deregistration_delay_timeout=dict(type='int'), + deregistration_connection_termination=dict(type='bool', default=False), health_check_protocol=dict(choices=protocols_list), health_check_port=dict(), health_check_path=dict(), @@ -897,6 +914,9 @@ def main(): connection = module.client('elbv2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) if module.params.get('state') == 'present': + if module.params.get('protocol') in ['http', 'https', 'HTTP', 'HTTPS'] and module.params.get('deregistration_connection_termination', None): + module.fail_json(msg="A target group with HTTP/S protocol does not support setting deregistration_connection_termination") + create_or_update_target_group(connection, module) else: delete_target_group(connection, module) diff --git a/tests/integration/targets/elb_target/aliases b/tests/integration/targets/elb_target/aliases index 36af861d8b9..5d10f812415 100644 --- a/tests/integration/targets/elb_target/aliases +++ b/tests/integration/targets/elb_target/aliases @@ -1,6 +1,4 @@ cloud/aws -# currently broken -# e.g: https://3d7660cef77b937e1585-998cb574f2547d50f5110d6a2d4ac097.ssl.cf1.rackcdn.com/636/067f6f84c20701ccf4bf0654471613af598c6e89/check/ansible-test-cloud-integration-aws-py36_2/be6c4b3/job-output.txt -disabled + slow elb_target_group diff --git a/tests/integration/targets/elb_target/defaults/main.yml b/tests/integration/targets/elb_target/defaults/main.yml index 14068f1e5c0..88f68a0bf43 100644 --- a/tests/integration/targets/elb_target/defaults/main.yml +++ b/tests/integration/targets/elb_target/defaults/main.yml @@ -4,12 +4,13 @@ unique_id: "ansible-test-{{ tiny_prefix }}" lambda_role_name: '{{ unique_id }}-elb-target' lambda_name: '{{ unique_id }}-elb-target' -elb_target_group_name: "{{ unique_id }}-elb-tg" +elb_target_group_name: "{{ unique_id }}-elb" # Defaults used by the EC2 based test ec2_ami_name: 'amzn2-ami-hvm-2.0.20190612-x86_64-gp2' tg_name: "{{ unique_id }}-tg" -tg_tcpudp_name: "{{ unique_id }}-tgtcpudp" +tg_used_name: "{{ unique_id }}-tgu" +tg_tcpudp_name: "{{ unique_id }}-udp" lb_name: "{{ unique_id }}-lb" healthy_state: state: 'healthy' diff --git a/tests/integration/targets/elb_target/meta/main.yml b/tests/integration/targets/elb_target/meta/main.yml new file mode 100644 index 00000000000..e9ce9b3e3ed --- /dev/null +++ b/tests/integration/targets/elb_target/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2 \ No newline at end of file diff --git a/tests/integration/targets/elb_target/tasks/ec2_target.yml b/tests/integration/targets/elb_target/tasks/ec2_target.yml index f350672cafe..da85c705c63 100644 --- a/tests/integration/targets/elb_target/tasks/ec2_target.yml +++ b/tests/integration/targets/elb_target/tasks/ec2_target.yml @@ -1,7 +1,6 @@ --- - - name: set up ec2 based test prerequisites - block: - +- name: set up ec2 based test prerequisites + block: # ============================================================ - name: @@ -10,12 +9,12 @@ # ============================================================ - name: Find AMI to use ec2_ami_info: - owners: 'amazon' + owners: "amazon" filters: - name: '{{ ec2_ami_name }}' + name: "{{ ec2_ami_name }}" register: ec2_amis - set_fact: - ec2_ami_image: '{{ ec2_amis.images[0].image_id }}' + ec2_ami_image: "{{ ec2_amis.images[0].image_id }}" - name: set up testing VPC ec2_vpc_net: @@ -88,7 +87,7 @@ health_check_port: 80 protocol: http port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance tags: @@ -99,7 +98,7 @@ name: "{{ tg_tcpudp_name }}" protocol: udp port: 53 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance tags: @@ -108,15 +107,27 @@ - name: set up testing target group for ALB (type=instance) elb_target_group: - name: "{{ tg_name }}-used" + name: "{{ tg_used_name }}" health_check_port: 80 protocol: http port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance tags: Description: "Created by {{ resource_prefix }}" + register: result + + - name: set up testing target group for ALB (type=instance) + assert: + that: + - result.changed + - result.target_group_name == tg_used_name + - result.target_type == 'instance' + - result.vpc_id == vpc.vpc.id + - result.port == 80 + - '"health_check_port" in result' + - '"tags" in result' - name: set up testing target group for NLB (type=instance) elb_target_group: @@ -124,9 +135,11 @@ health_check_port: 80 protocol: tcp port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance + deregistration_delay_timeout: 60 + deregistration_connection_termination: yes tags: Description: "Created by {{ resource_prefix }}" register: result @@ -143,7 +156,9 @@ - '"target_group_arn" in result' - result.target_group_name == "{{ tg_name }}-nlb" - result.target_type == 'instance' - - result.vpc_id == '{{ vpc.vpc.id }}' + - result.deregistration_delay_timeout_seconds == '60' + - result.deregistration_delay_connection_termination_enabled + - result.vpc_id == vpc.vpc.id - name: set up ec2 instance to use as a target ec2_instance: @@ -157,6 +172,8 @@ volumes: [] wait: true ebs_optimized: false + tags: + Name: "{{ resource_prefix }}-inst" user_data: | #cloud-config package_upgrade: true @@ -184,7 +201,7 @@ Port: 80 DefaultActions: - Type: forward - TargetGroupName: "{{ tg_name }}-used" + TargetGroupName: "{{ tg_used_name }}" state: present - name: create a network load balancer @@ -194,9 +211,9 @@ - "{{ subnet_1.subnet.id }}" - "{{ subnet_2.subnet.id }}" listeners: - - Protocol: TCP - Port: 80 - DefaultActions: + - Protocol: TCP + Port: 80 + DefaultActions: - Type: forward TargetGroupName: "{{ tg_name }}-nlb" state: present @@ -218,7 +235,7 @@ health_check_port: 80 protocol: tcp port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance modify_targets: true @@ -240,7 +257,7 @@ health_check_port: 80 protocol: tcp port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance modify_targets: true @@ -262,7 +279,7 @@ health_check_port: 80 protocol: tcp port: 80 - vpc_id: '{{ vpc.vpc.id }}' + vpc_id: "{{ vpc.vpc.id }}" state: present target_type: instance modify_targets: true @@ -334,7 +351,7 @@ - name: register an instance to used target group and wait until healthy elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: present target_status: healthy @@ -353,7 +370,7 @@ - name: remove a target from used target group elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: absent target_status: unused @@ -369,7 +386,7 @@ - name: test idempotence elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: absent register: result @@ -383,7 +400,7 @@ - name: register an instance to used target group and wait until healthy again to test deregistering differently elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: present target_status: healthy @@ -400,7 +417,7 @@ - name: start deregisteration but don't wait elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: absent register: result @@ -413,7 +430,7 @@ - name: now wait for target to finish deregistering elb_target: - target_group_name: "{{ tg_name }}-used" + target_group_name: "{{ tg_used_name }}" target_id: "{{ instance_id }}" state: absent target_status: unused @@ -426,98 +443,29 @@ - not result.changed - not result.target_health_descriptions - # ============================================================ - - always: + # ============================================================ + always: - name: debug: msg="********** Tearing down elb_target test dependencies **********" - name: remove ec2 instance ec2_instance: - name: "{{ resource_prefix }}-inst" + instance_ids: + - "{{ instance_id }}" state: absent wait: True - ignore_errors: true - - - name: remove testing target groups - elb_target_group: - name: "{{ item }}" - health_check_port: 80 - protocol: http - port: 80 - vpc_id: '{{ vpc.vpc.id }}' - state: absent - target_type: instance - tags: - Description: "Created by {{ resource_prefix }}" - wait: true - wait_timeout: 400 - register: removed - retries: 10 - until: removed is not failed - with_items: - - "{{ tg_name }}" - - "{{ tg_name }}-used" - ignore_errors: true - - - name: remove udp testing target groups - elb_target_group: - name: "{{ item }}" - protocol: udp - port: 53 - vpc_id: '{{ vpc.vpc.id }}' - state: absent - target_type: instance - tags: - Description: "Created by {{ resource_prefix }}" - Protocol: "UDP" - wait: true - wait_timeout: 400 - register: removed - retries: 10 - until: removed is not failed - with_items: - - "{{ tg_tcpudp_name }}" - ignore_errors: true - - - name: remove tcp testing target groups - elb_target_group: - name: "{{ item }}" - protocol: tcp - port: 80 - vpc_id: '{{ vpc.vpc.id }}' - state: absent - target_type: instance - tags: - Description: "Created by {{ resource_prefix }}" - Protocol: "UDP" - wait: true - wait_timeout: 400 register: removed retries: 10 until: removed is not failed - with_items: - - "{{ tg_name }}-nlb" ignore_errors: true - name: remove application load balancer elb_application_lb: name: "{{ lb_name }}" - security_groups: - - "{{ sg.group_id }}" - subnets: - - "{{ subnet_1.subnet.id }}" - - "{{ subnet_2.subnet.id }}" - listeners: - - Protocol: HTTP - Port: 80 - DefaultActions: - - Type: forward - TargetGroupName: "{{ tg_name }}-used" state: absent wait: true - wait_timeout: 400 + wait_timeout: 600 register: removed retries: 10 until: removed is not failed @@ -526,41 +474,28 @@ - name: remove network load balancer elb_network_lb: name: "{{ lb_name }}-nlb" - subnets: - - "{{ subnet_1.subnet.id }}" - - "{{ subnet_2.subnet.id }}" - listeners: - - Protocol: TCP - Port: 80 - DefaultActions: - - Type: forward - TargetGroupName: "{{ tg_name }}-nlb" state: absent wait: true - wait_timeout: 400 + wait_timeout: 600 register: removed retries: 10 until: removed is not failed ignore_errors: true - - name: remove testing security group - ec2_group: + - name: remove testing target groups + elb_target_group: + name: "{{ item }}" state: absent - name: "{{ resource_prefix }}-sg" - description: a security group for ansible tests - vpc_id: "{{ vpc.vpc.id }}" - rules: - - proto: tcp - from_port: 80 - to_port: 80 - cidr_ip: 0.0.0.0/0 - - proto: tcp - from_port: 22 - to_port: 22 - cidr_ip: 0.0.0.0/0 + wait: true + wait_timeout: 600 register: removed retries: 10 until: removed is not failed + with_items: + - "{{ tg_name }}" + - "{{ tg_used_name }}" + - "{{ tg_tcpudp_name }}" + - "{{ tg_name }}-nlb" ignore_errors: true - name: remove routing rules @@ -579,10 +514,8 @@ vpc_id: "{{ vpc.vpc.id }}" cidr: 20.0.0.0/18 az: "{{ aws_region }}a" - resource_tags: - Name: "{{ resource_prefix }}-subnet" register: removed - retries: 10 + retries: 15 until: removed is not failed ignore_errors: true @@ -592,10 +525,17 @@ vpc_id: "{{ vpc.vpc.id }}" cidr: 20.0.64.0/18 az: "{{ aws_region }}b" - resource_tags: - Name: "{{ resource_prefix }}-subnet" register: removed - retries: 10 + retries: 15 + until: removed is not failed + ignore_errors: true + + - name: remove testing security group + ec2_group: + state: absent + name: "{{ resource_prefix }}-sg" + register: removed + retries: 15 until: removed is not failed ignore_errors: true @@ -611,14 +551,11 @@ - name: remove testing VPC ec2_vpc_net: name: "{{ resource_prefix }}-vpc" - state: absent cidr_block: 20.0.0.0/16 - tags: - Name: "{{ resource_prefix }}-vpc" - Description: "Created by ansible-test" + state: absent register: removed retries: 10 until: removed is not failed ignore_errors: true - # ============================================================ + # ============================================================ diff --git a/tests/integration/targets/elb_target/tasks/lambda_target.yml b/tests/integration/targets/elb_target/tasks/lambda_target.yml index 8b7955ddbe3..f43c490bf5b 100644 --- a/tests/integration/targets/elb_target/tasks/lambda_target.yml +++ b/tests/integration/targets/elb_target/tasks/lambda_target.yml @@ -1,29 +1,30 @@ - name: set up lambda as elb_target block: - - name: create zip to deploy lambda code archive: - path: '{{ role_path }}/files/ansible_lambda_target.py' - dest: /tmp/lambda.zip format: zip + path: "{{ role_path }}/files/ansible_lambda_target.py" + dest: "/tmp/lambda.zip" + - name: create or update service-role for lambda iam_role: - name: '{{ lambda_role_name }}' + name: "{{ lambda_role_name }}" assume_role_policy_document: '{{ lookup("file", role_path + "/files/assume-role.json") }}' managed_policy: - - 'arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess' + - "arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess" register: ROLE_ARN + - name: when it is too fast, the role is not usable. pause: seconds: 10 - name: deploy lambda.zip to ansible_lambda_target function lambda: - name: '{{ lambda_name }}' + name: "{{ lambda_name }}" state: present zip_file: /tmp/lambda.zip runtime: python3.7 - role: '{{ ROLE_ARN.arn }}' + role: "{{ ROLE_ARN.arn }}" handler: ansible_lambda_target.lambda_handler timeout: 30 register: lambda_function @@ -33,7 +34,7 @@ - name: create empty target group elb_target_group: - name: '{{ elb_target_group_name }}' + name: "{{ elb_target_group_name }}" target_type: lambda state: present modify_targets: false @@ -42,49 +43,49 @@ - name: tg is created, state must be changed assert: that: - - elb_target_group.changed + - elb_target_group.changed - name: allow elb to invoke the lambda function lambda_policy: state: present - function_name: '{{ lambda_name }}' - version: '{{ lambda_function.configuration.version }}' + function_name: "{{ lambda_name }}" + version: "{{ lambda_function.configuration.version }}" statement_id: elb1 action: lambda:InvokeFunction principal: elasticloadbalancing.amazonaws.com - source_arn: '{{ elb_target_group.target_group_arn }}' + source_arn: "{{ elb_target_group.target_group_arn }}" - name: add lambda to elb target elb_target_group: - name: '{{ elb_target_group_name }}' + name: "{{ elb_target_group_name }}" target_type: lambda state: present targets: - - Id: '{{ lambda_function.configuration.function_arn }}' + - Id: "{{ lambda_function.configuration.function_arn }}" register: elb_target_group - name: target is updated, state must be changed assert: that: - - elb_target_group.changed + - elb_target_group.changed - name: re-add lambda to elb target (idempotency) elb_target_group: - name: '{{ elb_target_group_name }}' + name: "{{ elb_target_group_name }}" target_type: lambda state: present targets: - - Id: '{{ lambda_function.configuration.function_arn }}' + - Id: "{{ lambda_function.configuration.function_arn }}" register: elb_target_group - name: target is still the same, state must not be changed (idempotency) assert: that: - - not elb_target_group.changed + - not elb_target_group.changed - name: remove lambda target from target group elb_target_group: - name: '{{ elb_target_group_name }}' + name: "{{ elb_target_group_name }}" target_type: lambda state: absent targets: [] @@ -93,24 +94,24 @@ - name: target is still the same, state must not be changed (idempotency) assert: that: - - elb_target_group.changed + - elb_target_group.changed always: - name: remove elb target group elb_target_group: - name: '{{ elb_target_group_name }}' + name: "{{ elb_target_group_name }}" target_type: lambda state: absent ignore_errors: true - name: remove lambda function lambda: - name: '{{ lambda_name }}' + name: "{{ lambda_name }}" state: absent ignore_errors: true - name: remove iam role for lambda iam_role: - name: '{{ lambda_role_name }}' + name: "{{ lambda_role_name }}" state: absent ignore_errors: true diff --git a/tests/integration/targets/elb_target/tasks/main.yml b/tests/integration/targets/elb_target/tasks/main.yml index e6c62f922d3..7627fc83219 100644 --- a/tests/integration/targets/elb_target/tasks/main.yml +++ b/tests/integration/targets/elb_target/tasks/main.yml @@ -7,7 +7,8 @@ security_token: "{{ security_token | default(omit) }}" region: "{{ aws_region }}" collections: + - community.general - amazon.aws block: - - include_tasks: lambda_target.yml - - include_tasks: ec2_target.yml + - include_tasks: ec2_target.yml + - include_tasks: lambda_target.yml