From 90babfae33a4ed85114a965fa0e28b9c194aff80 Mon Sep 17 00:00:00 2001 From: jillr Date: Mon, 2 Mar 2020 19:25:18 +0000 Subject: [PATCH 01/81] Initial commit This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/eb75681585a23ea79e642b86a0f8e64e0f40a6d7 --- plugins/modules/route53.py | 709 ++++++++++++++++++ plugins/modules/route53_facts.py | 1 + plugins/modules/route53_health_check.py | 375 +++++++++ plugins/modules/route53_info.py | 499 ++++++++++++ plugins/modules/route53_zone.py | 440 +++++++++++ tests/integration/targets/route53/aliases | 3 + .../targets/route53/defaults/main.yml | 2 + .../targets/route53/tasks/main.yml | 252 +++++++ .../integration/targets/route53/vars/main.yml | 0 .../integration/targets/route53_zone/aliases | 2 + .../targets/route53_zone/tasks/main.yml | 393 ++++++++++ 11 files changed, 2676 insertions(+) create mode 100644 plugins/modules/route53.py create mode 120000 plugins/modules/route53_facts.py create mode 100644 plugins/modules/route53_health_check.py create mode 100644 plugins/modules/route53_info.py create mode 100644 plugins/modules/route53_zone.py create mode 100644 tests/integration/targets/route53/aliases create mode 100644 tests/integration/targets/route53/defaults/main.yml create mode 100644 tests/integration/targets/route53/tasks/main.yml create mode 100644 tests/integration/targets/route53/vars/main.yml create mode 100644 tests/integration/targets/route53_zone/aliases create mode 100644 tests/integration/targets/route53_zone/tasks/main.yml diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py new file mode 100644 index 00000000000..dda106e3f9b --- /dev/null +++ b/plugins/modules/route53.py @@ -0,0 +1,709 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2018, 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 + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['stableinterface'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: route53 +short_description: add or delete entries in Amazons Route53 DNS service +description: + - Creates and deletes DNS records in Amazons Route53 service +options: + state: + description: + - Specifies the state of the resource record. As of Ansible 2.4, the I(command) option has been changed + to I(state) as default and the choices 'present' and 'absent' have been added, but I(command) still works as well. + required: true + aliases: [ 'command' ] + choices: [ 'present', 'absent', 'get', 'create', 'delete' ] + type: str + zone: + description: + - The DNS zone to modify. + - This is a required parameter, if parameter I(hosted_zone_id) is not supplied. + type: str + hosted_zone_id: + description: + - The Hosted Zone ID of the DNS zone to modify. + - This is a required parameter, if parameter I(zone) is not supplied. + type: str + record: + description: + - The full DNS record to create or delete. + required: true + type: str + ttl: + description: + - The TTL, in second, to give the new record. + default: 3600 + type: int + type: + description: + - The type of DNS record to create. + required: true + choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS', 'SOA' ] + type: str + alias: + description: + - Indicates if this is an alias record. + type: bool + default: false + alias_hosted_zone_id: + description: + - The hosted zone identifier. + type: str + alias_evaluate_target_health: + description: + - Whether or not to evaluate an alias target health. Useful for aliases to Elastic Load Balancers. + type: bool + default: false + value: + description: + - The new value when creating a DNS record. YAML lists or multiple comma-spaced values are allowed for non-alias records. + - When deleting a record all values for the record must be specified or Route53 will not delete it. + type: list + overwrite: + description: + - Whether an existing record should be overwritten on create if values do not match. + type: bool + retry_interval: + description: + - In the case that route53 is still servicing a prior request, this module will wait and try again after this many seconds. + If you have many domain names, the default of 500 seconds may be too long. + default: 500 + type: int + private_zone: + description: + - If set to C(yes), the private zone matching the requested name within the domain will be used if there are both public and private zones. + The default is to use the public zone. + type: bool + default: false + identifier: + description: + - Have to be specified for Weighted, latency-based and failover resource record sets only. + An identifier that differentiates among multiple resource record sets that have the same combination of DNS name and type. + type: str + weight: + description: + - Weighted resource record sets only. Among resource record sets that + have the same combination of DNS name and type, a value that + determines what portion of traffic for the current resource record set + is routed to the associated location. + type: int + region: + description: + - Latency-based resource record sets only Among resource record sets + that have the same combination of DNS name and type, a value that + determines which region this should be associated with for the + latency-based routing + type: str + health_check: + description: + - Health check to associate with this record + type: str + failover: + description: + - Failover resource record sets only. Whether this is the primary or + secondary resource record set. Allowed values are PRIMARY and SECONDARY + type: str + choices: ['SECONDARY', 'PRIMARY'] + vpc_id: + description: + - "When used in conjunction with private_zone: true, this will only modify records in the private hosted zone attached to this VPC." + - This allows you to have multiple private hosted zones, all with the same name, attached to different VPCs. + type: str + wait: + description: + - Wait until the changes have been replicated to all Amazon Route 53 DNS servers. + type: bool + default: false + wait_timeout: + description: + - How long to wait for the changes to be replicated, in seconds. + default: 300 + type: int +author: +- Bruce Pennypacker (@bpennypacker) +- Mike Buzzetti (@jimbydamonk) +extends_documentation_fragment: +- ansible.amazon.aws + +''' + +RETURN = ''' +nameservers: + description: Nameservers associated with the zone. + returned: when state is 'get' + type: list + sample: + - ns-1036.awsdns-00.org. + - ns-516.awsdns-00.net. + - ns-1504.awsdns-00.co.uk. + - ns-1.awsdns-00.com. +set: + description: Info specific to the resource record. + returned: when state is 'get' + type: complex + contains: + alias: + description: Whether this is an alias. + returned: always + type: bool + sample: false + failover: + description: Whether this is the primary or secondary resource record set. + returned: always + type: str + sample: PRIMARY + health_check: + description: health_check associated with this record. + returned: always + type: str + identifier: + description: An identifier that differentiates among multiple resource record sets that have the same combination of DNS name and type. + returned: always + type: str + record: + description: Domain name for the record set. + returned: always + type: str + sample: new.foo.com. + region: + description: Which region this should be associated with for latency-based routing. + returned: always + type: str + sample: us-west-2 + ttl: + description: Resource record cache TTL. + returned: always + type: str + sample: '3600' + type: + description: Resource record set type. + returned: always + type: str + sample: A + value: + description: Record value. + returned: always + type: str + sample: 52.43.18.27 + values: + description: Record Values. + returned: always + type: list + sample: + - 52.43.18.27 + weight: + description: Weight of the record. + returned: always + type: str + sample: '3' + zone: + description: Zone this record set belongs to. + returned: always + type: str + sample: foo.bar.com. +''' + +EXAMPLES = ''' +# Add new.foo.com as an A record with 3 IPs and wait until the changes have been replicated +- route53: + state: present + zone: foo.com + record: new.foo.com + type: A + ttl: 7200 + value: 1.1.1.1,2.2.2.2,3.3.3.3 + wait: yes + +# Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated +- route53: + state: present + zone: foo.com + record: new.foo.com + type: A + ttl: 7200 + value: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + wait: yes + +# Retrieve the details for new.foo.com +- route53: + state: get + zone: foo.com + record: new.foo.com + type: A + register: rec + +# Delete new.foo.com A record using the results from the get command +- route53: + state: absent + zone: foo.com + record: "{{ rec.set.record }}" + ttl: "{{ rec.set.ttl }}" + type: "{{ rec.set.type }}" + value: "{{ rec.set.value }}" + +# Add an AAAA record. Note that because there are colons in the value +# that the IPv6 address must be quoted. Also shows using the old form command=create. +- route53: + command: create + zone: foo.com + record: localhost.foo.com + type: AAAA + ttl: 7200 + value: "::1" + +# Add a SRV record with multiple fields for a service on port 22222 +# For more information on SRV records see: +# https://en.wikipedia.org/wiki/SRV_record +- route53: + state: present + zone: foo.com + record: "_example-service._tcp.foo.com" + type: SRV + value: "0 0 22222 host1.foo.com,0 0 22222 host2.foo.com" + +# Add a TXT record. Note that TXT and SPF records must be surrounded +# by quotes when sent to Route 53: +- route53: + state: present + zone: foo.com + record: localhost.foo.com + type: TXT + ttl: 7200 + value: '"bar"' + +# Add an alias record that points to an Amazon ELB: +- route53: + state: present + zone: foo.com + record: elb.foo.com + type: A + value: "{{ elb_dns_name }}" + alias: True + alias_hosted_zone_id: "{{ elb_zone_id }}" + +# Retrieve the details for elb.foo.com +- route53: + state: get + zone: foo.com + record: elb.foo.com + type: A + register: rec + +# Delete an alias record using the results from the get command +- route53: + state: absent + zone: foo.com + record: "{{ rec.set.record }}" + ttl: "{{ rec.set.ttl }}" + type: "{{ rec.set.type }}" + value: "{{ rec.set.value }}" + alias: True + alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}" + +# Add an alias record that points to an Amazon ELB and evaluates it health: +- route53: + state: present + zone: foo.com + record: elb.foo.com + type: A + value: "{{ elb_dns_name }}" + alias: True + alias_hosted_zone_id: "{{ elb_zone_id }}" + alias_evaluate_target_health: True + +# Add an AAAA record with Hosted Zone ID. +- route53: + state: present + zone: foo.com + hosted_zone_id: Z2AABBCCDDEEFF + record: localhost.foo.com + type: AAAA + ttl: 7200 + value: "::1" + +# Use a routing policy to distribute traffic: +- route53: + state: present + zone: foo.com + record: www.foo.com + type: CNAME + value: host1.foo.com + ttl: 30 + # Routing policy + identifier: "host1@www" + weight: 100 + health_check: "d994b780-3150-49fd-9205-356abdd42e75" + +# Add a CAA record (RFC 6844): +- route53: + state: present + zone: example.com + record: example.com + type: CAA + value: + - 0 issue "ca.example.net" + - 0 issuewild ";" + - 0 iodef "mailto:security@example.com" + +''' + +import time +import distutils.version + +try: + import boto + import boto.ec2 + from boto.route53 import Route53Connection + from boto.route53.record import Record, ResourceRecordSets + from boto.route53.status import Status + HAS_BOTO = True +except ImportError: + HAS_BOTO = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info + + +MINIMUM_BOTO_VERSION = '2.28.0' +WAIT_RETRY_SLEEP = 5 # how many seconds to wait between propagation status polls + + +class TimeoutError(Exception): + pass + + +def get_zone_id_by_name(conn, module, zone_name, want_private, want_vpc_id): + """Finds a zone by name or zone_id""" + for zone in invoke_with_throttling_retries(conn.get_zones): + # only save this zone id if the private status of the zone matches + # the private_zone_in boolean specified in the params + private_zone = module.boolean(zone.config.get('PrivateZone', False)) + if private_zone == want_private and zone.name == zone_name: + if want_vpc_id: + # NOTE: These details aren't available in other boto methods, hence the necessary + # extra API call + hosted_zone = invoke_with_throttling_retries(conn.get_hosted_zone, zone.id) + zone_details = hosted_zone['GetHostedZoneResponse'] + # this is to deal with this boto bug: https://github.com/boto/boto/pull/2882 + if isinstance(zone_details['VPCs'], dict): + if zone_details['VPCs']['VPC']['VPCId'] == want_vpc_id: + return zone.id + else: # Forward compatibility for when boto fixes that bug + if want_vpc_id in [v['VPCId'] for v in zone_details['VPCs']]: + return zone.id + else: + return zone.id + return None + + +def commit(changes, retry_interval, wait, wait_timeout): + """Commit changes, but retry PriorRequestNotComplete errors.""" + result = None + retry = 10 + while True: + try: + retry -= 1 + result = changes.commit() + break + except boto.route53.exception.DNSServerError as e: + code = e.body.split("")[1] + code = code.split("")[0] + if code != 'PriorRequestNotComplete' or retry < 0: + raise e + time.sleep(float(retry_interval)) + + if wait: + timeout_time = time.time() + wait_timeout + connection = changes.connection + change = result['ChangeResourceRecordSetsResponse']['ChangeInfo'] + status = Status(connection, change) + while status.status != 'INSYNC' and time.time() < timeout_time: + time.sleep(WAIT_RETRY_SLEEP) + status.update() + if time.time() >= timeout_time: + raise TimeoutError() + return result + + +# Shamelessly copied over from https://git.io/vgmDG +IGNORE_CODE = 'Throttling' +MAX_RETRIES = 5 + + +def invoke_with_throttling_retries(function_ref, *argv, **kwargs): + retries = 0 + while True: + try: + retval = function_ref(*argv, **kwargs) + return retval + except boto.exception.BotoServerError as e: + if e.code != IGNORE_CODE or retries == MAX_RETRIES: + raise e + time.sleep(5 * (2**retries)) + retries += 1 + + +def decode_name(name): + # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round + # tripping of things like * and @. + return name.encode().decode('unicode_escape') + + +def to_dict(rset, zone_in, zone_id): + record = dict() + record['zone'] = zone_in + record['type'] = rset.type + record['record'] = decode_name(rset.name) + record['ttl'] = str(rset.ttl) + record['identifier'] = rset.identifier + record['weight'] = rset.weight + record['region'] = rset.region + record['failover'] = rset.failover + record['health_check'] = rset.health_check + record['hosted_zone_id'] = zone_id + if rset.alias_dns_name: + record['alias'] = True + record['value'] = rset.alias_dns_name + record['values'] = [rset.alias_dns_name] + record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id + record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health + else: + record['alias'] = False + record['value'] = ','.join(sorted(rset.resource_records)) + record['values'] = sorted(rset.resource_records) + return record + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + state=dict(type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']), + zone=dict(type='str'), + hosted_zone_id=dict(type='str'), + record=dict(type='str', required=True), + ttl=dict(type='int', default=3600), + type=dict(type='str', required=True, choices=['A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NS', 'PTR', 'SOA', 'SPF', 'SRV', 'TXT']), + alias=dict(type='bool'), + alias_hosted_zone_id=dict(type='str'), + alias_evaluate_target_health=dict(type='bool', default=False), + value=dict(type='list'), + overwrite=dict(type='bool'), + retry_interval=dict(type='int', default=500), + private_zone=dict(type='bool', default=False), + identifier=dict(type='str'), + weight=dict(type='int'), + region=dict(type='str'), + health_check=dict(type='str'), + failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']), + vpc_id=dict(type='str'), + wait=dict(type='bool', default=False), + wait_timeout=dict(type='int', default=300), + )) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_one_of=[['zone', 'hosted_zone_id']], + # If alias is True then you must specify alias_hosted_zone as well + required_together=[['alias', 'alias_hosted_zone_id']], + # state=present, absent, create, delete THEN value is required + required_if=( + ('state', 'present', ['value']), + ('state', 'create', ['value']), + ('state', 'absent', ['value']), + ('state', 'delete', ['value']), + ), + # failover, region and weight are mutually exclusive + mutually_exclusive=[('failover', 'region', 'weight')], + # failover, region and weight require identifier + required_by=dict( + failover=('identifier',), + region=('identifier',), + weight=('identifier',), + ), + ) + + if not HAS_BOTO: + module.fail_json(msg='boto required for this module') + + if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION): + module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION)) + + if module.params['state'] in ('present', 'create'): + command_in = 'create' + elif module.params['state'] in ('absent', 'delete'): + command_in = 'delete' + elif module.params['state'] == 'get': + command_in = 'get' + + zone_in = (module.params.get('zone') or '').lower() + hosted_zone_id_in = module.params.get('hosted_zone_id') + ttl_in = module.params.get('ttl') + record_in = module.params.get('record').lower() + type_in = module.params.get('type') + value_in = module.params.get('value') or [] + alias_in = module.params.get('alias') + alias_hosted_zone_id_in = module.params.get('alias_hosted_zone_id') + alias_evaluate_target_health_in = module.params.get('alias_evaluate_target_health') + retry_interval_in = module.params.get('retry_interval') + + if module.params['vpc_id'] is not None: + private_zone_in = True + else: + private_zone_in = module.params.get('private_zone') + + identifier_in = module.params.get('identifier') + weight_in = module.params.get('weight') + region_in = module.params.get('region') + health_check_in = module.params.get('health_check') + failover_in = module.params.get('failover') + vpc_id_in = module.params.get('vpc_id') + wait_in = module.params.get('wait') + wait_timeout_in = module.params.get('wait_timeout') + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) + + if zone_in[-1:] != '.': + zone_in += "." + + if record_in[-1:] != '.': + record_in += "." + + if command_in == 'create' or command_in == 'delete': + if alias_in and len(value_in) != 1: + module.fail_json(msg="parameter 'value' must contain a single dns name for alias records") + if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None: + module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.") + + # connect to the route53 endpoint + try: + conn = Route53Connection(**aws_connect_kwargs) + except boto.exception.BotoServerError as e: + module.fail_json(msg=e.error_message) + + # Find the named zone ID + zone_id = hosted_zone_id_in or get_zone_id_by_name(conn, module, zone_in, private_zone_in, vpc_id_in) + + # Verify that the requested zone is already defined in Route53 + if zone_id is None: + errmsg = "Zone %s does not exist in Route53" % (zone_in or hosted_zone_id_in) + module.fail_json(msg=errmsg) + + record = {} + + found_record = False + wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in, + identifier=identifier_in, weight=weight_in, + region=region_in, health_check=health_check_in, + failover=failover_in) + for v in value_in: + if alias_in: + wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in) + else: + wanted_rset.add_value(v) + + need_to_sort_records = (type_in == 'CAA') + + # Sort records for wanted_rset if necessary (keep original list) + unsorted_records = wanted_rset.resource_records + if need_to_sort_records: + wanted_rset.resource_records = sorted(unsorted_records) + + sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone_id, name=record_in, + type=type_in, identifier=identifier_in) + sets_iter = iter(sets) + while True: + try: + rset = invoke_with_throttling_retries(next, sets_iter) + except StopIteration: + break + # Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block + rset.name = decode_name(rset.name) + + if identifier_in is not None: + identifier_in = str(identifier_in) + + if rset.type == type_in and rset.name.lower() == record_in.lower() and rset.identifier == identifier_in: + if need_to_sort_records: + # Sort records + rset.resource_records = sorted(rset.resource_records) + found_record = True + record = to_dict(rset, zone_in, zone_id) + if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml(): + module.exit_json(changed=False) + + # We need to look only at the first rrset returned by the above call, + # so break here. The returned elements begin with the one matching our + # requested name, type, and identifier, if such an element exists, + # followed by all others that come after it in alphabetical order. + # Therefore, if the first set does not match, no subsequent set will + # match either. + break + + if command_in == 'get': + if type_in == 'NS': + ns = record.get('values', []) + else: + # Retrieve name servers associated to the zone. + z = invoke_with_throttling_retries(conn.get_zone, zone_in) + ns = invoke_with_throttling_retries(z.get_nameservers) + + module.exit_json(changed=False, set=record, nameservers=ns) + + if command_in == 'delete' and not found_record: + module.exit_json(changed=False) + + changes = ResourceRecordSets(conn, zone_id) + + if command_in == 'create' or command_in == 'delete': + if command_in == 'create' and found_record: + if not module.params['overwrite']: + module.fail_json(msg="Record already exists with different value. Set 'overwrite' to replace it") + command = 'UPSERT' + else: + command = command_in.upper() + # Restore original order of records + wanted_rset.resource_records = unsorted_records + changes.add_change_record(command, wanted_rset) + + if not module.check_mode: + try: + invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in) + except boto.route53.exception.DNSServerError as e: + txt = e.body.split("")[1] + txt = txt.split("")[0] + if "but it already exists" in txt: + module.exit_json(changed=False) + else: + module.fail_json(msg=txt) + except TimeoutError: + module.fail_json(msg='Timeout waiting for changes to replicate') + + module.exit_json( + changed=True, + diff=dict( + before=record, + after=to_dict(wanted_rset, zone_in, zone_id) if command != 'delete' else {}, + ), + ) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/route53_facts.py b/plugins/modules/route53_facts.py new file mode 120000 index 00000000000..6b40f0529b0 --- /dev/null +++ b/plugins/modules/route53_facts.py @@ -0,0 +1 @@ +route53_info.py \ No newline at end of file diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py new file mode 100644 index 00000000000..778b4c0595d --- /dev/null +++ b/plugins/modules/route53_health_check.py @@ -0,0 +1,375 @@ +#!/usr/bin/python +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['stableinterface'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: route53_health_check +short_description: Add or delete health-checks in Amazons Route53 DNS service +description: + - Creates and deletes DNS Health checks in Amazons Route53 service. + - Only the port, resource_path, string_match and request_interval are + considered when updating existing health-checks. +options: + state: + description: + - Specifies the action to take. + choices: [ 'present', 'absent' ] + type: str + default: 'present' + ip_address: + description: + - IP address of the end-point to check. Either this or I(fqdn) has to be provided. + type: str + port: + description: + - The port on the endpoint on which you want Amazon Route 53 to perform + health checks. Required for TCP checks. + type: int + type: + description: + - The type of health check that you want to create, which indicates how + Amazon Route 53 determines whether an endpoint is healthy. + required: true + choices: [ 'HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP' ] + type: str + resource_path: + description: + - The path that you want Amazon Route 53 to request when performing + health checks. The path can be any value for which your endpoint will + return an HTTP status code of 2xx or 3xx when the endpoint is healthy, + for example the file /docs/route53-health-check.html. + - Required for all checks except TCP. + - The path must begin with a / + - Maximum 255 characters. + type: str + fqdn: + description: + - Domain name of the endpoint to check. Either this or I(ip_address) has + to be provided. When both are given the `fqdn` is used in the `Host:` + header of the HTTP request. + type: str + string_match: + description: + - If the check type is HTTP_STR_MATCH or HTTP_STR_MATCH, the string + that you want Amazon Route 53 to search for in the response body from + the specified resource. If the string appears in the first 5120 bytes + of the response body, Amazon Route 53 considers the resource healthy. + type: str + request_interval: + description: + - The number of seconds between the time that Amazon Route 53 gets a + response from your endpoint and the time that it sends the next + health-check request. + default: 30 + choices: [ 10, 30 ] + type: int + failure_threshold: + description: + - The number of consecutive health checks that an endpoint must pass or + fail for Amazon Route 53 to change the current status of the endpoint + from unhealthy to healthy or vice versa. + default: 3 + choices: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] + type: int +author: "zimbatm (@zimbatm)" +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +''' + +EXAMPLES = ''' +# Create a health-check for host1.example.com and use it in record +- route53_health_check: + state: present + fqdn: host1.example.com + type: HTTP_STR_MATCH + resource_path: / + string_match: "Hello" + request_interval: 10 + failure_threshold: 2 + register: my_health_check + +- route53: + action: create + zone: "example.com" + type: CNAME + record: "www.example.com" + value: host1.example.com + ttl: 30 + # Routing policy + identifier: "host1@www" + weight: 100 + health_check: "{{ my_health_check.health_check.id }}" + +# Delete health-check +- route53_health_check: + state: absent + fqdn: host1.example.com + +''' + +import uuid + +try: + import boto + import boto.ec2 + from boto import route53 + from boto.route53 import Route53Connection, exception + from boto.route53.healthcheck import HealthCheck + HAS_BOTO = True +except ImportError: + HAS_BOTO = False + +# import module snippets +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info + + +# Things that can't get changed: +# protocol +# ip_address or domain +# request_interval +# string_match if not previously enabled +def find_health_check(conn, wanted): + """Searches for health checks that have the exact same set of immutable values""" + + results = conn.get_list_health_checks() + + while True: + for check in results.HealthChecks: + config = check.HealthCheckConfig + if ( + config.get('IPAddress') == wanted.ip_addr and + config.get('FullyQualifiedDomainName') == wanted.fqdn and + config.get('Type') == wanted.hc_type and + config.get('RequestInterval') == str(wanted.request_interval) and + config.get('Port') == str(wanted.port) + ): + return check + + if (results.IsTruncated == 'true'): + results = conn.get_list_health_checks(marker=results.NextMarker) + else: + return None + + +def to_health_check(config): + return HealthCheck( + config.get('IPAddress'), + int(config.get('Port')), + config.get('Type'), + config.get('ResourcePath'), + fqdn=config.get('FullyQualifiedDomainName'), + string_match=config.get('SearchString'), + request_interval=int(config.get('RequestInterval')), + failure_threshold=int(config.get('FailureThreshold')), + ) + + +def health_check_diff(a, b): + a = a.__dict__ + b = b.__dict__ + if a == b: + return {} + diff = {} + for key in set(a.keys()) | set(b.keys()): + if a.get(key) != b.get(key): + diff[key] = b.get(key) + return diff + + +def to_template_params(health_check): + params = { + 'ip_addr_part': '', + 'port': health_check.port, + 'type': health_check.hc_type, + 'resource_path_part': '', + 'fqdn_part': '', + 'string_match_part': '', + 'request_interval': health_check.request_interval, + 'failure_threshold': health_check.failure_threshold, + } + if health_check.ip_addr: + params['ip_addr_part'] = HealthCheck.XMLIpAddrPart % {'ip_addr': health_check.ip_addr} + if health_check.resource_path: + params['resource_path_part'] = XMLResourcePathPart % {'resource_path': health_check.resource_path} + if health_check.fqdn: + params['fqdn_part'] = HealthCheck.XMLFQDNPart % {'fqdn': health_check.fqdn} + if health_check.string_match: + params['string_match_part'] = HealthCheck.XMLStringMatchPart % {'string_match': health_check.string_match} + return params + + +XMLResourcePathPart = """%(resource_path)s""" + +POSTXMLBody = """ + + %(caller_ref)s + + %(ip_addr_part)s + %(port)s + %(type)s + %(resource_path_part)s + %(fqdn_part)s + %(string_match_part)s + %(request_interval)s + %(failure_threshold)s + + + """ + +UPDATEHCXMLBody = """ + + %(health_check_version)s + %(ip_addr_part)s + %(port)s + %(resource_path_part)s + %(fqdn_part)s + %(string_match_part)s + %(failure_threshold)i + + """ + + +def create_health_check(conn, health_check, caller_ref=None): + if caller_ref is None: + caller_ref = str(uuid.uuid4()) + uri = '/%s/healthcheck' % conn.Version + params = to_template_params(health_check) + params.update(xmlns=conn.XMLNameSpace, caller_ref=caller_ref) + + xml_body = POSTXMLBody % params + response = conn.make_request('POST', uri, {'Content-Type': 'text/xml'}, xml_body) + body = response.read() + boto.log.debug(body) + if response.status == 201: + e = boto.jsonresponse.Element() + h = boto.jsonresponse.XmlHandler(e, None) + h.parse(body) + return e + else: + raise exception.DNSServerError(response.status, response.reason, body) + + +def update_health_check(conn, health_check_id, health_check_version, health_check): + uri = '/%s/healthcheck/%s' % (conn.Version, health_check_id) + params = to_template_params(health_check) + params.update( + xmlns=conn.XMLNameSpace, + health_check_version=health_check_version, + ) + xml_body = UPDATEHCXMLBody % params + response = conn.make_request('POST', uri, {'Content-Type': 'text/xml'}, xml_body) + body = response.read() + boto.log.debug(body) + if response.status not in (200, 204): + raise exception.DNSServerError(response.status, + response.reason, + body) + e = boto.jsonresponse.Element() + h = boto.jsonresponse.XmlHandler(e, None) + h.parse(body) + return e + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + state=dict(choices=['present', 'absent'], default='present'), + ip_address=dict(), + port=dict(type='int'), + type=dict(required=True, choices=['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP']), + resource_path=dict(), + fqdn=dict(), + string_match=dict(), + request_interval=dict(type='int', choices=[10, 30], default=30), + failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default=3), + ) + ) + module = AnsibleModule(argument_spec=argument_spec) + + if not HAS_BOTO: + module.fail_json(msg='boto 2.27.0+ required for this module') + + state_in = module.params.get('state') + ip_addr_in = module.params.get('ip_address') + port_in = module.params.get('port') + type_in = module.params.get('type') + resource_path_in = module.params.get('resource_path') + fqdn_in = module.params.get('fqdn') + string_match_in = module.params.get('string_match') + request_interval_in = module.params.get('request_interval') + failure_threshold_in = module.params.get('failure_threshold') + + if ip_addr_in is None and fqdn_in is None: + module.fail_json(msg="parameter 'ip_address' or 'fqdn' is required") + + # Default port + if port_in is None: + if type_in in ['HTTP', 'HTTP_STR_MATCH']: + port_in = 80 + elif type_in in ['HTTPS', 'HTTPS_STR_MATCH']: + port_in = 443 + else: + module.fail_json(msg="parameter 'port' is required for 'type' TCP") + + # string_match in relation with type + if type_in in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: + if string_match_in is None: + module.fail_json(msg="parameter 'string_match' is required for the HTTP(S)_STR_MATCH types") + elif len(string_match_in) > 255: + module.fail_json(msg="parameter 'string_match' is limited to 255 characters max") + elif string_match_in: + module.fail_json(msg="parameter 'string_match' argument is only for the HTTP(S)_STR_MATCH types") + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) + # connect to the route53 endpoint + try: + conn = Route53Connection(**aws_connect_kwargs) + except boto.exception.BotoServerError as e: + module.fail_json(msg=e.error_message) + + changed = False + action = None + check_id = None + wanted_config = HealthCheck(ip_addr_in, port_in, type_in, resource_path_in, fqdn_in, string_match_in, request_interval_in, failure_threshold_in) + existing_check = find_health_check(conn, wanted_config) + if existing_check: + check_id = existing_check.Id + existing_config = to_health_check(existing_check.HealthCheckConfig) + + if state_in == 'present': + if existing_check is None: + action = "create" + check_id = create_health_check(conn, wanted_config).HealthCheck.Id + changed = True + else: + diff = health_check_diff(existing_config, wanted_config) + if diff: + action = "update" + update_health_check(conn, existing_check.Id, int(existing_check.HealthCheckVersion), wanted_config) + changed = True + elif state_in == 'absent': + if check_id: + action = "delete" + conn.delete_health_check(check_id) + changed = True + else: + module.fail_json(msg="Logic Error: Unknown state") + + module.exit_json(changed=changed, health_check=dict(id=check_id), action=action) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py new file mode 100644 index 00000000000..a3d8d76f6de --- /dev/null +++ b/plugins/modules/route53_info.py @@ -0,0 +1,499 @@ +#!/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 + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +module: route53_info +short_description: Retrieves route53 details using AWS methods +description: + - Gets various details related to Route53 zone, record set or health check details. + - This module was called C(route53_facts) before Ansible 2.9. The usage did not change. +options: + query: + description: + - Specifies the query action to take. + required: True + choices: [ + 'change', + 'checker_ip_range', + 'health_check', + 'hosted_zone', + 'record_sets', + 'reusable_delegation_set', + ] + type: str + change_id: + description: + - The ID of the change batch request. + - The value that you specify here is the value that + ChangeResourceRecordSets returned in the Id element + when you submitted the request. + - Required if I(query=change). + required: false + type: str + hosted_zone_id: + description: + - The Hosted Zone ID of the DNS zone. + - Required if I(query) is set to I(hosted_zone) and I(hosted_zone_method) is set to I(details). + - Required if I(query) is set to I(record_sets). + required: false + type: str + max_items: + description: + - Maximum number of items to return for various get/list requests. + required: false + type: str + next_marker: + description: + - "Some requests such as list_command: hosted_zones will return a maximum + number of entries - EG 100 or the number specified by I(max_items). + If the number of entries exceeds this maximum another request can be sent + using the NextMarker entry from the first response to get the next page + of results." + required: false + type: str + delegation_set_id: + description: + - The DNS Zone delegation set ID. + required: false + type: str + start_record_name: + description: + - "The first name in the lexicographic ordering of domain names that you want + the list_command: record_sets to start listing from." + required: false + type: str + type: + description: + - The type of DNS record. + required: false + choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS' ] + type: str + dns_name: + description: + - The first name in the lexicographic ordering of domain names that you want + the list_command to start listing from. + required: false + type: str + resource_id: + description: + - The ID/s of the specified resource/s. + - Required if I(query=health_check) and I(health_check_method=tags). + - Required if I(query=hosted_zone) and I(hosted_zone_method=tags). + required: false + aliases: ['resource_ids'] + type: list + elements: str + health_check_id: + description: + - The ID of the health check. + - Required if C(query) is set to C(health_check) and + C(health_check_method) is set to C(details) or C(status) or C(failure_reason). + required: false + type: str + hosted_zone_method: + description: + - "This is used in conjunction with query: hosted_zone. + It allows for listing details, counts or tags of various + hosted zone details." + required: false + choices: [ + 'details', + 'list', + 'list_by_name', + 'count', + 'tags', + ] + default: 'list' + type: str + health_check_method: + description: + - "This is used in conjunction with query: health_check. + It allows for listing details, counts or tags of various + health check details." + required: false + choices: [ + 'list', + 'details', + 'status', + 'failure_reason', + 'count', + 'tags', + ] + default: 'list' + type: str +author: Karen Cheng (@Etherdaemon) +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +''' + +EXAMPLES = ''' +# Simple example of listing all hosted zones +- name: List all hosted zones + route53_info: + query: hosted_zone + register: hosted_zones + +# Getting a count of hosted zones +- name: Return a count of all hosted zones + route53_info: + query: hosted_zone + hosted_zone_method: count + register: hosted_zone_count + +- name: List the first 20 resource record sets in a given hosted zone + route53_info: + profile: account_name + query: record_sets + hosted_zone_id: ZZZ1111112222 + max_items: 20 + register: record_sets + +- name: List first 20 health checks + route53_info: + query: health_check + health_check_method: list + max_items: 20 + register: health_checks + +- name: Get health check last failure_reason + route53_info: + query: health_check + health_check_method: failure_reason + health_check_id: 00000000-1111-2222-3333-12345678abcd + register: health_check_failure_reason + +- name: Retrieve reusable delegation set details + route53_info: + query: reusable_delegation_set + delegation_set_id: delegation id + register: delegation_sets + +- name: setup of example for using next_marker + route53_info: + query: hosted_zone + max_items: 1 + register: first_info + +- name: example for using next_marker + route53_info: + query: hosted_zone + next_marker: "{{ first_info.NextMarker }}" + max_items: 1 + when: "{{ 'NextMarker' in first_info }}" + +- name: retrieve host entries starting with host1.workshop.test.io + block: + - name: grab zone id + route53_zone: + zone: "test.io" + register: AWSINFO + + - name: grab Route53 record information + route53_info: + type: A + query: record_sets + hosted_zone_id: "{{ AWSINFO.zone_id }}" + start_record_name: "host1.workshop.test.io" + register: RECORDS +''' +try: + import boto + import botocore + HAS_BOTO = True +except ImportError: + HAS_BOTO = False + +try: + import boto3 + HAS_BOTO3 = True +except ImportError: + HAS_BOTO3 = False + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info +from ansible.module_utils._text import to_native + + +def get_hosted_zone(client, module): + params = dict() + + if module.params.get('hosted_zone_id'): + params['Id'] = module.params.get('hosted_zone_id') + else: + module.fail_json(msg="Hosted Zone Id is required") + + return client.get_hosted_zone(**params) + + +def reusable_delegation_set_details(client, module): + params = dict() + if not module.params.get('delegation_set_id'): + if module.params.get('max_items'): + params['MaxItems'] = module.params.get('max_items') + + if module.params.get('next_marker'): + params['Marker'] = module.params.get('next_marker') + + results = client.list_reusable_delegation_sets(**params) + else: + params['DelegationSetId'] = module.params.get('delegation_set_id') + results = client.get_reusable_delegation_set(**params) + + return results + + +def list_hosted_zones(client, module): + params = dict() + + if module.params.get('max_items'): + params['MaxItems'] = module.params.get('max_items') + + if module.params.get('next_marker'): + params['Marker'] = module.params.get('next_marker') + + if module.params.get('delegation_set_id'): + params['DelegationSetId'] = module.params.get('delegation_set_id') + + paginator = client.get_paginator('list_hosted_zones') + zones = paginator.paginate(**params).build_full_result()['HostedZones'] + return { + "HostedZones": zones, + "list": zones, + } + + +def list_hosted_zones_by_name(client, module): + params = dict() + + if module.params.get('hosted_zone_id'): + params['HostedZoneId'] = module.params.get('hosted_zone_id') + + if module.params.get('dns_name'): + params['DNSName'] = module.params.get('dns_name') + + if module.params.get('max_items'): + params['MaxItems'] = module.params.get('max_items') + + return client.list_hosted_zones_by_name(**params) + + +def change_details(client, module): + params = dict() + + if module.params.get('change_id'): + params['Id'] = module.params.get('change_id') + else: + module.fail_json(msg="change_id is required") + + results = client.get_change(**params) + return results + + +def checker_ip_range_details(client, module): + return client.get_checker_ip_ranges() + + +def get_count(client, module): + if module.params.get('query') == 'health_check': + results = client.get_health_check_count() + else: + results = client.get_hosted_zone_count() + + return results + + +def get_health_check(client, module): + params = dict() + + if not module.params.get('health_check_id'): + module.fail_json(msg="health_check_id is required") + else: + params['HealthCheckId'] = module.params.get('health_check_id') + + if module.params.get('health_check_method') == 'details': + results = client.get_health_check(**params) + elif module.params.get('health_check_method') == 'failure_reason': + results = client.get_health_check_last_failure_reason(**params) + elif module.params.get('health_check_method') == 'status': + results = client.get_health_check_status(**params) + + return results + + +def get_resource_tags(client, module): + params = dict() + + if module.params.get('resource_id'): + params['ResourceIds'] = module.params.get('resource_id') + else: + module.fail_json(msg="resource_id or resource_ids is required") + + if module.params.get('query') == 'health_check': + params['ResourceType'] = 'healthcheck' + else: + params['ResourceType'] = 'hostedzone' + + return client.list_tags_for_resources(**params) + + +def list_health_checks(client, module): + params = dict() + + if module.params.get('max_items'): + params['MaxItems'] = module.params.get('max_items') + + if module.params.get('next_marker'): + params['Marker'] = module.params.get('next_marker') + + paginator = client.get_paginator('list_health_checks') + health_checks = paginator.paginate(**params).build_full_result()['HealthChecks'] + return { + "HealthChecks": health_checks, + "list": health_checks, + } + + +def record_sets_details(client, module): + params = dict() + + if module.params.get('hosted_zone_id'): + params['HostedZoneId'] = module.params.get('hosted_zone_id') + else: + module.fail_json(msg="Hosted Zone Id is required") + + if module.params.get('max_items'): + params['MaxItems'] = module.params.get('max_items') + + if module.params.get('start_record_name'): + params['StartRecordName'] = module.params.get('start_record_name') + + if module.params.get('type') and not module.params.get('start_record_name'): + module.fail_json(msg="start_record_name must be specified if type is set") + elif module.params.get('type'): + params['StartRecordType'] = module.params.get('type') + + paginator = client.get_paginator('list_resource_record_sets') + record_sets = paginator.paginate(**params).build_full_result()['ResourceRecordSets'] + return { + "ResourceRecordSets": record_sets, + "list": record_sets, + } + + +def health_check_details(client, module): + health_check_invocations = { + 'list': list_health_checks, + 'details': get_health_check, + 'status': get_health_check, + 'failure_reason': get_health_check, + 'count': get_count, + 'tags': get_resource_tags, + } + + results = health_check_invocations[module.params.get('health_check_method')](client, module) + return results + + +def hosted_zone_details(client, module): + hosted_zone_invocations = { + 'details': get_hosted_zone, + 'list': list_hosted_zones, + 'list_by_name': list_hosted_zones_by_name, + 'count': get_count, + 'tags': get_resource_tags, + } + + results = hosted_zone_invocations[module.params.get('hosted_zone_method')](client, module) + return results + + +def main(): + argument_spec = ec2_argument_spec() + argument_spec.update(dict( + query=dict(choices=[ + 'change', + 'checker_ip_range', + 'health_check', + 'hosted_zone', + 'record_sets', + 'reusable_delegation_set', + ], required=True), + change_id=dict(), + hosted_zone_id=dict(), + max_items=dict(), + next_marker=dict(), + delegation_set_id=dict(), + start_record_name=dict(), + type=dict(choices=[ + 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS' + ]), + dns_name=dict(), + resource_id=dict(type='list', aliases=['resource_ids']), + health_check_id=dict(), + hosted_zone_method=dict(choices=[ + 'details', + 'list', + 'list_by_name', + 'count', + 'tags' + ], default='list'), + health_check_method=dict(choices=[ + 'list', + 'details', + 'status', + 'failure_reason', + 'count', + 'tags', + ], default='list'), + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + mutually_exclusive=[ + ['hosted_zone_method', 'health_check_method'], + ], + ) + if module._name == 'route53_facts': + module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", version='2.13') + + # Validate Requirements + if not (HAS_BOTO or HAS_BOTO3): + module.fail_json(msg='json and boto/boto3 is required.') + + region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) + route53 = boto3_conn(module, conn_type='client', resource='route53', region=region, endpoint=ec2_url, **aws_connect_kwargs) + + invocations = { + 'change': change_details, + 'checker_ip_range': checker_ip_range_details, + 'health_check': health_check_details, + 'hosted_zone': hosted_zone_details, + 'record_sets': record_sets_details, + 'reusable_delegation_set': reusable_delegation_set_details, + } + + results = dict(changed=False) + try: + results = invocations[module.params.get('query')](route53, module) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json(msg=to_native(e)) + + module.exit_json(**results) + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py new file mode 100644 index 00000000000..2d13cb9073e --- /dev/null +++ b/plugins/modules/route53_zone.py @@ -0,0 +1,440 @@ +#!/usr/bin/python +# This file is part of Ansible +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['stableinterface'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +module: route53_zone +short_description: add or delete Route53 zones +description: + - Creates and deletes Route53 private and public zones. +requirements: [ boto3 ] +options: + zone: + description: + - "The DNS zone record (eg: foo.com.)" + required: true + type: str + state: + description: + - Whether or not the zone should exist or not. + default: present + choices: [ "present", "absent" ] + type: str + vpc_id: + description: + - The VPC ID the zone should be a part of (if this is going to be a private zone). + type: str + vpc_region: + description: + - The VPC Region the zone should be a part of (if this is going to be a private zone). + type: str + comment: + description: + - Comment associated with the zone. + default: '' + type: str + hosted_zone_id: + description: + - The unique zone identifier you want to delete or "all" if there are many zones with the same domain name. + - Required if there are multiple zones identified with the above options. + type: str + delegation_set_id: + description: + - The reusable delegation set ID to be associated with the zone. + - Note that you can't associate a reusable delegation set with a private hosted zone. + type: str +extends_documentation_fragment: +- ansible.amazon.aws +- ansible.amazon.ec2 + +author: "Christopher Troup (@minichate)" +''' + +EXAMPLES = ''' +- name: create a public zone + route53_zone: + zone: example.com + comment: this is an example + +- name: delete a public zone + route53_zone: + zone: example.com + state: absent + +- name: create a private zone + route53_zone: + zone: devel.example.com + vpc_id: '{{ myvpc_id }}' + vpc_region: us-west-2 + comment: developer domain + +- name: create a public zone associated with a specific reusable delegation set + route53_zone: + zone: example.com + comment: reusable delegation set example + delegation_set_id: A1BCDEF2GHIJKL +''' + +RETURN = ''' +comment: + description: optional hosted zone comment + returned: when hosted zone exists + type: str + sample: "Private zone" +name: + description: hosted zone name + returned: when hosted zone exists + type: str + sample: "private.local." +private_zone: + description: whether hosted zone is private or public + returned: when hosted zone exists + type: bool + sample: true +vpc_id: + description: id of vpc attached to private hosted zone + returned: for private hosted zone + type: str + sample: "vpc-1d36c84f" +vpc_region: + description: region of vpc attached to private hosted zone + returned: for private hosted zone + type: str + sample: "eu-west-1" +zone_id: + description: hosted zone id + returned: when hosted zone exists + type: str + sample: "Z6JQG9820BEFMW" +delegation_set_id: + description: id of the associated reusable delegation set + returned: for public hosted zones, if they have been associated with a reusable delegation set + type: str + sample: "A1BCDEF2GHIJKL" +''' + +import time +from ansible_collections.ansible.amazon.plugins.module_utils.aws.core import AnsibleAWSModule + +try: + from botocore.exceptions import BotoCoreError, ClientError +except ImportError: + pass # caught by AnsibleAWSModule + + +def find_zones(module, client, zone_in, private_zone): + try: + paginator = client.get_paginator('list_hosted_zones') + results = paginator.paginate().build_full_result() + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not list current hosted zones") + zones = [] + for r53zone in results['HostedZones']: + if r53zone['Name'] != zone_in: + continue + # only save zone names that match the public/private setting + if (r53zone['Config']['PrivateZone'] and private_zone) or \ + (not r53zone['Config']['PrivateZone'] and not private_zone): + zones.append(r53zone) + + return zones + + +def create(module, client, matching_zones): + zone_in = module.params.get('zone').lower() + vpc_id = module.params.get('vpc_id') + vpc_region = module.params.get('vpc_region') + comment = module.params.get('comment') + delegation_set_id = module.params.get('delegation_set_id') + + if not zone_in.endswith('.'): + zone_in += "." + + private_zone = bool(vpc_id and vpc_region) + + record = { + 'private_zone': private_zone, + 'vpc_id': vpc_id, + 'vpc_region': vpc_region, + 'comment': comment, + 'name': zone_in, + 'delegation_set_id': delegation_set_id, + 'zone_id': None, + } + + if private_zone: + changed, result = create_or_update_private(module, client, matching_zones, record) + else: + changed, result = create_or_update_public(module, client, matching_zones, record) + + return changed, result + + +def create_or_update_private(module, client, matching_zones, record): + for z in matching_zones: + try: + result = client.get_hosted_zone(Id=z['Id']) # could be in different regions or have different VPCids + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not get details about hosted zone %s" % z['Id']) + zone_details = result['HostedZone'] + vpc_details = result['VPCs'] + current_vpc_id = None + current_vpc_region = None + if isinstance(vpc_details, dict): + if vpc_details['VPC']['VPCId'] == record['vpc_id']: + current_vpc_id = vpc_details['VPC']['VPCId'] + current_vpc_region = vpc_details['VPC']['VPCRegion'] + else: + if record['vpc_id'] in [v['VPCId'] for v in vpc_details]: + current_vpc_id = record['vpc_id'] + if record['vpc_region'] in [v['VPCRegion'] for v in vpc_details]: + current_vpc_region = record['vpc_region'] + + if record['vpc_id'] == current_vpc_id and record['vpc_region'] == current_vpc_region: + record['zone_id'] = zone_details['Id'].replace('/hostedzone/', '') + if 'Comment' in zone_details['Config'] and zone_details['Config']['Comment'] != record['comment']: + if not module.check_mode: + try: + client.update_hosted_zone_comment(Id=zone_details['Id'], Comment=record['comment']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not update comment for hosted zone %s" % zone_details['Id']) + return True, record + else: + record['msg'] = "There is already a private hosted zone in the same region with the same VPC \ + you chose. Unable to create a new private hosted zone in the same name space." + return False, record + + if not module.check_mode: + try: + result = client.create_hosted_zone( + Name=record['name'], + HostedZoneConfig={ + 'Comment': record['comment'] if record['comment'] is not None else "", + 'PrivateZone': True, + }, + VPC={ + 'VPCRegion': record['vpc_region'], + 'VPCId': record['vpc_id'], + }, + CallerReference="%s-%s" % (record['name'], time.time()), + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not create hosted zone") + + hosted_zone = result['HostedZone'] + zone_id = hosted_zone['Id'].replace('/hostedzone/', '') + record['zone_id'] = zone_id + + changed = True + return changed, record + + +def create_or_update_public(module, client, matching_zones, record): + zone_details, zone_delegation_set_details = None, {} + for matching_zone in matching_zones: + try: + zone = client.get_hosted_zone(Id=matching_zone['Id']) + zone_details = zone['HostedZone'] + zone_delegation_set_details = zone.get('DelegationSet', {}) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not get details about hosted zone %s" % matching_zone['Id']) + if 'Comment' in zone_details['Config'] and zone_details['Config']['Comment'] != record['comment']: + if not module.check_mode: + try: + client.update_hosted_zone_comment( + Id=zone_details['Id'], + Comment=record['comment'] + ) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not update comment for hosted zone %s" % zone_details['Id']) + changed = True + else: + changed = False + break + + if zone_details is None: + if not module.check_mode: + try: + params = dict( + Name=record['name'], + HostedZoneConfig={ + 'Comment': record['comment'] if record['comment'] is not None else "", + 'PrivateZone': False, + }, + CallerReference="%s-%s" % (record['name'], time.time()), + ) + + if record.get('delegation_set_id') is not None: + params['DelegationSetId'] = record['delegation_set_id'] + + result = client.create_hosted_zone(**params) + zone_details = result['HostedZone'] + zone_delegation_set_details = result.get('DelegationSet', {}) + + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not create hosted zone") + changed = True + + if module.check_mode: + if zone_details: + record['zone_id'] = zone_details['Id'].replace('/hostedzone/', '') + else: + record['zone_id'] = zone_details['Id'].replace('/hostedzone/', '') + record['name'] = zone_details['Name'] + record['delegation_set_id'] = zone_delegation_set_details.get('Id', '').replace('/delegationset/', '') + + return changed, record + + +def delete_private(module, client, matching_zones, vpc_id, vpc_region): + for z in matching_zones: + try: + result = client.get_hosted_zone(Id=z['Id']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not get details about hosted zone %s" % z['Id']) + zone_details = result['HostedZone'] + vpc_details = result['VPCs'] + if isinstance(vpc_details, dict): + if vpc_details['VPC']['VPCId'] == vpc_id and vpc_region == vpc_details['VPC']['VPCRegion']: + if not module.check_mode: + try: + client.delete_hosted_zone(Id=z['Id']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not delete hosted zone %s" % z['Id']) + return True, "Successfully deleted %s" % zone_details['Name'] + else: + if vpc_id in [v['VPCId'] for v in vpc_details] and vpc_region in [v['VPCRegion'] for v in vpc_details]: + if not module.check_mode: + try: + client.delete_hosted_zone(Id=z['Id']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not delete hosted zone %s" % z['Id']) + return True, "Successfully deleted %s" % zone_details['Name'] + + return False, "The vpc_id and the vpc_region do not match a private hosted zone." + + +def delete_public(module, client, matching_zones): + if len(matching_zones) > 1: + changed = False + msg = "There are multiple zones that match. Use hosted_zone_id to specify the correct zone." + else: + if not module.check_mode: + try: + client.delete_hosted_zone(Id=matching_zones[0]['Id']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not get delete hosted zone %s" % matching_zones[0]['Id']) + changed = True + msg = "Successfully deleted %s" % matching_zones[0]['Id'] + return changed, msg + + +def delete_hosted_id(module, client, hosted_zone_id, matching_zones): + if hosted_zone_id == "all": + deleted = [] + for z in matching_zones: + deleted.append(z['Id']) + if not module.check_mode: + try: + client.delete_hosted_zone(Id=z['Id']) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not delete hosted zone %s" % z['Id']) + changed = True + msg = "Successfully deleted zones: %s" % deleted + elif hosted_zone_id in [zo['Id'].replace('/hostedzone/', '') for zo in matching_zones]: + if not module.check_mode: + try: + client.delete_hosted_zone(Id=hosted_zone_id) + except (BotoCoreError, ClientError) as e: + module.fail_json_aws(e, msg="Could not delete hosted zone %s" % hosted_zone_id) + changed = True + msg = "Successfully deleted zone: %s" % hosted_zone_id + else: + changed = False + msg = "There is no zone to delete that matches hosted_zone_id %s." % hosted_zone_id + return changed, msg + + +def delete(module, client, matching_zones): + zone_in = module.params.get('zone').lower() + vpc_id = module.params.get('vpc_id') + vpc_region = module.params.get('vpc_region') + hosted_zone_id = module.params.get('hosted_zone_id') + + if not zone_in.endswith('.'): + zone_in += "." + + private_zone = bool(vpc_id and vpc_region) + + if zone_in in [z['Name'] for z in matching_zones]: + if hosted_zone_id: + changed, result = delete_hosted_id(module, client, hosted_zone_id, matching_zones) + else: + if private_zone: + changed, result = delete_private(module, client, matching_zones, vpc_id, vpc_region) + else: + changed, result = delete_public(module, client, matching_zones) + else: + changed = False + result = "No zone to delete." + + return changed, result + + +def main(): + argument_spec = dict( + zone=dict(required=True), + state=dict(default='present', choices=['present', 'absent']), + vpc_id=dict(default=None), + vpc_region=dict(default=None), + comment=dict(default=''), + hosted_zone_id=dict(), + delegation_set_id=dict(), + ) + + mutually_exclusive = [ + ['delegation_set_id', 'vpc_id'], + ['delegation_set_id', 'vpc_region'], + ] + + module = AnsibleAWSModule( + argument_spec=argument_spec, + mutually_exclusive=mutually_exclusive, + supports_check_mode=True, + ) + + zone_in = module.params.get('zone').lower() + state = module.params.get('state').lower() + vpc_id = module.params.get('vpc_id') + vpc_region = module.params.get('vpc_region') + + if not zone_in.endswith('.'): + zone_in += "." + + private_zone = bool(vpc_id and vpc_region) + + client = module.client('route53') + + zones = find_zones(module, client, zone_in, private_zone) + if state == 'present': + changed, result = create(module, client, matching_zones=zones) + elif state == 'absent': + changed, result = delete(module, client, matching_zones=zones) + + if isinstance(result, dict): + module.exit_json(changed=changed, result=result, **result) + else: + module.exit_json(changed=changed, result=result) + + +if __name__ == '__main__': + main() diff --git a/tests/integration/targets/route53/aliases b/tests/integration/targets/route53/aliases new file mode 100644 index 00000000000..f6cc7ad00c5 --- /dev/null +++ b/tests/integration/targets/route53/aliases @@ -0,0 +1,3 @@ +route53_info +cloud/aws +shippable/aws/group2 diff --git a/tests/integration/targets/route53/defaults/main.yml b/tests/integration/targets/route53/defaults/main.yml new file mode 100644 index 00000000000..cc0d3b78d04 --- /dev/null +++ b/tests/integration/targets/route53/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for route53 tests diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml new file mode 100644 index 00000000000..de332a7ba0c --- /dev/null +++ b/tests/integration/targets/route53/tasks/main.yml @@ -0,0 +1,252 @@ +--- +# tasks file for Route53 integration tests + +- set_fact: + zone_one: '{{ resource_prefix | replace("-", "") }}.one.fakeansible.com.' + zone_two: '{{ resource_prefix | replace("-", "") }}.two.fakeansible.com.' +- debug: msg='Set zones {{ zone_one }} and {{ zone_two }}' + +- name: Test basics (new zone, A and AAAA records) + 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 }}" + route53: + region: null + block: + - route53_zone: + zone: '{{ zone_one }}' + comment: Created in Ansible test {{ resource_prefix }} + register: z1 + + - assert: + that: + - z1 is success + - z1 is changed + - "z1.comment == 'Created in Ansible test {{ resource_prefix }}'" + + - name: Get zone details + route53_info: + query: hosted_zone + hosted_zone_id: '{{ z1.zone_id }}' + hosted_zone_method: details + register: hosted_zones + + - name: Assert newly created hosted zone only has NS and SOA records + assert: + that: + - hosted_zones.HostedZone.ResourceRecordSetCount == 2 + + - name: Create A record using zone fqdn + route53: + state: present + zone: '{{ zone_one }}' + record: 'qdn_test.{{ zone_one }}' + type: A + value: 1.2.3.4 + register: qdn + - assert: + that: + - qdn is not failed + - qdn is changed + + - name: Create same A record using zone non-qualified domain + route53: + state: present + zone: '{{ zone_one[:-1] }}' + record: 'qdn_test.{{ zone_one[:-1] }}' + type: A + value: 1.2.3.4 + register: non_qdn + - assert: + that: + - non_qdn is not failed + - non_qdn is not changed + + - name: Create A record using zone ID + route53: + state: present + hosted_zone_id: '{{ z1.zone_id }}' + record: 'zid_test.{{ zone_one }}' + type: A + value: 1.2.3.4 + register: zid + - assert: + that: + - zid is not failed + - zid is changed + + - name: Create a multi-value A record with values in different order + route53: + state: present + zone: '{{ zone_one }}' + record: 'order_test.{{ zone_one }}' + type: A + value: + - 4.5.6.7 + - 1.2.3.4 + register: mv_a_record + - assert: + that: + - mv_a_record is not failed + - mv_a_record is changed + + - name: Create same multi-value A record with values in different order + route53: + state: present + zone: '{{ zone_one }}' + record: 'order_test.{{ zone_one }}' + type: A + value: + - 4.5.6.7 + - 1.2.3.4 + register: mv_a_record + - assert: + that: + - mv_a_record is not failed + - mv_a_record is not changed + + - name: get Route53 A record information + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'order_test.{{ zone_one }}' + max_items: 50 + register: records + - assert: + that: + - records.ResourceRecordSets|length == 3 + - records.ResourceRecordSets[0].ResourceRecords|length == 2 + - records.ResourceRecordSets[0].ResourceRecords[0].Value == "4.5.6.7" + - records.ResourceRecordSets[0].ResourceRecords[1].Value == "1.2.3.4" + + - name: Remove a member from multi-value A record with values in different order + route53: + state: present + zone: '{{ zone_one }}' + record: 'order_test.{{ zone_one }}' + type: A + value: + - 4.5.6.7 + register: del_a_record + ignore_errors: true + - name: This should fail, because `overwrite` is false + assert: + that: + - del_a_record is failed + + - name: Remove a member from multi-value A record with values in different order + route53: + state: present + zone: '{{ zone_one }}' + record: 'order_test.{{ zone_one }}' + overwrite: true + type: A + value: + - 4.5.6.7 + register: del_a_record + ignore_errors: true + - name: This should not fail, because `overwrite` is true + assert: + that: + - del_a_record is not failed + - del_a_record is changed + + - name: get Route53 zone A record information + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'order_test.{{ zone_one }}' + max_items: 50 + register: records + - assert: + that: + - records.ResourceRecordSets|length == 3 + - records.ResourceRecordSets[0].ResourceRecords|length == 1 + - records.ResourceRecordSets[0].ResourceRecords[0].Value == "4.5.6.7" + + - name: Create a LetsEncrypt CAA record + route53: + state: present + zone: '{{ zone_one }}' + record: '{{ zone_one }}' + type: CAA + value: + - 0 issue "letsencrypt.org;" + - 0 issuewild "letsencrypt.org;" + overwrite: true + register: caa + - assert: + that: + - caa is not failed + - caa is changed + + - name: Re-create the same LetsEncrypt CAA record + route53: + state: present + zone: '{{ zone_one }}' + record: '{{ zone_one }}' + type: CAA + value: + - 0 issue "letsencrypt.org;" + - 0 issuewild "letsencrypt.org;" + overwrite: true + register: caa + - assert: + that: + - caa is not failed + - caa is not changed + + - name: Re-create the same LetsEncrypt CAA record in opposite-order + route53: + state: present + zone: '{{ zone_one }}' + record: '{{ zone_one }}' + type: CAA + value: + - 0 issuewild "letsencrypt.org;" + - 0 issue "letsencrypt.org;" + overwrite: true + register: caa + - name: This should not be changed, as CAA records are not order sensitive + assert: + that: + - caa is not failed + - caa is not changed + + + always: + - route53_info: + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + register: z1_records + - debug: var=z1_records + - name: Loop over A/AAAA/CNAME records and delete them + route53: + state: absent + zone: '{{ zone_one }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + - name: Delete test zone one '{{ zone_one }}' + route53_zone: + state: absent + zone: '{{ zone_one }}' + register: delete_one + ignore_errors: yes + retries: 10 + until: delete_one is not failed + - name: Delete test zone two '{{ zone_two }}' + route53_zone: + state: absent + zone: '{{ zone_two }}' + register: delete_two + ignore_errors: yes + retries: 10 + until: delete_two is not failed + when: false diff --git a/tests/integration/targets/route53/vars/main.yml b/tests/integration/targets/route53/vars/main.yml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/integration/targets/route53_zone/aliases b/tests/integration/targets/route53_zone/aliases new file mode 100644 index 00000000000..6e3860bee23 --- /dev/null +++ b/tests/integration/targets/route53_zone/aliases @@ -0,0 +1,2 @@ +cloud/aws +shippable/aws/group2 diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml new file mode 100644 index 00000000000..132d58c5d2e --- /dev/null +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -0,0 +1,393 @@ +--- +- block: + + # ============================================================ + - name: set connection information for all tasks + set_fact: + aws_connection_info: &aws_connection_info + aws_access_key: "{{ aws_access_key }}" + aws_secret_key: "{{ aws_secret_key }}" + security_token: "{{ security_token }}" + region: "{{ aws_region }}" + no_log: true + + - name: Create VPC for use in testing + ec2_vpc_net: + name: "{{ resource_prefix }}-vpc" + cidr_block: 10.22.32.0/23 + tags: + Name: Ansible ec2_instance Testing VPC + tenancy: default + <<: *aws_connection_info + register: testing_vpc + + # ============================================================ + - name: Create a public zone + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: original comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - output.comment == 'original comment' + - output.name == '{{ resource_prefix }}.public.' + - not output.private_zone + + # ============================================================ + - name: Create a public zone (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.check.public" + comment: original comment + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - output.comment == 'original comment' + - output.name == '{{ resource_prefix }}.check.public.' + - not output.private_zone + + # ============================================================ + - name: Do an idemptotent update of a public zone + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: original comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - not output.changed + - output.comment == 'original comment' + - output.name == '{{ resource_prefix }}.public.' + - not output.private_zone + + - name: Do an idemptotent update of a public zone (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: original comment + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - not output.changed + - output.comment == 'original comment' + - output.name == '{{ resource_prefix }}.public.' + - not output.private_zone + + # ============================================================ + - name: Update comment of a public zone + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: updated comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - output.result.comment == "updated comment" + + - name: Update comment of a public zone (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: updated comment for check + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - output.result.comment == "updated comment for check" + + # ============================================================ + - name: Delete public zone (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.public" + state: absent + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + - name: Delete public zone + route53_zone: + zone: "{{ resource_prefix }}.public" + state: absent + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + # ============================================================ + - name: Create a private zone (CHECK MODE) + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: original comment + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + + - name: Create a private zone + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: original comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + # ============================================================ + - name: Idemptotent update a private zone + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: original comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - not output.changed + - "'There is already a private hosted zone in the same region with the same VPC' in output.msg" + + - name: Idemptotent update a private zone (CHECK MODE) + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: original comment + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - not output.changed + - "'There is already a private hosted zone in the same region with the same VPC' in output.msg" + + # ============================================================ + - name: Update private zone comment + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: updated_comment + state: present + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - output.result.comment == "updated_comment" + + - name: Update private zone comment (CHECK MODE) + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + comment: updated_comment check + state: present + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - output.result.comment == "updated_comment check" + + # ============================================================ + - name: Try to delete private zone without setting vpc_id and vpc_region + route53_zone: + zone: "{{ resource_prefix }}.private" + state: absent + <<: *aws_connection_info + register: output + + - assert: + that: + - not output.changed + - "output.result == 'No zone to delete.'" + + - name: Try to delete private zone without setting vpc_id and vpc_region (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.private" + state: absent + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - not output.changed + - "output.result == 'No zone to delete.'" + + # ============================================================ + - name: Try to delete a public zone that does not exists + route53_zone: + zone: "{{ resource_prefix }}.publicfake" + comment: original comment + state: absent + <<: *aws_connection_info + register: output + + - assert: + that: + - not output.changed + - "output.result == 'No zone to delete.'" + + - name: Try to delete a public zone that does not exists (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.publicfake" + comment: original comment + state: absent + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - not output.changed + - "output.result == 'No zone to delete.'" + + # ============================================================ + - name: Delete private zone (CHECK MODE) + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + state: absent + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + - name: Delete private zone + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + state: absent + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + # ============================================================ + - name: Create a public zone + route53_zone: + zone: "{{ resource_prefix }}.public2" + comment: this is an example + state: present + <<: *aws_connection_info + register: new_zone + + # Delete zone using its id + - name: Delete zone using attribute hosted_zone_id (CHECK MODE) + route53_zone: + zone: "{{ resource_prefix }}.public2" + hosted_zone_id: "{{new_zone.zone_id}}" + state: absent + <<: *aws_connection_info + register: output + check_mode: yes + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + - name: Delete zone using attribute hosted_zone_id + route53_zone: + zone: "{{ resource_prefix }}.public2" + hosted_zone_id: "{{new_zone.zone_id}}" + state: absent + <<: *aws_connection_info + register: output + + - assert: + that: + - output.changed + - "'Successfully deleted' in output.result" + + # ============================================================ + always: + - name: Ensure public zone is deleted + route53_zone: + zone: "{{ item }}" + state: absent + <<: *aws_connection_info + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 + with_items: + - "{{ resource_prefix }}.public" + - "{{ resource_prefix }}.public2" + + - name: Ensure private zone is deleted + route53_zone: + vpc_id: "{{ testing_vpc.vpc.id }}" + vpc_region: "{{ aws_region }}" + zone: "{{ resource_prefix }}.private" + state: absent + <<: *aws_connection_info + 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: 10.22.32.0/23 + state: absent + <<: *aws_connection_info + register: removed + until: removed is not failed + ignore_errors: yes + retries: 10 From 694dc7b5028844b57834d2fc1fe234e815a1684a Mon Sep 17 00:00:00 2001 From: jillr Date: Tue, 3 Mar 2020 19:43:21 +0000 Subject: [PATCH 02/81] migration test cleanup This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/13b104b912784bb31a0bff23eed4c27b0f5e0283 --- tests/integration/targets/route53_zone/tasks/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index 132d58c5d2e..c75f45b26b2 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -1,5 +1,8 @@ --- -- block: +- name: route53_zone tests + collections: + - ansible.amazon + block: # ============================================================ - name: set connection information for all tasks From 2d6d4d3ba267ea94ea97e7674aa3a997d831eaa8 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 25 Mar 2020 15:39:40 -0700 Subject: [PATCH 03/81] Rename collection (#12) * Rename core collection Rename references to ansible.amazon to amazon.aws. * Rename community.amazon to community.aws Fix pep8 line lengths for rewritten amazon.aws imports * Missed a path in shippable.sh * Dependency repos moved This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/235c5db571cc45db5839476c94356c9b91e1f228 --- plugins/modules/route53.py | 4 ++-- plugins/modules/route53_health_check.py | 6 +++--- plugins/modules/route53_info.py | 6 +++--- plugins/modules/route53_zone.py | 6 +++--- tests/integration/targets/route53_zone/tasks/main.yml | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index dda106e3f9b..3347f51aaca 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -137,7 +137,7 @@ - Bruce Pennypacker (@bpennypacker) - Mike Buzzetti (@jimbydamonk) extends_documentation_fragment: -- ansible.amazon.aws +- amazon.aws.aws ''' @@ -378,7 +378,7 @@ HAS_BOTO = False from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info MINIMUM_BOTO_VERSION = '2.28.0' diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 778b4c0595d..c974c72a5f1 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -83,8 +83,8 @@ type: int author: "zimbatm (@zimbatm)" extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 ''' @@ -133,7 +133,7 @@ # import module snippets from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info # Things that can't get changed: diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index a3d8d76f6de..d3d2f3abae9 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -133,8 +133,8 @@ type: str author: Karen Cheng (@Etherdaemon) extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 ''' @@ -222,7 +222,7 @@ HAS_BOTO3 = False from ansible.module_utils.basic import AnsibleModule -from ansible_collections.ansible.amazon.plugins.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info from ansible.module_utils._text import to_native diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 2d13cb9073e..698ef524996 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -53,8 +53,8 @@ - Note that you can't associate a reusable delegation set with a private hosted zone. type: str extends_documentation_fragment: -- ansible.amazon.aws -- ansible.amazon.ec2 +- amazon.aws.aws +- amazon.aws.ec2 author: "Christopher Troup (@minichate)" ''' @@ -123,7 +123,7 @@ ''' import time -from ansible_collections.ansible.amazon.plugins.module_utils.aws.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.aws.core import AnsibleAWSModule try: from botocore.exceptions import BotoCoreError, ClientError diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index c75f45b26b2..f6baa68fffc 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -1,7 +1,7 @@ --- - name: route53_zone tests collections: - - ansible.amazon + - amazon.aws block: # ============================================================ From 8b584914a4067260b827717c4327f482ccc22253 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 19 May 2020 16:06:12 -0700 Subject: [PATCH 04/81] Remove METADATA and cleanup galaxy.yml (#70) * Remove ANSIBLE_METADATA entirely, see ansible/ansible/pull/69454. Remove `license` field from galaxy.yml, in favor of `license_file`. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/05672a64e2362cc2d865b5af6a57da6bc3cd08e3 --- plugins/modules/route53.py | 5 ----- plugins/modules/route53_health_check.py | 5 ----- plugins/modules/route53_info.py | 5 ----- plugins/modules/route53_zone.py | 5 ----- 4 files changed, 20 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 3347f51aaca..c93d941f39f 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -8,11 +8,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' --- module: route53 diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index c974c72a5f1..414f27a3eee 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -6,11 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' --- module: route53_health_check diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index d3d2f3abae9..454875bb47f 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -6,11 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' module: route53_info short_description: Retrieves route53 details using AWS methods diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 698ef524996..3eee17506f4 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -6,11 +6,6 @@ __metaclass__ = type -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['stableinterface'], - 'supported_by': 'community'} - - DOCUMENTATION = ''' module: route53_zone short_description: add or delete Route53 zones From ec9da224029f48462dd590f5b740302f123f015a Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 16 Jun 2020 11:23:52 -0700 Subject: [PATCH 05/81] Collections related fixes for CI (#96) * Update module deprecations Switch version to `removed_at_date` * Don't install amazon.aws from galaxy We've been using galaxy to install amazon.aws in shippable, but that doesn't really work if we aren't publising faster. Get that collection from git so it is most up to date. * We need to declare python test deps now * missed a python dep This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/7cd211e9383db26bc2aa4cc06e657cf60ed0acc0 --- plugins/modules/route53_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 454875bb47f..cb6f74b369a 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -463,7 +463,7 @@ def main(): ], ) if module._name == 'route53_facts': - module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", version='2.13') + module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", date='2021-12-01', collection_name='community.aws') # Validate Requirements if not (HAS_BOTO or HAS_BOTO3): From b8bf908ecfea9276a6724c6c69550ab0ada157b7 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Wed, 17 Jun 2020 01:24:54 +0530 Subject: [PATCH 06/81] Update Examples with FQCN (#67) Updated module examples with FQCN Signed-off-by: Abhijeet Kasurde This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/98173aefbbceed7fc0d9db62687b73f96a55a999 --- plugins/modules/route53.py | 252 ++++++++++++------------ plugins/modules/route53_health_check.py | 10 +- plugins/modules/route53_info.py | 20 +- plugins/modules/route53_zone.py | 8 +- 4 files changed, 146 insertions(+), 144 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index c93d941f39f..385a1d10ec4 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -212,108 +212,110 @@ sample: foo.bar.com. ''' -EXAMPLES = ''' -# Add new.foo.com as an A record with 3 IPs and wait until the changes have been replicated -- route53: - state: present - zone: foo.com - record: new.foo.com - type: A - ttl: 7200 - value: 1.1.1.1,2.2.2.2,3.3.3.3 - wait: yes - -# Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated -- route53: - state: present - zone: foo.com - record: new.foo.com - type: A - ttl: 7200 - value: - - 1.1.1.1 - - 2.2.2.2 - - 3.3.3.3 - wait: yes - -# Retrieve the details for new.foo.com -- route53: - state: get - zone: foo.com - record: new.foo.com - type: A +EXAMPLES = r''' +- name: Add new.foo.com as an A record with 3 IPs and wait until the changes have been replicated + community.aws.route53: + state: present + zone: foo.com + record: new.foo.com + type: A + ttl: 7200 + value: 1.1.1.1,2.2.2.2,3.3.3.3 + wait: yes + +- name: Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated + community.aws.route53: + state: present + zone: foo.com + record: new.foo.com + type: A + ttl: 7200 + value: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + wait: yes + +- name: Retrieve the details for new.foo.com + community.aws.route53: + state: get + zone: foo.com + record: new.foo.com + type: A register: rec -# Delete new.foo.com A record using the results from the get command -- route53: - state: absent - zone: foo.com - record: "{{ rec.set.record }}" - ttl: "{{ rec.set.ttl }}" - type: "{{ rec.set.type }}" - value: "{{ rec.set.value }}" +- name: Delete new.foo.com A record using the results from the get command + community.aws.route53: + state: absent + zone: foo.com + record: "{{ rec.set.record }}" + ttl: "{{ rec.set.ttl }}" + type: "{{ rec.set.type }}" + value: "{{ rec.set.value }}" # Add an AAAA record. Note that because there are colons in the value # that the IPv6 address must be quoted. Also shows using the old form command=create. -- route53: - command: create - zone: foo.com - record: localhost.foo.com - type: AAAA - ttl: 7200 - value: "::1" - -# Add a SRV record with multiple fields for a service on port 22222 +- name: Add an AAAA record + community.aws.route53: + command: create + zone: foo.com + record: localhost.foo.com + type: AAAA + ttl: 7200 + value: "::1" + # For more information on SRV records see: # https://en.wikipedia.org/wiki/SRV_record -- route53: - state: present - zone: foo.com - record: "_example-service._tcp.foo.com" - type: SRV - value: "0 0 22222 host1.foo.com,0 0 22222 host2.foo.com" - -# Add a TXT record. Note that TXT and SPF records must be surrounded +- name: Add a SRV record with multiple fields for a service on port 22222 + community.aws.route53: + state: present + zone: foo.com + record: "_example-service._tcp.foo.com" + type: SRV + value: "0 0 22222 host1.foo.com,0 0 22222 host2.foo.com" + +# Note that TXT and SPF records must be surrounded # by quotes when sent to Route 53: -- route53: - state: present - zone: foo.com - record: localhost.foo.com - type: TXT - ttl: 7200 - value: '"bar"' - -# Add an alias record that points to an Amazon ELB: -- route53: - state: present - zone: foo.com - record: elb.foo.com - type: A - value: "{{ elb_dns_name }}" - alias: True - alias_hosted_zone_id: "{{ elb_zone_id }}" - -# Retrieve the details for elb.foo.com -- route53: - state: get - zone: foo.com - record: elb.foo.com - type: A +- name: Add a TXT record. + community.aws.route53: + state: present + zone: foo.com + record: localhost.foo.com + type: TXT + ttl: 7200 + value: '"bar"' + +- name: Add an alias record that points to an Amazon ELB + community.aws.route53: + state: present + zone: foo.com + record: elb.foo.com + type: A + value: "{{ elb_dns_name }}" + alias: True + alias_hosted_zone_id: "{{ elb_zone_id }}" + +- name: Retrieve the details for elb.foo.com + community.aws.route53: + state: get + zone: foo.com + record: elb.foo.com + type: A register: rec -# Delete an alias record using the results from the get command -- route53: - state: absent - zone: foo.com - record: "{{ rec.set.record }}" - ttl: "{{ rec.set.ttl }}" - type: "{{ rec.set.type }}" - value: "{{ rec.set.value }}" - alias: True - alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}" - -# Add an alias record that points to an Amazon ELB and evaluates it health: -- route53: +- name: Delete an alias record using the results from the get command + community.aws.route53: + state: absent + zone: foo.com + record: "{{ rec.set.record }}" + ttl: "{{ rec.set.ttl }}" + type: "{{ rec.set.type }}" + value: "{{ rec.set.value }}" + alias: True + alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}" + +- name: Add an alias record that points to an Amazon ELB and evaluates it health + community.aws.route53: state: present zone: foo.com record: elb.foo.com @@ -323,39 +325,39 @@ alias_hosted_zone_id: "{{ elb_zone_id }}" alias_evaluate_target_health: True -# Add an AAAA record with Hosted Zone ID. -- route53: - state: present - zone: foo.com - hosted_zone_id: Z2AABBCCDDEEFF - record: localhost.foo.com - type: AAAA - ttl: 7200 - value: "::1" - -# Use a routing policy to distribute traffic: -- route53: - state: present - zone: foo.com - record: www.foo.com - type: CNAME - value: host1.foo.com - ttl: 30 - # Routing policy - identifier: "host1@www" - weight: 100 - health_check: "d994b780-3150-49fd-9205-356abdd42e75" - -# Add a CAA record (RFC 6844): -- route53: - state: present - zone: example.com - record: example.com - type: CAA - value: - - 0 issue "ca.example.net" - - 0 issuewild ";" - - 0 iodef "mailto:security@example.com" +- name: Add an AAAA record with Hosted Zone ID + community.aws.route53: + state: present + zone: foo.com + hosted_zone_id: Z2AABBCCDDEEFF + record: localhost.foo.com + type: AAAA + ttl: 7200 + value: "::1" + +- name: Use a routing policy to distribute traffic + community.aws.route53: + state: present + zone: foo.com + record: www.foo.com + type: CNAME + value: host1.foo.com + ttl: 30 + # Routing policy + identifier: "host1@www" + weight: 100 + health_check: "d994b780-3150-49fd-9205-356abdd42e75" + +- name: Add a CAA record (RFC 6844) + community.aws.route53: + state: present + zone: example.com + record: example.com + type: CAA + value: + - 0 issue "ca.example.net" + - 0 issuewild ";" + - 0 iodef "mailto:security@example.com" ''' diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 414f27a3eee..80f6691407a 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -84,8 +84,8 @@ ''' EXAMPLES = ''' -# Create a health-check for host1.example.com and use it in record -- route53_health_check: +- name: Create a health-check for host1.example.com and use it in record + community.aws.route53_health_check: state: present fqdn: host1.example.com type: HTTP_STR_MATCH @@ -95,7 +95,7 @@ failure_threshold: 2 register: my_health_check -- route53: +- community.aws.route53: action: create zone: "example.com" type: CNAME @@ -107,8 +107,8 @@ weight: 100 health_check: "{{ my_health_check.health_check.id }}" -# Delete health-check -- route53_health_check: +- name: Delete health-check + community.aws.route53_health_check: state: absent fqdn: host1.example.com diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index cb6f74b369a..466e39bd79f 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -136,19 +136,19 @@ EXAMPLES = ''' # Simple example of listing all hosted zones - name: List all hosted zones - route53_info: + community.aws.route53_info: query: hosted_zone register: hosted_zones # Getting a count of hosted zones - name: Return a count of all hosted zones - route53_info: + community.aws.route53_info: query: hosted_zone hosted_zone_method: count register: hosted_zone_count - name: List the first 20 resource record sets in a given hosted zone - route53_info: + community.aws.route53_info: profile: account_name query: record_sets hosted_zone_id: ZZZ1111112222 @@ -156,33 +156,33 @@ register: record_sets - name: List first 20 health checks - route53_info: + community.aws.route53_info: query: health_check health_check_method: list max_items: 20 register: health_checks - name: Get health check last failure_reason - route53_info: + community.aws.route53_info: query: health_check health_check_method: failure_reason health_check_id: 00000000-1111-2222-3333-12345678abcd register: health_check_failure_reason - name: Retrieve reusable delegation set details - route53_info: + community.aws.route53_info: query: reusable_delegation_set delegation_set_id: delegation id register: delegation_sets - name: setup of example for using next_marker - route53_info: + community.aws.route53_info: query: hosted_zone max_items: 1 register: first_info - name: example for using next_marker - route53_info: + community.aws.route53_info: query: hosted_zone next_marker: "{{ first_info.NextMarker }}" max_items: 1 @@ -191,12 +191,12 @@ - name: retrieve host entries starting with host1.workshop.test.io block: - name: grab zone id - route53_zone: + community.aws.route53_zone: zone: "test.io" register: AWSINFO - name: grab Route53 record information - route53_info: + community.aws.route53_info: type: A query: record_sets hosted_zone_id: "{{ AWSINFO.zone_id }}" diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 3eee17506f4..b7a2b6858ce 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -56,24 +56,24 @@ EXAMPLES = ''' - name: create a public zone - route53_zone: + community.aws.route53_zone: zone: example.com comment: this is an example - name: delete a public zone - route53_zone: + community.aws.route53_zone: zone: example.com state: absent - name: create a private zone - route53_zone: + community.aws.route53_zone: zone: devel.example.com vpc_id: '{{ myvpc_id }}' vpc_region: us-west-2 comment: developer domain - name: create a public zone associated with a specific reusable delegation set - route53_zone: + community.aws.route53_zone: zone: example.com comment: reusable delegation set example delegation_set_id: A1BCDEF2GHIJKL From 25d2333ec1383aabce513ac2f87488861803a60b Mon Sep 17 00:00:00 2001 From: flowerysong Date: Tue, 16 Jun 2020 19:30:00 -0400 Subject: [PATCH 07/81] Update module_utils paths to remove aws subdir (#23) Co-authored-by: Ezekiel Hendrickson This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/10853d9441a586ba177006dd889325cfb24a3dd6 --- plugins/modules/route53_zone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index b7a2b6858ce..bcab3b2e167 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -118,7 +118,7 @@ ''' import time -from ansible_collections.amazon.aws.plugins.module_utils.aws.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule try: from botocore.exceptions import BotoCoreError, ClientError From c8e9bf9d32c98bb8f7b70c375aa764cbc1c298dd Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Wed, 17 Jun 2020 09:31:32 -0700 Subject: [PATCH 08/81] Update docs (#99) * Update docs Remove .git from repo url so links in readme will generate correctly Add required ansible version Run latest version of add_docs.py Add version_added string to modules * galaxy.yml was missing authors This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/96ee268e5267f5b12c3d59892bc1279f75aa3135 --- plugins/modules/route53.py | 1 + plugins/modules/route53_health_check.py | 1 + plugins/modules/route53_info.py | 1 + plugins/modules/route53_zone.py | 1 + 4 files changed, 4 insertions(+) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 385a1d10ec4..72ca73faeb1 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -11,6 +11,7 @@ DOCUMENTATION = ''' --- module: route53 +version_added: 1.0.0 short_description: add or delete entries in Amazons Route53 DNS service description: - Creates and deletes DNS records in Amazons Route53 service diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 80f6691407a..77fcf912e08 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' --- module: route53_health_check +version_added: 1.0.0 short_description: Add or delete health-checks in Amazons Route53 DNS service description: - Creates and deletes DNS Health checks in Amazons Route53 service. diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 466e39bd79f..77d72603ffe 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' module: route53_info short_description: Retrieves route53 details using AWS methods +version_added: 1.0.0 description: - Gets various details related to Route53 zone, record set or health check details. - This module was called C(route53_facts) before Ansible 2.9. The usage did not change. diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index bcab3b2e167..6467dd04527 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -9,6 +9,7 @@ DOCUMENTATION = ''' module: route53_zone short_description: add or delete Route53 zones +version_added: 1.0.0 description: - Creates and deletes Route53 private and public zones. requirements: [ boto3 ] From f9f89949a8854acec7743235afa8a1360917b46d Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Thu, 16 Jul 2020 01:31:41 +0530 Subject: [PATCH 09/81] Docs: sanity fixes (#133) Signed-off-by: Abhijeet Kasurde This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/059cf9efc95bb976de21ab4f8e4d9ddd001983fc --- plugins/modules/route53.py | 7 ++++--- plugins/modules/route53_info.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 72ca73faeb1..ad25e38ecd0 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -8,7 +8,7 @@ __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: route53 version_added: 1.0.0 @@ -69,6 +69,7 @@ - The new value when creating a DNS record. YAML lists or multiple comma-spaced values are allowed for non-alias records. - When deleting a record all values for the record must be specified or Route53 will not delete it. type: list + elements: str overwrite: description: - Whether an existing record should be overwritten on create if values do not match. @@ -137,7 +138,7 @@ ''' -RETURN = ''' +RETURN = r''' nameservers: description: Nameservers associated with the zone. returned: when state is 'get' @@ -501,7 +502,7 @@ def main(): alias=dict(type='bool'), alias_hosted_zone_id=dict(type='str'), alias_evaluate_target_health=dict(type='bool', default=False), - value=dict(type='list'), + value=dict(type='list', elements='str'), overwrite=dict(type='bool'), retry_interval=dict(type='int', default=500), private_zone=dict(type='bool', default=False), diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 77d72603ffe..3a937a40653 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -6,7 +6,7 @@ __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' module: route53_info short_description: Retrieves route53 details using AWS methods version_added: 1.0.0 @@ -134,7 +134,7 @@ ''' -EXAMPLES = ''' +EXAMPLES = r''' # Simple example of listing all hosted zones - name: List all hosted zones community.aws.route53_info: @@ -436,7 +436,7 @@ def main(): 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS' ]), dns_name=dict(), - resource_id=dict(type='list', aliases=['resource_ids']), + resource_id=dict(type='list', aliases=['resource_ids'], elements='str'), health_check_id=dict(), hosted_zone_method=dict(choices=[ 'details', From e06365d84880a4d6fbf40af36c844d33c59b6432 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 12 Aug 2020 13:06:35 +0200 Subject: [PATCH 10/81] Bulk migration to AnsibleAWSModule (#173) * Update comments to reference AnsibleAWSModule rather than AnsibleModule * Bulk re-order imports and split onto one from import per-line. * Add AnsibleAWSModule imports * Migrate boto 2 based modules to AnsibleAWSModule * Move boto3-only modules over to AnsibleAWSModule * Remove extra ec2_argument_spec calls - not needed now we're using AnsibleAWSModule * Remove most HAS_BOTO3 code, it's handled by AnsibleAWSModule * Handle missing Boto 2 consistently (HAS_BOTO) * Remove AnsibleModule imports * Changelog fragment This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/818c6d2faa046974a9bdfa9346122d11e5bef3b1 --- plugins/modules/route53.py | 16 ++++++++-------- plugins/modules/route53_health_check.py | 14 ++++++-------- plugins/modules/route53_info.py | 23 ++++++++++------------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index ad25e38ecd0..6b69363f2d0 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -372,12 +372,12 @@ from boto.route53 import Route53Connection from boto.route53.record import Record, ResourceRecordSets from boto.route53.status import Status - HAS_BOTO = True except ImportError: - HAS_BOTO = False + pass # Handled by HAS_BOTO -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO MINIMUM_BOTO_VERSION = '2.28.0' @@ -491,8 +491,7 @@ def to_dict(rset, zone_in, zone_id): def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( + argument_spec = dict( state=dict(type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']), zone=dict(type='str'), hosted_zone_id=dict(type='str'), @@ -514,9 +513,9 @@ def main(): vpc_id=dict(type='str'), wait=dict(type='bool', default=False), wait_timeout=dict(type='int', default=300), - )) + ) - module = AnsibleModule( + module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, required_one_of=[['zone', 'hosted_zone_id']], @@ -537,6 +536,7 @@ def main(): region=('identifier',), weight=('identifier',), ), + check_boto3=False, ) if not HAS_BOTO: diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 77fcf912e08..a1f9c9a268c 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -123,13 +123,13 @@ from boto import route53 from boto.route53 import Route53Connection, exception from boto.route53.healthcheck import HealthCheck - HAS_BOTO = True except ImportError: - HAS_BOTO = False + pass # Handled by HAS_BOTO # import module snippets -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ec2_argument_spec, get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO # Things that can't get changed: @@ -280,8 +280,7 @@ def update_health_check(conn, health_check_id, health_check_version, health_chec def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( + argument_spec = dict( state=dict(choices=['present', 'absent'], default='present'), ip_address=dict(), port=dict(type='int'), @@ -292,8 +291,7 @@ def main(): request_interval=dict(type='int', choices=[10, 30], default=30), failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default=3), ) - ) - module = AnsibleModule(argument_spec=argument_spec) + module = AnsibleAWSModule(argument_spec=argument_spec, check_boto3=False) if not HAS_BOTO: module.fail_json(msg='boto 2.27.0+ required for this module') diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 3a937a40653..64caeaef23c 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -207,20 +207,18 @@ try: import boto import botocore - HAS_BOTO = True -except ImportError: - HAS_BOTO = False - -try: import boto3 - HAS_BOTO3 = True except ImportError: - HAS_BOTO3 = False + pass # Handled by HAS_BOTO and HAS_BOTO3 -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn, ec2_argument_spec, get_aws_connection_info from ansible.module_utils._text import to_native +from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 + def get_hosted_zone(client, module): params = dict() @@ -416,8 +414,7 @@ def hosted_zone_details(client, module): def main(): - argument_spec = ec2_argument_spec() - argument_spec.update(dict( + argument_spec = dict( query=dict(choices=[ 'change', 'checker_ip_range', @@ -454,14 +451,14 @@ def main(): 'tags', ], default='list'), ) - ) - module = AnsibleModule( + module = AnsibleAWSModule( argument_spec=argument_spec, supports_check_mode=True, mutually_exclusive=[ ['hosted_zone_method', 'health_check_method'], ], + check_boto3=False, ) if module._name == 'route53_facts': module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", date='2021-12-01', collection_name='community.aws') From d8df308b973a7d1b1526a9698e757d80d1434c94 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 26 Aug 2020 11:35:32 +0200 Subject: [PATCH 11/81] Cleanup: Bulk Migration from boto3_conn to module.client() (#188) * Migrate from boto3_conn to module.client * Simplify error handling when creating connections * Simplify Region handling * Remove unused imports * Changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6bdf00d2198927bdaa119ae76ddd379a8b6eeb3d --- plugins/modules/route53_info.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 64caeaef23c..38d0bc540f5 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -214,8 +214,6 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_conn -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 @@ -467,8 +465,10 @@ def main(): if not (HAS_BOTO or HAS_BOTO3): module.fail_json(msg='json and boto/boto3 is required.') - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True) - route53 = boto3_conn(module, conn_type='client', resource='route53', region=region, endpoint=ec2_url, **aws_connect_kwargs) + try: + route53 = module.client('route53') + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') invocations = { 'change': change_details, From e801557dcf847cc9963d348aafc76714c3d6dba6 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 7 Dec 2020 08:48:06 +0100 Subject: [PATCH 12/81] Sanity test / doc-default-does-not-match-spec fixups (#320) * Sanity test / doc-default-does-not-match-spec fixups * General docs cleanup This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/b22f2804372a3d35683debffb9a1c0908a819ca1 --- plugins/modules/route53.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 6b69363f2d0..6caf385002f 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -12,14 +12,14 @@ --- module: route53 version_added: 1.0.0 -short_description: add or delete entries in Amazons Route53 DNS service +short_description: add or delete entries in Amazons Route 53 DNS service description: - - Creates and deletes DNS records in Amazons Route53 service + - Creates and deletes DNS records in Amazons Route 53 service. options: state: description: - Specifies the state of the resource record. As of Ansible 2.4, the I(command) option has been changed - to I(state) as default and the choices 'present' and 'absent' have been added, but I(command) still works as well. + to I(state) as default and the choices C(present) and C(absent) have been added, but I(command) still works as well. required: true aliases: [ 'command' ] choices: [ 'present', 'absent', 'get', 'create', 'delete' ] @@ -53,8 +53,8 @@ alias: description: - Indicates if this is an alias record. + - Defaults to C(false). type: bool - default: false alias_hosted_zone_id: description: - The hosted zone identifier. @@ -67,7 +67,7 @@ value: description: - The new value when creating a DNS record. YAML lists or multiple comma-spaced values are allowed for non-alias records. - - When deleting a record all values for the record must be specified or Route53 will not delete it. + - When deleting a record all values for the record must be specified or Route 53 will not delete it. type: list elements: str overwrite: @@ -76,14 +76,14 @@ type: bool retry_interval: description: - - In the case that route53 is still servicing a prior request, this module will wait and try again after this many seconds. - If you have many domain names, the default of 500 seconds may be too long. + - In the case that Route 53 is still servicing a prior request, this module will wait and try again after this many seconds. + If you have many domain names, the default of C(500) seconds may be too long. default: 500 type: int private_zone: description: - - If set to C(yes), the private zone matching the requested name within the domain will be used if there are both public and private zones. - The default is to use the public zone. + - If set to C(true), the private zone matching the requested name within the domain will be used if there are both public and private zones. + - The default is to use the public zone. type: bool default: false identifier: From 02c37c0ab0922b824628938e1e499023a4e6abaf Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 16 Jan 2021 10:50:49 +0100 Subject: [PATCH 13/81] Bulk import cleanup (#360) * Split imports and reorder * Import camel_dict_to_snake_dict and snake_dict_to_camel_dict direct from ansible.module_utils.common.dict_transformations * Remove unused imports * Route53 Info was migrated to Boto3 drop the HAS_BOTO check and import * changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/130cf3cc5980014020632f19fdab79c9bcf28add --- plugins/modules/route53_health_check.py | 5 +---- plugins/modules/route53_info.py | 11 ++--------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index a1f9c9a268c..03ac8b09af0 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -118,18 +118,15 @@ import uuid try: - import boto import boto.ec2 - from boto import route53 from boto.route53 import Route53Connection, exception from boto.route53.healthcheck import HealthCheck except ImportError: pass # Handled by HAS_BOTO -# import module snippets from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info # Things that can't get changed: diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 38d0bc540f5..abdf7e44709 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -204,18 +204,15 @@ start_record_name: "host1.workshop.test.io" register: RECORDS ''' + try: - import boto import botocore - import boto3 except ImportError: - pass # Handled by HAS_BOTO and HAS_BOTO3 + pass # Handled by AnsibleAWSModule from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO3 def get_hosted_zone(client, module): @@ -461,10 +458,6 @@ def main(): if module._name == 'route53_facts': module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", date='2021-12-01', collection_name='community.aws') - # Validate Requirements - if not (HAS_BOTO or HAS_BOTO3): - module.fail_json(msg='json and boto/boto3 is required.') - try: route53 = module.client('route53') except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: From cda0b0b2b5f7f6b869493d921aa963559ce7faf8 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 10 Feb 2021 12:32:15 +0100 Subject: [PATCH 14/81] Move tests to using module_defaults (#368) * Bulk migration to module_defaults * Tests for missing creds/region * Move dms_identifier and dms_sg_identifier into defaults This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/365b6ef9a7a5e78e1bcf0d190ab66957bed5e3dd --- .../targets/route53_zone/tasks/main.yml | 43 +++---------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index f6baa68fffc..5fe154a6712 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -1,18 +1,16 @@ --- -- name: route53_zone tests +- name: 'route53_zone integration tests' collections: - amazon.aws + 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 }}' block: # ============================================================ - - name: set connection information for all tasks - set_fact: - aws_connection_info: &aws_connection_info - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token }}" - region: "{{ aws_region }}" - no_log: true - name: Create VPC for use in testing ec2_vpc_net: @@ -21,7 +19,6 @@ tags: Name: Ansible ec2_instance Testing VPC tenancy: default - <<: *aws_connection_info register: testing_vpc # ============================================================ @@ -30,7 +27,6 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present - <<: *aws_connection_info register: output - assert: @@ -46,7 +42,6 @@ zone: "{{ resource_prefix }}.check.public" comment: original comment state: present - <<: *aws_connection_info register: output check_mode: yes @@ -63,7 +58,6 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present - <<: *aws_connection_info register: output - assert: @@ -78,7 +72,6 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present - <<: *aws_connection_info register: output check_mode: yes @@ -95,7 +88,6 @@ zone: "{{ resource_prefix }}.public" comment: updated comment state: present - <<: *aws_connection_info register: output - assert: @@ -108,7 +100,6 @@ zone: "{{ resource_prefix }}.public" comment: updated comment for check state: present - <<: *aws_connection_info register: output check_mode: yes @@ -122,7 +113,6 @@ route53_zone: zone: "{{ resource_prefix }}.public" state: absent - <<: *aws_connection_info register: output check_mode: yes @@ -135,7 +125,6 @@ route53_zone: zone: "{{ resource_prefix }}.public" state: absent - <<: *aws_connection_info register: output - assert: @@ -151,7 +140,6 @@ zone: "{{ resource_prefix }}.private" comment: original comment state: present - <<: *aws_connection_info register: output check_mode: yes @@ -166,7 +154,6 @@ zone: "{{ resource_prefix }}.private" comment: original comment state: present - <<: *aws_connection_info register: output - assert: @@ -180,7 +167,6 @@ zone: "{{ resource_prefix }}.private" comment: original comment state: present - <<: *aws_connection_info register: output - assert: @@ -195,7 +181,6 @@ zone: "{{ resource_prefix }}.private" comment: original comment state: present - <<: *aws_connection_info register: output check_mode: yes @@ -212,7 +197,6 @@ zone: "{{ resource_prefix }}.private" comment: updated_comment state: present - <<: *aws_connection_info register: output - assert: @@ -227,7 +211,6 @@ zone: "{{ resource_prefix }}.private" comment: updated_comment check state: present - <<: *aws_connection_info register: output check_mode: yes @@ -241,7 +224,6 @@ route53_zone: zone: "{{ resource_prefix }}.private" state: absent - <<: *aws_connection_info register: output - assert: @@ -253,7 +235,6 @@ route53_zone: zone: "{{ resource_prefix }}.private" state: absent - <<: *aws_connection_info register: output check_mode: yes @@ -268,7 +249,6 @@ zone: "{{ resource_prefix }}.publicfake" comment: original comment state: absent - <<: *aws_connection_info register: output - assert: @@ -281,7 +261,6 @@ zone: "{{ resource_prefix }}.publicfake" comment: original comment state: absent - <<: *aws_connection_info register: output check_mode: yes @@ -297,7 +276,6 @@ vpc_region: "{{ aws_region }}" zone: "{{ resource_prefix }}.private" state: absent - <<: *aws_connection_info register: output check_mode: yes @@ -312,7 +290,6 @@ vpc_region: "{{ aws_region }}" zone: "{{ resource_prefix }}.private" state: absent - <<: *aws_connection_info register: output - assert: @@ -326,7 +303,6 @@ zone: "{{ resource_prefix }}.public2" comment: this is an example state: present - <<: *aws_connection_info register: new_zone # Delete zone using its id @@ -335,7 +311,6 @@ zone: "{{ resource_prefix }}.public2" hosted_zone_id: "{{new_zone.zone_id}}" state: absent - <<: *aws_connection_info register: output check_mode: yes @@ -349,7 +324,6 @@ zone: "{{ resource_prefix }}.public2" hosted_zone_id: "{{new_zone.zone_id}}" state: absent - <<: *aws_connection_info register: output - assert: @@ -363,7 +337,6 @@ route53_zone: zone: "{{ item }}" state: absent - <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -378,7 +351,6 @@ vpc_region: "{{ aws_region }}" zone: "{{ resource_prefix }}.private" state: absent - <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes @@ -389,7 +361,6 @@ name: "{{ resource_prefix }}-vpc" cidr_block: 10.22.32.0/23 state: absent - <<: *aws_connection_info register: removed until: removed is not failed ignore_errors: yes From 7cb580670f2c5dbb872dccbc61401371b6d6bfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Magalh=C3=A3es?= <4622652+pjrm@users.noreply.github.com> Date: Fri, 12 Feb 2021 13:22:41 +0000 Subject: [PATCH 15/81] route53 - Refactor to use boto3 (#405) * route53 - Refactor to use boto3 * Changelog Co-authored-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/08d0b9cdc400ed3f763a54abfd1505a68cf81a91 --- plugins/modules/route53.py | 321 ++++++++++++++----------------------- 1 file changed, 119 insertions(+), 202 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 6caf385002f..495be280fc5 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -5,6 +5,7 @@ # 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 @@ -135,7 +136,6 @@ - Mike Buzzetti (@jimbydamonk) extends_documentation_fragment: - amazon.aws.aws - ''' RETURN = r''' @@ -224,7 +224,6 @@ ttl: 7200 value: 1.1.1.1,2.2.2.2,3.3.3.3 wait: yes - - name: Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated community.aws.route53: state: present @@ -237,7 +236,6 @@ - 2.2.2.2 - 3.3.3.3 wait: yes - - name: Retrieve the details for new.foo.com community.aws.route53: state: get @@ -245,7 +243,6 @@ record: new.foo.com type: A register: rec - - name: Delete new.foo.com A record using the results from the get command community.aws.route53: state: absent @@ -254,7 +251,6 @@ ttl: "{{ rec.set.ttl }}" type: "{{ rec.set.type }}" value: "{{ rec.set.value }}" - # Add an AAAA record. Note that because there are colons in the value # that the IPv6 address must be quoted. Also shows using the old form command=create. - name: Add an AAAA record @@ -265,7 +261,6 @@ type: AAAA ttl: 7200 value: "::1" - # For more information on SRV records see: # https://en.wikipedia.org/wiki/SRV_record - name: Add a SRV record with multiple fields for a service on port 22222 @@ -275,7 +270,6 @@ record: "_example-service._tcp.foo.com" type: SRV value: "0 0 22222 host1.foo.com,0 0 22222 host2.foo.com" - # Note that TXT and SPF records must be surrounded # by quotes when sent to Route 53: - name: Add a TXT record. @@ -286,7 +280,6 @@ type: TXT ttl: 7200 value: '"bar"' - - name: Add an alias record that points to an Amazon ELB community.aws.route53: state: present @@ -296,7 +289,6 @@ value: "{{ elb_dns_name }}" alias: True alias_hosted_zone_id: "{{ elb_zone_id }}" - - name: Retrieve the details for elb.foo.com community.aws.route53: state: get @@ -304,7 +296,6 @@ record: elb.foo.com type: A register: rec - - name: Delete an alias record using the results from the get command community.aws.route53: state: absent @@ -315,7 +306,6 @@ value: "{{ rec.set.value }}" alias: True alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}" - - name: Add an alias record that points to an Amazon ELB and evaluates it health community.aws.route53: state: present @@ -326,7 +316,6 @@ alias: True alias_hosted_zone_id: "{{ elb_zone_id }}" alias_evaluate_target_health: True - - name: Add an AAAA record with Hosted Zone ID community.aws.route53: state: present @@ -336,7 +325,6 @@ type: AAAA ttl: 7200 value: "::1" - - name: Use a routing policy to distribute traffic community.aws.route53: state: present @@ -349,7 +337,6 @@ identifier: "host1@www" weight: 100 health_check: "d994b780-3150-49fd-9205-356abdd42e75" - - name: Add a CAA record (RFC 6844) community.aws.route53: state: present @@ -360,136 +347,82 @@ - 0 issue "ca.example.net" - 0 issuewild ";" - 0 iodef "mailto:security@example.com" - ''' -import time -import distutils.version +from operator import itemgetter try: - import boto - import boto.ec2 - from boto.route53 import Route53Connection - from boto.route53.record import Record, ResourceRecordSets - from boto.route53.status import Status + import botocore except ImportError: - pass # Handled by HAS_BOTO + pass # Handled by AnsibleAWSModule + +from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message +from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + +MAX_AWS_RETRIES = 10 # How many retries to perform when an API call is failing +WAIT_RETRY = 5 # how many seconds to wait between propagation status polls + + +@AWSRetry.jittered_backoff(retries=MAX_AWS_RETRIES) +def _list_record_sets(route53, **kwargs): + paginator = route53.get_paginator('list_resource_record_sets') + return paginator.paginate(**kwargs).build_full_result()['ResourceRecordSets'] + + +@AWSRetry.jittered_backoff(retries=MAX_AWS_RETRIES) +def _list_hosted_zones(route53, **kwargs): + paginator = route53.get_paginator('list_hosted_zones') + return paginator.paginate(**kwargs).build_full_result()['HostedZones'] -MINIMUM_BOTO_VERSION = '2.28.0' -WAIT_RETRY_SLEEP = 5 # how many seconds to wait between propagation status polls +def get_record(route53, zone_id, record_name, record_type, record_identifier): + record_sets_results = _list_record_sets(route53, HostedZoneId=zone_id) + for record_set in record_sets_results: + # If the record name and type is not equal, move to the next record + if (record_name, record_type) != (record_set['Name'], record_set['Type']): + continue -class TimeoutError(Exception): - pass + if record_identifier and record_identifier != record_set.get("SetIdentifier"): + continue + return record_set -def get_zone_id_by_name(conn, module, zone_name, want_private, want_vpc_id): + return None + + +def get_zone_id_by_name(route53, module, zone_name, want_private, want_vpc_id): """Finds a zone by name or zone_id""" - for zone in invoke_with_throttling_retries(conn.get_zones): + hosted_zones_results = _list_hosted_zones(route53) + + for zone in hosted_zones_results: # only save this zone id if the private status of the zone matches # the private_zone_in boolean specified in the params - private_zone = module.boolean(zone.config.get('PrivateZone', False)) - if private_zone == want_private and zone.name == zone_name: + private_zone = module.boolean(zone['Config'].get('PrivateZone', False)) + zone_id = zone['Id'].replace("/hostedzone/", "") + + if private_zone == want_private and zone['Name'] == zone_name: if want_vpc_id: # NOTE: These details aren't available in other boto methods, hence the necessary # extra API call - hosted_zone = invoke_with_throttling_retries(conn.get_hosted_zone, zone.id) - zone_details = hosted_zone['GetHostedZoneResponse'] + hosted_zone = route53.get_hosted_zone(aws_retry=True, Id=zone.id) + zone_details = hosted_zone['HostedZone'] # this is to deal with this boto bug: https://github.com/boto/boto/pull/2882 if isinstance(zone_details['VPCs'], dict): if zone_details['VPCs']['VPC']['VPCId'] == want_vpc_id: - return zone.id + return zone_id else: # Forward compatibility for when boto fixes that bug if want_vpc_id in [v['VPCId'] for v in zone_details['VPCs']]: - return zone.id + return zone_id else: - return zone.id + return zone_id return None -def commit(changes, retry_interval, wait, wait_timeout): - """Commit changes, but retry PriorRequestNotComplete errors.""" - result = None - retry = 10 - while True: - try: - retry -= 1 - result = changes.commit() - break - except boto.route53.exception.DNSServerError as e: - code = e.body.split("")[1] - code = code.split("")[0] - if code != 'PriorRequestNotComplete' or retry < 0: - raise e - time.sleep(float(retry_interval)) - - if wait: - timeout_time = time.time() + wait_timeout - connection = changes.connection - change = result['ChangeResourceRecordSetsResponse']['ChangeInfo'] - status = Status(connection, change) - while status.status != 'INSYNC' and time.time() < timeout_time: - time.sleep(WAIT_RETRY_SLEEP) - status.update() - if time.time() >= timeout_time: - raise TimeoutError() - return result - - -# Shamelessly copied over from https://git.io/vgmDG -IGNORE_CODE = 'Throttling' -MAX_RETRIES = 5 - - -def invoke_with_throttling_retries(function_ref, *argv, **kwargs): - retries = 0 - while True: - try: - retval = function_ref(*argv, **kwargs) - return retval - except boto.exception.BotoServerError as e: - if e.code != IGNORE_CODE or retries == MAX_RETRIES: - raise e - time.sleep(5 * (2**retries)) - retries += 1 - - -def decode_name(name): - # Due to a bug in either AWS or Boto, "special" characters are returned as octals, preventing round - # tripping of things like * and @. - return name.encode().decode('unicode_escape') - - -def to_dict(rset, zone_in, zone_id): - record = dict() - record['zone'] = zone_in - record['type'] = rset.type - record['record'] = decode_name(rset.name) - record['ttl'] = str(rset.ttl) - record['identifier'] = rset.identifier - record['weight'] = rset.weight - record['region'] = rset.region - record['failover'] = rset.failover - record['health_check'] = rset.health_check - record['hosted_zone_id'] = zone_id - if rset.alias_dns_name: - record['alias'] = True - record['value'] = rset.alias_dns_name - record['values'] = [rset.alias_dns_name] - record['alias_hosted_zone_id'] = rset.alias_hosted_zone_id - record['alias_evaluate_target_health'] = rset.alias_evaluate_target_health - else: - record['alias'] = False - record['value'] = ','.join(sorted(rset.resource_records)) - record['values'] = sorted(rset.resource_records) - return record - - def main(): argument_spec = dict( state=dict(type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']), @@ -536,15 +469,8 @@ def main(): region=('identifier',), weight=('identifier',), ), - check_boto3=False, ) - if not HAS_BOTO: - module.fail_json(msg='boto required for this module') - - if distutils.version.StrictVersion(boto.__version__) < distutils.version.StrictVersion(MINIMUM_BOTO_VERSION): - module.fail_json(msg='Found boto in version %s, but >= %s is required' % (boto.__version__, MINIMUM_BOTO_VERSION)) - if module.params['state'] in ('present', 'create'): command_in = 'create' elif module.params['state'] in ('absent', 'delete'): @@ -577,8 +503,6 @@ def main(): wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) - if zone_in[-1:] != '.': zone_in += "." @@ -593,113 +517,106 @@ def main(): # connect to the route53 endpoint try: - conn = Route53Connection(**aws_connect_kwargs) - except boto.exception.BotoServerError as e: - module.fail_json(msg=e.error_message) + route53 = module.client( + 'route53', + retry_decorator=AWSRetry.jittered_backoff(retries=MAX_AWS_RETRIES, delay=retry_interval_in) + ) + except botocore.exceptions.HTTPClientError as e: + module.fail_json_aws(e, msg='Failed to connect to AWS') # Find the named zone ID - zone_id = hosted_zone_id_in or get_zone_id_by_name(conn, module, zone_in, private_zone_in, vpc_id_in) + zone_id = hosted_zone_id_in or get_zone_id_by_name(route53, module, zone_in, private_zone_in, vpc_id_in) # Verify that the requested zone is already defined in Route53 if zone_id is None: errmsg = "Zone %s does not exist in Route53" % (zone_in or hosted_zone_id_in) module.fail_json(msg=errmsg) - record = {} - - found_record = False - wanted_rset = Record(name=record_in, type=type_in, ttl=ttl_in, - identifier=identifier_in, weight=weight_in, - region=region_in, health_check=health_check_in, - failover=failover_in) - for v in value_in: - if alias_in: - wanted_rset.set_alias(alias_hosted_zone_id_in, v, alias_evaluate_target_health_in) - else: - wanted_rset.add_value(v) - - need_to_sort_records = (type_in == 'CAA') - - # Sort records for wanted_rset if necessary (keep original list) - unsorted_records = wanted_rset.resource_records - if need_to_sort_records: - wanted_rset.resource_records = sorted(unsorted_records) - - sets = invoke_with_throttling_retries(conn.get_all_rrsets, zone_id, name=record_in, - type=type_in, identifier=identifier_in) - sets_iter = iter(sets) - while True: - try: - rset = invoke_with_throttling_retries(next, sets_iter) - except StopIteration: - break - # Need to save this changes in rset, because of comparing rset.to_xml() == wanted_rset.to_xml() in next block - rset.name = decode_name(rset.name) - - if identifier_in is not None: - identifier_in = str(identifier_in) - - if rset.type == type_in and rset.name.lower() == record_in.lower() and rset.identifier == identifier_in: - if need_to_sort_records: - # Sort records - rset.resource_records = sorted(rset.resource_records) - found_record = True - record = to_dict(rset, zone_in, zone_id) - if command_in == 'create' and rset.to_xml() == wanted_rset.to_xml(): - module.exit_json(changed=False) - - # We need to look only at the first rrset returned by the above call, - # so break here. The returned elements begin with the one matching our - # requested name, type, and identifier, if such an element exists, - # followed by all others that come after it in alphabetical order. - # Therefore, if the first set does not match, no subsequent set will - # match either. - break + aws_record = get_record(route53, zone_id, record_in, type_in, identifier_in) + + resource_record_set = scrub_none_parameters({ + 'Name': record_in, + 'Type': type_in, + 'Weight': weight_in, + 'Region': region_in, + 'Failover': failover_in, + 'TTL': ttl_in, + 'ResourceRecords': [dict(Value=value) for value in value_in], + 'HealthCheckId': health_check_in, + }) + + if alias_in: + resource_record_set['AliasTarget'] = dict( + HostedZoneId=alias_hosted_zone_id_in, + DNSName=value_in[0], + EvaluateTargetHealth=alias_evaluate_target_health_in + ) + + # On CAA records order doesn't matter + if type_in == 'CAA': + resource_record_set['ResourceRecords'] = sorted(resource_record_set['ResourceRecords'], key=itemgetter('Value')) + + if command_in == 'create' and aws_record == resource_record_set: + module.exit_json(changed=False) if command_in == 'get': if type_in == 'NS': - ns = record.get('values', []) + ns = aws_record.get('values', []) else: # Retrieve name servers associated to the zone. - z = invoke_with_throttling_retries(conn.get_zone, zone_in) - ns = invoke_with_throttling_retries(z.get_nameservers) + ns = route53.get_hosted_zone(aws_retry=True, Id=zone_id)['DelegationSet']['NameServers'] - module.exit_json(changed=False, set=record, nameservers=ns) + module.exit_json(changed=False, set=aws_record, nameservers=ns) - if command_in == 'delete' and not found_record: + if command_in == 'delete' and not aws_record: module.exit_json(changed=False) - changes = ResourceRecordSets(conn, zone_id) - if command_in == 'create' or command_in == 'delete': - if command_in == 'create' and found_record: + if command_in == 'create' and aws_record: if not module.params['overwrite']: module.fail_json(msg="Record already exists with different value. Set 'overwrite' to replace it") command = 'UPSERT' else: command = command_in.upper() - # Restore original order of records - wanted_rset.resource_records = unsorted_records - changes.add_change_record(command, wanted_rset) if not module.check_mode: try: - invoke_with_throttling_retries(commit, changes, retry_interval_in, wait_in, wait_timeout_in) - except boto.route53.exception.DNSServerError as e: - txt = e.body.split("")[1] - txt = txt.split("")[0] - if "but it already exists" in txt: - module.exit_json(changed=False) - else: - module.fail_json(msg=txt) - except TimeoutError: - module.fail_json(msg='Timeout waiting for changes to replicate') + change_resource_record_sets = route53.change_resource_record_sets( + aws_retry=True, + HostedZoneId=zone_id, + ChangeBatch=dict( + Changes=[ + dict( + Action=command, + ResourceRecordSet=resource_record_set + ) + ] + ) + ) + + if wait_in: + waiter = route53.get_waiter('resource_record_sets_changed') + waiter.wait( + Id=change_resource_record_sets['ChangeInfo']['Id'], + WaiterConfig=dict( + Delay=WAIT_RETRY, + MaxAttemps=wait_timeout_in // WAIT_RETRY, + ) + ) + except is_boto3_error_message('but it already exists'): + module.exit_json(changed=False) + except botocore.exceptions.WaiterError as e: + module.fail_json_aws(e, msg='Timeout waiting for resource records changes to be applied') + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Failed to update records') + except Exception as e: + module.fail_json(msg='Unhandled exception. (%s)' % to_native(e)) module.exit_json( changed=True, diff=dict( - before=record, - after=to_dict(wanted_rset, zone_in, zone_id) if command != 'delete' else {}, + before=aws_record, + after=resource_record_set if command != 'delete' else {}, ), ) From 2fe7623fce7f01b919ab633b37cdd7c70aff30b0 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Tue, 16 Feb 2021 00:26:30 -0700 Subject: [PATCH 16/81] Add boto3 requirements to route53 module docs (#417) This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/5f3e385c3a19d86ce45dcb9bf72d4e5808ac5d64 --- plugins/modules/route53.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 495be280fc5..43b17a44f5b 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -13,6 +13,7 @@ --- module: route53 version_added: 1.0.0 +requirements: [ "boto3", "botocore" ] short_description: add or delete entries in Amazons Route 53 DNS service description: - Creates and deletes DNS records in Amazons Route 53 service. From d02a795700cefdbdebcf1294aca0ff36799059fc Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 21 Feb 2021 15:40:04 +0100 Subject: [PATCH 17/81] Yet more integration test aliases file cleanup (#431) * More aliases cleanup * Mark ec2_classic_lb tests unstable * Add more comments about why tests aren't enabled This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/cb55efa7a4fda574b637f3b18c072cd2436e2a21 --- tests/integration/targets/route53/aliases | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/targets/route53/aliases b/tests/integration/targets/route53/aliases index f6cc7ad00c5..a70e7ee70b8 100644 --- a/tests/integration/targets/route53/aliases +++ b/tests/integration/targets/route53/aliases @@ -1,3 +1,4 @@ -route53_info cloud/aws shippable/aws/group2 + +route53_info From e1829f59e0b4c365d02b3b73185ad61cc5fc421a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20Magalh=C3=A3es?= <4622652+pjrm@users.noreply.github.com> Date: Thu, 11 Mar 2021 13:15:18 +0000 Subject: [PATCH 18/81] Fix state=get on route53 module (Issue #423) (#424) * Fix state=get on route53 module This bug was introduced when refactoring from boto to boto3 library. This happens because the method "get_hosted_zone" only returns the DelegationSet when the DNS zone is external. Therefore this breaks when trying to get internal records. The solution is to search for getting DNS records of type ''NS'' with the same name as the hosted zone. * Update changelogs/fragments/406-route53-state-get.yml Co-authored-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/331ada12fa7467f380db7a4075d2f8ada472396f --- plugins/modules/route53.py | 13 ++++++++++++- tests/integration/targets/route53/tasks/main.yml | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 43b17a44f5b..84a8dc997fb 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -424,6 +424,17 @@ def get_zone_id_by_name(route53, module, zone_name, want_private, want_vpc_id): return None +def get_hosted_zone_nameservers(route53, zone_id): + hosted_zone_name = route53.get_hosted_zone(aws_retry=True, Id=zone_id)['HostedZone']['Name'] + resource_records_sets = _list_record_sets(route53, HostedZoneId=zone_id) + + nameservers_records = list( + filter(lambda record: record['Name'] == hosted_zone_name and record['Type'] == 'NS', resource_records_sets) + )[0]['ResourceRecords'] + + return [ns_record['Value'] for ns_record in nameservers_records] + + def main(): argument_spec = dict( state=dict(type='str', required=True, choices=['absent', 'create', 'delete', 'get', 'present'], aliases=['command']), @@ -565,7 +576,7 @@ def main(): ns = aws_record.get('values', []) else: # Retrieve name servers associated to the zone. - ns = route53.get_hosted_zone(aws_retry=True, Id=zone_id)['DelegationSet']['NameServers'] + ns = get_hosted_zone_nameservers(route53, zone_id) module.exit_json(changed=False, set=aws_record, nameservers=ns) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index de332a7ba0c..a770eba2f28 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -52,6 +52,20 @@ - qdn is not failed - qdn is changed + - name: Get A record using 'get' method of route53 module + route53: + state: get + zone: "{{ zone_one }}" + record: "qdn_test.{{ zone_one }}" + type: A + register: get_result + - assert: + that: + - get_result.nameservers|length > 0 + - get_result.set.Name == "qdn_test.{{ zone_one }}" + - get_result.set.ResourceRecords[0].Value == "1.2.3.4" + - get_result.set.Type == "A" + - name: Create same A record using zone non-qualified domain route53: state: present From e05dfc5f0f5d4c385eaace8ff1c7e114057aa024 Mon Sep 17 00:00:00 2001 From: Nicolas Boutet Date: Tue, 30 Mar 2021 12:40:51 +0200 Subject: [PATCH 19/81] route53: fix getting private zones by name when a vpc_id is provided (#510) * Fix route53 get_zone_id_by_name for private zones * Add tests for route53 modules with private zone * Add tests for route53 with vpc_id * Add changelog fragment * Remove dead code * Use private IPs in tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/91938715349f6204f703ab072b3ec36475e8eae9 --- plugins/modules/route53.py | 12 +- .../targets/route53/tasks/main.yml | 171 ++++++++++++++++-- 2 files changed, 161 insertions(+), 22 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 84a8dc997fb..78e17437d74 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -410,15 +410,9 @@ def get_zone_id_by_name(route53, module, zone_name, want_private, want_vpc_id): if want_vpc_id: # NOTE: These details aren't available in other boto methods, hence the necessary # extra API call - hosted_zone = route53.get_hosted_zone(aws_retry=True, Id=zone.id) - zone_details = hosted_zone['HostedZone'] - # this is to deal with this boto bug: https://github.com/boto/boto/pull/2882 - if isinstance(zone_details['VPCs'], dict): - if zone_details['VPCs']['VPC']['VPCId'] == want_vpc_id: - return zone_id - else: # Forward compatibility for when boto fixes that bug - if want_vpc_id in [v['VPCId'] for v in zone_details['VPCs']]: - return zone_id + hosted_zone = route53.get_hosted_zone(aws_retry=True, Id=zone_id) + if want_vpc_id in [v['VPCId'] for v in hosted_zone['VPCs']]: + return zone_id else: return zone_id return None diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index a770eba2f28..8f4a3e5c303 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -16,6 +16,14 @@ route53: region: null block: + + - name: create VPC + ec2_vpc_net: + cidr_block: 192.0.2.0/24 + name: '{{ resource_prefix }}_vpc' + state: present + register: vpc + - route53_zone: zone: '{{ zone_one }}' comment: Created in Ansible test {{ resource_prefix }} @@ -39,13 +47,39 @@ that: - hosted_zones.HostedZone.ResourceRecordSetCount == 2 + - route53_zone: + zone: '{{ zone_two }}' + vpc_id: '{{ vpc.vpc.id }}' + vpc_region: '{{ aws_region }}' + comment: Created in Ansible test {{ resource_prefix }} + register: z2 + + - assert: + that: + - z2 is success + - z2 is changed + - "z2.comment == 'Created in Ansible test {{ resource_prefix }}'" + + - name: Get zone details + route53_info: + query: hosted_zone + hosted_zone_id: '{{ z2.zone_id }}' + hosted_zone_method: details + register: hosted_zones + + - name: Assert newly created hosted zone only has NS and SOA records + assert: + that: + - hosted_zones.HostedZone.ResourceRecordSetCount == 2 + - hosted_zones.HostedZone.Config.PrivateZone + - name: Create A record using zone fqdn route53: state: present zone: '{{ zone_one }}' record: 'qdn_test.{{ zone_one }}' type: A - value: 1.2.3.4 + value: 192.0.2.1 register: qdn - assert: that: @@ -63,7 +97,7 @@ that: - get_result.nameservers|length > 0 - get_result.set.Name == "qdn_test.{{ zone_one }}" - - get_result.set.ResourceRecords[0].Value == "1.2.3.4" + - get_result.set.ResourceRecords[0].Value == "192.0.2.1" - get_result.set.Type == "A" - name: Create same A record using zone non-qualified domain @@ -72,7 +106,7 @@ zone: '{{ zone_one[:-1] }}' record: 'qdn_test.{{ zone_one[:-1] }}' type: A - value: 1.2.3.4 + value: 192.0.2.1 register: non_qdn - assert: that: @@ -85,7 +119,7 @@ hosted_zone_id: '{{ z1.zone_id }}' record: 'zid_test.{{ zone_one }}' type: A - value: 1.2.3.4 + value: 192.0.2.1 register: zid - assert: that: @@ -99,8 +133,8 @@ record: 'order_test.{{ zone_one }}' type: A value: - - 4.5.6.7 - - 1.2.3.4 + - 192.0.2.2 + - 192.0.2.1 register: mv_a_record - assert: that: @@ -114,8 +148,8 @@ record: 'order_test.{{ zone_one }}' type: A value: - - 4.5.6.7 - - 1.2.3.4 + - 192.0.2.2 + - 192.0.2.1 register: mv_a_record - assert: that: @@ -134,8 +168,8 @@ that: - records.ResourceRecordSets|length == 3 - records.ResourceRecordSets[0].ResourceRecords|length == 2 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == "4.5.6.7" - - records.ResourceRecordSets[0].ResourceRecords[1].Value == "1.2.3.4" + - records.ResourceRecordSets[0].ResourceRecords[0].Value == "192.0.2.2" + - records.ResourceRecordSets[0].ResourceRecords[1].Value == "192.0.2.1" - name: Remove a member from multi-value A record with values in different order route53: @@ -144,7 +178,7 @@ record: 'order_test.{{ zone_one }}' type: A value: - - 4.5.6.7 + - 192.0.2.2 register: del_a_record ignore_errors: true - name: This should fail, because `overwrite` is false @@ -160,7 +194,7 @@ overwrite: true type: A value: - - 4.5.6.7 + - 192.0.2.2 register: del_a_record ignore_errors: true - name: This should not fail, because `overwrite` is true @@ -181,7 +215,7 @@ that: - records.ResourceRecordSets|length == 3 - records.ResourceRecordSets[0].ResourceRecords|length == 1 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == "4.5.6.7" + - records.ResourceRecordSets[0].ResourceRecords[0].Value == "192.0.2.2" - name: Create a LetsEncrypt CAA record route53: @@ -232,6 +266,93 @@ - caa is not failed - caa is not changed + # Tests on zone two (private zone) + - name: Create A record using zone fqdn + route53: + state: present + zone: '{{ zone_two }}' + record: 'qdn_test.{{ zone_two }}' + type: A + value: 192.0.2.1 + private_zone: true + register: qdn + - assert: + that: + - qdn is not failed + - qdn is changed + + - name: Get A record using 'get' method of route53 module + route53: + state: get + zone: "{{ zone_two }}" + record: "qdn_test.{{ zone_two }}" + type: A + private_zone: true + register: get_result + - assert: + that: + - get_result.nameservers|length > 0 + - get_result.set.Name == "qdn_test.{{ zone_two }}" + - get_result.set.ResourceRecords[0].Value == "192.0.2.1" + - get_result.set.Type == "A" + + - name: Create same A record using zone non-qualified domain + route53: + state: present + zone: '{{ zone_two[:-1] }}' + record: 'qdn_test.{{ zone_two[:-1] }}' + type: A + value: 192.0.2.1 + private_zone: true + register: non_qdn + - assert: + that: + - non_qdn is not failed + - non_qdn is not changed + + - name: Create A record using zone ID + route53: + state: present + hosted_zone_id: '{{ z2.zone_id }}' + record: 'zid_test.{{ zone_two }}' + type: A + value: 192.0.2.2 + private_zone: true + register: zid + - assert: + that: + - zid is not failed + - zid is changed + + - name: Create A record using zone fqdn and vpc_id + route53: + state: present + zone: '{{ zone_two }}' + record: 'qdn_test_vpc.{{ zone_two }}' + type: A + value: 192.0.2.3 + private_zone: true + vpc_id: '{{ vpc.vpc.id }}' + register: qdn + - assert: + that: + - qdn is not failed + - qdn is changed + + - name: Create A record using zone ID and vpc_id + route53: + state: present + hosted_zone_id: '{{ z2.zone_id }}' + record: 'zid_test_vpc.{{ zone_two }}' + type: A + value: 192.0.2.4 + private_zone: true + vpc_id: '{{ vpc.vpc.id }}' + register: zid + - assert: + that: + - zid is not failed + - zid is changed always: - route53_info: @@ -247,6 +368,20 @@ type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + - route53_info: + query: record_sets + hosted_zone_id: '{{ z2.zone_id }}' + register: z2_records + - debug: var=z2_records + - name: Loop over A/AAAA/CNAME records and delete them + route53: + state: absent + zone: '{{ zone_two }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + private_zone: true + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' - name: Delete test zone one '{{ zone_one }}' route53_zone: state: absent @@ -264,3 +399,13 @@ retries: 10 until: delete_two is not failed when: false + - name: destroy VPC + ec2_vpc_net: + cidr_block: 192.0.2.0/24 + name: '{{ resource_prefix }}_vpc' + state: absent + register: remove_vpc + retries: 10 + delay: 5 + until: remove_vpc is success + ignore_errors: true From bb2dee76426f97a302727d711480982aaebce7f9 Mon Sep 17 00:00:00 2001 From: Kyle Date: Mon, 5 Apr 2021 11:59:45 -0600 Subject: [PATCH 20/81] Remove 'ResourceRecords' when 'AliasTarget' (#502) * Remove 'ResourceRecords' when 'AliasTarget' sending a change to the route53 api that includes both an AliasTarget and a ResourceRecord causes the api to return with an error. removing the ResourceRecord when an AliasTarget is preset allows this module to continue without error * Cleanup tests and use RFC2602 Domains and RFC5737 CIDRs * Add integration test for aliases * Make Alias and TTL mutually exclusive * Update docs to list region/failover/weight as mutually exclusive. * changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8a2a138dac1b9552b5db7c2a9f888bd75a6effe9 --- plugins/modules/route53.py | 14 +- .../targets/route53/tasks/main.yml | 189 ++++++++++++------ 2 files changed, 141 insertions(+), 62 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 78e17437d74..2168a0b11b6 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -44,6 +44,7 @@ ttl: description: - The TTL, in second, to give the new record. + - Mutually exclusive with I(alias). default: 3600 type: int type: @@ -55,6 +56,7 @@ alias: description: - Indicates if this is an alias record. + - Mutually exclusive with I(ttl). - Defaults to C(false). type: bool alias_hosted_zone_id: @@ -99,6 +101,7 @@ have the same combination of DNS name and type, a value that determines what portion of traffic for the current resource record set is routed to the associated location. + - Mutually exclusive with I(region) and I(failover). type: int region: description: @@ -106,6 +109,7 @@ that have the same combination of DNS name and type, a value that determines which region this should be associated with for the latency-based routing + - Mutually exclusive with I(weight) and I(failover). type: str health_check: description: @@ -115,6 +119,7 @@ description: - Failover resource record sets only. Whether this is the primary or secondary resource record set. Allowed values are PRIMARY and SECONDARY + - Mutually exclusive with I(weight) and I(region). type: str choices: ['SECONDARY', 'PRIMARY'] vpc_id: @@ -468,7 +473,10 @@ def main(): ('state', 'delete', ['value']), ), # failover, region and weight are mutually exclusive - mutually_exclusive=[('failover', 'region', 'weight')], + mutually_exclusive=[ + ('failover', 'region', 'weight'), + ('alias', 'ttl'), + ], # failover, region and weight require identifier required_by=dict( failover=('identifier',), @@ -557,6 +565,10 @@ def main(): DNSName=value_in[0], EvaluateTargetHealth=alias_evaluate_target_health_in ) + if 'ResourceRecords' in resource_record_set: + del resource_record_set['ResourceRecords'] + if 'TTL' in resource_record_set: + del resource_record_set['TTL'] # On CAA records order doesn't matter if type_in == 'CAA': diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 8f4a3e5c303..bb8ca5ef059 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -2,21 +2,22 @@ # tasks file for Route53 integration tests - set_fact: - zone_one: '{{ resource_prefix | replace("-", "") }}.one.fakeansible.com.' - zone_two: '{{ resource_prefix | replace("-", "") }}.two.fakeansible.com.' -- debug: msg='Set zones {{ zone_one }} and {{ zone_two }}' + zone_one: '{{ resource_prefix | replace("-", "") }}.one.ansible.test.' + zone_two: '{{ resource_prefix | replace("-", "") }}.two.ansible.test.' +- debug: + msg: 'Set zones {{ zone_one }} and {{ zone_two }}' - name: Test basics (new zone, A and AAAA records) 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 }}" + aws_access_key: '{{ aws_access_key }}' + aws_secret_key: '{{ aws_secret_key }}' + security_token: '{{ security_token | default(omit) }}' + region: '{{ aws_region }}' route53: + # Route53 is explicitly a global service region: null block: - - name: create VPC ec2_vpc_net: cidr_block: 192.0.2.0/24 @@ -24,36 +25,35 @@ state: present register: vpc - - route53_zone: + - name: 'Create a zone' + route53_zone: zone: '{{ zone_one }}' - comment: Created in Ansible test {{ resource_prefix }} + comment: 'Created in Ansible test {{ resource_prefix }}' register: z1 - - assert: that: - z1 is success - z1 is changed - "z1.comment == 'Created in Ansible test {{ resource_prefix }}'" - - name: Get zone details + - name: 'Get zone details' route53_info: query: hosted_zone hosted_zone_id: '{{ z1.zone_id }}' hosted_zone_method: details register: hosted_zones - - - name: Assert newly created hosted zone only has NS and SOA records + - name: 'Assert newly created hosted zone only has NS and SOA records' assert: that: - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - - route53_zone: + - name: 'Create a second zone' + route53_zone: zone: '{{ zone_two }}' vpc_id: '{{ vpc.vpc.id }}' vpc_region: '{{ aws_region }}' comment: Created in Ansible test {{ resource_prefix }} register: z2 - - assert: that: - z2 is success @@ -73,90 +73,90 @@ - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - hosted_zones.HostedZone.Config.PrivateZone - - name: Create A record using zone fqdn + - name: 'Create A record using zone fqdn' route53: state: present zone: '{{ zone_one }}' record: 'qdn_test.{{ zone_one }}' type: A - value: 192.0.2.1 + value: '192.0.2.1' register: qdn - assert: that: - qdn is not failed - qdn is changed - - name: Get A record using 'get' method of route53 module + - name: 'Get A record using "get" method of route53 module' route53: state: get - zone: "{{ zone_one }}" - record: "qdn_test.{{ zone_one }}" + zone: '{{ zone_one }}' + record: 'qdn_test.{{ zone_one }}' type: A register: get_result - assert: that: - get_result.nameservers|length > 0 - - get_result.set.Name == "qdn_test.{{ zone_one }}" - - get_result.set.ResourceRecords[0].Value == "192.0.2.1" - - get_result.set.Type == "A" + - get_result.set.Name == 'qdn_test.{{ zone_one }}' + - get_result.set.ResourceRecords[0].Value == '192.0.2.1' + - get_result.set.Type == 'A' - - name: Create same A record using zone non-qualified domain + - name: 'Create same A record using zone non-qualified domain' route53: state: present zone: '{{ zone_one[:-1] }}' record: 'qdn_test.{{ zone_one[:-1] }}' type: A - value: 192.0.2.1 + value: '192.0.2.1' register: non_qdn - assert: that: - non_qdn is not failed - non_qdn is not changed - - name: Create A record using zone ID + - name: 'Create A record using zone ID' route53: state: present hosted_zone_id: '{{ z1.zone_id }}' record: 'zid_test.{{ zone_one }}' type: A - value: 192.0.2.1 + value: '192.0.2.1' register: zid - assert: that: - zid is not failed - zid is changed - - name: Create a multi-value A record with values in different order + - name: 'Create a multi-value A record with values in different order' route53: state: present zone: '{{ zone_one }}' record: 'order_test.{{ zone_one }}' type: A value: - - 192.0.2.2 - - 192.0.2.1 + - '192.0.2.2' + - '192.0.2.1' register: mv_a_record - assert: that: - mv_a_record is not failed - mv_a_record is changed - - name: Create same multi-value A record with values in different order + - name: 'Create same multi-value A record with values in different order' route53: state: present zone: '{{ zone_one }}' record: 'order_test.{{ zone_one }}' type: A value: - - 192.0.2.2 - - 192.0.2.1 + - '192.0.2.2' + - '192.0.2.1' register: mv_a_record - assert: that: - mv_a_record is not failed - mv_a_record is not changed - - name: get Route53 A record information + - name: 'get Route53 A record information' route53_info: type: A query: record_sets @@ -168,25 +168,25 @@ that: - records.ResourceRecordSets|length == 3 - records.ResourceRecordSets[0].ResourceRecords|length == 2 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == "192.0.2.2" - - records.ResourceRecordSets[0].ResourceRecords[1].Value == "192.0.2.1" + - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' + - records.ResourceRecordSets[0].ResourceRecords[1].Value == '192.0.2.1' - - name: Remove a member from multi-value A record with values in different order + - name: 'Remove a member from multi-value A record with values in different order' route53: state: present zone: '{{ zone_one }}' record: 'order_test.{{ zone_one }}' type: A value: - - 192.0.2.2 + - '192.0.2.2' register: del_a_record ignore_errors: true - - name: This should fail, because `overwrite` is false + - name: 'This should fail, because `overwrite` is false' assert: that: - del_a_record is failed - - name: Remove a member from multi-value A record with values in different order + - name: 'Remove a member from multi-value A record with values in different order' route53: state: present zone: '{{ zone_one }}' @@ -194,16 +194,16 @@ overwrite: true type: A value: - - 192.0.2.2 + - '192.0.2.2' register: del_a_record ignore_errors: true - - name: This should not fail, because `overwrite` is true + - name: 'This should not fail, because `overwrite` is true' assert: that: - del_a_record is not failed - del_a_record is changed - - name: get Route53 zone A record information + - name: 'get Route53 zone A record information' route53_info: type: A query: record_sets @@ -215,17 +215,17 @@ that: - records.ResourceRecordSets|length == 3 - records.ResourceRecordSets[0].ResourceRecords|length == 1 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == "192.0.2.2" + - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' - - name: Create a LetsEncrypt CAA record + - name: 'Create a LetsEncrypt CAA record' route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - 0 issue "letsencrypt.org;" - - 0 issuewild "letsencrypt.org;" + - '0 issue "letsencrypt.org;"' + - '0 issuewild "letsencrypt.org;"' overwrite: true register: caa - assert: @@ -233,15 +233,15 @@ - caa is not failed - caa is changed - - name: Re-create the same LetsEncrypt CAA record + - name: 'Re-create the same LetsEncrypt CAA record' route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - 0 issue "letsencrypt.org;" - - 0 issuewild "letsencrypt.org;" + - '0 issue "letsencrypt.org;"' + - '0 issuewild "letsencrypt.org;"' overwrite: true register: caa - assert: @@ -249,18 +249,18 @@ - caa is not failed - caa is not changed - - name: Re-create the same LetsEncrypt CAA record in opposite-order + - name: 'Re-create the same LetsEncrypt CAA record in opposite-order' route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - 0 issuewild "letsencrypt.org;" - - 0 issue "letsencrypt.org;" + - '0 issuewild "letsencrypt.org;"' + - '0 issue "letsencrypt.org;"' overwrite: true register: caa - - name: This should not be changed, as CAA records are not order sensitive + - name: 'This should not be changed, as CAA records are not order sensitive' assert: that: - caa is not failed @@ -354,26 +354,89 @@ - zid is not failed - zid is changed + - name: 'Create an Alias record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'alias.{{ zone_one }}' + type: A + alias: True + alias_hosted_zone_id: '{{ z1.zone_id }}' + value: 'zid_test.{{ zone_one }}' + overwrite: True + register: alias_record + - name: 'This should be changed' + assert: + that: + - alias_record is not failed + - alias_record is changed + + - name: 'Re-Create an Alias record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'alias.{{ zone_one }}' + type: A + alias: True + alias_hosted_zone_id: '{{ z1.zone_id }}' + value: 'zid_test.{{ zone_one }}' + overwrite: True + register: alias_record + - name: 'This should not be changed' + assert: + that: + - alias_record is not failed + - alias_record is not changed + always: - route53_info: query: record_sets hosted_zone_id: '{{ z1.zone_id }}' register: z1_records - - debug: var=z1_records - - name: Loop over A/AAAA/CNAME records and delete them + - name: 'Loop over A/AAAA/CNAME Alias records and delete them' + route53: + state: absent + alias: True + alias_hosted_zone_id: '{{ item.AliasTarget.HostedZoneId }}' + zone: '{{ zone_one }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.AliasTarget.DNSName }}' + ignore_errors: True + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + when: + - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent zone: '{{ zone_one }}' record: '{{ item.Name }}' type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + ignore_errors: True loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + when: + - '"ResourceRecords" in item' + - route53_info: query: record_sets hosted_zone_id: '{{ z2.zone_id }}' register: z2_records - - debug: var=z2_records - - name: Loop over A/AAAA/CNAME records and delete them + - name: 'Loop over A/AAAA/CNAME Alias records and delete them' + route53: + state: absent + alias: True + alias_hosted_zone_id: '{{ item.AliasTarget.HostedZoneId }}' + zone: '{{ zone_two }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.AliasTarget.DNSName }}' + private_zone: true + ignore_errors: True + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + when: + - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent zone: '{{ zone_two }}' @@ -381,8 +444,12 @@ type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' private_zone: true + ignore_errors: True loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' - - name: Delete test zone one '{{ zone_one }}' + when: + - '"ResourceRecords" in item' + + - name: 'Delete test zone one {{ zone_one }}' route53_zone: state: absent zone: '{{ zone_one }}' @@ -390,7 +457,7 @@ ignore_errors: yes retries: 10 until: delete_one is not failed - - name: Delete test zone two '{{ zone_two }}' + - name: 'Delete test zone two {{ zone_two }}' route53_zone: state: absent zone: '{{ zone_two }}' From 1bec55092b77284bfca81063b6dbdcb38fab43aa Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 6 Apr 2021 09:22:36 +0200 Subject: [PATCH 21/81] Fix route53 idempotency issues (#525) * Fix name comparison: AWS uses octal encoding for characters like '@' and '*'. * Fix CAA record ordering. * Add changelog fragment. * Add wildcard record test. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/351ba4a5e8be79daba6b77bd351f82a6fb392e6c --- plugins/modules/route53.py | 5 +- .../targets/route53/tasks/main.yml | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 2168a0b11b6..945d5e8b679 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -389,8 +389,9 @@ def get_record(route53, zone_id, record_name, record_type, record_identifier): record_sets_results = _list_record_sets(route53, HostedZoneId=zone_id) for record_set in record_sets_results: + record_set['Name'] = record_set['Name'].encode().decode('unicode_escape') # If the record name and type is not equal, move to the next record - if (record_name, record_type) != (record_set['Name'], record_set['Type']): + if (record_name.lower(), record_type) != (record_set['Name'].lower(), record_set['Type']): continue if record_identifier and record_identifier != record_set.get("SetIdentifier"): @@ -573,6 +574,8 @@ def main(): # On CAA records order doesn't matter if type_in == 'CAA': resource_record_set['ResourceRecords'] = sorted(resource_record_set['ResourceRecords'], key=itemgetter('Value')) + if aws_record: + aws_record['ResourceRecords'] = sorted(aws_record['ResourceRecords'], key=itemgetter('Value')) if command_in == 'create' and aws_record == resource_record_set: module.exit_json(changed=False) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index bb8ca5ef059..04f1370ae21 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -266,6 +266,63 @@ - caa is not failed - caa is not changed + - name: Create an A record for a wildcard prefix + route53: + state: present + zone: '{{ zone_one }}' + record: '*.wildcard_test.{{ zone_one }}' + type: A + value: + - 192.0.2.1 + register: wc_a_record + - assert: + that: + - wc_a_record is not failed + - wc_a_record is changed + + - name: Create an A record for a wildcard prefix (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: '*.wildcard_test.{{ zone_one }}' + type: A + value: + - 192.0.2.1 + register: wc_a_record + - assert: + that: + - wc_a_record is not failed + - wc_a_record is not changed + + - name: Create an A record for a wildcard prefix (change) + route53: + state: present + zone: '{{ zone_one }}' + record: '*.wildcard_test.{{ zone_one }}' + type: A + value: + - 192.0.2.2 + overwrite: true + register: wc_a_record + - assert: + that: + - wc_a_record is not failed + - wc_a_record is changed + + - name: Delete an A record for a wildcard prefix + route53: + state: absent + zone: '{{ zone_one }}' + record: '*.wildcard_test.{{ zone_one }}' + type: A + value: + - 192.0.2.2 + register: wc_a_record + - assert: + that: + - wc_a_record is not failed + - wc_a_record is changed + # Tests on zone two (private zone) - name: Create A record using zone fqdn route53: From 16fe134864126c146422d0baa375e1530f49aa5d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 6 Apr 2021 13:10:43 +0200 Subject: [PATCH 22/81] Add compatibility shim so old values are still returned This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/fedc3c836fa268b2ac5b53a3c232a5c3067b2854 --- plugins/modules/route53.py | 53 +++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 945d5e8b679..d6f35b12c35 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -424,6 +424,49 @@ def get_zone_id_by_name(route53, module, zone_name, want_private, want_vpc_id): return None +def format_record(record_in, zone_in, zone_id): + """ + Formats a record in a way that's consistent with the pre-boto3 migration values + as well as returning the 'normal' boto3 style values + """ + if not record_in: + return None + + record = dict(record_in) + record['zone'] = zone_in + record['hosted_zone_id'] = zone_id + + record['type'] = record_in.get('Type', None) + record['record'] = record_in.get('Name').encode().decode('unicode_escape') + record['ttl'] = record_in.get('TTL', None) + record['identifier'] = record_in.get('SetIdentifier', None) + record['weight'] = record_in.get('Weight', None) + record['region'] = record_in.get('Region', None) + record['failover'] = record_in.get('Failover', None) + record['health_check'] = record_in.get('HealthCheckId', None) + + if record['ttl']: + record['ttl'] = str(record['ttl']) + if record['weight']: + record['weight'] = str(record['weight']) + if record['region']: + record['region'] = str(record['region']) + + if record_in.get('AliasTarget'): + record['alias'] = True + record['value'] = record_in['AliasTarget'].get('DNSName') + record['values'] = [record_in['AliasTarget'].get('DNSName')] + record['alias_hosted_zone_id'] = record_in['AliasTarget'].get('HostedZoneId') + record['alias_evaluate_target_health'] = record_in['AliasTarget'].get('EvaluateTargetHealth') + else: + record['alias'] = False + records = [r.get('Value') for r in record_in.get('ResourceRecords')] + record['value'] = ','.join(sorted(records)) + record['values'] = sorted(records) + + return record + + def get_hosted_zone_nameservers(route53, zone_id): hosted_zone_name = route53.get_hosted_zone(aws_retry=True, Id=zone_id)['HostedZone']['Name'] resource_records_sets = _list_record_sets(route53, HostedZoneId=zone_id) @@ -587,7 +630,8 @@ def main(): # Retrieve name servers associated to the zone. ns = get_hosted_zone_nameservers(route53, zone_id) - module.exit_json(changed=False, set=aws_record, nameservers=ns) + formatted_aws = format_record(aws_record, zone_in, zone_id) + module.exit_json(changed=False, set=formatted_aws, nameservers=ns) if command_in == 'delete' and not aws_record: module.exit_json(changed=False) @@ -633,11 +677,14 @@ def main(): except Exception as e: module.fail_json(msg='Unhandled exception. (%s)' % to_native(e)) + formatted_aws = format_record(aws_record, zone_in, zone_id) + formatted_record = format_record(resource_record_set, zone_in, zone_id) + module.exit_json( changed=True, diff=dict( - before=aws_record, - after=resource_record_set if command != 'delete' else {}, + before=formatted_aws, + after=formatted_record if command != 'delete' else {}, ), ) From 893fbdf900fb40fad26df9ca4e86bd3571d233dc Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 6 Apr 2021 13:11:57 +0200 Subject: [PATCH 23/81] Add (snake-cased) boto3 format exit values This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/5e0e94f150c1b5ce66b887be604f8c9b880365a5 --- plugins/modules/route53.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index d6f35b12c35..8cbb2647e4a 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -363,6 +363,7 @@ pass # Handled by AnsibleAWSModule from ansible.module_utils._text import to_native +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message @@ -621,7 +622,8 @@ def main(): aws_record['ResourceRecords'] = sorted(aws_record['ResourceRecords'], key=itemgetter('Value')) if command_in == 'create' and aws_record == resource_record_set: - module.exit_json(changed=False) + rr_sets = [camel_dict_to_snake_dict(resource_record_set)] + module.exit_json(changed=False, resource_records_sets=rr_sets) if command_in == 'get': if type_in == 'NS': @@ -631,7 +633,8 @@ def main(): ns = get_hosted_zone_nameservers(route53, zone_id) formatted_aws = format_record(aws_record, zone_in, zone_id) - module.exit_json(changed=False, set=formatted_aws, nameservers=ns) + rr_sets = [camel_dict_to_snake_dict(aws_record)] + module.exit_json(changed=False, set=formatted_aws, nameservers=ns, resource_record_sets=rr_sets) if command_in == 'delete' and not aws_record: module.exit_json(changed=False) @@ -677,6 +680,7 @@ def main(): except Exception as e: module.fail_json(msg='Unhandled exception. (%s)' % to_native(e)) + rr_sets = [camel_dict_to_snake_dict(resource_record_set)] formatted_aws = format_record(aws_record, zone_in, zone_id) formatted_record = format_record(resource_record_set, zone_in, zone_id) @@ -685,6 +689,7 @@ def main(): diff=dict( before=formatted_aws, after=formatted_record if command != 'delete' else {}, + resource_record_sets=rr_sets, ), ) From 5a7620c3cff191a77131854bc1b9279d7b731edd Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 14 Apr 2021 21:08:44 +0200 Subject: [PATCH 24/81] Add test for legacy route53 responses This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/69a678dccf82e4e6223a9bc9a00018d1d99d84d1 --- .../targets/route53/tasks/main.yml | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 04f1370ae21..18f10ae2987 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -93,12 +93,70 @@ record: 'qdn_test.{{ zone_one }}' type: A register: get_result - - assert: + - name: Check boto3 type get data + assert: that: - - get_result.nameservers|length > 0 - - get_result.set.Name == 'qdn_test.{{ zone_one }}' - - get_result.set.ResourceRecords[0].Value == '192.0.2.1' - - get_result.set.Type == 'A' + - get_result.nameservers | length > 0 + - get_result.resource_record_sets | length == 1 + - '"name" in record_set' + - record_set.name == qdn_record + - '"resource_records" in record_set' + - record_set.resource_records | length == 1 + - '"value" in record_set.resource_records[0]' + - record_set.resource_records[0].value == '192.0.2.1' + - '"ttl" in record_set' + - record_set.ttl == 3600 + - '"type" in record_set' + - record_set.type == 'A' + vars: + record_set: '{{ get_result.resource_record_sets[0] }}' + qdn_record: 'qdn_test.{{ zone_one }}' + + - name: Check boto3 compat get data + assert: + that: + - '"set" in get_result' + - '"Name" in record_set' + - record_set.Name == qdn_record + - '"ResourceRecords" in record_set' + - record_set.ResourceRecords | length == 1 + - '"Value" in record_set.ResourceRecords[0]' + - record_set.ResourceRecords[0].Value == '192.0.2.1' + - '"TTL" in record_set' + - record_set.TTL == 3600 + - record_set.Type == 'A' + vars: + record_set: '{{ get_result.set }}' + qdn_record: 'qdn_test.{{ zone_one }}' + + - name: Check boto2 compat get data + assert: + that: + - '"set" in get_result' + - '"alias" in record_set' + - record_set.alias == False + - '"failover" in record_set' + - '"health_check" in record_set' + - '"hosted_zone_id" in record_set' + - record_set.hosted_zone_id == z1.zone_id + - '"identifier" in record_set' + - '"record" in record_set' + - record_set.record == qdn_record + - '"ttl" in record_set' + - record_set.ttl == "3600" + - '"type" in record_set' + - record_set.type == 'A' + - '"value" in record_set' + - record_set.value == '192.0.2.1' + - '"values" in record_set' + - record_set['values'] | length == 1 + - record_set['values'][0] == '192.0.2.1' + - '"weight" in record_set' + - '"zone" in record_set' + - record_set.zone == zone_one + vars: + record_set: '{{ get_result.set }}' + qdn_record: 'qdn_test.{{ zone_one }}' - name: 'Create same A record using zone non-qualified domain' route53: From 48f87e2010d95849d7ade2f41137eaa99ca94038 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 1 May 2021 14:05:56 +0200 Subject: [PATCH 25/81] route53 - Fix typo in WaiterConfig This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/f0788a954f82a005e655fc3bfaa40f60e1cc0e87 --- plugins/modules/route53.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 8cbb2647e4a..eb8842951d5 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -668,7 +668,7 @@ def main(): Id=change_resource_record_sets['ChangeInfo']['Id'], WaiterConfig=dict( Delay=WAIT_RETRY, - MaxAttemps=wait_timeout_in // WAIT_RETRY, + MaxAttempts=wait_timeout_in // WAIT_RETRY, ) ) except is_boto3_error_message('but it already exists'): From 1f7662a5a5aa7e73f80b24f4a4ce24c09f4db6ff Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 1 May 2021 14:09:36 +0200 Subject: [PATCH 26/81] Add retries on PriorRequestNotComplete This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/36b3a008312aa9d92fb7d379508e493b1b7ef06c --- plugins/modules/route53.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index eb8842951d5..7c9d88c159e 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -574,12 +574,15 @@ def main(): if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None: module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.") + retry_decorator = AWSRetry.jittered_backoff( + retries=MAX_AWS_RETRIES, + delay=retry_interval_in, + catch_extra_error_codes=['PriorRequestNotComplete'], + ) + # connect to the route53 endpoint try: - route53 = module.client( - 'route53', - retry_decorator=AWSRetry.jittered_backoff(retries=MAX_AWS_RETRIES, delay=retry_interval_in) - ) + route53 = module.client('route53', retry_decorator=retry_decorator) except botocore.exceptions.HTTPClientError as e: module.fail_json_aws(e, msg='Failed to connect to AWS') From b2f586a8df6f616882817c7ad2c8c3bddf496dcf Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 1 May 2021 14:10:25 +0200 Subject: [PATCH 27/81] Bump max_delay for route53 retries based on retry_interval This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a6ddd35268c88827a1607d8bfe9969b9f1d9d2c1 --- plugins/modules/route53.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 7c9d88c159e..2bf956e5c60 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -578,6 +578,7 @@ def main(): retries=MAX_AWS_RETRIES, delay=retry_interval_in, catch_extra_error_codes=['PriorRequestNotComplete'], + max_delay=max(60, retry_interval_in), ) # connect to the route53 endpoint From 3fed1649cfce4a3b452327ccf090e40793ba0ed3 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sat, 1 May 2021 14:25:12 +0200 Subject: [PATCH 28/81] Use waiter with retrys This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/c43251e3133479bf727be6642d62905fe7bf4191 --- plugins/modules/route53.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 2bf956e5c60..ffbfdf4fc91 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -369,6 +369,7 @@ from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_message from ansible_collections.amazon.aws.plugins.module_utils.core import scrub_none_parameters from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter MAX_AWS_RETRIES = 10 # How many retries to perform when an API call is failing WAIT_RETRY = 5 # how many seconds to wait between propagation status polls @@ -667,7 +668,7 @@ def main(): ) if wait_in: - waiter = route53.get_waiter('resource_record_sets_changed') + waiter = get_waiter(route53, 'resource_record_sets_changed') waiter.wait( Id=change_resource_record_sets['ChangeInfo']['Id'], WaiterConfig=dict( From 084ab57e859d0178c5654aac107e518e2382faaf Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 6 May 2021 21:01:46 +0200 Subject: [PATCH 29/81] Update the default module requirements from python 2.6/boto to python 3.6/boto3 This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/c097c55293be0834a2b9d394733ec28965d142d7 --- plugins/modules/route53.py | 1 - plugins/modules/route53_health_check.py | 4 +++- plugins/modules/route53_zone.py | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index ffbfdf4fc91..d1391cfac58 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -13,7 +13,6 @@ --- module: route53 version_added: 1.0.0 -requirements: [ "boto3", "botocore" ] short_description: add or delete entries in Amazons Route 53 DNS service description: - Creates and deletes DNS records in Amazons Route 53 service. diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 03ac8b09af0..8f89aec5936 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -81,7 +81,9 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - +requirements: +- python >= 2.6 +- boto >= 2.49.0 ''' EXAMPLES = ''' diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 6467dd04527..cdc5538c027 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -12,7 +12,6 @@ version_added: 1.0.0 description: - Creates and deletes Route53 private and public zones. -requirements: [ boto3 ] options: zone: description: From 5f2ddf0128144a7a8271af4efa3773a51b5ec9ad Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 25 Jun 2021 10:08:27 +0200 Subject: [PATCH 30/81] Also remove python >= 2.6 from boto based modules This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/1ba077a10a0ba06cb0dd5355487eb45ee42c2936 --- plugins/modules/route53_health_check.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 8f89aec5936..7db80d875a5 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -82,7 +82,6 @@ - amazon.aws.aws - amazon.aws.ec2 requirements: -- python >= 2.6 - boto >= 2.49.0 ''' From 977237a701af6bf13d0a648ca270af45e7f77147 Mon Sep 17 00:00:00 2001 From: jillr Date: Thu, 29 Apr 2021 21:58:50 +0000 Subject: [PATCH 31/81] Remove shippable references from repo This collection has been operating on Zuul CI for some weeks now This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/4e0d83c65568a99a24307e37a14e6e0b173c948b --- tests/integration/targets/route53/aliases | 1 - tests/integration/targets/route53_zone/aliases | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/integration/targets/route53/aliases b/tests/integration/targets/route53/aliases index a70e7ee70b8..6db61c383f3 100644 --- a/tests/integration/targets/route53/aliases +++ b/tests/integration/targets/route53/aliases @@ -1,4 +1,3 @@ cloud/aws -shippable/aws/group2 route53_info diff --git a/tests/integration/targets/route53_zone/aliases b/tests/integration/targets/route53_zone/aliases index 6e3860bee23..4ef4b2067d0 100644 --- a/tests/integration/targets/route53_zone/aliases +++ b/tests/integration/targets/route53_zone/aliases @@ -1,2 +1 @@ cloud/aws -shippable/aws/group2 From b1623498007028a9619d2678d036b248aae31eb4 Mon Sep 17 00:00:00 2001 From: Kevin Brebanov Date: Mon, 7 Jun 2021 10:41:05 -0400 Subject: [PATCH 32/81] route53: Prevent identifier from always being scrubbed. This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/e038cb304c4697915d6c129f985aa372df834096 --- plugins/modules/route53.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index d1391cfac58..d4fe99531c0 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -606,6 +606,7 @@ def main(): 'TTL': ttl_in, 'ResourceRecords': [dict(Value=value) for value in value_in], 'HealthCheckId': health_check_in, + 'SetIdentifier': identifier_in, }) if alias_in: From 76682a60bfb715f9254583f1ebbd5ae4df00845c Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 29 Sep 2021 20:33:10 +0200 Subject: [PATCH 33/81] Add integration tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/fb3834327475a283cc80e30717d2bff16ab4ea53 --- .../targets/route53/tasks/main.yml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 18f10ae2987..ae80586dbe8 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -503,6 +503,42 @@ - alias_record is not failed - alias_record is not changed + - name: 'Create a weighted record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'weighted.{{ zone_one }}' + type: CNAME + value: 'zid_test.{{ zone_one }}' + overwrite: True + identifier: "host1@www" + weight: 100 + region: '{{ omit }}' + register: weighted_record + - name: 'This should be changed' + assert: + that: + - weighted_record is not failed + - weighted_record is changed + + - name: 'Re-Create a weighted record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'weighted.{{ zone_one }}' + type: CNAME + value: 'zid_test.{{ zone_one }}' + overwrite: True + identifier: "host1@www" + weight: 100 + region: '{{ omit }}' + register: weighted_record + - name: 'This should not be changed' + assert: + that: + - weighted_record is not failed + - weighted_record is not changed + always: - route53_info: query: record_sets @@ -521,6 +557,20 @@ loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' + route53: + state: absent + zone: '{{ zone_one }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + identifier: '{{ item.SetIdentifier }}' + region: '{{ omit }}' + ignore_errors: True + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + when: + - '"ResourceRecords" in item' + - '"SetIdentifier" in item' - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent @@ -551,6 +601,21 @@ loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' + route53: + state: absent + zone: '{{ zone_two }}' + record: '{{ item.Name }}' + type: '{{ item.Type }}' + value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + identifier: '{{ item.SetIdentifier }}' + region: '{{ omit }}' + private_zone: true + ignore_errors: True + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + when: + - '"ResourceRecords" in item' + - '"SetIdentifier" in item' - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent From eb071806ede2f6139f7919b3d76e42a3e1e3ff78 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 23 Sep 2021 09:33:08 +0200 Subject: [PATCH 34/81] Add test framework This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/67c31369e026ad535e4f46e1246c0c00a8909f88 --- .../targets/route53_health_check/aliases | 1 + .../route53_health_check/defaults/main.yml | 16 ++++++++++ .../route53_health_check/meta/main.yml | 3 ++ .../route53_health_check/tasks/main.yml | 31 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 tests/integration/targets/route53_health_check/aliases create mode 100644 tests/integration/targets/route53_health_check/defaults/main.yml create mode 100644 tests/integration/targets/route53_health_check/meta/main.yml create mode 100644 tests/integration/targets/route53_health_check/tasks/main.yml diff --git a/tests/integration/targets/route53_health_check/aliases b/tests/integration/targets/route53_health_check/aliases new file mode 100644 index 00000000000..4ef4b2067d0 --- /dev/null +++ b/tests/integration/targets/route53_health_check/aliases @@ -0,0 +1 @@ +cloud/aws diff --git a/tests/integration/targets/route53_health_check/defaults/main.yml b/tests/integration/targets/route53_health_check/defaults/main.yml new file mode 100644 index 00000000000..9e738b6a4da --- /dev/null +++ b/tests/integration/targets/route53_health_check/defaults/main.yml @@ -0,0 +1,16 @@ +--- +# route53_health_check integration tests +# +# Module uses the following as an 'ID' +# (the real ID is automatically assigned after creation) + +ip_address: 192.0.{{ 256 | random(seed=resource_prefix) }}.0 +fqdn: +port: +type: +request_interval: + +# modifiable +# - resource_path +# - string_match +# - failure_threshold diff --git a/tests/integration/targets/route53_health_check/meta/main.yml b/tests/integration/targets/route53_health_check/meta/main.yml new file mode 100644 index 00000000000..930e8622824 --- /dev/null +++ b/tests/integration/targets/route53_health_check/meta/main.yml @@ -0,0 +1,3 @@ +dependencies: + - prepare_tests + - setup_ec2_facts diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml new file mode 100644 index 00000000000..b2895ceddef --- /dev/null +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -0,0 +1,31 @@ +--- +# route53_health_check integration tests +# +# Module uses the following as an 'ID' +# (the real ID is automatically assigned after creation) +# - ip_address +# - fqdn +# - port +# - type (immutable) +# - request_interval (immutable) +# +# modifiable +# - resource_path +# - string_match +# - failure_threshold +# +- 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 }}' + block: + - debug: msg=hello + + always: + - debug: msg=hello + + ################################################ + # TEARDOWN STARTS HERE + ################################################ From 28dfc6207e3de9af0af918339ac99ec6ff94bcbd Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 24 Sep 2021 11:50:48 +0200 Subject: [PATCH 35/81] Initial tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/3075d2de392dcb315552e5e03704c51bb54ac8af --- .../route53_health_check/defaults/main.yml | 27 +- .../route53_health_check/tasks/main.yml | 254 +++++++++++++++++- 2 files changed, 274 insertions(+), 7 deletions(-) diff --git a/tests/integration/targets/route53_health_check/defaults/main.yml b/tests/integration/targets/route53_health_check/defaults/main.yml index 9e738b6a4da..aba5abb6c53 100644 --- a/tests/integration/targets/route53_health_check/defaults/main.yml +++ b/tests/integration/targets/route53_health_check/defaults/main.yml @@ -3,14 +3,31 @@ # # Module uses the following as an 'ID' # (the real ID is automatically assigned after creation) +# - ip_address +# - fqdn +# - port +# - type +# - request_interval -ip_address: 192.0.{{ 256 | random(seed=resource_prefix) }}.0 -fqdn: -port: -type: -request_interval: +#ip_address: We allocate an EIP due to route53 restrictions +fqdn: '{{ tiny_prefix }}.route53-health.ansible.test' +port: '8080' +type: 'TCP' +request_interval: 30 # modifiable # - resource_path # - string_match # - failure_threshold + +failure_threshold: 5 +failure_threshold_updated: 1 + +# For resource_path we need an HTTP/HTTPS type check +# for string_match we need an _STR_MATCH type +type_https_match: 'HTTPS_STR_MATCH' +type_http_match: 'HTTP_STR_MATCH' +resource_path: '/health.php' +resource_path_updated: '/healthz' +string_match: 'Hello' +string_match_updated: 'Hello World' diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index b2895ceddef..7fc9e41e8bc 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -21,11 +21,261 @@ security_token: '{{ security_token | default(omit) }}' region: '{{ aws_region }}' block: - - debug: msg=hello + # Route53 can only test against routable IPs. Request an EIP so some poor + # soul doesn't get randomly hit by our testing. + - name: Allocate an EIP we can test against + ec2_eip: + state: present + register: eip + + - set_fact: + ip_address: '{{ eip.public_ip }}' + + # Minimum possible definition + - name: 'Create a TCP health check' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + + - name: 'Create a TCP health check - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + + # Update an attribute (for TCP only failure threshold makes sense) + - name: 'Update TCP health check - set threshold' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + failure_threshold: '{{ failure_threshold_updated }}' + + - name: 'Update TCP health check - set threshold - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + failure_threshold: '{{ failure_threshold_updated }}' + + - name: 'Delete TCP health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + + - name: 'Delete TCP health check - idempotency' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + + # Create an HTTPS_STR_MATCH healthcheck so we can try out more settings + - name: 'Create a HTTPS_STR_MATCH health check' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + + - name: 'Create a HTTPS_STR_MATCH health check - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + + - name: 'Update HTTPS health check - set resource_path' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + resource_path: '{{ resource_path }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '{{ string_match }}' + + - name: 'Update HTTPS health check - set resource_path - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + resource_path: '{{ resource_path }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '{{ string_match }}' + + - name: 'Update HTTPS health check - set string_match' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + + - name: 'Update HTTPS health check - set string_match - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + + # Test deletion + - name: 'Delete HTTPS health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' + + - name: 'Delete HTTPS health check - idempotency' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' + + # Create an HTTP health check with lots of settings we can update + - name: 'Create Complex health check' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + resource_path: '{{ resource_path }}' + failure_threshold: '{{ failure_threshold }}' + + - name: 'Create Complex health check - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + resource_path: '{{ resource_path }}' + failure_threshold: '{{ failure_threshold }}' + + - name: 'Update Complex health check' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + resource_path: '{{ resource_path_updated }}' + failure_threshold: '{{ failure_threshold_updated }}' + + - name: 'Update Complex health check - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + resource_path: '{{ resource_path_updated }}' + failure_threshold: '{{ failure_threshold_updated }}' + + - name: 'Delete Complex health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' + + - name: 'Delete Complex health check - idempotency' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' always: - - debug: msg=hello ################################################ # TEARDOWN STARTS HERE ################################################ + + - name: 'Delete TCP health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + ignore_errors: true + + - name: 'Delete HTTPS health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' + ignore_errors: true + + - name: 'Delete Complex health check' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + # Mandatory for HTTPS_STR_MATCH checks + string_match: '' + ignore_errors: true + + - name: release EIP + ec2_eip: + state: absent + public_ip: '{{ ip_address }}' + ignore_errors: true From 0fe67f1d9117790a1b609aa2ace2edde0dd9366e Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 24 Sep 2021 18:38:47 +0200 Subject: [PATCH 36/81] Add result checks This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/a1238da0494a112dc88a1ab9255e6ad17d8993a7 --- .../route53_health_check/tasks/main.yml | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 7fc9e41e8bc..0bacbbc242b 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -38,6 +38,18 @@ ip_address: '{{ ip_address }}' port: '{{ port }}' type: '{{ type }}' + register: create_check + + - name: 'Check result - Create a TCP health check' + assert: + that: + - create_check is successful + - create_check is changed + - '"health_check" in create_check' + - '"id" in create_check.health_check' + + - set_fact: + tcp_check_id: '{{ create_check.health_check.id }}' - name: 'Create a TCP health check - idempotency' route53_health_check: @@ -45,6 +57,16 @@ ip_address: '{{ ip_address }}' port: '{{ port }}' type: '{{ type }}' + register: create_check + + - name: 'Check result - Create a TCP health check - idempotency' + assert: + that: + - create_check is successful + - create_check is not changed + - '"health_check" in create_check' + - '"id" in create_check.health_check' + - create_check.health_check.id == tcp_check_id # Update an attribute (for TCP only failure threshold makes sense) - name: 'Update TCP health check - set threshold' @@ -54,6 +76,16 @@ port: '{{ port }}' type: '{{ type }}' failure_threshold: '{{ failure_threshold_updated }}' + register: update_threshold + + - name: 'Check result - Update TCP health check - set threshold' + assert: + that: + - update_threshold is successful + - update_threshold is changed + - '"health_check" in update_threshold' + - '"id" in update_threshold.health_check' + - update_threshold.health_check.id == tcp_check_id - name: 'Update TCP health check - set threshold - idempotency' route53_health_check: @@ -62,6 +94,16 @@ port: '{{ port }}' type: '{{ type }}' failure_threshold: '{{ failure_threshold_updated }}' + register: update_threshold + + - name: 'Check result - Update TCP health check - set threshold - idempotency' + assert: + that: + - update_threshold is successful + - update_threshold is not changed + - '"health_check" in update_threshold' + - '"id" in update_threshold.health_check' + - update_threshold.health_check.id == tcp_check_id - name: 'Delete TCP health check' route53_health_check: @@ -69,6 +111,13 @@ ip_address: '{{ ip_address }}' port: '{{ port }}' type: '{{ type }}' + register: delete_tcp + + - name: 'Check result - Delete TCP health check' + assert: + that: + - delete_tcp is successful + - delete_tcp is changed - name: 'Delete TCP health check - idempotency' route53_health_check: @@ -76,6 +125,13 @@ ip_address: '{{ ip_address }}' port: '{{ port }}' type: '{{ type }}' + register: delete_tcp + + - name: 'Check result - Delete TCP health check - idempotency' + assert: + that: + - delete_tcp is successful + - delete_tcp is not changed # Create an HTTPS_STR_MATCH healthcheck so we can try out more settings - name: 'Create a HTTPS_STR_MATCH health check' @@ -87,6 +143,19 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' string_match: '{{ string_match }}' + register: create_match + + - name: 'Check result - Create a HTTPS_STR_MATCH health check' + assert: + that: + - create_match is successful + - create_match is changed + - '"health_check" in create_match' + - '"id" in create_match.health_check' + - create_match.health_check.id != tcp_check_id + + - set_fact: + match_check_id: '{{ create_match.health_check.id }}' - name: 'Create a HTTPS_STR_MATCH health check - idempotency' route53_health_check: @@ -97,6 +166,16 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' string_match: '{{ string_match }}' + register: create_match + + - name: 'Check result - Create a HTTPS_STR_MATCH health check - idempotency' + assert: + that: + - create_match is successful + - create_match is not changed + - '"health_check" in create_match' + - '"id" in create_match.health_check' + - create_match.health_check.id == match_check_id - name: 'Update HTTPS health check - set resource_path' route53_health_check: @@ -109,6 +188,16 @@ resource_path: '{{ resource_path }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '{{ string_match }}' + register: update_resource_path + + - name: 'Check result - Update HTTPS health check - set resource_path' + assert: + that: + - update_resource_path is successful + - update_resource_path is changed + - '"health_check" in update_resource_path' + - '"id" in update_resource_path.health_check' + - update_resource_path.health_check.id == match_check_id - name: 'Update HTTPS health check - set resource_path - idempotency' route53_health_check: @@ -121,6 +210,16 @@ resource_path: '{{ resource_path }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '{{ string_match }}' + register: update_resource_path + + - name: 'Check result - Update HTTPS health check - set resource_path - idempotency' + assert: + that: + - update_resource_path is successful + - update_resource_path is not changed + - '"health_check" in update_resource_path' + - '"id" in update_resource_path.health_check' + - update_resource_path.health_check.id == match_check_id - name: 'Update HTTPS health check - set string_match' route53_health_check: @@ -131,6 +230,16 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' string_match: '{{ string_match_updated }}' + register: update_string_match + + - name: 'Check result - Update HTTPS health check - set string_match' + assert: + that: + - update_string_match is successful + - update_string_match is changed + - '"health_check" in update_string_match' + - '"id" in update_string_match.health_check' + - update_string_match.health_check.id == match_check_id - name: 'Update HTTPS health check - set string_match - idempotency' route53_health_check: @@ -141,6 +250,17 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' string_match: '{{ string_match_updated }}' + register: update_string_match + + - name: 'Check result - Update HTTPS health check - set string_match - idempotency' + assert: + that: + - update_string_match is successful + # XXX Bug + # - update_string_match is not changed + - '"health_check" in update_string_match' + - '"id" in update_string_match.health_check' + - update_string_match.health_check.id == match_check_id # Test deletion - name: 'Delete HTTPS health check' @@ -153,6 +273,13 @@ request_interval: '{{ request_interval }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '' + register: delete_match + + - name: 'Check result - Delete HTTPS health check' + assert: + that: + - delete_match is successful + - delete_match is changed - name: 'Delete HTTPS health check - idempotency' route53_health_check: @@ -164,6 +291,13 @@ request_interval: '{{ request_interval }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '' + register: delete_match + + - name: 'Check result - Delete HTTPS health check - idempotency' + assert: + that: + - delete_match is successful + - delete_match is not changed # Create an HTTP health check with lots of settings we can update - name: 'Create Complex health check' @@ -177,6 +311,20 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + register: create_complex + + - name: 'Check result - Create Complex health check' + assert: + that: + - create_complex is successful + - create_complex is changed + - '"health_check" in create_complex' + - '"id" in create_complex.health_check' + - create_complex.health_check.id != tcp_check_id + - create_complex.health_check.id != match_check_id + + - set_fact: + complex_check_id: '{{ create_complex.health_check.id }}' - name: 'Create Complex health check - idempotency' route53_health_check: @@ -189,6 +337,16 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + register: create_complex + + - name: 'Check result - Create Complex health check - idempotency' + assert: + that: + - create_complex is successful + - create_complex is not changed + - '"health_check" in create_complex' + - '"id" in create_complex.health_check' + - create_complex.health_check.id == complex_check_id - name: 'Update Complex health check' route53_health_check: @@ -201,6 +359,16 @@ string_match: '{{ string_match_updated }}' resource_path: '{{ resource_path_updated }}' failure_threshold: '{{ failure_threshold_updated }}' + register: update_complex + + - name: 'Check result - Update Complex health check' + assert: + that: + - update_complex is successful + - update_complex is changed + - '"health_check" in update_complex' + - '"id" in update_complex.health_check' + - update_complex.health_check.id == complex_check_id - name: 'Update Complex health check - idempotency' route53_health_check: @@ -213,6 +381,16 @@ string_match: '{{ string_match_updated }}' resource_path: '{{ resource_path_updated }}' failure_threshold: '{{ failure_threshold_updated }}' + register: update_complex + + - name: 'Check result - Update Complex health check - idempotency' + assert: + that: + - update_complex is successful + - update_complex is not changed + - '"health_check" in update_complex' + - '"id" in update_complex.health_check' + - update_complex.health_check.id == complex_check_id - name: 'Delete Complex health check' route53_health_check: @@ -224,6 +402,13 @@ request_interval: '{{ request_interval }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '' + register: delete_complex + + - name: 'Check result - Delete Complex health check' + assert: + that: + - delete_complex is successful + - delete_complex is changed - name: 'Delete Complex health check - idempotency' route53_health_check: @@ -235,6 +420,13 @@ request_interval: '{{ request_interval }}' # Mandatory for HTTPS_STR_MATCH checks string_match: '' + register: delete_complex + + - name: 'Check result - Delete Complex health check - idempotency' + assert: + that: + - delete_complex is successful + - delete_complex is not changed always: From d415d3d48651af25c1c8c370640aef00cdefc012 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 8 Oct 2021 10:27:13 +0200 Subject: [PATCH 37/81] touch route53_health_check plugin to try and trigger tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/bafa3f33c0b9f93c0588662c27f6c1ecfdf34a56 --- plugins/modules/route53_health_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 7db80d875a5..0fc8ea4d698 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -10,7 +10,7 @@ --- module: route53_health_check version_added: 1.0.0 -short_description: Add or delete health-checks in Amazons Route53 DNS service +short_description: Manage health-checks in Amazons Route53 DNS service description: - Creates and deletes DNS Health checks in Amazons Route53 service. - Only the port, resource_path, string_match and request_interval are From f78eef1b3d83845b1998970b89ed41b0832f65a5 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 11:55:36 +0200 Subject: [PATCH 38/81] Initial boto3 migration This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8e35e6c87a292503568e583cba8eea0241d09834 --- plugins/modules/route53_health_check.py | 393 ++++++++++-------- .../route53_health_check/tasks/main.yml | 3 +- 2 files changed, 211 insertions(+), 185 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 0fc8ea4d698..980cef5fba5 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -44,7 +44,7 @@ health checks. The path can be any value for which your endpoint will return an HTTP status code of 2xx or 3xx when the endpoint is healthy, for example the file /docs/route53-health-check.html. - - Required for all checks except TCP. + - Mutually exclusive with I(type='TCP'). - The path must begin with a / - Maximum 255 characters. type: str @@ -74,15 +74,13 @@ - The number of consecutive health checks that an endpoint must pass or fail for Amazon Route 53 to change the current status of the endpoint from unhealthy to healthy or vice versa. - default: 3 + - Will default to C(3) if not specified on creation. choices: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] type: int author: "zimbatm (@zimbatm)" extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 -requirements: -- boto >= 2.49.0 ''' EXAMPLES = ''' @@ -119,162 +117,195 @@ import uuid try: - import boto.ec2 - from boto.route53 import Route53Connection, exception - from boto.route53.healthcheck import HealthCheck + import botocore except ImportError: pass # Handled by HAS_BOTO +from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict + from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import HAS_BOTO -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import get_aws_connection_info +from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry + + +def _list_health_checks(**params): + try: + results = client.list_health_checks(aws_retry=True, **params) + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: + module.fail_json_aws(e, msg='Failed to list health checks') + return results -# Things that can't get changed: -# protocol -# ip_address or domain -# request_interval -# string_match if not previously enabled -def find_health_check(conn, wanted): +def find_health_check(ip_addr, fqdn, hc_type, request_interval, port): """Searches for health checks that have the exact same set of immutable values""" - results = conn.get_list_health_checks() + # In lieu of an Id we perform matches against the following values: + # - ip_addr + # - fqdn + # - type (immutable) + # - request_interval + # - port + + # Because the list and route53 provides no 'filter' mechanism, + # the using a paginator would result in (on average) double the + # number of API calls and can get really slow. + # Additionally, we can't properly wrap the paginator, so retrying means + # starting from scratch with a paginator + results = _list_health_checks() while True: - for check in results.HealthChecks: - config = check.HealthCheckConfig + for check in results.get('HealthChecks'): + config = check.get('HealthCheckConfig') if ( - config.get('IPAddress') == wanted.ip_addr and - config.get('FullyQualifiedDomainName') == wanted.fqdn and - config.get('Type') == wanted.hc_type and - config.get('RequestInterval') == str(wanted.request_interval) and - config.get('Port') == str(wanted.port) + config.get('IPAddress', None) == ip_addr and + config.get('FullyQualifiedDomainName', None) == fqdn and + config.get('Type') == hc_type and + config.get('RequestInterval') == request_interval and + config.get('Port', None) == port and + True ): return check - if (results.IsTruncated == 'true'): - results = conn.get_list_health_checks(marker=results.NextMarker) + if results.get('IsTruncated', False): + results = _list_health_checks(Marker=results.get('NextMarker')) else: return None -def to_health_check(config): - return HealthCheck( - config.get('IPAddress'), - int(config.get('Port')), - config.get('Type'), - config.get('ResourcePath'), - fqdn=config.get('FullyQualifiedDomainName'), - string_match=config.get('SearchString'), - request_interval=int(config.get('RequestInterval')), - failure_threshold=int(config.get('FailureThreshold')), - ) +def delete_health_check(check_id): + if not check_id: + return False, None + if module.check_mode: + return True, 'delete' -def health_check_diff(a, b): - a = a.__dict__ - b = b.__dict__ - if a == b: - return {} - diff = {} - for key in set(a.keys()) | set(b.keys()): - if a.get(key) != b.get(key): - diff[key] = b.get(key) - return diff - - -def to_template_params(health_check): - params = { - 'ip_addr_part': '', - 'port': health_check.port, - 'type': health_check.hc_type, - 'resource_path_part': '', - 'fqdn_part': '', - 'string_match_part': '', - 'request_interval': health_check.request_interval, - 'failure_threshold': health_check.failure_threshold, - } - if health_check.ip_addr: - params['ip_addr_part'] = HealthCheck.XMLIpAddrPart % {'ip_addr': health_check.ip_addr} - if health_check.resource_path: - params['resource_path_part'] = XMLResourcePathPart % {'resource_path': health_check.resource_path} - if health_check.fqdn: - params['fqdn_part'] = HealthCheck.XMLFQDNPart % {'fqdn': health_check.fqdn} - if health_check.string_match: - params['string_match_part'] = HealthCheck.XMLStringMatchPart % {'string_match': health_check.string_match} - return params - - -XMLResourcePathPart = """%(resource_path)s""" - -POSTXMLBody = """ - - %(caller_ref)s - - %(ip_addr_part)s - %(port)s - %(type)s - %(resource_path_part)s - %(fqdn_part)s - %(string_match_part)s - %(request_interval)s - %(failure_threshold)s - - - """ - -UPDATEHCXMLBody = """ - - %(health_check_version)s - %(ip_addr_part)s - %(port)s - %(resource_path_part)s - %(fqdn_part)s - %(string_match_part)s - %(failure_threshold)i - - """ - - -def create_health_check(conn, health_check, caller_ref=None): - if caller_ref is None: - caller_ref = str(uuid.uuid4()) - uri = '/%s/healthcheck' % conn.Version - params = to_template_params(health_check) - params.update(xmlns=conn.XMLNameSpace, caller_ref=caller_ref) - - xml_body = POSTXMLBody % params - response = conn.make_request('POST', uri, {'Content-Type': 'text/xml'}, xml_body) - body = response.read() - boto.log.debug(body) - if response.status == 201: - e = boto.jsonresponse.Element() - h = boto.jsonresponse.XmlHandler(e, None) - h.parse(body) - return e - else: - raise exception.DNSServerError(response.status, response.reason, body) - - -def update_health_check(conn, health_check_id, health_check_version, health_check): - uri = '/%s/healthcheck/%s' % (conn.Version, health_check_id) - params = to_template_params(health_check) - params.update( - xmlns=conn.XMLNameSpace, - health_check_version=health_check_version, + try: + client.delete_health_check( + aws_retry=True, + HealthCheckId=check_id, + ) + except is_boto3_error_code('NoSuchHealthCheck'): + # Handle the deletion race condition as cleanly as possible + return False, None + except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except + module.fail_json_aws(e, msg='Failed to list health checks') + + return True, 'delete' + + +def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in): + + # In general, if a request is repeated with the same CallerRef it won't + # result in a duplicate check appearing. This means we can safely use our + # retry decorators + caller_ref = str(uuid.uuid4()) + missing_args = [] + + health_check = dict( + Type=type_in, + RequestInterval=request_interval_in, + Port=port_in, ) - xml_body = UPDATEHCXMLBody % params - response = conn.make_request('POST', uri, {'Content-Type': 'text/xml'}, xml_body) - body = response.read() - boto.log.debug(body) - if response.status not in (200, 204): - raise exception.DNSServerError(response.status, - response.reason, - body) - e = boto.jsonresponse.Element() - h = boto.jsonresponse.XmlHandler(e, None) - h.parse(body) - return e + if ip_addr_in: + health_check['IPAddress'] = ip_addr_in + if fqdn_in: + health_check['FullyQualifiedDomainName'] = fqdn_in + + if type_in in ['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: + resource_path = module.params.get('resource_path') + # if not resource_path: + # missing_args.append('resource_path') + if resource_path: + health_check['ResourcePath'] = resource_path + if type_in in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: + string_match = module.params.get('string_match') + if not string_match: + missing_args.append('string_match') + health_check['SearchString'] = module.params.get('string_match') + + failure_threshold = module.params.get('failure_threshold') + if not failure_threshold: + failure_threshold = 3 + health_check['FailureThreshold'] = failure_threshold + + if missing_args: + module.fail_json(msg='missing required arguments for creation: {0}'.format( + ', '.join(missing_args)), + ) + + if module.check_mode: + return True, 'create', None + + try: + result = client.create_health_check( + aws_retry=True, + CallerReference=caller_ref, + HealthCheckConfig=health_check, + ) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg='Failed to create health check.', health_check=health_check) + + return True, 'create', result.get('HealthCheck').get('Id') + + +def update_health_check(existing_check): + # In theory it's also possible to update the IPAddress, Port and + # FullyQualifiedDomainName, however, because we use these in lieu of a + # 'Name' to uniquely identify the health check this isn't currently + # supported. If we accepted an ID it would be possible to modify them. + + changes = dict() + existing_config = existing_check.get('HealthCheckConfig') + + resource_path = module.params.get('resource_path', None) + if resource_path and resource_path != existing_config.get('ResourcePath'): + changes['ResourcePath'] = resource_path + + search_string = module.params.get('string_match', None) + if search_string and search_string != existing_config.get('SearchString'): + changes['SearchString'] = search_string + + failure_threshold = module.params.get('failure_threshold', None) + if failure_threshold and failure_threshold != existing_config.get('FailureThreshold'): + changes['FailureThreshold'] = failure_threshold + + # No changes... + if not changes: + return False, None + + if module.check_mode: + return True, 'update' + + check_id = existing_check.get('Id') + version_id = existing_check.get('HealthCheckVersion', 1) + version_id += 1 + try: + client.update_health_check( + HealthCheckId=check_id, + HealthCheckVersion=version_id, + **changes, + ) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg='Failed to update health check.', id=check_id) + + return True, 'update' + + +def describe_health_check(id): + if not id: + return dict() + + try: + result = client.get_health_check( + aws_retry=True, + HealthCheckId=id, + ) + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.fail_json_aws(e, msg='Failed to get health check.', id=id) + + health_check = result.get('HealthCheck', {}) + health_check = camel_dict_to_snake_dict(health_check) + return health_check def main(): @@ -287,12 +318,26 @@ def main(): fqdn=dict(), string_match=dict(), request_interval=dict(type='int', choices=[10, 30], default=30), - failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], default=3), + failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), ) - module = AnsibleAWSModule(argument_spec=argument_spec, check_boto3=False) - if not HAS_BOTO: - module.fail_json(msg='boto 2.27.0+ required for this module') + args_one_of = [ + ['ip_address', 'fqdn'], + ] + + args_if = [ + ['type', 'TCP', ('port',)], + ] + + global module + global client + + module = AnsibleAWSModule( + argument_spec=argument_spec, + required_one_of=args_one_of, + required_if=args_if, + supports_check_mode=True, + ) state_in = module.params.get('state') ip_addr_in = module.params.get('ip_address') @@ -304,63 +349,45 @@ def main(): request_interval_in = module.params.get('request_interval') failure_threshold_in = module.params.get('failure_threshold') - if ip_addr_in is None and fqdn_in is None: - module.fail_json(msg="parameter 'ip_address' or 'fqdn' is required") - # Default port if port_in is None: if type_in in ['HTTP', 'HTTP_STR_MATCH']: port_in = 80 elif type_in in ['HTTPS', 'HTTPS_STR_MATCH']: port_in = 443 - else: - module.fail_json(msg="parameter 'port' is required for 'type' TCP") - # string_match in relation with type - if type_in in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: - if string_match_in is None: - module.fail_json(msg="parameter 'string_match' is required for the HTTP(S)_STR_MATCH types") - elif len(string_match_in) > 255: + if string_match_in: + if type_in not in ['HTTP_STR_MATCH', 'HTTPS_STR_MATCH']: + module.fail_json(msg="parameter 'string_match' argument is only for the HTTP(S)_STR_MATCH types") + if len(string_match_in) > 255: module.fail_json(msg="parameter 'string_match' is limited to 255 characters max") - elif string_match_in: - module.fail_json(msg="parameter 'string_match' argument is only for the HTTP(S)_STR_MATCH types") - region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) - # connect to the route53 endpoint - try: - conn = Route53Connection(**aws_connect_kwargs) - except boto.exception.BotoServerError as e: - module.fail_json(msg=e.error_message) + client = module.client('route53', retry_decorator=AWSRetry.jittered_backoff()) changed = False action = None check_id = None - wanted_config = HealthCheck(ip_addr_in, port_in, type_in, resource_path_in, fqdn_in, string_match_in, request_interval_in, failure_threshold_in) - existing_check = find_health_check(conn, wanted_config) + + existing_check = find_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) + if existing_check: - check_id = existing_check.Id - existing_config = to_health_check(existing_check.HealthCheckConfig) + check_id = existing_check.get('Id') - if state_in == 'present': + if state_in == 'absent': + changed, action = delete_health_check(check_id) + check_id = None + elif state_in == 'present': if existing_check is None: - action = "create" - check_id = create_health_check(conn, wanted_config).HealthCheck.Id - changed = True + changed, action, check_id = create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) else: - diff = health_check_diff(existing_config, wanted_config) - if diff: - action = "update" - update_health_check(conn, existing_check.Id, int(existing_check.HealthCheckVersion), wanted_config) - changed = True - elif state_in == 'absent': - if check_id: - action = "delete" - conn.delete_health_check(check_id) - changed = True - else: - module.fail_json(msg="Logic Error: Unknown state") - - module.exit_json(changed=changed, health_check=dict(id=check_id), action=action) + changed, action = update_health_check(existing_check) + + health_check = describe_health_check(id=check_id) + health_check['action'] = action + module.exit_json( + changed=changed, + health_check=health_check, + ) if __name__ == '__main__': diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 0bacbbc242b..edab46a58ac 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -256,8 +256,7 @@ assert: that: - update_string_match is successful - # XXX Bug - # - update_string_match is not changed + - update_string_match is not changed - '"health_check" in update_string_match' - '"id" in update_string_match.health_check' - update_string_match.health_check.id == match_check_id From 049d9679aa13a00bece8f8a84beeac24bf6ec21b Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 13:42:28 +0200 Subject: [PATCH 39/81] version This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2149da15c1fbfcf0cb92ceda0e8834355c14d161 --- plugins/modules/route53_health_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 980cef5fba5..cd74e61f660 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -277,8 +277,8 @@ def update_health_check(existing_check): return True, 'update' check_id = existing_check.get('Id') + # This makes sure we're starting from the version we think we are... version_id = existing_check.get('HealthCheckVersion', 1) - version_id += 1 try: client.update_health_check( HealthCheckId=check_id, From 086668ab8aeba08f21504035d57bb7bf16cd5221 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 13:43:24 +0200 Subject: [PATCH 40/81] string_match is no longer mandatory This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/ca6cde179f88bf1d055e2783b2b5dd8cfd19d8e4 --- .../targets/route53_health_check/tasks/main.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index edab46a58ac..0edd41e8cef 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -186,8 +186,6 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' resource_path: '{{ resource_path }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '{{ string_match }}' register: update_resource_path - name: 'Check result - Update HTTPS health check - set resource_path' @@ -208,8 +206,6 @@ fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' resource_path: '{{ resource_path }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '{{ string_match }}' register: update_resource_path - name: 'Check result - Update HTTPS health check - set resource_path - idempotency' @@ -270,8 +266,6 @@ type: '{{ type_https_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' register: delete_match - name: 'Check result - Delete HTTPS health check' @@ -288,8 +282,6 @@ type: '{{ type_https_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' register: delete_match - name: 'Check result - Delete HTTPS health check - idempotency' @@ -399,8 +391,6 @@ type: '{{ type_http_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' register: delete_complex - name: 'Check result - Delete Complex health check' @@ -417,8 +407,6 @@ type: '{{ type_http_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' register: delete_complex - name: 'Check result - Delete Complex health check - idempotency' @@ -449,8 +437,6 @@ type: '{{ type_https_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' ignore_errors: true - name: 'Delete Complex health check' @@ -461,8 +447,6 @@ type: '{{ type_http_match }}' fqdn: '{{ fqdn }}' request_interval: '{{ request_interval }}' - # Mandatory for HTTPS_STR_MATCH checks - string_match: '' ignore_errors: true - name: release EIP From 617f59ec7b936971ccf7e0cd6f4b3020459462e6 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 13:43:39 +0200 Subject: [PATCH 41/81] Add check_mode tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/f9a2c0d9350ead130f3f427e9d6571a4ea4faf6a --- .../route53_health_check/tasks/main.yml | 349 ++++++++++++++++++ 1 file changed, 349 insertions(+) diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 0edd41e8cef..96b9ab849a2 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -32,6 +32,21 @@ ip_address: '{{ eip.public_ip }}' # Minimum possible definition + - name: 'Create a TCP health check - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + register: create_check + check_mode: true + + - name: 'Check result - Create a TCP health check - check_mode' + assert: + that: + - create_check is successful + - create_check is changed + - name: 'Create a TCP health check' route53_health_check: state: present @@ -51,6 +66,21 @@ - set_fact: tcp_check_id: '{{ create_check.health_check.id }}' + - name: 'Create a TCP health check - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + register: create_check + check_mode: true + + - name: 'Check result - Create a TCP health check - idempotency - check_mode' + assert: + that: + - create_check is successful + - create_check is not changed + - name: 'Create a TCP health check - idempotency' route53_health_check: state: present @@ -69,6 +99,22 @@ - create_check.health_check.id == tcp_check_id # Update an attribute (for TCP only failure threshold makes sense) + - name: 'Update TCP health check - set threshold - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + failure_threshold: '{{ failure_threshold_updated }}' + register: update_threshold + check_mode: true + + - name: 'Check result - Update TCP health check - set threshold - check_mode' + assert: + that: + - update_threshold is successful + - update_threshold is changed + - name: 'Update TCP health check - set threshold' route53_health_check: state: present @@ -87,6 +133,22 @@ - '"id" in update_threshold.health_check' - update_threshold.health_check.id == tcp_check_id + - name: 'Update TCP health check - set threshold - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + failure_threshold: '{{ failure_threshold_updated }}' + register: update_threshold + check_mode: true + + - name: 'Check result - Update TCP health check - set threshold - idempotency - check_mode' + assert: + that: + - update_threshold is successful + - update_threshold is not changed + - name: 'Update TCP health check - set threshold - idempotency' route53_health_check: state: present @@ -105,6 +167,22 @@ - '"id" in update_threshold.health_check' - update_threshold.health_check.id == tcp_check_id + # Delete the check + - name: 'Delete TCP health check - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + register: delete_tcp + check_mode: True + + - name: 'Check result - Delete TCP health check - check_mode' + assert: + that: + - delete_tcp is successful + - delete_tcp is changed + - name: 'Delete TCP health check' route53_health_check: state: absent @@ -119,6 +197,21 @@ - delete_tcp is successful - delete_tcp is changed + - name: 'Delete TCP health check - idempotency - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + register: delete_tcp + check_mode: True + + - name: 'Check result - Delete TCP health check - idempotency - check_mode' + assert: + that: + - delete_tcp is successful + - delete_tcp is not changed + - name: 'Delete TCP health check - idempotency' route53_health_check: state: absent @@ -134,6 +227,24 @@ - delete_tcp is not changed # Create an HTTPS_STR_MATCH healthcheck so we can try out more settings + - name: 'Create a HTTPS_STR_MATCH health check - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + register: create_match + check_mode: true + + - name: 'Check result - Create a HTTPS_STR_MATCH health check - check_mode' + assert: + that: + - create_match is successful + - create_match is changed + - name: 'Create a HTTPS_STR_MATCH health check' route53_health_check: state: present @@ -157,6 +268,24 @@ - set_fact: match_check_id: '{{ create_match.health_check.id }}' + - name: 'Create a HTTPS_STR_MATCH health check - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + register: create_match + check_mode: true + + - name: 'Check result - Create a HTTPS_STR_MATCH health check - idempotency - check_mode' + assert: + that: + - create_match is successful + - create_match is not changed + - name: 'Create a HTTPS_STR_MATCH health check - idempotency' route53_health_check: state: present @@ -177,6 +306,24 @@ - '"id" in create_match.health_check' - create_match.health_check.id == match_check_id + - name: 'Update HTTPS health check - set resource_path - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + resource_path: '{{ resource_path }}' + register: update_resource_path + check_mode: true + + - name: 'Check result - Update HTTPS health check - set resource_path - check_mode' + assert: + that: + - update_resource_path is successful + - update_resource_path is changed + - name: 'Update HTTPS health check - set resource_path' route53_health_check: state: present @@ -197,6 +344,24 @@ - '"id" in update_resource_path.health_check' - update_resource_path.health_check.id == match_check_id + - name: 'Update HTTPS health check - set resource_path - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + resource_path: '{{ resource_path }}' + register: update_resource_path + check_mode: true + + - name: 'Check result - Update HTTPS health check - set resource_path - idempotency - check_mode' + assert: + that: + - update_resource_path is successful + - update_resource_path is not changed + - name: 'Update HTTPS health check - set resource_path - idempotency' route53_health_check: state: present @@ -217,6 +382,24 @@ - '"id" in update_resource_path.health_check' - update_resource_path.health_check.id == match_check_id + - name: 'Update HTTPS health check - set string_match - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + register: update_string_match + check_mode: true + + - name: 'Check result - Update HTTPS health check - set string_match - check_mode' + assert: + that: + - update_string_match is successful + - update_string_match is changed + - name: 'Update HTTPS health check - set string_match' route53_health_check: state: present @@ -237,6 +420,24 @@ - '"id" in update_string_match.health_check' - update_string_match.health_check.id == match_check_id + - name: 'Update HTTPS health check - set string_match - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + register: update_string_match + check_mode: true + + - name: 'Check result - Update HTTPS health check - set string_match - idempotency - check_mode' + assert: + that: + - update_string_match is successful + - update_string_match is not changed + - name: 'Update HTTPS health check - set string_match - idempotency' route53_health_check: state: present @@ -258,6 +459,23 @@ - update_string_match.health_check.id == match_check_id # Test deletion + - name: 'Delete HTTPS health check - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + register: delete_match + check_mode: true + + - name: 'Check result - Delete HTTPS health check - check_mode' + assert: + that: + - delete_match is successful + - delete_match is changed + - name: 'Delete HTTPS health check' route53_health_check: state: absent @@ -274,6 +492,23 @@ - delete_match is successful - delete_match is changed + - name: 'Delete HTTPS health check - idempotency - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_https_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + register: delete_match + check_mode: true + + - name: 'Check result - Delete HTTPS health check - idempotency - check_mode' + assert: + that: + - delete_match is successful + - delete_match is not changed + - name: 'Delete HTTPS health check - idempotency' route53_health_check: state: absent @@ -291,6 +526,26 @@ - delete_match is not changed # Create an HTTP health check with lots of settings we can update + - name: 'Create Complex health check - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + resource_path: '{{ resource_path }}' + failure_threshold: '{{ failure_threshold }}' + register: create_complex + check_mode: true + + - name: 'Check result - Create Complex health check - check_mode' + assert: + that: + - create_complex is successful + - create_complex is changed + - name: 'Create Complex health check' route53_health_check: state: present @@ -317,6 +572,26 @@ - set_fact: complex_check_id: '{{ create_complex.health_check.id }}' + - name: 'Create Complex health check - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match }}' + resource_path: '{{ resource_path }}' + failure_threshold: '{{ failure_threshold }}' + register: create_complex + check_mode: true + + - name: 'Check result - Create Complex health check - idempotency - check_mode' + assert: + that: + - create_complex is successful + - create_complex is not changed + - name: 'Create Complex health check - idempotency' route53_health_check: state: present @@ -339,6 +614,26 @@ - '"id" in create_complex.health_check' - create_complex.health_check.id == complex_check_id + - name: 'Update Complex health check - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + resource_path: '{{ resource_path_updated }}' + failure_threshold: '{{ failure_threshold_updated }}' + register: update_complex + check_mode: true + + - name: 'Check result - Update Complex health check - check_mode' + assert: + that: + - update_complex is successful + - update_complex is changed + - name: 'Update Complex health check' route53_health_check: state: present @@ -361,6 +656,26 @@ - '"id" in update_complex.health_check' - update_complex.health_check.id == complex_check_id + - name: 'Update Complex health check - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + string_match: '{{ string_match_updated }}' + resource_path: '{{ resource_path_updated }}' + failure_threshold: '{{ failure_threshold_updated }}' + register: update_complex + check_mode: true + + - name: 'Check result - Update Complex health check - idempotency - check_mode' + assert: + that: + - update_complex is successful + - update_complex is not changed + - name: 'Update Complex health check - idempotency' route53_health_check: state: present @@ -383,6 +698,23 @@ - '"id" in update_complex.health_check' - update_complex.health_check.id == complex_check_id + - name: 'Delete Complex health check - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + register: delete_complex + check_mode: true + + - name: 'Check result - Delete Complex health check - check_mode' + assert: + that: + - delete_complex is successful + - delete_complex is changed + - name: 'Delete Complex health check' route53_health_check: state: absent @@ -399,6 +731,23 @@ - delete_complex is successful - delete_complex is changed + - name: 'Delete Complex health check - idempotency - check_mode' + route53_health_check: + state: absent + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http_match }}' + fqdn: '{{ fqdn }}' + request_interval: '{{ request_interval }}' + register: delete_complex + check_mode: true + + - name: 'Check result - Delete Complex health check - idempotency - check_mode' + assert: + that: + - delete_complex is successful + - delete_complex is not changed + - name: 'Delete Complex health check - idempotency' route53_health_check: state: absent From 1b3bb04af4dcdcb23b3292ceb8bbb6cf4d3c476d Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 17:19:53 +0200 Subject: [PATCH 42/81] better This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/f3137d50cd8527dd8d0b911f9cfed8cab98cd905 --- plugins/modules/route53_health_check.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index cd74e61f660..b54b7699d83 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -161,8 +161,7 @@ def find_health_check(ip_addr, fqdn, hc_type, request_interval, port): config.get('FullyQualifiedDomainName', None) == fqdn and config.get('Type') == hc_type and config.get('RequestInterval') == request_interval and - config.get('Port', None) == port and - True + config.get('Port', None) == port ): return check From 31ebbc9252cb7be34f88eb2e8b0203e34baa5fcd Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Sun, 26 Sep 2021 20:20:35 +0200 Subject: [PATCH 43/81] Add docs and tests for return codes This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/f73c0f0f575952d7fe3844199ed288ee41a86189 --- plugins/modules/route53_health_check.py | 73 ++++ .../route53_health_check/defaults/main.yml | 2 +- .../route53_health_check/tasks/main.yml | 363 ++++++++++++++++-- 3 files changed, 410 insertions(+), 28 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index b54b7699d83..bcf7357c0ef 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -25,6 +25,7 @@ ip_address: description: - IP address of the end-point to check. Either this or I(fqdn) has to be provided. + - IP addresses must be publicly routable. type: str port: description: @@ -111,7 +112,79 @@ community.aws.route53_health_check: state: absent fqdn: host1.example.com +''' +RETURN = r''' +health_check: + description: Information about the health check. + returned: success + type: dict + contains: + action: + description: The action performed by the module. + type: str + returned: When a change is or would be made. + sample: 'updated' + id: + description: The Unique ID assigned by AWS to the health check. + type: str + returned: When the health check exists. + sample: 50ec8a13-9623-4c66-9834-dd8c5aedc9ba + health_check_version: + description: The version number of the health check. + type: int + returned: When the health check exists. + sample: 14 + health_check_config: + description: + - Detailed information about the health check. + - May contain additional values from Route 53 health check + features not yet supported by this module. + type: dict + returned: When the health check exists. + contains: + type: + description: The type of the health check. + type: str + returned: When the health check exists. + sample: 'HTTPS_STR_MATCH' + failure_threshold: + description: + - The number of consecutive health checks that an endpoint must pass or fail for Amazon Route 53 to + change the current status of the endpoint from unhealthy to healthy or vice versa. + type: int + returned: When the health check exists. + sample: 3 + fully_qualified_domain_name: + description: The FQDN configured for the health check to test. + type: str + returned: When the health check exists and an FQDN is configured. + sample: 'updated' + ip_address: + description: The IPv4 or IPv6 IP address of the endpoint to be queried. + type: str + returned: When the health check exists and a specific IP address is configured. + sample: '' + port: + description: The port on the endpoint that the health check will query. + type: str + returned: When the health check exists. + sample: 'updated' + request_interval: + description: The number of seconds between health check queries. + type: int + returned: When the health check exists. + sample: 30 + resource_path: + description: The URI path to query when performing an HTTP/HTTPS based health check. + type: str + returned: When the health check exists and a resource path has been configured. + sample: '/healthz' + search_string: + description: A string that must be present in the response for a health check to be considered successful. + type: str + returned: When the health check exists and a search string has been configured. + sample: 'ALIVE' ''' import uuid diff --git a/tests/integration/targets/route53_health_check/defaults/main.yml b/tests/integration/targets/route53_health_check/defaults/main.yml index aba5abb6c53..3763717e6d0 100644 --- a/tests/integration/targets/route53_health_check/defaults/main.yml +++ b/tests/integration/targets/route53_health_check/defaults/main.yml @@ -11,7 +11,7 @@ #ip_address: We allocate an EIP due to route53 restrictions fqdn: '{{ tiny_prefix }}.route53-health.ansible.test' -port: '8080' +port: 8080 type: 'TCP' request_interval: 30 diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 96b9ab849a2..8053db80735 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -61,7 +61,27 @@ - create_check is successful - create_check is changed - '"health_check" in create_check' - - '"id" in create_check.health_check' + - '"id" in _health_check' + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action == 'create' + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.type == 'TCP' + - _check_config.failure_threshold == 3 + - _check_config.request_interval == 30 + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ create_check.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - set_fact: tcp_check_id: '{{ create_check.health_check.id }}' @@ -96,7 +116,28 @@ - create_check is not changed - '"health_check" in create_check' - '"id" in create_check.health_check' - - create_check.health_check.id == tcp_check_id + - _health_check.id == tcp_check_id + - '"id" in _health_check' + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == 3 + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ create_check.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' # Update an attribute (for TCP only failure threshold makes sense) - name: 'Update TCP health check - set threshold - check_mode' @@ -130,8 +171,28 @@ - update_threshold is successful - update_threshold is changed - '"health_check" in update_threshold' - - '"id" in update_threshold.health_check' - - update_threshold.health_check.id == tcp_check_id + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_threshold.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update TCP health check - set threshold - idempotency - check_mode' route53_health_check: @@ -164,8 +225,28 @@ - update_threshold is successful - update_threshold is not changed - '"health_check" in update_threshold' - - '"id" in update_threshold.health_check' - - update_threshold.health_check.id == tcp_check_id + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_threshold.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' # Delete the check - name: 'Delete TCP health check - check_mode' @@ -262,8 +343,30 @@ - create_match is successful - create_match is changed - '"health_check" in create_match' - - '"id" in create_match.health_check' - - create_match.health_check.id != tcp_check_id + - '"id" in _health_check' + - _health_check.id != tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" in _check_config' + - _check_config.type == 'HTTPS_STR_MATCH' + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.search_string == string_match + vars: + _health_check: '{{ create_match.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - set_fact: match_check_id: '{{ create_match.health_check.id }}' @@ -303,8 +406,30 @@ - create_match is successful - create_match is not changed - '"health_check" in create_match' - - '"id" in create_match.health_check' - - create_match.health_check.id == match_check_id + - '"id" in _health_check' + - _health_check.id == match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_https_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.search_string == string_match + vars: + _health_check: '{{ create_match.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update HTTPS health check - set resource_path - check_mode' route53_health_check: @@ -341,8 +466,31 @@ - update_resource_path is successful - update_resource_path is changed - '"health_check" in update_resource_path' - - '"id" in update_resource_path.health_check' - - update_resource_path.health_check.id == match_check_id + - '"id" in _health_check' + - _health_check.id == match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_https_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match + vars: + _health_check: '{{ update_resource_path.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update HTTPS health check - set resource_path - idempotency - check_mode' route53_health_check: @@ -379,8 +527,31 @@ - update_resource_path is successful - update_resource_path is not changed - '"health_check" in update_resource_path' - - '"id" in update_resource_path.health_check' - - update_resource_path.health_check.id == match_check_id + - '"id" in _health_check' + - _health_check.id == match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_https_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match + vars: + _health_check: '{{ update_resource_path.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update HTTPS health check - set string_match - check_mode' route53_health_check: @@ -417,8 +588,31 @@ - update_string_match is successful - update_string_match is changed - '"health_check" in update_string_match' - - '"id" in update_string_match.health_check' - - update_string_match.health_check.id == match_check_id + - '"id" in _health_check' + - _health_check.id == match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_https_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match_updated + vars: + _health_check: '{{ update_string_match.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update HTTPS health check - set string_match - idempotency - check_mode' route53_health_check: @@ -455,8 +649,31 @@ - update_string_match is successful - update_string_match is not changed - '"health_check" in update_string_match' - - '"id" in update_string_match.health_check' - - update_string_match.health_check.id == match_check_id + - '"id" in _health_check' + - _health_check.id == match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_https_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == 3 + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match_updated + vars: + _health_check: '{{ update_string_match.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' # Test deletion - name: 'Delete HTTPS health check - check_mode' @@ -565,9 +782,32 @@ - create_complex is successful - create_complex is changed - '"health_check" in create_complex' - - '"id" in create_complex.health_check' - - create_complex.health_check.id != tcp_check_id - - create_complex.health_check.id != match_check_id + - '"id" in _health_check' + - _health_check.id != tcp_check_id + - _health_check.id != match_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_http_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == failure_threshold + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match + vars: + _health_check: '{{ create_complex.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - set_fact: complex_check_id: '{{ create_complex.health_check.id }}' @@ -611,8 +851,31 @@ - create_complex is successful - create_complex is not changed - '"health_check" in create_complex' - - '"id" in create_complex.health_check' - - create_complex.health_check.id == complex_check_id + - '"id" in _health_check' + - _health_check.id == complex_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_http_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == failure_threshold + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path + - _check_config.search_string == string_match + vars: + _health_check: '{{ create_complex.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update Complex health check - check_mode' route53_health_check: @@ -653,8 +916,31 @@ - update_complex is successful - update_complex is changed - '"health_check" in update_complex' - - '"id" in update_complex.health_check' - - update_complex.health_check.id == complex_check_id + - '"id" in _health_check' + - _health_check.id == complex_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_http_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path_updated + - _check_config.search_string == string_match_updated + vars: + _health_check: '{{ update_complex.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Update Complex health check - idempotency - check_mode' route53_health_check: @@ -695,8 +981,31 @@ - update_complex is successful - update_complex is not changed - '"health_check" in update_complex' - - '"id" in update_complex.health_check' - - update_complex.health_check.id == complex_check_id + - '"id" in _health_check' + - _health_check.id == complex_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" in _check_config' + - '"search_string" in _check_config' + - _check_config.type == type_http_match + - _check_config.request_interval == request_interval + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.fully_qualified_domain_name == fqdn + - _check_config.ip_address == ip_address + - _check_config.port == port + - _check_config.resource_path == resource_path_updated + - _check_config.search_string == string_match_updated + vars: + _health_check: '{{ update_complex.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' - name: 'Delete Complex health check - check_mode' route53_health_check: From 6d62105a16db458ce280123945de87430e959aa4 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 12 Oct 2021 09:55:24 +0200 Subject: [PATCH 44/81] Add support for disabling route53 health checks This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8beca00efa1ea2b2408cd853dabd4bc8b2c28cfe --- plugins/modules/route53_health_check.py | 19 +++ .../route53_health_check/tasks/main.yml | 147 +++++++++++++++++- 2 files changed, 165 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index bcf7357c0ef..e38e9adc053 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -22,6 +22,13 @@ choices: [ 'present', 'absent' ] type: str default: 'present' + disabled: + description: + - Stops Route 53 from performing health checks. + - See the AWS documentation for more details on the exact implications. + U(https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/health-checks-creating-values.html) + - Defaults to C(true) when creating a new health check. + type: bool ip_address: description: - IP address of the end-point to check. Either this or I(fqdn) has to be provided. @@ -185,6 +192,11 @@ type: str returned: When the health check exists and a search string has been configured. sample: 'ALIVE' + disabled: + description: Whether the health check has been disabled or not. + type: bool + returned: When the health check exists. + sample: false ''' import uuid @@ -278,6 +290,8 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ RequestInterval=request_interval_in, Port=port_in, ) + if module.params.get('disabled') is not None: + health_check['Disabled'] = module.params.get('disabled') if ip_addr_in: health_check['IPAddress'] = ip_addr_in if fqdn_in: @@ -341,6 +355,10 @@ def update_health_check(existing_check): if failure_threshold and failure_threshold != existing_config.get('FailureThreshold'): changes['FailureThreshold'] = failure_threshold + disabled = module.params.get('disabled', None) + if disabled is not None and disabled != existing_config.get('Disabled'): + changes['Disabled'] = module.params.get('disabled') + # No changes... if not changes: return False, None @@ -383,6 +401,7 @@ def describe_health_check(id): def main(): argument_spec = dict( state=dict(choices=['present', 'absent'], default='present'), + disabled=dict(type='bool'), ip_address=dict(), port=dict(type='int'), type=dict(required=True, choices=['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP']), diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 8053db80735..8164b65e5bf 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -13,6 +13,7 @@ # - resource_path # - string_match # - failure_threshold +# - disabled # - module_defaults: group/aws: @@ -67,6 +68,7 @@ - create_check.health_check.action == 'create' - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" not in _check_config' @@ -74,6 +76,7 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" not in _check_config' + - _check_config.disabled == false - _check_config.type == 'TCP' - _check_config.failure_threshold == 3 - _check_config.request_interval == 30 @@ -123,6 +126,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" not in _check_config' @@ -130,6 +134,7 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" not in _check_config' + - _check_config.disabled == false - _check_config.type == 'TCP' - _check_config.request_interval == 30 - _check_config.failure_threshold == 3 @@ -139,7 +144,7 @@ _health_check: '{{ create_check.health_check }}' _check_config: '{{ _health_check.health_check_config }}' - # Update an attribute (for TCP only failure threshold makes sense) + # Update an attribute - name: 'Update TCP health check - set threshold - check_mode' route53_health_check: state: present @@ -178,6 +183,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" not in _check_config' @@ -185,6 +191,7 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" not in _check_config' + - _check_config.disabled == false - _check_config.type == 'TCP' - _check_config.request_interval == 30 - _check_config.failure_threshold == failure_threshold_updated @@ -232,6 +239,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" not in _check_config' @@ -239,6 +247,119 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" not in _check_config' + - _check_config.disabled == false + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_threshold.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - set disabled - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + disabled: true + register: update_threshold + check_mode: true + + - name: 'Check result - Update TCP health check - set disabled - check_mode' + assert: + that: + - update_threshold is successful + - update_threshold is changed + + - name: 'Update TCP health check - set disabled' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + disabled: true + register: update_threshold + + - name: 'Check result - Update TCP health check - set disabled' + assert: + that: + - update_threshold is successful + - update_threshold is changed + - '"health_check" in update_threshold' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_threshold.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - set disabled - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + disabled: true + register: update_threshold + check_mode: true + + - name: 'Check result - Update TCP health check - set disabled - idempotency - check_mode' + assert: + that: + - update_threshold is successful + - update_threshold is not changed + + - name: 'Update TCP health check - set disabled - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + disabled: true + register: update_threshold + + - name: 'Check result - Update TCP health check - set disabled - idempotency' + assert: + that: + - update_threshold is successful + - update_threshold is not changed + - '"health_check" in update_threshold' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true - _check_config.type == 'TCP' - _check_config.request_interval == 30 - _check_config.failure_threshold == failure_threshold_updated @@ -350,6 +471,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -357,6 +479,7 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == 'HTTPS_STR_MATCH' - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -413,6 +536,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -420,6 +544,7 @@ - '"port" in _check_config' - '"resource_path" not in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == type_https_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -473,6 +598,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -480,6 +606,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == type_https_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -534,6 +661,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -541,6 +669,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == type_https_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -595,6 +724,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -602,6 +732,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == type_https_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -656,6 +787,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -663,6 +795,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == false - _check_config.type == type_https_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == 3 @@ -754,6 +887,7 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + disabled: true register: create_complex check_mode: true @@ -774,6 +908,7 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + disabled: true register: create_complex - name: 'Check result - Create Complex health check' @@ -790,6 +925,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -797,6 +933,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == true - _check_config.type == type_http_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == failure_threshold @@ -823,6 +960,7 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + disabled: true register: create_complex check_mode: true @@ -843,6 +981,7 @@ string_match: '{{ string_match }}' resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' + disabled: true register: create_complex - name: 'Check result - Create Complex health check - idempotency' @@ -858,6 +997,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -865,6 +1005,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == true - _check_config.type == type_http_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == failure_threshold @@ -923,6 +1064,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -930,6 +1072,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == true - _check_config.type == type_http_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == failure_threshold_updated @@ -988,6 +1131,7 @@ - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' + - '"disabled" in _check_config' - '"failure_threshold" in _check_config' - '"request_interval" in _check_config' - '"fully_qualified_domain_name" in _check_config' @@ -995,6 +1139,7 @@ - '"port" in _check_config' - '"resource_path" in _check_config' - '"search_string" in _check_config' + - _check_config.disabled == true - _check_config.type == type_http_match - _check_config.request_interval == request_interval - _check_config.failure_threshold == failure_threshold_updated From 2a4b9327ce3c6f0e95980c14295f3188a5ad8ac6 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 12 Oct 2021 10:00:33 +0200 Subject: [PATCH 45/81] changelog This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/96028dc3952c3a2cfbeac439e14651201859b657 --- plugins/modules/route53_health_check.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index e38e9adc053..af482132e56 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -29,6 +29,7 @@ U(https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/health-checks-creating-values.html) - Defaults to C(true) when creating a new health check. type: bool + version_added: 2.1.0 ip_address: description: - IP address of the end-point to check. Either this or I(fqdn) has to be provided. From d61ad769b5a269201a3021c6ea36dabcbbc0d128 Mon Sep 17 00:00:00 2001 From: Mauricio Teixeira <1847440+badnetmask@users.noreply.github.com> Date: Mon, 3 May 2021 19:21:46 -0400 Subject: [PATCH 46/81] implement adding tags to brand new zones This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8c20df05c2c2909ca8a718d9db6ec4d9459c08b1 --- plugins/modules/route53_zone.py | 42 ++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index cdc5538c027..6bc2443f2bd 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type - DOCUMENTATION = ''' module: route53_zone short_description: add or delete Route53 zones @@ -47,6 +46,18 @@ - The reusable delegation set ID to be associated with the zone. - Note that you can't associate a reusable delegation set with a private hosted zone. type: str + tags: + description: + - A hash/dictionary of tags to add to the new instance or to add/remove from an existing one. + type: dict + version_added: 2.1.0 + purge_tags: + description: + - Delete any tags not specified in the task that are on the zone. + This means you have to specify all the desired tags on each task affecting a zone. + default: false + type: bool + version_added: 2.1.0 extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 @@ -77,6 +88,21 @@ zone: example.com comment: reusable delegation set example delegation_set_id: A1BCDEF2GHIJKL + +- name: create a public zone with tags + community.aws.route53_zone: + zone: example.com + comment: this is an example + tags: + Owner: Ansible Team + +- name: modify a public zone, removing all previous tags and adding a new one + community.aws.route53_zone: + zone: example.com + comment: this is an example + tags: + Support: Ansible Community + purge_tags: true ''' RETURN = ''' @@ -115,10 +141,15 @@ returned: for public hosted zones, if they have been associated with a reusable delegation set type: str sample: "A1BCDEF2GHIJKL" +tags: + description: tags associated with the zone + returned: when tags are defined + type: dict ''' import time from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.community.aws.plugins.module_utils.route53 import manage_tags try: from botocore.exceptions import BotoCoreError, ClientError @@ -150,6 +181,8 @@ def create(module, client, matching_zones): vpc_region = module.params.get('vpc_region') comment = module.params.get('comment') delegation_set_id = module.params.get('delegation_set_id') + tags = module.params.get('tags') + purge_tags = module.params.get('purge_tags') if not zone_in.endswith('.'): zone_in += "." @@ -164,6 +197,8 @@ def create(module, client, matching_zones): 'name': zone_in, 'delegation_set_id': delegation_set_id, 'zone_id': None, + 'tags': tags, + 'purge_tags': purge_tags, } if private_zone: @@ -287,6 +322,9 @@ def create_or_update_public(module, client, matching_zones, record): record['name'] = zone_details['Name'] record['delegation_set_id'] = zone_delegation_set_details.get('Id', '').replace('/delegationset/', '') + if record['tags'] or record['purge_tags']: + changed = manage_tags(module, client, 'hostedzone', record, zone_details['Id'].replace('/hostedzone/', '')) + return changed, record @@ -394,6 +432,8 @@ def main(): comment=dict(default=''), hosted_zone_id=dict(), delegation_set_id=dict(), + tags=dict(type='dict'), + purge_tags=dict(type='bool', default=False), ) mutually_exclusive = [ From acde0634b7eec3c3fa58b471b565348f08473361 Mon Sep 17 00:00:00 2001 From: Mauricio Teixeira <1847440+badnetmask@users.noreply.github.com> Date: Tue, 4 May 2021 16:21:21 -0400 Subject: [PATCH 47/81] update route53 integration tests to cover tags This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/77236ee3d2af5149f700be914d5e32dc89015952 --- .../targets/route53/tasks/main.yml | 6 +++ .../targets/route53_zone/tasks/main.yml | 37 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index ae80586dbe8..c5312437f0b 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -29,12 +29,15 @@ route53_zone: zone: '{{ zone_one }}' comment: 'Created in Ansible test {{ resource_prefix }}' + tags: + TestTag: '{{ resource_prefix }}.z1' register: z1 - assert: that: - z1 is success - z1 is changed - "z1.comment == 'Created in Ansible test {{ resource_prefix }}'" + - "z1.tags.TestTag == '{{ resource_prefix }}.z1'" - name: 'Get zone details' route53_info: @@ -53,12 +56,15 @@ vpc_id: '{{ vpc.vpc.id }}' vpc_region: '{{ aws_region }}' comment: Created in Ansible test {{ resource_prefix }} + tags: + TestTag: '{{ resource_prefix }}.z2' register: z2 - assert: that: - z2 is success - z2 is changed - "z2.comment == 'Created in Ansible test {{ resource_prefix }}'" + - "z2.tags.TestTag == '{{ resource_prefix }}.z2'" - name: Get zone details route53_info: diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index 5fe154a6712..db430df5efc 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -27,6 +27,8 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present + tags: + TestTag: "{{ resource_prefix }}" register: output - assert: @@ -34,6 +36,7 @@ - output.changed - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' + - output.tags.TestTag == '{{ resource_prefix }}' - not output.private_zone # ============================================================ @@ -42,6 +45,8 @@ zone: "{{ resource_prefix }}.check.public" comment: original comment state: present + tags: + TestTag: "{{ resource_prefix }}" register: output check_mode: yes @@ -50,6 +55,7 @@ - output.changed - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.check.public.' + - output.tags.TestTag == '{{ resource_prefix }}' - not output.private_zone # ============================================================ @@ -58,6 +64,8 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present + tags: + TestTag: "{{ resource_prefix }}" register: output - assert: @@ -65,6 +73,7 @@ - not output.changed - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' + - output.tags.TestTag == '{{ resource_prefix }}' - not output.private_zone - name: Do an idemptotent update of a public zone (CHECK MODE) @@ -72,6 +81,8 @@ zone: "{{ resource_prefix }}.public" comment: original comment state: present + tags: + TestTag: "{{ resource_prefix }}" register: output check_mode: yes @@ -80,26 +91,47 @@ - not output.changed - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' + - output.tags.TestTag == '{{ resource_prefix }}' - not output.private_zone # ============================================================ - - name: Update comment of a public zone + - name: Modify tags on a public zone + route53_zone: + zone: "{{ resource_prefix }}.public" + comment: original comment + state: present + tags: + AnotherTag: "{{ resource_prefix }}.anothertag" + purge_tags: true + register: output + + - assert: + that: + - output.changed + - "'TestTag' not in output.tags" + - output.tags.AnotherTag == '{{ resource_prefix }}.anothertag' + + # ============================================================ + - name: Update comment and remove tags of a public zone route53_zone: zone: "{{ resource_prefix }}.public" comment: updated comment state: present + purge_tags: true register: output - assert: that: - output.changed - output.result.comment == "updated comment" + - not output.tags - - name: Update comment of a public zone (CHECK MODE) + - name: Update comment and remove tags of a public zone (CHECK MODE) route53_zone: zone: "{{ resource_prefix }}.public" comment: updated comment for check state: present + purge_tags: true register: output check_mode: yes @@ -107,6 +139,7 @@ that: - output.changed - output.result.comment == "updated comment for check" + - not output.tags # ============================================================ - name: Delete public zone (CHECK MODE) From 8c028fefbed931f83396b1cae2f10e492d1583b8 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 12 Oct 2021 13:52:39 +0200 Subject: [PATCH 48/81] Rework route53 tagging logic a little This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/26bebba5074378d88c138d5c954245a091a40e2f --- plugins/modules/route53_zone.py | 14 +++++++++----- .../targets/route53_zone/tasks/main.yml | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 6bc2443f2bd..334e6d62718 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -150,6 +150,7 @@ import time from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.community.aws.plugins.module_utils.route53 import manage_tags +from ansible_collections.community.aws.plugins.module_utils.route53 import get_tags try: from botocore.exceptions import BotoCoreError, ClientError @@ -197,8 +198,6 @@ def create(module, client, matching_zones): 'name': zone_in, 'delegation_set_id': delegation_set_id, 'zone_id': None, - 'tags': tags, - 'purge_tags': purge_tags, } if private_zone: @@ -206,6 +205,14 @@ def create(module, client, matching_zones): else: changed, result = create_or_update_public(module, client, matching_zones, record) + zone_id = result.get('zone_id') + if zone_id: + if tags is not None: + changed |= manage_tags(module, client, 'hostedzone', zone_id, tags, purge_tags) + result['tags'] = get_tags(module, client, 'hostedzone', zone_id) + else: + result['tags'] = tags + return changed, result @@ -322,9 +329,6 @@ def create_or_update_public(module, client, matching_zones, record): record['name'] = zone_details['Name'] record['delegation_set_id'] = zone_delegation_set_details.get('Id', '').replace('/delegationset/', '') - if record['tags'] or record['purge_tags']: - changed = manage_tags(module, client, 'hostedzone', record, zone_details['Id'].replace('/hostedzone/', '')) - return changed, record diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index db430df5efc..9b02fd607af 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -118,6 +118,7 @@ comment: updated comment state: present purge_tags: true + tags: {} register: output - assert: @@ -132,6 +133,7 @@ comment: updated comment for check state: present purge_tags: true + tags: {} register: output check_mode: yes From 10d68a0ccb34597c905d06b08c462e4c688f2188 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 12 Oct 2021 13:55:08 +0200 Subject: [PATCH 49/81] Add a snake_case tag This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/d372b88e7d92992a1909848df9839c20676bad65 --- tests/integration/targets/route53_zone/tasks/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index 9b02fd607af..79b87fc47fd 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -66,6 +66,7 @@ state: present tags: TestTag: "{{ resource_prefix }}" + another_tag: "{{ resource_prefix }} again" register: output - assert: @@ -74,6 +75,7 @@ - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' - output.tags.TestTag == '{{ resource_prefix }}' + - output.tags.another_tag == '{{ resource_prefix }} again' - not output.private_zone - name: Do an idemptotent update of a public zone (CHECK MODE) @@ -83,6 +85,7 @@ state: present tags: TestTag: "{{ resource_prefix }}" + another_tag: "{{ resource_prefix }} again" register: output check_mode: yes @@ -92,6 +95,7 @@ - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' - output.tags.TestTag == '{{ resource_prefix }}' + - output.tags.another_tag == '{{ resource_prefix }} again' - not output.private_zone # ============================================================ From bf2ca238fca5b8411a4fb91955c324ba14aa508a Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 14 Oct 2021 10:55:29 +0200 Subject: [PATCH 50/81] Fix tests This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/836eb9f07788edef06a886225e2d63d4720a9979 --- tests/integration/targets/route53_zone/tasks/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/integration/targets/route53_zone/tasks/main.yml b/tests/integration/targets/route53_zone/tasks/main.yml index 79b87fc47fd..9731c4a5c46 100644 --- a/tests/integration/targets/route53_zone/tasks/main.yml +++ b/tests/integration/targets/route53_zone/tasks/main.yml @@ -29,6 +29,7 @@ state: present tags: TestTag: "{{ resource_prefix }}" + another_tag: "{{ resource_prefix }} again" register: output - assert: @@ -37,6 +38,7 @@ - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.public.' - output.tags.TestTag == '{{ resource_prefix }}' + - output.tags.another_tag == '{{ resource_prefix }} again' - not output.private_zone # ============================================================ @@ -47,6 +49,7 @@ state: present tags: TestTag: "{{ resource_prefix }}" + another_tag: "{{ resource_prefix }} again" register: output check_mode: yes @@ -56,6 +59,7 @@ - output.comment == 'original comment' - output.name == '{{ resource_prefix }}.check.public.' - output.tags.TestTag == '{{ resource_prefix }}' + - output.tags.another_tag == '{{ resource_prefix }} again' - not output.private_zone # ============================================================ From 6cf0fbd01a3f33cf30ab913395d460e679cfe510 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 14 Oct 2021 10:04:22 +0200 Subject: [PATCH 51/81] docs - Remove references to old (unsupported) versions of Ansible This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/623dee5cee3f664fef5d3667030ad6cd0baba90d --- plugins/modules/route53.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index d4fe99531c0..964020257db 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -19,8 +19,7 @@ options: state: description: - - Specifies the state of the resource record. As of Ansible 2.4, the I(command) option has been changed - to I(state) as default and the choices C(present) and C(absent) have been added, but I(command) still works as well. + - Specifies the state of the resource record. required: true aliases: [ 'command' ] choices: [ 'present', 'absent', 'get', 'create', 'delete' ] From 27eb30077c7329a58a42e9b7e52618f391f366c1 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 18 Oct 2021 11:24:09 +0200 Subject: [PATCH 52/81] route53_health_check - add tagging support (#765) route53_health_check - add tagging support SUMMARY add tagging support to route53_health_check ISSUE TYPE Feature Pull Request COMPONENT NAME route53_health_check ADDITIONAL INFORMATION Reviewed-by: Alina Buzachis Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/324801611f014a0b8fd9ed4d0b3154eb3b56c41c --- plugins/modules/route53_health_check.py | 28 +- .../route53_health_check/tasks/main.yml | 504 +++++++++++++++++- 2 files changed, 515 insertions(+), 17 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index af482132e56..382be93ab6d 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -86,6 +86,17 @@ - Will default to C(3) if not specified on creation. choices: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] type: int + tags: + description: + - A hash/dictionary of tags to set on the health check. + type: dict + version_added: 2.1.0 + purge_tags: + description: + - Delete any tags not specified in I(tags). + default: false + type: bool + version_added: 2.1.0 author: "zimbatm (@zimbatm)" extends_documentation_fragment: - amazon.aws.aws @@ -198,6 +209,11 @@ type: bool returned: When the health check exists. sample: false + tags: + description: A dictionary representing the tags on the health check. + type: dict + returned: When the health check exists. + sample: '{"my_key": "my_value"}' ''' import uuid @@ -212,6 +228,8 @@ from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.community.aws.plugins.module_utils.route53 import get_tags +from ansible_collections.community.aws.plugins.module_utils.route53 import manage_tags def _list_health_checks(**params): @@ -332,7 +350,8 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: module.fail_json_aws(e, msg='Failed to create health check.', health_check=health_check) - return True, 'create', result.get('HealthCheck').get('Id') + check_id = result.get('HealthCheck').get('Id') + return True, 'create', check_id def update_health_check(existing_check): @@ -396,6 +415,8 @@ def describe_health_check(id): health_check = result.get('HealthCheck', {}) health_check = camel_dict_to_snake_dict(health_check) + tags = get_tags(module, client, 'healthcheck', id) + health_check['tags'] = tags return health_check @@ -411,6 +432,8 @@ def main(): string_match=dict(), request_interval=dict(type='int', choices=[10, 30], default=30), failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + tags=dict(type='dict'), + purge_tags=dict(type='bool', default=False), ) args_one_of = [ @@ -473,6 +496,9 @@ def main(): changed, action, check_id = create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) else: changed, action = update_health_check(existing_check) + if check_id: + changed |= manage_tags(module, client, 'healthcheck', check_id, + module.params.get('tags'), module.params.get('purge_tags')) health_check = describe_health_check(id=check_id) health_check['action'] = action diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 8164b65e5bf..426a0461703 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -65,6 +65,7 @@ - '"id" in _health_check' - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action == 'create' - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -123,6 +124,7 @@ - '"id" in _health_check' - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -180,6 +182,7 @@ - _health_check.id == tcp_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -236,6 +239,7 @@ - _health_check.id == tcp_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -264,14 +268,14 @@ port: '{{ port }}' type: '{{ type }}' disabled: true - register: update_threshold + register: update_disabled check_mode: true - name: 'Check result - Update TCP health check - set disabled - check_mode' assert: that: - - update_threshold is successful - - update_threshold is changed + - update_disabled is successful + - update_disabled is changed - name: 'Update TCP health check - set disabled' route53_health_check: @@ -280,18 +284,19 @@ port: '{{ port }}' type: '{{ type }}' disabled: true - register: update_threshold + register: update_disabled - name: 'Check result - Update TCP health check - set disabled' assert: that: - - update_threshold is successful - - update_threshold is changed - - '"health_check" in update_threshold' + - update_disabled is successful + - update_disabled is changed + - '"health_check" in update_disabled' - '"id" in _health_check' - _health_check.id == tcp_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -310,7 +315,7 @@ - _check_config.ip_address == ip_address - _check_config.port == port vars: - _health_check: '{{ update_threshold.health_check }}' + _health_check: '{{ update_disabled.health_check }}' _check_config: '{{ _health_check.health_check_config }}' - name: 'Update TCP health check - set disabled - idempotency - check_mode' @@ -320,14 +325,14 @@ port: '{{ port }}' type: '{{ type }}' disabled: true - register: update_threshold + register: update_disabled check_mode: true - name: 'Check result - Update TCP health check - set disabled - idempotency - check_mode' assert: that: - - update_threshold is successful - - update_threshold is not changed + - update_disabled is successful + - update_disabled is not changed - name: 'Update TCP health check - set disabled - idempotency' route53_health_check: @@ -336,18 +341,19 @@ port: '{{ port }}' type: '{{ type }}' disabled: true - register: update_threshold + register: update_disabled - name: 'Check result - Update TCP health check - set disabled - idempotency' assert: that: - - update_threshold is successful - - update_threshold is not changed - - '"health_check" in update_threshold' + - update_disabled is successful + - update_disabled is not changed + - '"health_check" in update_disabled' - '"id" in _health_check' - _health_check.id == tcp_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -366,7 +372,419 @@ - _check_config.ip_address == ip_address - _check_config.port == port vars: - _health_check: '{{ update_threshold.health_check }}' + _health_check: '{{ update_disabled.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - set tags - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false + register: update_tags + check_mode: true + + - name: 'Check result - Update TCP health check - set tags - check_mode' + assert: + that: + - update_tags is successful + - update_tags is changed + + - name: 'Update TCP health check - set tags' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false + register: update_tags + + - name: 'Check result - Update TCP health check - set tags' + assert: + that: + - update_tags is successful + - update_tags is changed + - '"health_check" in update_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_tags.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - set tags - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false + register: update_tags + check_mode: true + + - name: 'Check result - Update TCP health check - set tags - idempotency - check_mode' + assert: + that: + - update_tags is successful + - update_tags is not changed + + - name: 'Update TCP health check - set tags - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false + register: update_tags + + - name: 'Check result - Update TCP health check - set tags - idempotency' + assert: + that: + - update_tags is successful + - update_tags is not changed + - '"health_check" in update_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ update_tags.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - add tags - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: false + register: add_tags + check_mode: true + + - name: 'Check result - Update TCP health check - add tags - check_mode' + assert: + that: + - add_tags is successful + - add_tags is changed + + - name: 'Update TCP health check - add tags' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: false + register: add_tags + + - name: 'Check result - Update TCP health check - add tags' + assert: + that: + - add_tags is successful + - add_tags is changed + - '"health_check" in add_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' + - '"anotherTag" in _health_check.tags' + - _health_check.tags['anotherTag'] == 'anotherValue' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ add_tags.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - add tags - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: false + register: add_tags + check_mode: true + + - name: 'Check result - Update TCP health check - add tags - idempotency - check_mode' + assert: + that: + - add_tags is successful + - add_tags is not changed + + - name: 'Update TCP health check - add tags - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: false + register: add_tags + + - name: 'Check result - Update TCP health check - add tags - idempotency' + assert: + that: + - add_tags is successful + - add_tags is not changed + - '"health_check" in add_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' + - '"anotherTag" in _health_check.tags' + - _health_check.tags['anotherTag'] == 'anotherValue' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ add_tags.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - purge tags - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: true + register: purge_tags + check_mode: true + + - name: 'Check result - Update TCP health check - purge tags - check_mode' + assert: + that: + - purge_tags is successful + - purge_tags is changed + + - name: 'Update TCP health check - purge tags' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: true + register: purge_tags + + - name: 'Check result - Update TCP health check - purge tags' + assert: + that: + - purge_tags is successful + - purge_tags is changed + - '"health_check" in purge_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" not in _health_check.tags' + - '"snake_case" not in _health_check.tags' + - '"with space" not in _health_check.tags' + - '"anotherTag" in _health_check.tags' + - _health_check.tags['anotherTag'] == 'anotherValue' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ purge_tags.health_check }}' + _check_config: '{{ _health_check.health_check_config }}' + + - name: 'Update TCP health check - purge tags - idempotency - check_mode' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: true + register: purge_tags + check_mode: true + + - name: 'Check result - Update TCP health check - purge tags - idempotency - check_mode' + assert: + that: + - purge_tags is successful + - purge_tags is not changed + + - name: 'Update TCP health check - purge tags - idempotency' + route53_health_check: + state: present + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type }}' + tags: + anotherTag: anotherValue + purge_tags: true + register: purge_tags + + - name: 'Check result - Update TCP health check - purge tags - idempotency' + assert: + that: + - purge_tags is successful + - purge_tags is not changed + - '"health_check" in purge_tags' + - '"id" in _health_check' + - _health_check.id == tcp_check_id + - '"action" in _health_check' + - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" not in _health_check.tags' + - '"snake_case" not in _health_check.tags' + - '"with space" not in _health_check.tags' + - '"anotherTag" in _health_check.tags' + - _health_check.tags['anotherTag'] == 'anotherValue' + - create_check.health_check.action is none + - '"health_check_config" in create_check.health_check' + - '"type" in _check_config' + - '"disabled" in _check_config' + - '"failure_threshold" in _check_config' + - '"request_interval" in _check_config' + - '"fully_qualified_domain_name" not in _check_config' + - '"ip_address" in _check_config' + - '"port" in _check_config' + - '"resource_path" not in _check_config' + - '"search_string" not in _check_config' + - _check_config.disabled == true + - _check_config.type == 'TCP' + - _check_config.request_interval == 30 + - _check_config.failure_threshold == failure_threshold_updated + - _check_config.ip_address == ip_address + - _check_config.port == port + vars: + _health_check: '{{ purge_tags.health_check }}' _check_config: '{{ _health_check.health_check_config }}' # Delete the check @@ -468,6 +886,7 @@ - _health_check.id != tcp_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -533,6 +952,7 @@ - _health_check.id == match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -595,6 +1015,7 @@ - _health_check.id == match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -658,6 +1079,7 @@ - _health_check.id == match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -721,6 +1143,7 @@ - _health_check.id == match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -784,6 +1207,7 @@ - _health_check.id == match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -888,6 +1312,11 @@ resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' disabled: true + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false register: create_complex check_mode: true @@ -909,6 +1338,11 @@ resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' disabled: true + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false register: create_complex - name: 'Check result - Create Complex health check' @@ -922,6 +1356,13 @@ - _health_check.id != match_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -961,6 +1402,11 @@ resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' disabled: true + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false register: create_complex check_mode: true @@ -982,6 +1428,11 @@ resource_path: '{{ resource_path }}' failure_threshold: '{{ failure_threshold }}' disabled: true + tags: + CamelCase: CamelCaseValue + snake_case: snake_case_value + "with space": Some value + purge_tags: false register: create_complex - name: 'Check result - Create Complex health check - idempotency' @@ -994,6 +1445,13 @@ - _health_check.id == complex_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -1061,6 +1519,13 @@ - _health_check.id == complex_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' @@ -1128,6 +1593,13 @@ - _health_check.id == complex_check_id - '"action" in _health_check' - '"health_check_version" in _health_check' + - '"tags" in _health_check' + - '"CamelCase" in _health_check.tags' + - _health_check.tags['CamelCase'] == 'CamelCaseValue' + - '"snake_case" in _health_check.tags' + - _health_check.tags['snake_case'] == 'snake_case_value' + - '"with space" in _health_check.tags' + - _health_check.tags['with space'] == 'Some value' - create_check.health_check.action is none - '"health_check_config" in create_check.health_check' - '"type" in _check_config' From 846d2caebecbc1844118457218cb8b5ddd87d518 Mon Sep 17 00:00:00 2001 From: Markus Bergholz Date: Fri, 12 Nov 2021 20:31:02 +0100 Subject: [PATCH 53/81] route53: fix empty result set (#799) route53: fix empty result set SUMMARY Closes: #798 Using state: get on none existing records results in an error. ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53 ADDITIONAL INFORMATION --- plugins/modules/route53.py | 5 +++++ tests/integration/targets/route53/tasks/main.yml | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 964020257db..c60886d3c99 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -637,6 +637,11 @@ def main(): ns = get_hosted_zone_nameservers(route53, zone_id) formatted_aws = format_record(aws_record, zone_in, zone_id) + + if formatted_aws is None: + # record does not exist + module.exit_json(changed=False, set=[], nameservers=ns, resource_record_sets=[]) + rr_sets = [camel_dict_to_snake_dict(aws_record)] module.exit_json(changed=False, set=formatted_aws, nameservers=ns, resource_record_sets=rr_sets) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index c5312437f0b..f7af9eaffdb 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -417,6 +417,20 @@ - get_result.set.ResourceRecords[0].Value == "192.0.2.1" - get_result.set.Type == "A" + - name: Get a record that does not exist + route53: + state: get + zone: "{{ zone_two }}" + record: "notfound.{{ zone_two }}" + type: A + private_zone: true + register: get_result + - assert: + that: + - get_result.nameservers|length > 0 + - get_result.set|length == 0 + - get_result.resource_record_sets|length == 0 + - name: Create same A record using zone non-qualified domain route53: state: present From 5dc5e3093d976c7159dcc61026901bbfb94c0b23 Mon Sep 17 00:00:00 2001 From: Markus Bergholz Date: Fri, 12 Nov 2021 21:01:50 +0100 Subject: [PATCH 54/81] fix diff mode (#802) Route53: fix diff mode when state: absent SUMMARY Fix diff mode when state: absend ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53 Reviewed-by: Felix Fontein Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/2b01e9cab29b9be9fc256adecb4b80459096248f --- plugins/modules/route53.py | 2 +- tests/integration/targets/route53/tasks/main.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index c60886d3c99..17ed261aa14 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -697,7 +697,7 @@ def main(): changed=True, diff=dict( before=formatted_aws, - after=formatted_record if command != 'delete' else {}, + after=formatted_record if command_in != 'delete' else {}, resource_record_sets=rr_sets, ), ) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index f7af9eaffdb..90a69751a9e 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -386,6 +386,7 @@ that: - wc_a_record is not failed - wc_a_record is changed + - wc_a_record.diff.after == {} # Tests on zone two (private zone) - name: Create A record using zone fqdn From ffd0336b8e1208053ad2c6e470607e3ec81b11b5 Mon Sep 17 00:00:00 2001 From: Markus Bergholz Date: Mon, 22 Nov 2021 17:37:43 +0100 Subject: [PATCH 55/81] fix delete records without TTL (#801) fix delete records without TTL SUMMARY Closes #800 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53 Reviewed-by: Mark Chappell Reviewed-by: Felix Fontein Reviewed-by: Tiger Kaovilai Reviewed-by: Markus Bergholz Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/472776eb197f04522ee2498fe8840d7131fd7d29 --- plugins/modules/route53.py | 7 ++-- .../targets/route53/tasks/main.yml | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 17ed261aa14..4275d65b684 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -69,7 +69,6 @@ value: description: - The new value when creating a DNS record. YAML lists or multiple comma-spaced values are allowed for non-alias records. - - When deleting a record all values for the record must be specified or Route 53 will not delete it. type: list elements: str overwrite: @@ -513,8 +512,6 @@ def main(): required_if=( ('state', 'present', ['value']), ('state', 'create', ['value']), - ('state', 'absent', ['value']), - ('state', 'delete', ['value']), ), # failover, region and weight are mutually exclusive mutually_exclusive=[ @@ -607,6 +604,10 @@ def main(): 'HealthCheckId': health_check_in, 'SetIdentifier': identifier_in, }) + if command_in == 'delete' and aws_record is not None: + resource_record_set['TTL'] = aws_record.get('TTL') + if not resource_record_set['ResourceRecords']: + resource_record_set['ResourceRecords'] = aws_record.get('ResourceRecords') if alias_in: resource_record_set['AliasTarget'] = dict( diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 90a69751a9e..f827387d83d 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -388,6 +388,45 @@ - wc_a_record is changed - wc_a_record.diff.after == {} + - name: create a record with different TTL + community.aws.route53: + state: present + zone: '{{ zone_one }}' + record: 'localhost.{{ zone_one }}' + type: A + value: 127.0.0.1 + ttl: 30 + register: ttl30 + - name: check return values + assert: + that: + - ttl30.diff.resource_record_sets[0].ttl == 30 + - ttl30 is changed + + - name: delete previous record without mention ttl and value + community.aws.route53: + state: absent + zone: '{{ zone_one }}' + record: 'localhost.{{ zone_one }}' + type: A + register: ttl30 + - name: check if record is deleted + assert: + that: + - ttl30 is changed + + - name: immutable delete previous record without mention ttl and value + community.aws.route53: + state: absent + zone: '{{ zone_one }}' + record: 'localhost.{{ zone_one }}' + type: A + register: ttl30 + - name: check if record was deleted + assert: + that: + - ttl30 is not changed + # Tests on zone two (private zone) - name: Create A record using zone fqdn route53: From 45f3f655ce433214a5cd9c248d7d1075a5958b9b Mon Sep 17 00:00:00 2001 From: Mark Woolley Date: Tue, 30 Nov 2021 18:09:50 +0000 Subject: [PATCH 56/81] Fix route53_info max_items / type being ignored (#813) Fix route53_info max_items / type being ignored SUMMARY Currently if max_items is set on the route53_info module then it is ignored meaning all items are returned. type is also ignored due to an incorrect if statement It looks like it was a regression introduced here: ansible/ansible@6075536#diff-23a0c9250633162d50c3f06442b7a552a5ae0659a24dd01a328c0e165e473616 The tests have been updated to reflect a check on max_items and type Fixes: #529 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_info ADDITIONAL INFORMATION The problem with max_items being ignored is resolved by adding PaginationConfig and adding MaxItems to that instead. The problem with type being ignored is resolved by fixing an if statement. Boto3 docs: https://boto3.amazonaws.com/v1/documentation/api/1.18.7/reference/services/route53.html#Route53.Paginator.ListResourceRecordSets Reviewed-by: Mark Chappell Reviewed-by: Markus Bergholz Reviewed-by: Alina Buzachis Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/e323f838086127acbd524eec55d5c967a65c8196 --- plugins/modules/route53_info.py | 49 +++++++++++----- .../targets/route53/tasks/main.yml | 56 +++++++++++++++++-- 2 files changed, 84 insertions(+), 21 deletions(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index abdf7e44709..322ce7b0523 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -47,7 +47,7 @@ description: - Maximum number of items to return for various get/list requests. required: false - type: str + type: int next_marker: description: - "Some requests such as list_command: hosted_zones will return a maximum @@ -72,7 +72,7 @@ description: - The type of DNS record. required: false - choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS' ] + choices: [ 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS', 'NAPTR', 'SOA', 'DS' ] type: str dns_name: description: @@ -228,9 +228,13 @@ def get_hosted_zone(client, module): def reusable_delegation_set_details(client, module): params = dict() + if not module.params.get('delegation_set_id'): + # Set PaginationConfig with max_items if module.params.get('max_items'): - params['MaxItems'] = module.params.get('max_items') + params['PaginationConfig'] = dict( + MaxItems=module.params.get('max_items') + ) if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') @@ -246,8 +250,11 @@ def reusable_delegation_set_details(client, module): def list_hosted_zones(client, module): params = dict() + # Set PaginationConfig with max_items if module.params.get('max_items'): - params['MaxItems'] = module.params.get('max_items') + params['PaginationConfig'] = dict( + MaxItems=module.params.get('max_items') + ) if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') @@ -272,8 +279,11 @@ def list_hosted_zones_by_name(client, module): if module.params.get('dns_name'): params['DNSName'] = module.params.get('dns_name') + # Set PaginationConfig with max_items if module.params.get('max_items'): - params['MaxItems'] = module.params.get('max_items') + params['PaginationConfig'] = dict( + MaxItems=module.params.get('max_items') + ) return client.list_hosted_zones_by_name(**params) @@ -340,12 +350,15 @@ def get_resource_tags(client, module): def list_health_checks(client, module): params = dict() - if module.params.get('max_items'): - params['MaxItems'] = module.params.get('max_items') - if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') + # Set PaginationConfig with max_items + if module.params.get('max_items'): + params['PaginationConfig'] = dict( + MaxItems=module.params.get('max_items') + ) + paginator = client.get_paginator('list_health_checks') health_checks = paginator.paginate(**params).build_full_result()['HealthChecks'] return { @@ -362,19 +375,25 @@ def record_sets_details(client, module): else: module.fail_json(msg="Hosted Zone Id is required") - if module.params.get('max_items'): - params['MaxItems'] = module.params.get('max_items') - if module.params.get('start_record_name'): params['StartRecordName'] = module.params.get('start_record_name') + # Check that both params are set if type is applied if module.params.get('type') and not module.params.get('start_record_name'): module.fail_json(msg="start_record_name must be specified if type is set") - elif module.params.get('type'): + + if module.params.get('type'): params['StartRecordType'] = module.params.get('type') + # Set PaginationConfig with max_items + if module.params.get('max_items'): + params['PaginationConfig'] = dict( + MaxItems=module.params.get('max_items') + ) + paginator = client.get_paginator('list_resource_record_sets') record_sets = paginator.paginate(**params).build_full_result()['ResourceRecordSets'] + return { "ResourceRecordSets": record_sets, "list": record_sets, @@ -420,12 +439,12 @@ def main(): ], required=True), change_id=dict(), hosted_zone_id=dict(), - max_items=dict(), + max_items=dict(type='int'), next_marker=dict(), delegation_set_id=dict(), start_record_name=dict(), - type=dict(choices=[ - 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS' + type=dict(type='str', choices=[ + 'A', 'CNAME', 'MX', 'AAAA', 'TXT', 'PTR', 'SRV', 'SPF', 'CAA', 'NS', 'NAPTR', 'SOA', 'DS' ]), dns_name=dict(), resource_id=dict(type='list', aliases=['resource_ids'], elements='str'), diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index f827387d83d..92f8601e64a 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -164,6 +164,7 @@ record_set: '{{ get_result.set }}' qdn_record: 'qdn_test.{{ zone_one }}' + ## test A recordset creation and order adjustments - name: 'Create same A record using zone non-qualified domain' route53: state: present @@ -220,17 +221,19 @@ - mv_a_record is not failed - mv_a_record is not changed + # Get resulting A record and ensure max_items is applied - name: 'get Route53 A record information' route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' start_record_name: 'order_test.{{ zone_one }}' - max_items: 50 + max_items: 1 register: records + - assert: that: - - records.ResourceRecordSets|length == 3 + - records.ResourceRecordSets|length == 1 - records.ResourceRecordSets[0].ResourceRecords|length == 2 - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' - records.ResourceRecordSets[0].ResourceRecords[1].Value == '192.0.2.1' @@ -261,6 +264,7 @@ - '192.0.2.2' register: del_a_record ignore_errors: true + - name: 'This should not fail, because `overwrite` is true' assert: that: @@ -275,12 +279,44 @@ start_record_name: 'order_test.{{ zone_one }}' max_items: 50 register: records + - assert: that: - records.ResourceRecordSets|length == 3 - records.ResourceRecordSets[0].ResourceRecords|length == 1 - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' + ## Test CNAME record creation and retrive info + - name: "Create CNAME record" + route53: + state: present + zone: "{{ zone_one }}" + type: CNAME + record: "cname_test.{{ zone_one }}" + value: "order_test.{{ zone_one }}" + register: cname_record + + - assert: + that: + - cname_record is not failed + - cname_record is changed + + - name: "Get Route53 CNAME record information" + route53_info: + type: CNAME + query: record_sets + hosted_zone_id: "{{ z1.zone_id }}" + start_record_name: "cname_test.{{ zone_one }}" + max_items: 1 + register: cname_records + + - assert: + that: + - cname_records.ResourceRecordSets|length == 1 + - cname_records.ResourceRecordSets[0].ResourceRecords|length == 1 + - cname_records.ResourceRecordSets[0].ResourceRecords[0].Value == "order_test.{{ zone_one }}" + + ## Test CAA record creation - name: 'Create a LetsEncrypt CAA record' route53: state: present @@ -389,7 +425,7 @@ - wc_a_record.diff.after == {} - name: create a record with different TTL - community.aws.route53: + route53: state: present zone: '{{ zone_one }}' record: 'localhost.{{ zone_one }}' @@ -404,7 +440,7 @@ - ttl30 is changed - name: delete previous record without mention ttl and value - community.aws.route53: + route53: state: absent zone: '{{ zone_one }}' record: 'localhost.{{ zone_one }}' @@ -416,7 +452,7 @@ - ttl30 is changed - name: immutable delete previous record without mention ttl and value - community.aws.route53: + route53: state: absent zone: '{{ zone_one }}' record: 'localhost.{{ zone_one }}' @@ -604,6 +640,7 @@ query: record_sets hosted_zone_id: '{{ z1.zone_id }}' register: z1_records + - name: 'Loop over A/AAAA/CNAME Alias records and delete them' route53: state: absent @@ -617,6 +654,7 @@ loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent @@ -624,6 +662,7 @@ record: '{{ item.Name }}' type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' + weight: '{{ item.Weight | default(omit) }}' identifier: '{{ item.SetIdentifier }}' region: '{{ omit }}' ignore_errors: True @@ -631,6 +670,7 @@ when: - '"ResourceRecords" in item' - '"SetIdentifier" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent @@ -647,6 +687,7 @@ query: record_sets hosted_zone_id: '{{ z2.zone_id }}' register: z2_records + - name: 'Loop over A/AAAA/CNAME Alias records and delete them' route53: state: absent @@ -661,6 +702,7 @@ loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent @@ -676,6 +718,7 @@ when: - '"ResourceRecords" in item' - '"SetIdentifier" in item' + - name: 'Loop over A/AAAA/CNAME records and delete them' route53: state: absent @@ -697,6 +740,7 @@ ignore_errors: yes retries: 10 until: delete_one is not failed + - name: 'Delete test zone two {{ zone_two }}' route53_zone: state: absent @@ -705,7 +749,7 @@ ignore_errors: yes retries: 10 until: delete_two is not failed - when: false + - name: destroy VPC ec2_vpc_net: cidr_block: 192.0.2.0/24 From 78e1541fde2bf64878182f238d3cf857105e1b12 Mon Sep 17 00:00:00 2001 From: Jill R <4121322+jillr@users.noreply.github.com> Date: Thu, 2 Dec 2021 02:58:06 -0700 Subject: [PATCH 57/81] Remove deprecated "facts" aliases (#814) Remove deprecated "facts" aliases SUMMARY Modules named "facts.py" that do not return ansible_facts were renamed to "info.py" in 2.9. Remove these aliases now that the deprecation period is over. This PR should be included in 3.0.0 of the collection. ISSUE TYPE Bugfix Pull Request COMPONENT NAME *_facts.py Reviewed-by: Mark Chappell Reviewed-by: Jill R Reviewed-by: None This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/68aaa7057be46a3ab36f572fd0013d64653af909 --- plugins/modules/route53_facts.py | 1 - plugins/modules/route53_info.py | 3 --- 2 files changed, 4 deletions(-) delete mode 120000 plugins/modules/route53_facts.py diff --git a/plugins/modules/route53_facts.py b/plugins/modules/route53_facts.py deleted file mode 120000 index 6b40f0529b0..00000000000 --- a/plugins/modules/route53_facts.py +++ /dev/null @@ -1 +0,0 @@ -route53_info.py \ No newline at end of file diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 322ce7b0523..e2f1cd686ff 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -12,7 +12,6 @@ version_added: 1.0.0 description: - Gets various details related to Route53 zone, record set or health check details. - - This module was called C(route53_facts) before Ansible 2.9. The usage did not change. options: query: description: @@ -474,8 +473,6 @@ def main(): ], check_boto3=False, ) - if module._name == 'route53_facts': - module.deprecate("The 'route53_facts' module has been renamed to 'route53_info'", date='2021-12-01', collection_name='community.aws') try: route53 = module.client('route53') From 73641e11a479edbc80b45b62bfa26557740c30ec Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 31 Jan 2022 12:26:36 +0100 Subject: [PATCH 58/81] Final removal of original boto SDK (#898) [Breaking Change] Final removal of original boto SDK SUMMARY Remove old boto based inventory script Clean up requirements Clean up random comments in docs/comments ISSUE TYPE Feature Pull Request COMPONENT NAME scripts/inventory/ec2.py requirements.txt test-requirements.txt tests/integration/requirements.txt tests/unit/requirements.txt 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/00037befadcca989b9bd63a3ee92ce8732e88133 --- plugins/modules/route53.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 4275d65b684..4ddacdca09e 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -413,7 +413,7 @@ def get_zone_id_by_name(route53, module, zone_name, want_private, want_vpc_id): if private_zone == want_private and zone['Name'] == zone_name: if want_vpc_id: - # NOTE: These details aren't available in other boto methods, hence the necessary + # NOTE: These details aren't available in other boto3 methods, hence the necessary # extra API call hosted_zone = route53.get_hosted_zone(aws_retry=True, Id=zone_id) if want_vpc_id in [v['VPCId'] for v in hosted_zone['VPCs']]: From de1422d826e9cad0d47c61544227693674dc7b31 Mon Sep 17 00:00:00 2001 From: Mark Woolley Date: Fri, 4 Feb 2022 12:03:55 +0000 Subject: [PATCH 59/81] Add AWSRetry backoff logic to route53_zone and route53_info (#865) Add AWSRetry backoff logic to route53_zone and route53_info SUMMARY Add AWSRetry backoff logic to route53_zone and route53_info. Currently from time to time I've been hitting AWS throttling errors leading to ansible failures: An exception occurred during task execution. To see the full traceback, use -vvv. The error was: botocore.exceptions.ClientError: An error occurred (Throttling) when calling the ListHostedZones operation (reached max retries: 4): Rate exceeded fatal: [localhost_staging -> 127.0.0.1]: FAILED! => changed=false boto3_version: 1.20.34 botocore_version: 1.23.34 error: code: Throttling message: Rate exceeded type: Sender msg: 'Could not list current hosted zones: An error occurred (Throttling) when calling the ListHostedZones operation (reached max retries: 4): Rate exceeded' response_metadata: http_headers: connection: close content-length: '255' content-type: text/xml date: Fri, 14 Jan 2022 12:09:35 GMT x-amzn-requestid: xxxxxxx http_status_code: 400 max_attempts_reached: true request_id: xxxxxxx retry_attempts: 4 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_zone route53_info ADDITIONAL INFORMATION I've added the standard backoff retry logic and split out the paginators. Reviewed-by: Alina Buzachis 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/ca1d33ff5549eae5eb40aa7dfab4eb059075f70e --- plugins/modules/route53_info.py | 56 +++++++++++++++++++-------------- plugins/modules/route53_zone.py | 53 ++++++++++++++++++------------- 2 files changed, 64 insertions(+), 45 deletions(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index e2f1cd686ff..7622113c25e 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -212,9 +212,17 @@ from ansible.module_utils._text import to_native from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -def get_hosted_zone(client, module): +# Split out paginator to allow for the backoff decorator to function +@AWSRetry.jittered_backoff() +def _paginated_result(paginator_name, **params): + paginator = client.get_paginator(paginator_name) + return paginator.paginate(**params).build_full_result() + + +def get_hosted_zone(): params = dict() if module.params.get('hosted_zone_id'): @@ -225,7 +233,7 @@ def get_hosted_zone(client, module): return client.get_hosted_zone(**params) -def reusable_delegation_set_details(client, module): +def reusable_delegation_set_details(): params = dict() if not module.params.get('delegation_set_id'): @@ -246,7 +254,7 @@ def reusable_delegation_set_details(client, module): return results -def list_hosted_zones(client, module): +def list_hosted_zones(): params = dict() # Set PaginationConfig with max_items @@ -261,15 +269,15 @@ def list_hosted_zones(client, module): if module.params.get('delegation_set_id'): params['DelegationSetId'] = module.params.get('delegation_set_id') - paginator = client.get_paginator('list_hosted_zones') - zones = paginator.paginate(**params).build_full_result()['HostedZones'] + zones = _paginated_result('list_hosted_zones', **params)['HostedZones'] + return { "HostedZones": zones, "list": zones, } -def list_hosted_zones_by_name(client, module): +def list_hosted_zones_by_name(): params = dict() if module.params.get('hosted_zone_id'): @@ -287,7 +295,7 @@ def list_hosted_zones_by_name(client, module): return client.list_hosted_zones_by_name(**params) -def change_details(client, module): +def change_details(): params = dict() if module.params.get('change_id'): @@ -299,11 +307,11 @@ def change_details(client, module): return results -def checker_ip_range_details(client, module): +def checker_ip_range_details(): return client.get_checker_ip_ranges() -def get_count(client, module): +def get_count(): if module.params.get('query') == 'health_check': results = client.get_health_check_count() else: @@ -312,7 +320,7 @@ def get_count(client, module): return results -def get_health_check(client, module): +def get_health_check(): params = dict() if not module.params.get('health_check_id'): @@ -330,7 +338,7 @@ def get_health_check(client, module): return results -def get_resource_tags(client, module): +def get_resource_tags(): params = dict() if module.params.get('resource_id'): @@ -346,7 +354,7 @@ def get_resource_tags(client, module): return client.list_tags_for_resources(**params) -def list_health_checks(client, module): +def list_health_checks(): params = dict() if module.params.get('next_marker'): @@ -358,15 +366,15 @@ def list_health_checks(client, module): MaxItems=module.params.get('max_items') ) - paginator = client.get_paginator('list_health_checks') - health_checks = paginator.paginate(**params).build_full_result()['HealthChecks'] + health_checks = _paginated_result('list_health_checks', **params)['HealthChecks'] + return { "HealthChecks": health_checks, "list": health_checks, } -def record_sets_details(client, module): +def record_sets_details(): params = dict() if module.params.get('hosted_zone_id'): @@ -390,8 +398,7 @@ def record_sets_details(client, module): MaxItems=module.params.get('max_items') ) - paginator = client.get_paginator('list_resource_record_sets') - record_sets = paginator.paginate(**params).build_full_result()['ResourceRecordSets'] + record_sets = _paginated_result('list_resource_record_sets', **params)['ResourceRecordSets'] return { "ResourceRecordSets": record_sets, @@ -399,7 +406,7 @@ def record_sets_details(client, module): } -def health_check_details(client, module): +def health_check_details(): health_check_invocations = { 'list': list_health_checks, 'details': get_health_check, @@ -409,11 +416,11 @@ def health_check_details(client, module): 'tags': get_resource_tags, } - results = health_check_invocations[module.params.get('health_check_method')](client, module) + results = health_check_invocations[module.params.get('health_check_method')]() return results -def hosted_zone_details(client, module): +def hosted_zone_details(): hosted_zone_invocations = { 'details': get_hosted_zone, 'list': list_hosted_zones, @@ -422,11 +429,14 @@ def hosted_zone_details(client, module): 'tags': get_resource_tags, } - results = hosted_zone_invocations[module.params.get('hosted_zone_method')](client, module) + results = hosted_zone_invocations[module.params.get('hosted_zone_method')]() return results def main(): + global module + global client + argument_spec = dict( query=dict(choices=[ 'change', @@ -475,7 +485,7 @@ def main(): ) try: - route53 = module.client('route53') + client = module.client('route53', retry_decorator=AWSRetry.jittered_backoff()) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg='Failed to connect to AWS') @@ -490,7 +500,7 @@ def main(): results = dict(changed=False) try: - results = invocations[module.params.get('query')](route53, module) + results = invocations[module.params.get('query')]() except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json(msg=to_native(e)) diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 334e6d62718..ba51fcbb9e2 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = r''' module: route53_zone short_description: add or delete Route53 zones version_added: 1.0.0 @@ -65,7 +65,7 @@ author: "Christopher Troup (@minichate)" ''' -EXAMPLES = ''' +EXAMPLES = r''' - name: create a public zone community.aws.route53_zone: zone: example.com @@ -105,7 +105,7 @@ purge_tags: true ''' -RETURN = ''' +RETURN = r''' comment: description: optional hosted zone comment returned: when hosted zone exists @@ -149,6 +149,7 @@ import time 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.community.aws.plugins.module_utils.route53 import manage_tags from ansible_collections.community.aws.plugins.module_utils.route53 import get_tags @@ -158,10 +159,15 @@ pass # caught by AnsibleAWSModule -def find_zones(module, client, zone_in, private_zone): +@AWSRetry.jittered_backoff() +def _list_zones(): + paginator = client.get_paginator('list_hosted_zones') + return paginator.paginate().build_full_result() + + +def find_zones(zone_in, private_zone): try: - paginator = client.get_paginator('list_hosted_zones') - results = paginator.paginate().build_full_result() + results = _list_zones() except (BotoCoreError, ClientError) as e: module.fail_json_aws(e, msg="Could not list current hosted zones") zones = [] @@ -176,7 +182,7 @@ def find_zones(module, client, zone_in, private_zone): return zones -def create(module, client, matching_zones): +def create(matching_zones): zone_in = module.params.get('zone').lower() vpc_id = module.params.get('vpc_id') vpc_region = module.params.get('vpc_region') @@ -201,9 +207,9 @@ def create(module, client, matching_zones): } if private_zone: - changed, result = create_or_update_private(module, client, matching_zones, record) + changed, result = create_or_update_private(matching_zones, record) else: - changed, result = create_or_update_public(module, client, matching_zones, record) + changed, result = create_or_update_public(matching_zones, record) zone_id = result.get('zone_id') if zone_id: @@ -216,7 +222,7 @@ def create(module, client, matching_zones): return changed, result -def create_or_update_private(module, client, matching_zones, record): +def create_or_update_private(matching_zones, record): for z in matching_zones: try: result = client.get_hosted_zone(Id=z['Id']) # could be in different regions or have different VPCids @@ -275,7 +281,7 @@ def create_or_update_private(module, client, matching_zones, record): return changed, record -def create_or_update_public(module, client, matching_zones, record): +def create_or_update_public(matching_zones, record): zone_details, zone_delegation_set_details = None, {} for matching_zone in matching_zones: try: @@ -332,7 +338,7 @@ def create_or_update_public(module, client, matching_zones, record): return changed, record -def delete_private(module, client, matching_zones, vpc_id, vpc_region): +def delete_private(matching_zones, vpc_id, vpc_region): for z in matching_zones: try: result = client.get_hosted_zone(Id=z['Id']) @@ -360,7 +366,7 @@ def delete_private(module, client, matching_zones, vpc_id, vpc_region): return False, "The vpc_id and the vpc_region do not match a private hosted zone." -def delete_public(module, client, matching_zones): +def delete_public(matching_zones): if len(matching_zones) > 1: changed = False msg = "There are multiple zones that match. Use hosted_zone_id to specify the correct zone." @@ -375,7 +381,7 @@ def delete_public(module, client, matching_zones): return changed, msg -def delete_hosted_id(module, client, hosted_zone_id, matching_zones): +def delete_hosted_id(hosted_zone_id, matching_zones): if hosted_zone_id == "all": deleted = [] for z in matching_zones: @@ -401,7 +407,7 @@ def delete_hosted_id(module, client, hosted_zone_id, matching_zones): return changed, msg -def delete(module, client, matching_zones): +def delete(matching_zones): zone_in = module.params.get('zone').lower() vpc_id = module.params.get('vpc_id') vpc_region = module.params.get('vpc_region') @@ -414,12 +420,12 @@ def delete(module, client, matching_zones): if zone_in in [z['Name'] for z in matching_zones]: if hosted_zone_id: - changed, result = delete_hosted_id(module, client, hosted_zone_id, matching_zones) + changed, result = delete_hosted_id(hosted_zone_id, matching_zones) else: if private_zone: - changed, result = delete_private(module, client, matching_zones, vpc_id, vpc_region) + changed, result = delete_private(matching_zones, vpc_id, vpc_region) else: - changed, result = delete_public(module, client, matching_zones) + changed, result = delete_public(matching_zones) else: changed = False result = "No zone to delete." @@ -428,6 +434,9 @@ def delete(module, client, matching_zones): def main(): + global module + global client + argument_spec = dict( zone=dict(required=True), state=dict(default='present', choices=['present', 'absent']), @@ -461,13 +470,13 @@ def main(): private_zone = bool(vpc_id and vpc_region) - client = module.client('route53') + client = module.client('route53', retry_decorator=AWSRetry.jittered_backoff()) - zones = find_zones(module, client, zone_in, private_zone) + zones = find_zones(zone_in, private_zone) if state == 'present': - changed, result = create(module, client, matching_zones=zones) + changed, result = create(matching_zones=zones) elif state == 'absent': - changed, result = delete(module, client, matching_zones=zones) + changed, result = delete(matching_zones=zones) if isinstance(result, dict): module.exit_json(changed=changed, result=result, **result) From 85357f6357e38d86e110bcecfedb4537912d11b4 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 22 Apr 2022 11:44:07 +0200 Subject: [PATCH 60/81] Integration test dependency cleanup (#1086) Integration test dependency cleanup SUMMARY remove dependencies on setup_remote_tmp_dir where it's not used (often just copy & paste from another test) remove setup_ec2 (no main.yml means it's not doing anything) remove prepare_tests (empty main.yml means it's not doing anything) ISSUE TYPE Feature Pull Request COMPONENT NAME tests/integration/targets ADDITIONAL INFORMATION By cleaning up what we have we reduce the chance of people copying things about "because that's what test XYZ did". Reviewed-by: Alina Buzachis Reviewed-by: Mark Woolley This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/dd12046a1e2d5f39692b1890ff07e06c56b3bf0e --- tests/integration/targets/route53_health_check/meta/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/targets/route53_health_check/meta/main.yml b/tests/integration/targets/route53_health_check/meta/main.yml index 930e8622824..1471b11f658 100644 --- a/tests/integration/targets/route53_health_check/meta/main.yml +++ b/tests/integration/targets/route53_health_check/meta/main.yml @@ -1,3 +1,2 @@ dependencies: - - prepare_tests - setup_ec2_facts From bf82fb1b3c89758737380055ecec29d2735f3699 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Fri, 22 Apr 2022 12:20:04 +0200 Subject: [PATCH 61/81] For consistency - add empty dependencies file to targets with no current meta data (#1090) For consistency - add empty dependencies file to targets with no current meta data SUMMARY For consistency - add empty dependencies file to targets with no current meta data ISSUE TYPE Feature Pull Request COMPONENT NAME tests/integration/targets 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/96385803df04cfa34c62d1ab19be21b12f593af6 --- tests/integration/targets/route53/meta/main.yml | 1 + tests/integration/targets/route53_zone/meta/main.yml | 1 + 2 files changed, 2 insertions(+) create mode 100644 tests/integration/targets/route53/meta/main.yml create mode 100644 tests/integration/targets/route53_zone/meta/main.yml diff --git a/tests/integration/targets/route53/meta/main.yml b/tests/integration/targets/route53/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/tests/integration/targets/route53/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/tests/integration/targets/route53_zone/meta/main.yml b/tests/integration/targets/route53_zone/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/tests/integration/targets/route53_zone/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] From ca0395e8b0d45072e77534b119cd502eae8baa5c Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Thu, 28 Apr 2022 12:37:58 +0200 Subject: [PATCH 62/81] Docs linting fixups (#1100) Docs linting fixups SUMMARY While testing out linting/validation of the generated docs, a number of issues were highlighted. ISSUE TYPE Docs Pull Request COMPONENT NAME plugins/modules/aws_batch_job_definition.py plugins/modules/aws_config_rule.py plugins/modules/aws_secret.py plugins/modules/aws_waf_rule.py plugins/modules/cloudfront_invalidation.py plugins/modules/ec2_placement_group_info.py plugins/modules/iam_user.py plugins/modules/route53_health_check.py plugins/modules/s3_sync.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/c5363d56dc761e6a8b53be7d0ebd243af20adec7 --- plugins/modules/route53_health_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 382be93ab6d..22ce36beba8 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -60,7 +60,7 @@ fqdn: description: - Domain name of the endpoint to check. Either this or I(ip_address) has - to be provided. When both are given the `fqdn` is used in the `Host:` + to be provided. When both are given the I(fqdn) is used in the C(Host:) header of the HTTP request. type: str string_match: From 8b322bb15b4b8cec07749ac8edcdeb7cc997442f Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Tue, 10 May 2022 13:22:25 -0700 Subject: [PATCH 63/81] route53: add support for GeoLocation parameter (#1117) route53: add support for GeoLocation parameter SUMMARY Added support for GeoLocation parameter to community.aws.route53 Fixes #89. ISSUE TYPE Feature Pull Request COMPONENT NAME route53 ADDITIONAL INFORMATION Uses https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53.html#Route53.Client.change_resource_record_sets Reviewed-by: Joseph Torcasso Reviewed-by: Mandar Kulkarni Reviewed-by: Sloane Hertel 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/29e51f9ef07f331f451b2b66368afdbc274b25cc --- plugins/modules/route53.py | 91 ++++- .../targets/route53/tasks/main.yml | 311 +++++++++++++++++- 2 files changed, 397 insertions(+), 5 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 4ddacdca09e..bebdacdbf9a 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -108,6 +108,30 @@ latency-based routing - Mutually exclusive with I(weight) and I(failover). type: str + geo_location: + description: + - Allows to control how Amazon Route 53 responds to DNS queries based on the geographic origin of the query. + - Two geolocation resource record sets that specify same geographic location cannot be created. + - Non-geolocation resource record sets that have the same values for the Name and Type elements as geolocation + resource record sets cannot be created. + suboptions: + continent_code: + description: + - The two-letter code for the continent. + - Specifying I(continent_code) with either I(country_code) or I(subdivision_code) returns an InvalidInput error. + type: str + country_code: + description: + - The two-letter code for a country. + - Amazon Route 53 uses the two-letter country codes that are specified in ISO standard 3166-1 alpha-2 . + type: str + subdivision_code: + description: + - The two-letter code for a state of the United States. + - To specify I(subdivision_code), I(country_code) must be set to C(US). + type: str + type: dict + version_added: 3.3.0 health_check: description: - Health check to associate with this record @@ -166,6 +190,12 @@ returned: always type: str sample: PRIMARY + geo_location: + description: geograpic location based on which Route53 resonds to DNS queries. + returned: when configured + type: dict + sample: { continent_code: "NA", country_code: "US", subdivision_code: "CA" } + version_added: 3.3.0 health_check: description: health_check associated with this record. returned: always @@ -350,6 +380,29 @@ - 0 issue "ca.example.net" - 0 issuewild ";" - 0 iodef "mailto:security@example.com" +- name: Create a record with geo_location - country_code + community.aws.route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test.{{ zone_one }}' + identifier: "geohost@www" + type: A + value: 1.1.1.1 + ttl: 30 + geo_location: + country_code: US +- name: Create a record with geo_location - subdivision code + community.aws.route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test.{{ zone_one }}' + identifier: "geohost@www" + type: A + value: 1.1.1.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX ''' from operator import itemgetter @@ -495,6 +548,12 @@ def main(): identifier=dict(type='str'), weight=dict(type='int'), region=dict(type='str'), + geo_location=dict(type='dict', + options=dict( + continent_code=dict(type="str"), + country_code=dict(type="str"), + subdivision_code=dict(type="str")), + required=False), health_check=dict(type='str'), failover=dict(type='str', choices=['PRIMARY', 'SECONDARY']), vpc_id=dict(type='str'), @@ -518,11 +577,12 @@ def main(): ('failover', 'region', 'weight'), ('alias', 'ttl'), ], - # failover, region and weight require identifier + # failover, region, weight and geo_location require identifier required_by=dict( failover=('identifier',), region=('identifier',), weight=('identifier',), + geo_location=('identifier'), ), ) @@ -557,6 +617,7 @@ def main(): vpc_id_in = module.params.get('vpc_id') wait_in = module.params.get('wait') wait_timeout_in = module.params.get('wait_timeout') + geo_location = module.params.get('geo_location') if zone_in[-1:] != '.': zone_in += "." @@ -567,8 +628,8 @@ def main(): if command_in == 'create' or command_in == 'delete': if alias_in and len(value_in) != 1: module.fail_json(msg="parameter 'value' must contain a single dns name for alias records") - if (weight_in is None and region_in is None and failover_in is None) and identifier_in is not None: - module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region or failover.") + if not any([weight_in, region_in, failover_in, geo_location]) and identifier_in is not None: + module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region, geo_location or failover.") retry_decorator = AWSRetry.jittered_backoff( retries=MAX_AWS_RETRIES, @@ -604,6 +665,30 @@ def main(): 'HealthCheckId': health_check_in, 'SetIdentifier': identifier_in, }) + + if geo_location: + continent_code = geo_location.get('continent_code') + country_code = geo_location.get('country_code') + subdivision_code = geo_location.get('subdivision_code') + + if continent_code and (country_code or subdivision_code): + module.fail_json(changed=False, msg='While using geo_location, continent_code is mutually exclusive with country_code and subdivision_code.') + + if not any([continent_code, country_code, subdivision_code]): + module.fail_json(changed=False, msg='To use geo_location please specify either continent_code, country_code, or subdivision_code.') + + if geo_location.get('subdivision_code') and geo_location.get('country_code').lower() != 'us': + module.fail_json(changed=False, msg='To use subdivision_code, you must specify country_code as US.') + + # Build geo_location suboptions specification + resource_record_set['GeoLocation'] = {} + if continent_code: + resource_record_set['GeoLocation']['ContinentCode'] = continent_code + if country_code: + resource_record_set['GeoLocation']['CountryCode'] = country_code + if subdivision_code: + resource_record_set['GeoLocation']['SubdivisionCode'] = subdivision_code + if command_in == 'delete' and aws_record is not None: resource_record_set['TTL'] = aws_record.get('TTL') if not resource_record_set['ResourceRecords']: diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 92f8601e64a..063e279f1cf 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -635,7 +635,314 @@ - weighted_record is not failed - weighted_record is not changed +#Test Geo Location - Continent Code + - name: Create a record with geo_location - continent_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + check_mode: true + register: create_geo_continent_check_mode + - assert: + that: + - create_geo_continent_check_mode is changed + - create_geo_continent_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_check_mode.resource_actions' + + - name: Create a record with geo_location - continent_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + register: create_geo_continent + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-1.{{ zone_one }}' + max_items: 1 + register: result + + - assert: + that: + - create_geo_continent is changed + - create_geo_continent is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_continent.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.ContinentCode == "NA" + + - name: Create a record with geo_location - continent_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + register: create_geo_continent_idem + - assert: + that: + - create_geo_continent_idem is not changed + - create_geo_continent_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem.resource_actions' + + - name: Create a record with geo_location - continent_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + check_mode: true + register: create_geo_continent_idem_check + + - assert: + that: + - create_geo_continent_idem_check is not changed + - create_geo_continent_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem_check.resource_actions' + +#Test Geo Location - Country Code + - name: Create a record with geo_location - country_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + check_mode: true + register: create_geo_country_check_mode + - assert: + that: + - create_geo_country_check_mode is changed + - create_geo_country_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_check_mode.resource_actions' + + - name: Create a record with geo_location - country_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + register: create_geo_country + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-2.{{ zone_one }}' + max_items: 1 + register: result + - assert: + that: + - create_geo_country is changed + - create_geo_country is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_country.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + + - name: Create a record with geo_location - country_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + register: create_geo_country_idem + - assert: + that: + - create_geo_country_idem is not changed + - create_geo_country_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem.resource_actions' + + - name: Create a record with geo_location - country_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + check_mode: true + register: create_geo_country_idem_check + + - assert: + that: + - create_geo_country_idem_check is not changed + - create_geo_country_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem_check.resource_actions' + +#Test Geo Location - Subdivision Code + - name: Create a record with geo_location - subdivision_code (check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + check_mode: true + register: create_geo_subdivision_check_mode + - assert: + that: + - create_geo_subdivision_check_mode is changed + - create_geo_subdivision_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_check_mode.resource_actions' + + - name: Create a record with geo_location - subdivision_code + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + register: create_geo_subdivision + # Get resulting A record and geo_location parameters are applied + - name: 'get Route53 A record information' + route53_info: + type: A + query: record_sets + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: 'geo-test-3.{{ zone_one }}' + max_items: 1 + register: result + - assert: + that: + - create_geo_subdivision is changed + - create_geo_subdivision is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_subdivision.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + - result.ResourceRecordSets[0].GeoLocation.SubdivisionCode == "TX" + + - name: Create a record with geo_location - subdivision_code (idempotency) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + register: create_geo_subdivision_idem + - assert: + that: + - create_geo_subdivision_idem is not changed + - create_geo_subdivision_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem.resource_actions' + + - name: Create a record with geo_location - subdivision_code (idempotency - check_mode) + route53: + state: present + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + check_mode: true + register: create_geo_subdivision_idem_check + + - assert: + that: + - create_geo_subdivision_idem_check is not changed + - create_geo_subdivision_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem_check.resource_actions' + +#Cleanup------------------------------------------------------ + always: + + - name: delete a record with geo_location - continent_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-1.{{ zone_one }}' + identifier: "geohost1@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + continent_code: NA + ignore_errors: true + + - name: delete a record with geo_location - country_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-2.{{ zone_one }}' + identifier: "geohost2@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + ignore_errors: true + + - name: delete a record with geo_location - subdivision_code + route53: + state: absent + zone: '{{ zone_one }}' + record: 'geo-test-3.{{ zone_one }}' + identifier: "geohost3@www" + type: A + value: 127.0.0.1 + ttl: 30 + geo_location: + country_code: US + subdivision_code: TX + ignore_errors: true + - route53_info: query: record_sets hosted_zone_id: '{{ z1.zone_id }}' @@ -737,7 +1044,7 @@ state: absent zone: '{{ zone_one }}' register: delete_one - ignore_errors: yes + ignore_errors: true retries: 10 until: delete_one is not failed @@ -746,7 +1053,7 @@ state: absent zone: '{{ zone_two }}' register: delete_two - ignore_errors: yes + ignore_errors: True retries: 10 until: delete_two is not failed From 67231833a383c735bb89ccb1dfb5dc6e3617e988 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 31 May 2022 21:51:06 +0200 Subject: [PATCH 64/81] Tagging - Add simple deprecations for purge_tags=False (#1185) Tagging - Add simple deprecations for purge_tags=False Depends-On: ansible-collections/amazon.aws#844 SUMMARY Deprecate the use of purge_tags=False as a default ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/modules/aws_acm.py plugins/modules/route53_health_check.py plugins/modules/route53_zone.py plugins/modules/sqs_queue.py ADDITIONAL INFORMATION Reviewed-by: Markus Bergholz 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/42688f3f1101c404e83dfb7f5be1280cfdec432f --- plugins/modules/route53_health_check.py | 33 ++++++++++++----------- plugins/modules/route53_zone.py | 35 ++++++++++++------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 22ce36beba8..5b7cce3c147 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -86,21 +86,14 @@ - Will default to C(3) if not specified on creation. choices: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] type: int - tags: - description: - - A hash/dictionary of tags to set on the health check. - type: dict - version_added: 2.1.0 - purge_tags: - description: - - Delete any tags not specified in I(tags). - default: false - type: bool - version_added: 2.1.0 -author: "zimbatm (@zimbatm)" +author: + - "zimbatm (@zimbatm)" +notes: + - Support for I(tags) and I(purge_tags) was added in release 2.1.0. extends_documentation_fragment: -- amazon.aws.aws -- amazon.aws.ec2 + - amazon.aws.aws + - amazon.aws.ec2 + - amazon.aws.tags.deprecated_purge ''' EXAMPLES = ''' @@ -432,8 +425,8 @@ def main(): string_match=dict(), request_interval=dict(type='int', choices=[10, 30], default=30), failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), - tags=dict(type='dict'), - purge_tags=dict(type='bool', default=False), + tags=dict(type='dict', aliases=['resource_tags']), + purge_tags=dict(type='bool'), ) args_one_of = [ @@ -454,6 +447,14 @@ def main(): supports_check_mode=True, ) + if module.params.get('purge_tags') is None: + module.deprecate( + 'The purge_tags parameter currently defaults to False.' + ' For consistency across the collection, this default value' + ' will change to True in release 5.0.0.', + version='5.0.0', collection_name='community.aws') + module.params['purge_tags'] = False + state_in = module.params.get('state') ip_addr_in = module.params.get('ip_address') port_in = module.params.get('port') diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index ba51fcbb9e2..334233b4e44 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -46,23 +46,14 @@ - The reusable delegation set ID to be associated with the zone. - Note that you can't associate a reusable delegation set with a private hosted zone. type: str - tags: - description: - - A hash/dictionary of tags to add to the new instance or to add/remove from an existing one. - type: dict - version_added: 2.1.0 - purge_tags: - description: - - Delete any tags not specified in the task that are on the zone. - This means you have to specify all the desired tags on each task affecting a zone. - default: false - type: bool - version_added: 2.1.0 extends_documentation_fragment: -- amazon.aws.aws -- amazon.aws.ec2 - -author: "Christopher Troup (@minichate)" + - amazon.aws.aws + - amazon.aws.ec2 + - amazon.aws.tags.deprecated_purge +notes: + - Support for I(tags) and I(purge_tags) was added in release 2.1.0. +author: + - "Christopher Troup (@minichate)" ''' EXAMPLES = r''' @@ -445,8 +436,8 @@ def main(): comment=dict(default=''), hosted_zone_id=dict(), delegation_set_id=dict(), - tags=dict(type='dict'), - purge_tags=dict(type='bool', default=False), + tags=dict(type='dict', aliases=['resource_tags']), + purge_tags=dict(type='bool'), ) mutually_exclusive = [ @@ -460,6 +451,14 @@ def main(): supports_check_mode=True, ) + if module.params.get('purge_tags') is None: + module.deprecate( + 'The purge_tags parameter currently defaults to False.' + ' For consistency across the collection, this default value' + ' will change to True in release 5.0.0.', + version='5.0.0', collection_name='community.aws') + module.params['purge_tags'] = False + zone_in = module.params.get('zone').lower() state = module.params.get('state').lower() vpc_id = module.params.get('vpc_id') From aae56365f5744ac6fc5673e11e0aa6dc5ebb557b Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Tue, 14 Jun 2022 10:26:28 -0700 Subject: [PATCH 65/81] route53_info: Add snake_cased return key,values and a deprecation message (#1236) route53_info: Add snake_cased return key,values and a deprecation message Depends-On: ansible/ansible-zuul-jobs#1564 SUMMARY Add snake_case return values and a deprecation message for existing CamelCase return values. Route53_info currently returns CamelCase values, to have uniformity along all *_info modules in terms of return values, it should return snake_case values instead. Proposed change should make addition of snake_case return values and the deprecation message provides time for users to upgrade their playbooks to avoid breaking existing playbooks due to the proposed change. ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_info ADDITIONAL INFORMATION This PR is relation to the initiative for having updated developer guidelines for *_info modules specifically related to guidelines for deprecating return values. Reviewed-by: Mark Chappell Reviewed-by: Joseph Torcasso 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/3df423acf692fa53db7f729c2cc5baf440d55229 --- plugins/modules/route53_info.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 7622113c25e..5e40efa4aad 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -213,6 +213,7 @@ 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 # Split out paginator to allow for the backoff decorator to function @@ -270,10 +271,17 @@ def list_hosted_zones(): params['DelegationSetId'] = module.params.get('delegation_set_id') zones = _paginated_result('list_hosted_zones', **params)['HostedZones'] + snaked_zones = [camel_dict_to_snake_dict(zone) for zone in zones] + + module.deprecate("The 'CamelCase' return values with key 'HostedZones' and 'list' are deprecated and \ + will be replaced by 'snake_case' return values with key 'hosted_zones'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') return { "HostedZones": zones, "list": zones, + "hosted_zones": snaked_zones, } @@ -367,10 +375,17 @@ def list_health_checks(): ) health_checks = _paginated_result('list_health_checks', **params)['HealthChecks'] + snaked_health_checks = [camel_dict_to_snake_dict(health_check) for health_check in health_checks] + + module.deprecate("The 'CamelCase' return values with key 'HealthChecks' and 'list' are deprecated and \ + will be replaced by 'snake_case' return values with key 'health_checks'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') return { "HealthChecks": health_checks, "list": health_checks, + "health_checks": snaked_health_checks, } @@ -399,10 +414,17 @@ def record_sets_details(): ) record_sets = _paginated_result('list_resource_record_sets', **params)['ResourceRecordSets'] + snaked_record_sets = [camel_dict_to_snake_dict(record_set) for record_set in record_sets] + + module.deprecate("The 'CamelCase' return values with key 'ResourceRecordSets' and 'list' are deprecated and \ + will be replaced by 'snake_case' return values with key 'resource_record_sets'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') return { "ResourceRecordSets": record_sets, "list": record_sets, + "resource_record_sets": snaked_record_sets, } From a5b462bf434e3083dd041d8dd57e1889dd9a1ed2 Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Fri, 17 Jun 2022 14:11:50 -0700 Subject: [PATCH 66/81] route53_info: Add RETURN block (#1240) route53_info: Add RETURN block SUMMARY Currently route53_info is mising a return block. This is a follow up on #1236 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_info Reviewed-by: Joseph Torcasso Reviewed-by: Mike Graves Reviewed-by: Mandar Kulkarni This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/6e7f150793fd179f070d57db34db7dda2411259f --- plugins/modules/route53_info.py | 179 ++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 5e40efa4aad..4e90556a1ec 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -204,6 +204,185 @@ register: RECORDS ''' +RETURN = r''' +resource_record_sets: + description: A list of resource record sets returned by list_resource_record_sets in boto3. + returned: when I(query=record_sets) + type: list + elements: dict + contains: + name: + description: The name of a record in the specified hosted zone. + type: str + sample: 'www.example.com' + type: + description: The DNS record type. + type: str + sample: 'A' + ttl: + description: The resource record cache time to live (TTL), in seconds. + type: int + sample: 60 + set_identifier: + description: An identifier that differentiates among multiple resource record sets that have the same combination of name and type. + type: str + sample: 'abcd' + resource_records: + description: Information about the resource records. + type: list + elements: dict + contains: + value: + description: The current or new DNS record value. + type: str + sample: 'ns-12.awsdns-34.com.' + geo_location: + description: The specified geographic location for which the Route53 responds to based on location. + type: dict + elements: str + contains: + continent_code: + description: The two-letter code for the continent. + type: str + sample: 'NA' + country_code: + description: The two-letter code for a country. + type: str + sample: 'US' + subdivision_code: + description: The two-letter code for a state of the United States + type: str + sample: 'NY' + version_added: 4.0.0 +hosted_zones: + description: A list of hosted zones returned by list_hosted_zones in boto3. + returned: when I(query=hosted_zone) + type: list + elements: dict + contains: + id: + description: The ID of the hosted zone assigned by Amazon Route53 to the hosted zone at the creation time. + type: str + sample: '/hostedzone/Z01234567AB1234567890' + name: + description: The name of the domain. + type: str + sample: 'example.io' + resource_record_set_count: + description: The number of resource record sets in the hosted zone. + type: int + sample: 3 + caller_reference: + description: The value specified for CallerReference at the time of hosted zone creation. + type: str + sample: '01d0db12-x0x9-12a3-1234-0z000z00zz0z' + config: + description: A dict that contains Comment and PrivateZone elements. + type: dict + contains: + comment: + description: Any comments that included about in the hosted zone. + type: str + sample: 'HostedZone created by Route53 Registrar' + private_zone: + description: A value that indicates whether this is a private hosted zone or not. + type: bool + sample: false + version_added: 4.0.0 +health_checks: + description: A list of Route53 health checks returned by list_health_checks in boto3. + type: list + elements: dict + returned: when I(query=health_check) + contains: + id: + description: The identifier that Amazon Route53 assigned to the health check at the time of creation. + type: str + sample: '12345cdc-2cc4-1234-bed2-123456abc1a2' + health_check_version: + description: The version of the health check. + type: str + sample: 1 + caller_reference: + description: A unique string that you specified when you created the health check. + type: str + sample: '01d0db12-x0x9-12a3-1234-0z000z00zz0z' + health_check_config: + description: A dict that contains detailed information about one health check. + type: dict + contains: + disabled: + description: Whether Route53 should stop performing health checks on a endpoint. + type: bool + sample: false + enable_sni: + description: Whether Route53 should send value of FullyQualifiedDomainName to endpoint in client_hello message during TLS negotiation. + type: bool + sample: true + failure_threshold: + description: The number of consecutive health checks that an endpoint must pass/fail for Route53 to change current status of endpoint. + type: int + sample: 3 + fully_qualified_domain_name: + description: The fully qualified DNS name of the endpoint on which Route53 performs health checks. + type: str + sample: 'hello' + inverted: + description: Whether Route53 should invert the status of a health check. + type: bool + sample: false + ip_address: + description: The IPv4/IPv6 IP address of the endpoint that Route53 should perform health checks on. + type: str + sample: 192.0.2.44 + measure_latency: + description: Whether Route53 should measure latency between health checkers in multiple AWS regions and the endpoint. + type: bool + sample: false + port: + description: The port of the endpoint that Route53 should perform health checks on. + type: int + sample: 80 + request_interval: + description: The number of seconds between the time that Route53 gets a response from endpoint and the next health check request. + type: int + sample: 30 + resource_path: + description: The path that Route53 requests when performing health checks. + type: str + sample: '/welcome.html' + search_string: + description: The string that Route53 uses to search for in the response body from specified resource. + type: str + sample: 'test-string-to-match' + type: + description: The type of the health check. + type: str + sample: HTTPS + version_added: 4.0.0 +ResourceRecordSets: + description: A deprecated CamelCased list of resource record sets returned by list_resource_record_sets in boto3. \ + This list contains same elements/parameters as it's snake_cased version mentioned above. \ + This field is deprecated and will be removed in 6.0.0 version release. + returned: when I(query=record_sets) + type: list + elements: dict +HostedZones: + description: A deprecated CamelCased list of hosted zones returned by list_hosted_zones in boto3. \ + This list contains same elements/parameters as it's snake_cased version mentioned above. \ + This field is deprecated and will be removed in 6.0.0 version release. + returned: when I(query=hosted_zone) + type: list + elements: dict +HealthChecks: + description: A deprecated CamelCased list of Route53 health checks returned by list_health_checks in boto3. \ + This list contains same elements/parameters as it's snake_cased version mentioned above. \ + This field is deprecated and will be removed in 6.0.0 version release. + type: list + elements: dict + returned: when I(query=health_check) +''' + try: import botocore except ImportError: From d57f79d5f3830723c1417cb2e85ecc0c45e5ffb1 Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Thu, 7 Jul 2022 00:21:17 -0700 Subject: [PATCH 67/81] route53_health_check: Add feature to create multiple health checks without updating existing health check (#1143) * Add health_check_name / name parameter support to naming health checks during creation * Handle create/update when health_check when name is unique identifier * Fix bug: tags not specified but name is specified resulting in python error * Add integration tests for as unique identifier * code cleanup to remove redundant usage of manage_tags * Feat: ability to Update and Delete health checks by ID * Add integration tests for testing update/delete health check by ID * Minor fix, improve login to handle find_health_check() call * Use required_together param * Added changelogs fragment * use fqdn for examples, fix changelog variable names This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/8d3fec8e5331590c5d8f22f5e55acbb115ae39cb --- plugins/modules/route53_health_check.py | 175 ++++++++-- .../route53_health_check/defaults/main.yml | 3 + .../tasks/create_multiple_health_checks.yml | 134 ++++++++ .../route53_health_check/tasks/main.yml | 6 + .../tasks/update_delete_by_id.yml | 303 ++++++++++++++++++ 5 files changed, 603 insertions(+), 18 deletions(-) create mode 100644 tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml create mode 100644 tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 5b7cce3c147..83283ecf646 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -44,7 +44,7 @@ description: - The type of health check that you want to create, which indicates how Amazon Route 53 determines whether an endpoint is healthy. - required: true + - Once health_check is created, type can not be changed. choices: [ 'HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP' ] type: str resource_path: @@ -86,6 +86,28 @@ - Will default to C(3) if not specified on creation. choices: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] type: int + health_check_name: + description: + - Name of the Health Check. + - Used together with I(use_unique_names) to set/make use of I(health_check_name) as a unique identifier. + type: str + required: False + aliases: ['name'] + version_added: 4.1.0 + use_unique_names: + description: + - Used together with I(health_check_name) to set/make use of I(health_check_name) as a unique identifier. + type: bool + required: False + version_added: 4.1.0 + health_check_id: + description: + - ID of the health check to be update or deleted. + - If provided, a health check can be updated or deleted based on the ID as unique identifier. + type: str + required: False + aliases: ['id'] + version_added: 4.1.0 author: - "zimbatm (@zimbatm)" notes: @@ -120,10 +142,35 @@ weight: 100 health_check: "{{ my_health_check.health_check.id }}" +- name: create a simple health check with health_check_name as unique identifier + community.aws.route53_health_check: + state: present + health_check_name: ansible + fqdn: ansible.com + port: 443 + type: HTTPS + use_unique_names: true + - name: Delete health-check community.aws.route53_health_check: state: absent fqdn: host1.example.com + +- name: Update Health check by ID - update ip_address + community.aws.route53_health_check: + id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx + ip_address: 1.2.3.4 + +- name: Update Health check by ID - update port + community.aws.route53_health_check: + id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx + ip_address: 8080 + +- name: Delete Health check by ID + community.aws.route53_health_check: + state: absent + id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx + ''' RETURN = r''' @@ -249,7 +296,6 @@ def find_health_check(ip_addr, fqdn, hc_type, request_interval, port): # Additionally, we can't properly wrap the paginator, so retrying means # starting from scratch with a paginator results = _list_health_checks() - while True: for check in results.get('HealthChecks'): config = check.get('HealthCheckConfig') @@ -268,6 +314,20 @@ def find_health_check(ip_addr, fqdn, hc_type, request_interval, port): return None +def get_existing_checks_with_name(): + results = _list_health_checks() + health_checks_with_name = {} + while True: + for check in results.get('HealthChecks'): + if 'Name' in describe_health_check(check['Id'])['tags']: + check_name = describe_health_check(check['Id'])['tags']['Name'] + health_checks_with_name[check_name] = check + if results.get('IsTruncated', False): + results = _list_health_checks(Marker=results.get('NextMarker')) + else: + return health_checks_with_name + + def delete_health_check(check_id): if not check_id: return False, None @@ -348,10 +408,14 @@ def create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_ def update_health_check(existing_check): - # In theory it's also possible to update the IPAddress, Port and - # FullyQualifiedDomainName, however, because we use these in lieu of a - # 'Name' to uniquely identify the health check this isn't currently - # supported. If we accepted an ID it would be possible to modify them. + # It's possible to update following parameters + # - ResourcePath + # - SearchString + # - FailureThreshold + # - Disabled + # - IPAddress + # - Port + # - FullyQualifiedDomainName changes = dict() existing_config = existing_check.get('HealthCheckConfig') @@ -372,10 +436,23 @@ def update_health_check(existing_check): if disabled is not None and disabled != existing_config.get('Disabled'): changes['Disabled'] = module.params.get('disabled') + # If updating based on Health Check ID or health_check_name, we can update + if module.params.get('health_check_id') or module.params.get('use_unique_names'): + ip_address = module.params.get('ip_address', None) + if ip_address is not None and ip_address != existing_config.get('IPAddress'): + changes['IPAddress'] = module.params.get('ip_address') + + port = module.params.get('port', None) + if port is not None and port != existing_config.get('Port'): + changes['Port'] = module.params.get('port') + + fqdn = module.params.get('fqdn', None) + if fqdn is not None and fqdn != existing_config.get('FullyQualifiedDomainName'): + changes['FullyQualifiedDomainName'] = module.params.get('fqdn') + # No changes... if not changes: return False, None - if module.check_mode: return True, 'update' @@ -419,7 +496,7 @@ def main(): disabled=dict(type='bool'), ip_address=dict(), port=dict(type='int'), - type=dict(required=True, choices=['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP']), + type=dict(choices=['HTTP', 'HTTPS', 'HTTP_STR_MATCH', 'HTTPS_STR_MATCH', 'TCP']), resource_path=dict(), fqdn=dict(), string_match=dict(), @@ -427,16 +504,27 @@ def main(): failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), tags=dict(type='dict', aliases=['resource_tags']), purge_tags=dict(type='bool'), + health_check_id=dict(type='str', aliases=['id'], required=False), + health_check_name=dict(type='str', aliases=['name'], required=False), + use_unique_names=dict(type='bool', required=False), ) args_one_of = [ - ['ip_address', 'fqdn'], + ['ip_address', 'fqdn', 'health_check_id'], ] args_if = [ ['type', 'TCP', ('port',)], ] + args_required_together = [ + ['use_unique_names', 'health_check_name'], + ] + + args_mutually_exclusive = [ + ['health_check_id', 'health_check_name'] + ] + global module global client @@ -444,6 +532,8 @@ def main(): argument_spec=argument_spec, required_one_of=args_one_of, required_if=args_if, + required_together=args_required_together, + mutually_exclusive=args_mutually_exclusive, supports_check_mode=True, ) @@ -455,6 +545,9 @@ def main(): version='5.0.0', collection_name='community.aws') module.params['purge_tags'] = False + if not module.params.get('health_check_id') and not module.params.get('type'): + module.fail_json(msg="parameter 'type' is required if not updating or deleting health check by ID.") + state_in = module.params.get('state') ip_addr_in = module.params.get('ip_address') port_in = module.params.get('port') @@ -464,6 +557,8 @@ def main(): string_match_in = module.params.get('string_match') request_interval_in = module.params.get('request_interval') failure_threshold_in = module.params.get('failure_threshold') + health_check_name = module.params.get('health_check_name') + tags = module.params.get('tags') # Default port if port_in is None: @@ -484,22 +579,66 @@ def main(): action = None check_id = None - existing_check = find_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) - - if existing_check: - check_id = existing_check.get('Id') - + if module.params.get('use_unique_names') or module.params.get('health_check_id'): + module.deprecate( + 'The health_check_name is currently non required parameter.' + ' This behavior will change and health_check_name ' + ' will change to required=True and use_unique_names will change to default=True in release 6.0.0.', + version='6.0.0', collection_name='community.aws') + + # If update or delete Health Check based on ID + update_delete_by_id = False + if module.params.get('health_check_id'): + update_delete_by_id = True + id_to_update_delete = module.params.get('health_check_id') + try: + existing_check = client.get_health_check(HealthCheckId=id_to_update_delete)['HealthCheck'] + except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e: + module.exit_json(changed=False, msg='The specified health check with ID: {0} does not exist'.format(id_to_update_delete)) + else: + existing_check = find_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) + if existing_check: + check_id = existing_check.get('Id') + + # Delete Health Check if state_in == 'absent': - changed, action = delete_health_check(check_id) + if update_delete_by_id: + changed, action = delete_health_check(id_to_update_delete) + else: + changed, action = delete_health_check(check_id) check_id = None + + # Create Health Check elif state_in == 'present': - if existing_check is None: + if existing_check is None and not module.params.get('use_unique_names') and not update_delete_by_id: changed, action, check_id = create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) + + # Update Health Check else: - changed, action = update_health_check(existing_check) + # If health_check_name is a unique identifier + if module.params.get('use_unique_names'): + existing_checks_with_name = get_existing_checks_with_name() + # update the health_check if another health check with same name exists + if health_check_name in existing_checks_with_name: + changed, action = update_health_check(existing_checks_with_name[health_check_name]) + else: + # create a new health_check if another health check with same name does not exists + changed, action, check_id = create_health_check(ip_addr_in, fqdn_in, type_in, request_interval_in, port_in) + # Add tag to add name to health check + if check_id: + if not tags: + tags = {} + tags['Name'] = health_check_name + + else: + if update_delete_by_id: + changed, action = update_health_check(existing_check) + else: + changed, action = update_health_check(existing_check) + if check_id: changed |= manage_tags(module, client, 'healthcheck', check_id, - module.params.get('tags'), module.params.get('purge_tags')) + tags, module.params.get('purge_tags')) health_check = describe_health_check(id=check_id) health_check['action'] = action diff --git a/tests/integration/targets/route53_health_check/defaults/main.yml b/tests/integration/targets/route53_health_check/defaults/main.yml index 3763717e6d0..769e5079d1b 100644 --- a/tests/integration/targets/route53_health_check/defaults/main.yml +++ b/tests/integration/targets/route53_health_check/defaults/main.yml @@ -11,6 +11,7 @@ #ip_address: We allocate an EIP due to route53 restrictions fqdn: '{{ tiny_prefix }}.route53-health.ansible.test' +fqdn_1: '{{ tiny_prefix }}-1.route53-health.ansible.test' port: 8080 type: 'TCP' request_interval: 30 @@ -27,7 +28,9 @@ failure_threshold_updated: 1 # for string_match we need an _STR_MATCH type type_https_match: 'HTTPS_STR_MATCH' type_http_match: 'HTTP_STR_MATCH' +type_http: 'HTTP' resource_path: '/health.php' +resource_path_1: '/new-health.php' resource_path_updated: '/healthz' string_match: 'Hello' string_match_updated: 'Hello World' diff --git a/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml b/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml new file mode 100644 index 00000000000..41713673d67 --- /dev/null +++ b/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml @@ -0,0 +1,134 @@ +--- +- block: + - name: 'Create multiple HTTP health checks with different resource_path - check_mode' + route53_health_check: + state: present + name: '{{ tiny_prefix }}-{{ item }}-test-hc-delete-if-found' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ item }}' + use_unique_names: true + register: create_check + check_mode: true + with_items: + - '{{ resource_path }}' + - '{{ resource_path_1 }}' + + - name: 'Check result - Create a HTTP health check - check_mode' + assert: + that: + - create_check is not failed + - create_check is changed + - '"route53:CreateHealthCheck" not in create_check.results[0].resource_actions' + - '"route53:CreateHealthCheck" not in create_check.results[1].resource_actions' + + - name: 'Create multiple HTTP health checks with different resource_path' + route53_health_check: + state: present + name: '{{ tiny_prefix }}-{{ item }}-test-hc-delete-if-found' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ item }}' + use_unique_names: true + register: create_result + with_items: + - '{{ resource_path }}' + - '{{ resource_path_1 }}' + + - name: Get ID's for health_checks created in above task + set_fact: + health_check_1_id: "{{ create_result.results[0].health_check.id }}" + health_check_2_id: "{{ create_result.results[1].health_check.id }}" + + - name: Get health_check 1 info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_1_id }}" + health_check_method: details + register: health_check_1_info + + - name: Get health_check 2 info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_2_id }}" + health_check_method: details + register: health_check_2_info + + - name: 'Check result - Create multiple HTTP health check' + assert: + that: + - create_result is not failed + - create_result is changed + - '"route53:UpdateHealthCheck" not in create_result.results[0].resource_actions' + - '"route53:UpdateHealthCheck" not in create_result.results[1].resource_actions' + - health_check_1_id != health_check_2_id + - health_check_1_info.HealthCheck.HealthCheckConfig.ResourcePath == '{{ resource_path }}' + - health_check_2_info.HealthCheck.HealthCheckConfig.ResourcePath == '{{ resource_path_1 }}' + + - name: 'Create multiple HTTP health checks with different resource_path - idempotency - check_mode' + route53_health_check: + state: present + name: '{{ tiny_prefix }}-{{ item }}-test-hc-delete-if-found' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ item }}' + use_unique_names: true + register: create_idem_check + check_mode: true + with_items: + - '{{ resource_path }}' + - '{{ resource_path_1 }}' + + - name: 'Check result - Create multiple HTTP health check - idempotency - check_mode' + assert: + that: + - create_idem_check is not failed + - create_idem_check is not changed + - '"route53:CreateHealthCheck" not in create_idem_check.results[0].resource_actions' + - '"route53:CreateHealthCheck" not in create_idem_check.results[1].resource_actions' + - '"route53:UpdateHealthCheck" not in create_idem_check.results[0].resource_actions' + - '"route53:UpdateHealthCheck" not in create_idem_check.results[1].resource_actions' + + - name: 'Create multiple HTTP health checks with different resource_path - idempotency' + route53_health_check: + state: present + name: '{{ tiny_prefix }}-{{ item }}-test-hc-delete-if-found' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ item }}' + use_unique_names: true + register: create_idem + check_mode: true + with_items: + - '{{ resource_path }}' + - '{{ resource_path_1 }}' + + - name: 'Check result - Create multiple HTTP health check - idempotency - check_mode' + assert: + that: + - create_idem is not failed + - create_idem is not changed + - '"route53:CreateHealthCheck" not in create_idem.results[0].resource_actions' + - '"route53:CreateHealthCheck" not in create_idem.results[1].resource_actions' + - '"route53:UpdateHealthCheck" not in create_idem.results[0].resource_actions' + - '"route53:UpdateHealthCheck" not in create_idem.results[1].resource_actions' + + always: + # Cleanup starts here + - name: 'Delete multiple HTTP health checks with different resource_path' + route53_health_check: + state: absent + name: '{{ tiny_prefix }}-{{ item }}-test-hc-delete-if-found' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ item }}' + use_unique_names: true + register: delete_result + with_items: + - '{{ resource_path }}' + - '{{ resource_path_1 }}' diff --git a/tests/integration/targets/route53_health_check/tasks/main.yml b/tests/integration/targets/route53_health_check/tasks/main.yml index 426a0461703..ff8f41906a0 100644 --- a/tests/integration/targets/route53_health_check/tasks/main.yml +++ b/tests/integration/targets/route53_health_check/tasks/main.yml @@ -32,6 +32,12 @@ - set_fact: ip_address: '{{ eip.public_ip }}' + - name: Run tests for creating multiple health checks with name as unique identifier + include_tasks: create_multiple_health_checks.yml + + - name: Run tests for update and delete health check by ID + include_tasks: update_delete_by_id.yml + # Minimum possible definition - name: 'Create a TCP health check - check_mode' route53_health_check: diff --git a/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml b/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml new file mode 100644 index 00000000000..8bb4fb870f9 --- /dev/null +++ b/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml @@ -0,0 +1,303 @@ +--- +- block: + - name: 'Create HTTP health check for use in this test' + route53_health_check: + state: present + name: '{{ tiny_prefix }}-test-update-delete-by-id' + ip_address: '{{ ip_address }}' + port: '{{ port }}' + type: '{{ type_http }}' + resource_path: '{{ resource_path }}' + fqdn: '{{ fqdn }}' + use_unique_names: true + register: create_result + + - name: 'Check result - Create HTTP health check' + assert: + that: + - create_result is not failed + - create_result is changed + - '"route53:CreateHealthCheck" in create_result.resource_actions' + + - name: Get ID for health_checks created in above task + set_fact: + health_check_id: "{{ create_result.health_check.id }}" + + - name: Get health_check info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_id }}" + health_check_method: details + register: health_check_info + + # Update Health Check by ID Tests + - name: 'Update Health Check by ID - Update Port - check_mode' + route53_health_check: + id: "{{ health_check_id }}" + port: 8888 + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check Port - check_mode' + assert: + that: + - update_result is not failed + - update_result is changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by ID - Update Port' + route53_health_check: + id: "{{ health_check_id }}" + port: 8888 + register: update_result + + - name: Get health_check info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_id }}" + health_check_method: details + register: health_check_info + + - name: 'Check result - Update Health Check Port' + assert: + that: + - update_result is not failed + - update_result is changed + - health_check_info.HealthCheck.HealthCheckConfig.Port == 8888 + + + - name: 'Update Health Check by ID - Update Port - idempotency - check_mode' + route53_health_check: + id: "{{ health_check_id }}" + port: 8888 + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check Port - idempotency - check_mode' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by ID - Update Port - idempotency' + route53_health_check: + id: "{{ health_check_id }}" + port: 8888 + register: update_result + + - name: 'Check result - Update Health Check Port - idempotency' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + ## + - name: 'Update Health Check by ID - Update IP address and FQDN - check_mode' + route53_health_check: + id: "{{ health_check_id }}" + ip_address: 1.2.3.4 + fqdn: '{{ fqdn_1 }}' + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check IP address and FQDN - check_mode' + assert: + that: + - update_result is not failed + - update_result is changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by ID - Update IP address and FQDN' + route53_health_check: + id: "{{ health_check_id }}" + ip_address: 1.2.3.4 + fqdn: '{{ fqdn_1 }}' + register: update_result + + - name: Get health_check info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_id }}" + health_check_method: details + register: health_check_info + + - name: 'Check result - Update Health Check IP address and FQDN' + assert: + that: + - update_result is not failed + - update_result is changed + - health_check_info.HealthCheck.HealthCheckConfig.IPAddress == '1.2.3.4' + - health_check_info.HealthCheck.HealthCheckConfig.FullyQualifiedDomainName == "{{ fqdn_1 }}" + + + - name: 'Update Health Check by ID - Update IP address and FQDN - idempotency - check_mode' + route53_health_check: + id: "{{ health_check_id }}" + ip_address: 1.2.3.4 + fqdn: '{{ fqdn_1 }}' + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check IP address and FQDN - idempotency - check_mode' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by ID - Update IP address and FQDN - idempotency' + route53_health_check: + id: "{{ health_check_id }}" + ip_address: 1.2.3.4 + fqdn: '{{ fqdn_1 }}' + register: update_result + + - name: 'Check result - Update Health Check IP address and FQDN - idempotency' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + # Update Health Check (Port) by name + + - name: 'Update Health Check by name - Update Port - check_mode' + route53_health_check: + state: present + port: 8080 + type: '{{ type_http }}' + fqdn: '{{ fqdn }}' + health_check_name: '{{ tiny_prefix }}-test-update-delete-by-id' + use_unique_names: true + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check Port - check_mode' + assert: + that: + - update_result is not failed + - update_result is changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by name - Update Port' + route53_health_check: + state: present + port: 8080 + type: '{{ type_http }}' + fqdn: '{{ fqdn }}' + health_check_name: '{{ tiny_prefix }}-test-update-delete-by-id' + use_unique_names: true + register: update_result + + - name: Get health_check info + community.aws.route53_info: + query: health_check + health_check_id: "{{ health_check_id }}" + health_check_method: details + register: health_check_info + + - name: 'Check result - Update Health Check Port' + assert: + that: + - update_result is not failed + - update_result is changed + - health_check_info.HealthCheck.HealthCheckConfig.Port == 8080 + + - name: 'Update Health Check by name - Update Port - idempotency - check_mode' + route53_health_check: + state: present + port: 8080 + type: '{{ type_http }}' + fqdn: '{{ fqdn }}' + health_check_name: '{{ tiny_prefix }}-test-update-delete-by-id' + use_unique_names: true + register: update_result + check_mode: true + + - name: 'Check result - Update Health Check Port - idempotency - check_mode' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + - name: 'Update Health Check by name - Update Port - idempotency' + route53_health_check: + state: present + port: 8080 + type: '{{ type_http }}' + fqdn: '{{ fqdn }}' + health_check_name: '{{ tiny_prefix }}-test-update-delete-by-id' + use_unique_names: true + register: update_result + + - name: 'Check result - Update Health Check Port - idempotency' + assert: + that: + - update_result is not failed + - update_result is not changed + - '"route53:UpdateHealthCheck" not in update_result.resource_actions' + + # Delete Health Check by ID Tests + - name: Delete Health check by ID - check_mode + route53_health_check: + state: absent + id: "{{ health_check_id }}" + register: delete_result + check_mode: true + + - name: 'Check result - Delete Health Check by ID -check_mode' + assert: + that: + - delete_result is not failed + - delete_result is changed + - '"route53:DeleteHealthCheck" not in delete_result.resource_actions' + + - name: Delete Health check by ID + route53_health_check: + state: absent + id: "{{ health_check_id }}" + register: delete_result + + - name: 'Check result - Delete Health Check by ID' + assert: + that: + - delete_result is not failed + - delete_result is changed + - '"route53:DeleteHealthCheck" in delete_result.resource_actions' + + - name: Delete Health check by ID - idempotency - check_mode + route53_health_check: + state: absent + id: "{{ health_check_id }}" + register: delete_result + check_mode: true + + - name: 'Check result - Delete Health Check by ID -idempotency -check_mode' + assert: + that: + - delete_result is not failed + - delete_result is not changed + - '"route53:DeleteHealthCheck" not in delete_result.resource_actions' + + - name: Delete Health check by ID - idempotency + route53_health_check: + state: absent + id: "{{ health_check_id }}" + register: delete_result + + - name: 'Check result - Delete Health Check by ID -idempotency' + assert: + that: + - delete_result is not failed + - delete_result is not changed + - '"route53:DeleteHealthCheck" not in delete_result.resource_actions' + + # cleanup + always: + - name: Delete Health check by ID + route53_health_check: + state: absent + id: "{{ health_check_id }}" From aeb6af4226f74eb8b628bdee0c35d6ba43769337 Mon Sep 17 00:00:00 2001 From: Mandar Kulkarni Date: Thu, 7 Jul 2022 13:43:55 -0700 Subject: [PATCH 68/81] route53_info: Add snake_cased return key,values to remaining methods (#1322) route53_info: Add snake_cased return key,values to remaining methods SUMMARY Following up on #1236 Found more places where route53_info module does not return a snake_case output. Added snake_case output to checker_ip_range_details , reusable_delegation_set_details, and get_health_check methods. ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_info Reviewed-by: Joseph Torcasso 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/d7f38627fdb9e8309a6fcd7229610dc3f3da584e --- plugins/modules/route53_info.py | 123 +++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 4e90556a1ec..68b0bb54b73 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -360,6 +360,88 @@ type: str sample: HTTPS version_added: 4.0.0 +checker_ip_ranges: + description: A list of IP ranges in CIDR format for Amazon Route 53 health checkers. + returned: when I(query=checker_ip_range) + type: list + elements: str + version_added: 4.1.0 +delegation_sets: + description: A list of dicts that contains information about the reusable delegation set. + returned: when I(query=reusable_delegation_set) + type: list + elements: dict + version_added: 4.1.0 +health_check: + description: A dict of Route53 health check details returned by get_health_check_status in boto3. + type: dict + returned: when I(query=health_check) and I(health_check_method=details) + contains: + id: + description: The identifier that Amazon Route53 assigned to the health check at the time of creation. + type: str + sample: '12345cdc-2cc4-1234-bed2-123456abc1a2' + health_check_version: + description: The version of the health check. + type: str + sample: 1 + caller_reference: + description: A unique string that you specified when you created the health check. + type: str + sample: '01d0db12-x0x9-12a3-1234-0z000z00zz0z' + health_check_config: + description: A dict that contains detailed information about one health check. + type: dict + contains: + disabled: + description: Whether Route53 should stop performing health checks on a endpoint. + type: bool + sample: false + enable_sni: + description: Whether Route53 should send value of FullyQualifiedDomainName to endpoint in client_hello message during TLS negotiation. + type: bool + sample: true + failure_threshold: + description: The number of consecutive health checks that an endpoint must pass/fail for Route53 to change current status of endpoint. + type: int + sample: 3 + fully_qualified_domain_name: + description: The fully qualified DNS name of the endpoint on which Route53 performs health checks. + type: str + sample: 'hello' + inverted: + description: Whether Route53 should invert the status of a health check. + type: bool + sample: false + ip_address: + description: The IPv4/IPv6 IP address of the endpoint that Route53 should perform health checks on. + type: str + sample: 192.0.2.44 + measure_latency: + description: Whether Route53 should measure latency between health checkers in multiple AWS regions and the endpoint. + type: bool + sample: false + port: + description: The port of the endpoint that Route53 should perform health checks on. + type: int + sample: 80 + request_interval: + description: The number of seconds between the time that Route53 gets a response from endpoint and the next health check request. + type: int + sample: 30 + resource_path: + description: The path that Route53 requests when performing health checks. + type: str + sample: '/welcome.html' + search_string: + description: The string that Route53 uses to search for in the response body from specified resource. + type: str + sample: 'test-string-to-match' + type: + description: The type of the health check. + type: str + sample: HTTPS + version_added: 4.1.0 ResourceRecordSets: description: A deprecated CamelCased list of resource record sets returned by list_resource_record_sets in boto3. \ This list contains same elements/parameters as it's snake_cased version mentioned above. \ @@ -381,6 +463,26 @@ type: list elements: dict returned: when I(query=health_check) +CheckerIpRanges: + description: A deprecated CamelCased list of IP ranges in CIDR format for Amazon Route 53 health checkers.\ + This list contains same elements/parameters as it's snake_cased version mentioned abobe. \ + This field is deprecated and will be removed in 6.0.0 version release. + type: list + elements: str + returned: when I(query=checker_ip_range) +DelegationSets: + description: A deprecated CamelCased list of dicts that contains information about the reusable delegation set. \ + This list contains same elements/parameters as it's snake_cased version mentioned above. \ + This field is deprecated and will be removed in 6.0.0 version release. + type: list + elements: dict + returned: when I(query=reusable_delegation_set) +HealthCheck: + description: A deprecated CamelCased dict of Route53 health check details returned by get_health_check_status in boto3. \ + This dict contains same elements/parameters as it's snake_cased version mentioned above. \ + This field is deprecated and will be removed in 6.0.0 version release. + type: dict + returned: when I(query=health_check) and I(health_check_method=details) ''' try: @@ -431,6 +533,12 @@ def reusable_delegation_set_details(): params['DelegationSetId'] = module.params.get('delegation_set_id') results = client.get_reusable_delegation_set(**params) + results['delegation_sets'] = results['DelegationSets'] + module.deprecate("The 'CamelCase' return values with key 'DelegationSets' is deprecated and \ + will be replaced by 'snake_case' return values with key 'delegation_sets'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') + return results @@ -495,7 +603,14 @@ def change_details(): def checker_ip_range_details(): - return client.get_checker_ip_ranges() + results = client.get_checker_ip_ranges() + results['checker_ip_ranges'] = results['CheckerIpRanges'] + module.deprecate("The 'CamelCase' return values with key 'CheckerIpRanges' is deprecated and \ + will be replaced by 'snake_case' return values with key 'checker_ip_ranges'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') + + return results def get_count(): @@ -522,6 +637,12 @@ def get_health_check(): elif module.params.get('health_check_method') == 'status': results = client.get_health_check_status(**params) + results['health_check'] = camel_dict_to_snake_dict(results['HealthCheck']) + module.deprecate("The 'CamelCase' return values with key 'HealthCheck' is deprecated and \ + will be replaced by 'snake_case' return values with key 'health_check'. \ + Both case values are returned for now.", + date='2025-01-01', collection_name='community.aws') + return results From 6310b5dc688ead7dd2861a7d1dd9f425bc437562 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Mon, 11 Jul 2022 14:42:21 +0200 Subject: [PATCH 69/81] Update default value of purge_tags to True (#1343) Update default value of purge_tags to True SUMMARY Complete the deprecation cycle and update purge_tags default value to True ISSUE TYPE Feature Pull Request COMPONENT NAME plugins/modules/acm_certificate.py plugins/modules/cloudfront_distribution.py plugins/modules/ec2_vpc_vpn.py plugins/modules/kms_key.py plugins/modules/rds_param_group.py plugins/modules/route53_health_check.py plugins/modules/route53_zone.py plugins/modules/sqs_queue.py ADDITIONAL INFORMATION Reviewed-by: Markus Bergholz 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/b56f3c6302db8ebb076fcf83eedc5b73aca31f8f --- plugins/modules/route53_health_check.py | 12 ++---------- plugins/modules/route53_zone.py | 12 ++---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index 83283ecf646..b07672e9ddb 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -115,7 +115,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - - amazon.aws.tags.deprecated_purge + - amazon.aws.tags ''' EXAMPLES = ''' @@ -503,7 +503,7 @@ def main(): request_interval=dict(type='int', choices=[10, 30], default=30), failure_threshold=dict(type='int', choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), tags=dict(type='dict', aliases=['resource_tags']), - purge_tags=dict(type='bool'), + purge_tags=dict(type='bool', default=True), health_check_id=dict(type='str', aliases=['id'], required=False), health_check_name=dict(type='str', aliases=['name'], required=False), use_unique_names=dict(type='bool', required=False), @@ -537,14 +537,6 @@ def main(): supports_check_mode=True, ) - if module.params.get('purge_tags') is None: - module.deprecate( - 'The purge_tags parameter currently defaults to False.' - ' For consistency across the collection, this default value' - ' will change to True in release 5.0.0.', - version='5.0.0', collection_name='community.aws') - module.params['purge_tags'] = False - if not module.params.get('health_check_id') and not module.params.get('type'): module.fail_json(msg="parameter 'type' is required if not updating or deleting health check by ID.") diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index 334233b4e44..e5c6f199b8e 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -49,7 +49,7 @@ extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - - amazon.aws.tags.deprecated_purge + - amazon.aws.tags notes: - Support for I(tags) and I(purge_tags) was added in release 2.1.0. author: @@ -437,7 +437,7 @@ def main(): hosted_zone_id=dict(), delegation_set_id=dict(), tags=dict(type='dict', aliases=['resource_tags']), - purge_tags=dict(type='bool'), + purge_tags=dict(type='bool', default=True), ) mutually_exclusive = [ @@ -451,14 +451,6 @@ def main(): supports_check_mode=True, ) - if module.params.get('purge_tags') is None: - module.deprecate( - 'The purge_tags parameter currently defaults to False.' - ' For consistency across the collection, this default value' - ' will change to True in release 5.0.0.', - version='5.0.0', collection_name='community.aws') - module.params['purge_tags'] = False - zone_in = module.params.get('zone').lower() state = module.params.get('state').lower() vpc_id = module.params.get('vpc_id') From d1c04ae8cd2e32519cb089dfbb4287a7a84f05c1 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Tue, 2 Aug 2022 10:46:02 +0100 Subject: [PATCH 70/81] route53: Restore support for zero weighted DNS records (#1379) route53: Restore support for zero weighted DNS records SUMMARY In #1117 (comment) and https://github.com/ansible-collections/community.aws/pull/1117/files#r869391659 this line was recommended to be simplified, but not any will also return true if weight_in has a value of 0, not only when it is None Fixes #1378 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53 ADDITIONAL INFORMATION Previously it was possible to create weighted records with a weight of 0. Currently the playbook below returns the error: You have specified identifier which makes sense only if you specify one of: weight, region, geo_location or failover. - name: Bug demo hosts: localhost tasks: - name: Set 0 weight for old env route53: wait: yes ttl: '5' type: 'CNAME' identifier: old overwrite: yes record: 'record.example.com.' zone: 'example.com.' value: 'record-old.example.com.' weight: '0' state: present 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/9195021fb06fcee34651785958e2d7881ac497bf --- plugins/modules/route53.py | 2 +- .../targets/route53/tasks/main.yml | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index bebdacdbf9a..db97197ec6b 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -628,7 +628,7 @@ def main(): if command_in == 'create' or command_in == 'delete': if alias_in and len(value_in) != 1: module.fail_json(msg="parameter 'value' must contain a single dns name for alias records") - if not any([weight_in, region_in, failover_in, geo_location]) and identifier_in is not None: + if (weight_in is None and region_in is None and failover_in is None and geo_location is None) and identifier_in is not None: module.fail_json(msg="You have specified identifier which makes sense only if you specify one of: weight, region, geo_location or failover.") retry_decorator = AWSRetry.jittered_backoff( diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 063e279f1cf..62a82f44a31 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -635,6 +635,42 @@ - weighted_record is not failed - weighted_record is not changed + - name: 'Create a zero weighted record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'zero_weighted.{{ zone_one }}' + type: CNAME + value: 'zid_test.{{ zone_one }}' + overwrite: True + identifier: "host1@www" + weight: 0 + region: '{{ omit }}' + register: weighted_record + - name: 'This should be changed' + assert: + that: + - weighted_record is not failed + - weighted_record is changed + + - name: 'Re-Create a zero weighted record' + route53: + state: present + zone: '{{ zone_one }}' + record: 'zero_weighted.{{ zone_one }}' + type: CNAME + value: 'zid_test.{{ zone_one }}' + overwrite: True + identifier: "host1@www" + weight: 0 + region: '{{ omit }}' + register: weighted_record + - name: 'This should not be changed' + assert: + that: + - weighted_record is not failed + - weighted_record is not changed + #Test Geo Location - Continent Code - name: Create a record with geo_location - continent_code (check_mode) route53: From a868d64afae89912f0170f71bb175763e1fc49a2 Mon Sep 17 00:00:00 2001 From: Joseph Spearritt Date: Wed, 3 Aug 2022 00:03:16 +1000 Subject: [PATCH 71/81] route53_info - fix max_items when not paginating (#1384) route53_info - fix max_items when not paginating SUMMARY As reported in #1383, the route53_info module presently fails to run with a boto3 parameter validation error if run with particular combinations of parameters, specifically: query: hosted_zone parameter with hosted_zone_method: list_by_name query: reusable_delegation_set without specifying a delegation_set_id I believe this is a regression introduced in #813 ISSUE TYPE Bugfix Pull Request COMPONENT NAME route53_info ADDITIONAL INFORMATION Some further information is described in the issue but tl;dr the prior PR converted all cases in the module where params['MaxItems'] was set to instead pass a params['PaginationConfig'], however this should only be done if a boto3 paginator is actually being used, and will fail (as noted above, due to parameter validation) if called with a regular boto3 client method. Hence this PR switches back to directly setting MaxItems on the methods that do not use a paginator. 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/569fff4c802b226277dfee882d253821a1e1a5d9 --- plugins/modules/route53_info.py | 10 ++-------- tests/integration/targets/route53/tasks/main.yml | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index 68b0bb54b73..a331fae9319 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -519,11 +519,8 @@ def reusable_delegation_set_details(): params = dict() if not module.params.get('delegation_set_id'): - # Set PaginationConfig with max_items if module.params.get('max_items'): - params['PaginationConfig'] = dict( - MaxItems=module.params.get('max_items') - ) + params['MaxItems'] = str(module.params.get('max_items')) if module.params.get('next_marker'): params['Marker'] = module.params.get('next_marker') @@ -581,11 +578,8 @@ def list_hosted_zones_by_name(): if module.params.get('dns_name'): params['DNSName'] = module.params.get('dns_name') - # Set PaginationConfig with max_items if module.params.get('max_items'): - params['PaginationConfig'] = dict( - MaxItems=module.params.get('max_items') - ) + params['MaxItems'] = str(module.params.get('max_items')) return client.list_hosted_zones_by_name(**params) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 62a82f44a31..f453a879e7c 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -79,6 +79,22 @@ - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - hosted_zones.HostedZone.Config.PrivateZone +# Needs CI permissions updated +# # Ensure that we can use the non-paginated list_by_name method with max_items +# - name: Get zone 1 details only +# route53_info: +# query: hosted_zone +# hosted_zone_method: list_by_name +# dns_name: '{{ zone_one }}' +# max_items: 1 +# register: list_by_name_result +# +# - name: Assert that we found exactly one zone when querying by name +# assert: +# that: +# - list_by_name_result.HostedZones | length == 1 +# - list_by_name_result.HostedZones[0].Name == '{{ zone_one }}' + - name: 'Create A record using zone fqdn' route53: state: present From 236167039ac70be40ff28c63abb5c44776f41751 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Wed, 3 Aug 2022 09:17:07 +0200 Subject: [PATCH 72/81] route53_info - enable max_items tests (#1389) route53_info - enable max_items tests SUMMARY Enables the tests from #1384 ISSUE TYPE Feature Pull Request COMPONENT NAME route53_info ADDITIONAL INFORMATION This commit was initially merged in https://github.com/ansible-collections/community.aws See: https://github.com/ansible-collections/community.aws/commit/7427a0dbbf7a2707c22554c7540aeb8ca2e4486e --- .../targets/route53/tasks/main.yml | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index f453a879e7c..2004523a7be 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -79,21 +79,20 @@ - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - hosted_zones.HostedZone.Config.PrivateZone -# Needs CI permissions updated -# # Ensure that we can use the non-paginated list_by_name method with max_items -# - name: Get zone 1 details only -# route53_info: -# query: hosted_zone -# hosted_zone_method: list_by_name -# dns_name: '{{ zone_one }}' -# max_items: 1 -# register: list_by_name_result -# -# - name: Assert that we found exactly one zone when querying by name -# assert: -# that: -# - list_by_name_result.HostedZones | length == 1 -# - list_by_name_result.HostedZones[0].Name == '{{ zone_one }}' + # Ensure that we can use the non-paginated list_by_name method with max_items + - name: Get zone 1 details only + route53_info: + query: hosted_zone + hosted_zone_method: list_by_name + dns_name: '{{ zone_one }}' + max_items: 1 + register: list_by_name_result + + - name: Assert that we found exactly one zone when querying by name + assert: + that: + - list_by_name_result.HostedZones | length == 1 + - list_by_name_result.HostedZones[0].Name == '{{ zone_one }}' - name: 'Create A record using zone fqdn' route53: From e49c13745d670bcb29e1ab3a3fa64aed36f1ce29 Mon Sep 17 00:00:00 2001 From: Markus Bergholz Date: Fri, 26 Aug 2022 12:14:21 +0200 Subject: [PATCH 73/81] adjust booleans (#1420) adjust booleans: use true/false Depends-On: #1423 SUMMARY ansible-community/community-topics#116 ISSUE TYPE Docs Pull Request 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/cb9716e14d44357aaadd2be733bbaa0dd8a522bc --- plugins/modules/route53.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index db97197ec6b..620d1833b98 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -256,7 +256,7 @@ type: A ttl: 7200 value: 1.1.1.1,2.2.2.2,3.3.3.3 - wait: yes + wait: true - name: Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated community.aws.route53: state: present @@ -268,7 +268,7 @@ - 1.1.1.1 - 2.2.2.2 - 3.3.3.3 - wait: yes + wait: true - name: Retrieve the details for new.foo.com community.aws.route53: state: get From c66e95a1b0d63922398bd67b40367f77e7fe4cf5 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 16 Sep 2022 17:25:54 +0200 Subject: [PATCH 74/81] Update runtime --- meta/runtime.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta/runtime.yml b/meta/runtime.yml index 66cc9189741..56b930bd9a4 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -60,6 +60,10 @@ action_groups: - lambda_execute - lambda_info - lambda_policy + - route53 + - route53_health_check + - route53_info + - route53_zone - s3_bucket - s3_object plugin_routing: From b77e7373f946b7d27bef77857b35f32387dcc5c3 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 16 Sep 2022 17:25:54 +0200 Subject: [PATCH 75/81] Update FQDN --- plugins/modules/route53.py | 32 ++++++++++++------------- plugins/modules/route53_health_check.py | 16 ++++++------- plugins/modules/route53_info.py | 32 ++++++++++++------------- plugins/modules/route53_zone.py | 12 +++++----- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/plugins/modules/route53.py b/plugins/modules/route53.py index 620d1833b98..327b16f27fb 100644 --- a/plugins/modules/route53.py +++ b/plugins/modules/route53.py @@ -249,7 +249,7 @@ EXAMPLES = r''' - name: Add new.foo.com as an A record with 3 IPs and wait until the changes have been replicated - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: new.foo.com @@ -258,7 +258,7 @@ value: 1.1.1.1,2.2.2.2,3.3.3.3 wait: true - name: Update new.foo.com as an A record with a list of 3 IPs and wait until the changes have been replicated - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: new.foo.com @@ -270,14 +270,14 @@ - 3.3.3.3 wait: true - name: Retrieve the details for new.foo.com - community.aws.route53: + amazon.aws.route53: state: get zone: foo.com record: new.foo.com type: A register: rec - name: Delete new.foo.com A record using the results from the get command - community.aws.route53: + amazon.aws.route53: state: absent zone: foo.com record: "{{ rec.set.record }}" @@ -287,7 +287,7 @@ # Add an AAAA record. Note that because there are colons in the value # that the IPv6 address must be quoted. Also shows using the old form command=create. - name: Add an AAAA record - community.aws.route53: + amazon.aws.route53: command: create zone: foo.com record: localhost.foo.com @@ -297,7 +297,7 @@ # For more information on SRV records see: # https://en.wikipedia.org/wiki/SRV_record - name: Add a SRV record with multiple fields for a service on port 22222 - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: "_example-service._tcp.foo.com" @@ -306,7 +306,7 @@ # Note that TXT and SPF records must be surrounded # by quotes when sent to Route 53: - name: Add a TXT record. - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: localhost.foo.com @@ -314,7 +314,7 @@ ttl: 7200 value: '"bar"' - name: Add an alias record that points to an Amazon ELB - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: elb.foo.com @@ -323,14 +323,14 @@ alias: True alias_hosted_zone_id: "{{ elb_zone_id }}" - name: Retrieve the details for elb.foo.com - community.aws.route53: + amazon.aws.route53: state: get zone: foo.com record: elb.foo.com type: A register: rec - name: Delete an alias record using the results from the get command - community.aws.route53: + amazon.aws.route53: state: absent zone: foo.com record: "{{ rec.set.record }}" @@ -340,7 +340,7 @@ alias: True alias_hosted_zone_id: "{{ rec.set.alias_hosted_zone_id }}" - name: Add an alias record that points to an Amazon ELB and evaluates it health - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: elb.foo.com @@ -350,7 +350,7 @@ alias_hosted_zone_id: "{{ elb_zone_id }}" alias_evaluate_target_health: True - name: Add an AAAA record with Hosted Zone ID - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com hosted_zone_id: Z2AABBCCDDEEFF @@ -359,7 +359,7 @@ ttl: 7200 value: "::1" - name: Use a routing policy to distribute traffic - community.aws.route53: + amazon.aws.route53: state: present zone: foo.com record: www.foo.com @@ -371,7 +371,7 @@ weight: 100 health_check: "d994b780-3150-49fd-9205-356abdd42e75" - name: Add a CAA record (RFC 6844) - community.aws.route53: + amazon.aws.route53: state: present zone: example.com record: example.com @@ -381,7 +381,7 @@ - 0 issuewild ";" - 0 iodef "mailto:security@example.com" - name: Create a record with geo_location - country_code - community.aws.route53: + amazon.aws.route53: state: present zone: '{{ zone_one }}' record: 'geo-test.{{ zone_one }}' @@ -392,7 +392,7 @@ geo_location: country_code: US - name: Create a record with geo_location - subdivision code - community.aws.route53: + amazon.aws.route53: state: present zone: '{{ zone_one }}' record: 'geo-test.{{ zone_one }}' diff --git a/plugins/modules/route53_health_check.py b/plugins/modules/route53_health_check.py index b07672e9ddb..14f1b44e235 100644 --- a/plugins/modules/route53_health_check.py +++ b/plugins/modules/route53_health_check.py @@ -120,7 +120,7 @@ EXAMPLES = ''' - name: Create a health-check for host1.example.com and use it in record - community.aws.route53_health_check: + amazon.aws.route53_health_check: state: present fqdn: host1.example.com type: HTTP_STR_MATCH @@ -130,7 +130,7 @@ failure_threshold: 2 register: my_health_check -- community.aws.route53: +- amazon.aws.route53: action: create zone: "example.com" type: CNAME @@ -143,7 +143,7 @@ health_check: "{{ my_health_check.health_check.id }}" - name: create a simple health check with health_check_name as unique identifier - community.aws.route53_health_check: + amazon.aws.route53_health_check: state: present health_check_name: ansible fqdn: ansible.com @@ -152,22 +152,22 @@ use_unique_names: true - name: Delete health-check - community.aws.route53_health_check: + amazon.aws.route53_health_check: state: absent fqdn: host1.example.com - name: Update Health check by ID - update ip_address - community.aws.route53_health_check: + amazon.aws.route53_health_check: id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx ip_address: 1.2.3.4 - name: Update Health check by ID - update port - community.aws.route53_health_check: + amazon.aws.route53_health_check: id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx ip_address: 8080 - name: Delete Health check by ID - community.aws.route53_health_check: + amazon.aws.route53_health_check: state: absent id: 12345678-abcd-abcd-abcd-0fxxxxxxxxxx @@ -576,7 +576,7 @@ def main(): 'The health_check_name is currently non required parameter.' ' This behavior will change and health_check_name ' ' will change to required=True and use_unique_names will change to default=True in release 6.0.0.', - version='6.0.0', collection_name='community.aws') + version='6.0.0', collection_name='amazon.aws') # If update or delete Health Check based on ID update_delete_by_id = False diff --git a/plugins/modules/route53_info.py b/plugins/modules/route53_info.py index a331fae9319..03b50f58c6e 100644 --- a/plugins/modules/route53_info.py +++ b/plugins/modules/route53_info.py @@ -136,19 +136,19 @@ EXAMPLES = r''' # Simple example of listing all hosted zones - name: List all hosted zones - community.aws.route53_info: + amazon.aws.route53_info: query: hosted_zone register: hosted_zones # Getting a count of hosted zones - name: Return a count of all hosted zones - community.aws.route53_info: + amazon.aws.route53_info: query: hosted_zone hosted_zone_method: count register: hosted_zone_count - name: List the first 20 resource record sets in a given hosted zone - community.aws.route53_info: + amazon.aws.route53_info: profile: account_name query: record_sets hosted_zone_id: ZZZ1111112222 @@ -156,33 +156,33 @@ register: record_sets - name: List first 20 health checks - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_method: list max_items: 20 register: health_checks - name: Get health check last failure_reason - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_method: failure_reason health_check_id: 00000000-1111-2222-3333-12345678abcd register: health_check_failure_reason - name: Retrieve reusable delegation set details - community.aws.route53_info: + amazon.aws.route53_info: query: reusable_delegation_set delegation_set_id: delegation id register: delegation_sets - name: setup of example for using next_marker - community.aws.route53_info: + amazon.aws.route53_info: query: hosted_zone max_items: 1 register: first_info - name: example for using next_marker - community.aws.route53_info: + amazon.aws.route53_info: query: hosted_zone next_marker: "{{ first_info.NextMarker }}" max_items: 1 @@ -191,12 +191,12 @@ - name: retrieve host entries starting with host1.workshop.test.io block: - name: grab zone id - community.aws.route53_zone: + amazon.aws.route53_zone: zone: "test.io" register: AWSINFO - name: grab Route53 record information - community.aws.route53_info: + amazon.aws.route53_info: type: A query: record_sets hosted_zone_id: "{{ AWSINFO.zone_id }}" @@ -534,7 +534,7 @@ def reusable_delegation_set_details(): module.deprecate("The 'CamelCase' return values with key 'DelegationSets' is deprecated and \ will be replaced by 'snake_case' return values with key 'delegation_sets'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return results @@ -560,7 +560,7 @@ def list_hosted_zones(): module.deprecate("The 'CamelCase' return values with key 'HostedZones' and 'list' are deprecated and \ will be replaced by 'snake_case' return values with key 'hosted_zones'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return { "HostedZones": zones, @@ -602,7 +602,7 @@ def checker_ip_range_details(): module.deprecate("The 'CamelCase' return values with key 'CheckerIpRanges' is deprecated and \ will be replaced by 'snake_case' return values with key 'checker_ip_ranges'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return results @@ -635,7 +635,7 @@ def get_health_check(): module.deprecate("The 'CamelCase' return values with key 'HealthCheck' is deprecated and \ will be replaced by 'snake_case' return values with key 'health_check'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return results @@ -674,7 +674,7 @@ def list_health_checks(): module.deprecate("The 'CamelCase' return values with key 'HealthChecks' and 'list' are deprecated and \ will be replaced by 'snake_case' return values with key 'health_checks'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return { "HealthChecks": health_checks, @@ -713,7 +713,7 @@ def record_sets_details(): module.deprecate("The 'CamelCase' return values with key 'ResourceRecordSets' and 'list' are deprecated and \ will be replaced by 'snake_case' return values with key 'resource_record_sets'. \ Both case values are returned for now.", - date='2025-01-01', collection_name='community.aws') + date='2025-01-01', collection_name='amazon.aws') return { "ResourceRecordSets": record_sets, diff --git a/plugins/modules/route53_zone.py b/plugins/modules/route53_zone.py index e5c6f199b8e..7e6abd92a4b 100644 --- a/plugins/modules/route53_zone.py +++ b/plugins/modules/route53_zone.py @@ -58,37 +58,37 @@ EXAMPLES = r''' - name: create a public zone - community.aws.route53_zone: + amazon.aws.route53_zone: zone: example.com comment: this is an example - name: delete a public zone - community.aws.route53_zone: + amazon.aws.route53_zone: zone: example.com state: absent - name: create a private zone - community.aws.route53_zone: + amazon.aws.route53_zone: zone: devel.example.com vpc_id: '{{ myvpc_id }}' vpc_region: us-west-2 comment: developer domain - name: create a public zone associated with a specific reusable delegation set - community.aws.route53_zone: + amazon.aws.route53_zone: zone: example.com comment: reusable delegation set example delegation_set_id: A1BCDEF2GHIJKL - name: create a public zone with tags - community.aws.route53_zone: + amazon.aws.route53_zone: zone: example.com comment: this is an example tags: Owner: Ansible Team - name: modify a public zone, removing all previous tags and adding a new one - community.aws.route53_zone: + amazon.aws.route53_zone: zone: example.com comment: this is an example tags: From 6bc1f111fafd494eac47981e940f3094f0b27aec Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 16 Sep 2022 17:25:55 +0200 Subject: [PATCH 76/81] Remove collection reference inside the tests --- .../targets/route53/tasks/main.yml | 686 +++++++++--------- 1 file changed, 346 insertions(+), 340 deletions(-) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 2004523a7be..9f592bf31ad 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -1,11 +1,10 @@ ---- # tasks file for Route53 integration tests - set_fact: zone_one: '{{ resource_prefix | replace("-", "") }}.one.ansible.test.' zone_two: '{{ resource_prefix | replace("-", "") }}.two.ansible.test.' - debug: - msg: 'Set zones {{ zone_one }} and {{ zone_two }}' + msg: Set zones {{ zone_one }} and {{ zone_two }} - name: Test basics (new zone, A and AAAA records) module_defaults: @@ -16,7 +15,7 @@ region: '{{ aws_region }}' route53: # Route53 is explicitly a global service - region: null + region: block: - name: create VPC ec2_vpc_net: @@ -25,32 +24,32 @@ state: present register: vpc - - name: 'Create a zone' + - name: Create a zone route53_zone: zone: '{{ zone_one }}' - comment: 'Created in Ansible test {{ resource_prefix }}' + comment: Created in Ansible test {{ resource_prefix }} tags: TestTag: '{{ resource_prefix }}.z1' register: z1 - assert: that: - - z1 is success - - z1 is changed - - "z1.comment == 'Created in Ansible test {{ resource_prefix }}'" - - "z1.tags.TestTag == '{{ resource_prefix }}.z1'" + - z1 is success + - z1 is changed + - z1.comment == 'Created in Ansible test {{ resource_prefix }}' + - z1.tags.TestTag == '{{ resource_prefix }}.z1' - - name: 'Get zone details' + - name: Get zone details route53_info: query: hosted_zone hosted_zone_id: '{{ z1.zone_id }}' hosted_zone_method: details register: hosted_zones - - name: 'Assert newly created hosted zone only has NS and SOA records' + - name: Assert newly created hosted zone only has NS and SOA records assert: that: - - hosted_zones.HostedZone.ResourceRecordSetCount == 2 + - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - - name: 'Create a second zone' + - name: Create a second zone route53_zone: zone: '{{ zone_two }}' vpc_id: '{{ vpc.vpc.id }}' @@ -61,10 +60,10 @@ register: z2 - assert: that: - - z2 is success - - z2 is changed - - "z2.comment == 'Created in Ansible test {{ resource_prefix }}'" - - "z2.tags.TestTag == '{{ resource_prefix }}.z2'" + - z2 is success + - z2 is changed + - z2.comment == 'Created in Ansible test {{ resource_prefix }}' + - z2.tags.TestTag == '{{ resource_prefix }}.z2' - name: Get zone details route53_info: @@ -76,8 +75,8 @@ - name: Assert newly created hosted zone only has NS and SOA records assert: that: - - hosted_zones.HostedZone.ResourceRecordSetCount == 2 - - hosted_zones.HostedZone.Config.PrivateZone + - hosted_zones.HostedZone.ResourceRecordSetCount == 2 + - hosted_zones.HostedZone.Config.PrivateZone # Ensure that we can use the non-paginated list_by_name method with max_items - name: Get zone 1 details only @@ -91,295 +90,296 @@ - name: Assert that we found exactly one zone when querying by name assert: that: - - list_by_name_result.HostedZones | length == 1 - - list_by_name_result.HostedZones[0].Name == '{{ zone_one }}' + - list_by_name_result.HostedZones | length == 1 + - list_by_name_result.HostedZones[0].Name == '{{ zone_one }}' - - name: 'Create A record using zone fqdn' + - name: Create A record using zone fqdn route53: state: present zone: '{{ zone_one }}' - record: 'qdn_test.{{ zone_one }}' + record: qdn_test.{{ zone_one }} type: A - value: '192.0.2.1' + value: 192.0.2.1 register: qdn - assert: that: - - qdn is not failed - - qdn is changed + - qdn is not failed + - qdn is changed - - name: 'Get A record using "get" method of route53 module' + - name: Get A record using "get" method of route53 module route53: state: get zone: '{{ zone_one }}' - record: 'qdn_test.{{ zone_one }}' + record: qdn_test.{{ zone_one }} type: A register: get_result - name: Check boto3 type get data assert: that: - - get_result.nameservers | length > 0 - - get_result.resource_record_sets | length == 1 - - '"name" in record_set' - - record_set.name == qdn_record - - '"resource_records" in record_set' - - record_set.resource_records | length == 1 - - '"value" in record_set.resource_records[0]' - - record_set.resource_records[0].value == '192.0.2.1' - - '"ttl" in record_set' - - record_set.ttl == 3600 - - '"type" in record_set' - - record_set.type == 'A' + - get_result.nameservers | length > 0 + - get_result.resource_record_sets | length == 1 + - '"name" in record_set' + - record_set.name == qdn_record + - '"resource_records" in record_set' + - record_set.resource_records | length == 1 + - '"value" in record_set.resource_records[0]' + - record_set.resource_records[0].value == '192.0.2.1' + - '"ttl" in record_set' + - record_set.ttl == 3600 + - '"type" in record_set' + - record_set.type == 'A' vars: record_set: '{{ get_result.resource_record_sets[0] }}' - qdn_record: 'qdn_test.{{ zone_one }}' + qdn_record: qdn_test.{{ zone_one }} - name: Check boto3 compat get data assert: that: - - '"set" in get_result' - - '"Name" in record_set' - - record_set.Name == qdn_record - - '"ResourceRecords" in record_set' - - record_set.ResourceRecords | length == 1 - - '"Value" in record_set.ResourceRecords[0]' - - record_set.ResourceRecords[0].Value == '192.0.2.1' - - '"TTL" in record_set' - - record_set.TTL == 3600 - - record_set.Type == 'A' + - '"set" in get_result' + - '"Name" in record_set' + - record_set.Name == qdn_record + - '"ResourceRecords" in record_set' + - record_set.ResourceRecords | length == 1 + - '"Value" in record_set.ResourceRecords[0]' + - record_set.ResourceRecords[0].Value == '192.0.2.1' + - '"TTL" in record_set' + - record_set.TTL == 3600 + - record_set.Type == 'A' vars: record_set: '{{ get_result.set }}' - qdn_record: 'qdn_test.{{ zone_one }}' + qdn_record: qdn_test.{{ zone_one }} - name: Check boto2 compat get data assert: that: - - '"set" in get_result' - - '"alias" in record_set' - - record_set.alias == False - - '"failover" in record_set' - - '"health_check" in record_set' - - '"hosted_zone_id" in record_set' - - record_set.hosted_zone_id == z1.zone_id - - '"identifier" in record_set' - - '"record" in record_set' - - record_set.record == qdn_record - - '"ttl" in record_set' - - record_set.ttl == "3600" - - '"type" in record_set' - - record_set.type == 'A' - - '"value" in record_set' - - record_set.value == '192.0.2.1' - - '"values" in record_set' - - record_set['values'] | length == 1 - - record_set['values'][0] == '192.0.2.1' - - '"weight" in record_set' - - '"zone" in record_set' - - record_set.zone == zone_one + - '"set" in get_result' + - '"alias" in record_set' + - record_set.alias == False + - '"failover" in record_set' + - '"health_check" in record_set' + - '"hosted_zone_id" in record_set' + - record_set.hosted_zone_id == z1.zone_id + - '"identifier" in record_set' + - '"record" in record_set' + - record_set.record == qdn_record + - '"ttl" in record_set' + - record_set.ttl == "3600" + - '"type" in record_set' + - record_set.type == 'A' + - '"value" in record_set' + - record_set.value == '192.0.2.1' + - '"values" in record_set' + - record_set['values'] | length == 1 + - record_set['values'][0] == '192.0.2.1' + - '"weight" in record_set' + - '"zone" in record_set' + - record_set.zone == zone_one vars: record_set: '{{ get_result.set }}' - qdn_record: 'qdn_test.{{ zone_one }}' + qdn_record: qdn_test.{{ zone_one }} ## test A recordset creation and order adjustments - - name: 'Create same A record using zone non-qualified domain' + - name: Create same A record using zone non-qualified domain route53: state: present zone: '{{ zone_one[:-1] }}' - record: 'qdn_test.{{ zone_one[:-1] }}' + record: qdn_test.{{ zone_one[:-1] }} type: A - value: '192.0.2.1' + value: 192.0.2.1 register: non_qdn - assert: that: - - non_qdn is not failed - - non_qdn is not changed + - non_qdn is not failed + - non_qdn is not changed - - name: 'Create A record using zone ID' + - name: Create A record using zone ID route53: state: present hosted_zone_id: '{{ z1.zone_id }}' - record: 'zid_test.{{ zone_one }}' + record: zid_test.{{ zone_one }} type: A - value: '192.0.2.1' + value: 192.0.2.1 register: zid - assert: that: - - zid is not failed - - zid is changed + - zid is not failed + - zid is changed - - name: 'Create a multi-value A record with values in different order' + - name: Create a multi-value A record with values in different order route53: state: present zone: '{{ zone_one }}' - record: 'order_test.{{ zone_one }}' + record: order_test.{{ zone_one }} type: A value: - - '192.0.2.2' - - '192.0.2.1' + - 192.0.2.2 + - 192.0.2.1 register: mv_a_record - assert: that: - - mv_a_record is not failed - - mv_a_record is changed + - mv_a_record is not failed + - mv_a_record is changed - - name: 'Create same multi-value A record with values in different order' + - name: Create same multi-value A record with values in different order route53: state: present zone: '{{ zone_one }}' - record: 'order_test.{{ zone_one }}' + record: order_test.{{ zone_one }} type: A value: - - '192.0.2.2' - - '192.0.2.1' + - 192.0.2.2 + - 192.0.2.1 register: mv_a_record - assert: that: - - mv_a_record is not failed - - mv_a_record is not changed + - mv_a_record is not failed + - mv_a_record is not changed # Get resulting A record and ensure max_items is applied - - name: 'get Route53 A record information' + - name: get Route53 A record information route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' - start_record_name: 'order_test.{{ zone_one }}' + start_record_name: order_test.{{ zone_one }} max_items: 1 register: records - assert: that: - - records.ResourceRecordSets|length == 1 - - records.ResourceRecordSets[0].ResourceRecords|length == 2 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' - - records.ResourceRecordSets[0].ResourceRecords[1].Value == '192.0.2.1' + - records.ResourceRecordSets|length == 1 + - records.ResourceRecordSets[0].ResourceRecords|length == 2 + - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' + - records.ResourceRecordSets[0].ResourceRecords[1].Value == '192.0.2.1' - - name: 'Remove a member from multi-value A record with values in different order' + - name: Remove a member from multi-value A record with values in different order route53: state: present zone: '{{ zone_one }}' - record: 'order_test.{{ zone_one }}' + record: order_test.{{ zone_one }} type: A value: - - '192.0.2.2' + - 192.0.2.2 register: del_a_record ignore_errors: true - - name: 'This should fail, because `overwrite` is false' + - name: This should fail, because `overwrite` is false assert: that: - - del_a_record is failed + - del_a_record is failed - - name: 'Remove a member from multi-value A record with values in different order' + - name: Remove a member from multi-value A record with values in different order route53: state: present zone: '{{ zone_one }}' - record: 'order_test.{{ zone_one }}' + record: order_test.{{ zone_one }} overwrite: true type: A value: - - '192.0.2.2' + - 192.0.2.2 register: del_a_record ignore_errors: true - - name: 'This should not fail, because `overwrite` is true' + - name: This should not fail, because `overwrite` is true assert: that: - - del_a_record is not failed - - del_a_record is changed + - del_a_record is not failed + - del_a_record is changed - - name: 'get Route53 zone A record information' + - name: get Route53 zone A record information route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' - start_record_name: 'order_test.{{ zone_one }}' + start_record_name: order_test.{{ zone_one }} max_items: 50 register: records - assert: that: - - records.ResourceRecordSets|length == 3 - - records.ResourceRecordSets[0].ResourceRecords|length == 1 - - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' + - records.ResourceRecordSets|length == 3 + - records.ResourceRecordSets[0].ResourceRecords|length == 1 + - records.ResourceRecordSets[0].ResourceRecords[0].Value == '192.0.2.2' ## Test CNAME record creation and retrive info - - name: "Create CNAME record" + - name: Create CNAME record route53: state: present - zone: "{{ zone_one }}" + zone: '{{ zone_one }}' type: CNAME - record: "cname_test.{{ zone_one }}" - value: "order_test.{{ zone_one }}" + record: cname_test.{{ zone_one }} + value: order_test.{{ zone_one }} register: cname_record - assert: that: - - cname_record is not failed - - cname_record is changed + - cname_record is not failed + - cname_record is changed - - name: "Get Route53 CNAME record information" + - name: Get Route53 CNAME record information route53_info: type: CNAME query: record_sets - hosted_zone_id: "{{ z1.zone_id }}" - start_record_name: "cname_test.{{ zone_one }}" + hosted_zone_id: '{{ z1.zone_id }}' + start_record_name: cname_test.{{ zone_one }} max_items: 1 register: cname_records - assert: that: - - cname_records.ResourceRecordSets|length == 1 - - cname_records.ResourceRecordSets[0].ResourceRecords|length == 1 - - cname_records.ResourceRecordSets[0].ResourceRecords[0].Value == "order_test.{{ zone_one }}" + - cname_records.ResourceRecordSets|length == 1 + - cname_records.ResourceRecordSets[0].ResourceRecords|length == 1 + - cname_records.ResourceRecordSets[0].ResourceRecords[0].Value == "order_test.{{ + zone_one }}" ## Test CAA record creation - - name: 'Create a LetsEncrypt CAA record' + - name: Create a LetsEncrypt CAA record route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - '0 issue "letsencrypt.org;"' - - '0 issuewild "letsencrypt.org;"' + - 0 issue "letsencrypt.org;" + - 0 issuewild "letsencrypt.org;" overwrite: true register: caa - assert: that: - - caa is not failed - - caa is changed + - caa is not failed + - caa is changed - - name: 'Re-create the same LetsEncrypt CAA record' + - name: Re-create the same LetsEncrypt CAA record route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - '0 issue "letsencrypt.org;"' - - '0 issuewild "letsencrypt.org;"' + - 0 issue "letsencrypt.org;" + - 0 issuewild "letsencrypt.org;" overwrite: true register: caa - assert: that: - - caa is not failed - - caa is not changed + - caa is not failed + - caa is not changed - - name: 'Re-create the same LetsEncrypt CAA record in opposite-order' + - name: Re-create the same LetsEncrypt CAA record in opposite-order route53: state: present zone: '{{ zone_one }}' record: '{{ zone_one }}' type: CAA value: - - '0 issuewild "letsencrypt.org;"' - - '0 issue "letsencrypt.org;"' + - 0 issuewild "letsencrypt.org;" + - 0 issue "letsencrypt.org;" overwrite: true register: caa - - name: 'This should not be changed, as CAA records are not order sensitive' + - name: This should not be changed, as CAA records are not order sensitive assert: that: - - caa is not failed - - caa is not changed + - caa is not failed + - caa is not changed - name: Create an A record for a wildcard prefix route53: @@ -388,12 +388,12 @@ record: '*.wildcard_test.{{ zone_one }}' type: A value: - - 192.0.2.1 + - 192.0.2.1 register: wc_a_record - assert: that: - - wc_a_record is not failed - - wc_a_record is changed + - wc_a_record is not failed + - wc_a_record is changed - name: Create an A record for a wildcard prefix (idempotency) route53: @@ -402,12 +402,12 @@ record: '*.wildcard_test.{{ zone_one }}' type: A value: - - 192.0.2.1 + - 192.0.2.1 register: wc_a_record - assert: that: - - wc_a_record is not failed - - wc_a_record is not changed + - wc_a_record is not failed + - wc_a_record is not changed - name: Create an A record for a wildcard prefix (change) route53: @@ -416,13 +416,13 @@ record: '*.wildcard_test.{{ zone_one }}' type: A value: - - 192.0.2.2 + - 192.0.2.2 overwrite: true register: wc_a_record - assert: that: - - wc_a_record is not failed - - wc_a_record is changed + - wc_a_record is not failed + - wc_a_record is changed - name: Delete an A record for a wildcard prefix route53: @@ -431,19 +431,19 @@ record: '*.wildcard_test.{{ zone_one }}' type: A value: - - 192.0.2.2 + - 192.0.2.2 register: wc_a_record - assert: that: - - wc_a_record is not failed - - wc_a_record is changed - - wc_a_record.diff.after == {} + - wc_a_record is not failed + - wc_a_record is changed + - wc_a_record.diff.after == {} - name: create a record with different TTL route53: state: present zone: '{{ zone_one }}' - record: 'localhost.{{ zone_one }}' + record: localhost.{{ zone_one }} type: A value: 127.0.0.1 ttl: 30 @@ -451,110 +451,110 @@ - name: check return values assert: that: - - ttl30.diff.resource_record_sets[0].ttl == 30 - - ttl30 is changed + - ttl30.diff.resource_record_sets[0].ttl == 30 + - ttl30 is changed - name: delete previous record without mention ttl and value route53: state: absent zone: '{{ zone_one }}' - record: 'localhost.{{ zone_one }}' + record: localhost.{{ zone_one }} type: A register: ttl30 - name: check if record is deleted assert: that: - - ttl30 is changed + - ttl30 is changed - name: immutable delete previous record without mention ttl and value route53: state: absent zone: '{{ zone_one }}' - record: 'localhost.{{ zone_one }}' + record: localhost.{{ zone_one }} type: A register: ttl30 - name: check if record was deleted assert: that: - - ttl30 is not changed + - ttl30 is not changed # Tests on zone two (private zone) - name: Create A record using zone fqdn route53: state: present zone: '{{ zone_two }}' - record: 'qdn_test.{{ zone_two }}' + record: qdn_test.{{ zone_two }} type: A value: 192.0.2.1 private_zone: true register: qdn - assert: that: - - qdn is not failed - - qdn is changed + - qdn is not failed + - qdn is changed - name: Get A record using 'get' method of route53 module route53: state: get - zone: "{{ zone_two }}" - record: "qdn_test.{{ zone_two }}" + zone: '{{ zone_two }}' + record: qdn_test.{{ zone_two }} type: A private_zone: true register: get_result - assert: that: - - get_result.nameservers|length > 0 - - get_result.set.Name == "qdn_test.{{ zone_two }}" - - get_result.set.ResourceRecords[0].Value == "192.0.2.1" - - get_result.set.Type == "A" + - get_result.nameservers|length > 0 + - get_result.set.Name == "qdn_test.{{ zone_two }}" + - get_result.set.ResourceRecords[0].Value == "192.0.2.1" + - get_result.set.Type == "A" - name: Get a record that does not exist route53: state: get - zone: "{{ zone_two }}" - record: "notfound.{{ zone_two }}" + zone: '{{ zone_two }}' + record: notfound.{{ zone_two }} type: A private_zone: true register: get_result - assert: that: - - get_result.nameservers|length > 0 - - get_result.set|length == 0 - - get_result.resource_record_sets|length == 0 + - get_result.nameservers|length > 0 + - get_result.set|length == 0 + - get_result.resource_record_sets|length == 0 - name: Create same A record using zone non-qualified domain route53: state: present zone: '{{ zone_two[:-1] }}' - record: 'qdn_test.{{ zone_two[:-1] }}' + record: qdn_test.{{ zone_two[:-1] }} type: A value: 192.0.2.1 private_zone: true register: non_qdn - assert: that: - - non_qdn is not failed - - non_qdn is not changed + - non_qdn is not failed + - non_qdn is not changed - name: Create A record using zone ID route53: state: present hosted_zone_id: '{{ z2.zone_id }}' - record: 'zid_test.{{ zone_two }}' + record: zid_test.{{ zone_two }} type: A value: 192.0.2.2 private_zone: true register: zid - assert: that: - - zid is not failed - - zid is changed + - zid is not failed + - zid is changed - name: Create A record using zone fqdn and vpc_id route53: state: present zone: '{{ zone_two }}' - record: 'qdn_test_vpc.{{ zone_two }}' + record: qdn_test_vpc.{{ zone_two }} type: A value: 192.0.2.3 private_zone: true @@ -562,14 +562,14 @@ register: qdn - assert: that: - - qdn is not failed - - qdn is changed + - qdn is not failed + - qdn is changed - name: Create A record using zone ID and vpc_id route53: state: present hosted_zone_id: '{{ z2.zone_id }}' - record: 'zid_test_vpc.{{ zone_two }}' + record: zid_test_vpc.{{ zone_two }} type: A value: 192.0.2.4 private_zone: true @@ -577,122 +577,122 @@ register: zid - assert: that: - - zid is not failed - - zid is changed + - zid is not failed + - zid is changed - - name: 'Create an Alias record' + - name: Create an Alias record route53: state: present zone: '{{ zone_one }}' - record: 'alias.{{ zone_one }}' + record: alias.{{ zone_one }} type: A - alias: True + alias: true alias_hosted_zone_id: '{{ z1.zone_id }}' - value: 'zid_test.{{ zone_one }}' - overwrite: True + value: zid_test.{{ zone_one }} + overwrite: true register: alias_record - - name: 'This should be changed' + - name: This should be changed assert: that: - - alias_record is not failed - - alias_record is changed + - alias_record is not failed + - alias_record is changed - - name: 'Re-Create an Alias record' + - name: Re-Create an Alias record route53: state: present zone: '{{ zone_one }}' - record: 'alias.{{ zone_one }}' + record: alias.{{ zone_one }} type: A - alias: True + alias: true alias_hosted_zone_id: '{{ z1.zone_id }}' - value: 'zid_test.{{ zone_one }}' - overwrite: True + value: zid_test.{{ zone_one }} + overwrite: true register: alias_record - - name: 'This should not be changed' + - name: This should not be changed assert: that: - - alias_record is not failed - - alias_record is not changed + - alias_record is not failed + - alias_record is not changed - - name: 'Create a weighted record' + - name: Create a weighted record route53: state: present zone: '{{ zone_one }}' - record: 'weighted.{{ zone_one }}' + record: weighted.{{ zone_one }} type: CNAME - value: 'zid_test.{{ zone_one }}' - overwrite: True - identifier: "host1@www" + value: zid_test.{{ zone_one }} + overwrite: true + identifier: host1@www weight: 100 region: '{{ omit }}' register: weighted_record - - name: 'This should be changed' + - name: This should be changed assert: that: - - weighted_record is not failed - - weighted_record is changed + - weighted_record is not failed + - weighted_record is changed - - name: 'Re-Create a weighted record' + - name: Re-Create a weighted record route53: state: present zone: '{{ zone_one }}' - record: 'weighted.{{ zone_one }}' + record: weighted.{{ zone_one }} type: CNAME - value: 'zid_test.{{ zone_one }}' - overwrite: True - identifier: "host1@www" + value: zid_test.{{ zone_one }} + overwrite: true + identifier: host1@www weight: 100 region: '{{ omit }}' register: weighted_record - - name: 'This should not be changed' + - name: This should not be changed assert: that: - - weighted_record is not failed - - weighted_record is not changed + - weighted_record is not failed + - weighted_record is not changed - - name: 'Create a zero weighted record' + - name: Create a zero weighted record route53: state: present zone: '{{ zone_one }}' - record: 'zero_weighted.{{ zone_one }}' + record: zero_weighted.{{ zone_one }} type: CNAME - value: 'zid_test.{{ zone_one }}' - overwrite: True - identifier: "host1@www" + value: zid_test.{{ zone_one }} + overwrite: true + identifier: host1@www weight: 0 region: '{{ omit }}' register: weighted_record - - name: 'This should be changed' + - name: This should be changed assert: that: - - weighted_record is not failed - - weighted_record is changed + - weighted_record is not failed + - weighted_record is changed - - name: 'Re-Create a zero weighted record' + - name: Re-Create a zero weighted record route53: state: present zone: '{{ zone_one }}' - record: 'zero_weighted.{{ zone_one }}' + record: zero_weighted.{{ zone_one }} type: CNAME - value: 'zid_test.{{ zone_one }}' - overwrite: True - identifier: "host1@www" + value: zid_test.{{ zone_one }} + overwrite: true + identifier: host1@www weight: 0 region: '{{ omit }}' register: weighted_record - - name: 'This should not be changed' + - name: This should not be changed assert: that: - - weighted_record is not failed - - weighted_record is not changed + - weighted_record is not failed + - weighted_record is not changed #Test Geo Location - Continent Code - name: Create a record with geo_location - continent_code (check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-1.{{ zone_one }}' - identifier: "geohost1@www" + record: geo-test-1.{{ zone_one }} + identifier: geohost1@www type: A value: 127.0.0.1 ttl: 30 @@ -702,16 +702,16 @@ register: create_geo_continent_check_mode - assert: that: - - create_geo_continent_check_mode is changed - - create_geo_continent_check_mode is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_continent_check_mode.resource_actions' + - create_geo_continent_check_mode is changed + - create_geo_continent_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_check_mode.resource_actions' - name: Create a record with geo_location - continent_code route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-1.{{ zone_one }}' - identifier: "geohost1@www" + record: geo-test-1.{{ zone_one }} + identifier: geohost1@www type: A value: 127.0.0.1 ttl: 30 @@ -719,28 +719,28 @@ continent_code: NA register: create_geo_continent # Get resulting A record and geo_location parameters are applied - - name: 'get Route53 A record information' + - name: get Route53 A record information route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' - start_record_name: 'geo-test-1.{{ zone_one }}' + start_record_name: geo-test-1.{{ zone_one }} max_items: 1 register: result - assert: that: - - create_geo_continent is changed - - create_geo_continent is not failed - - '"route53:ChangeResourceRecordSets" in create_geo_continent.resource_actions' - - result.ResourceRecordSets[0].GeoLocation.ContinentCode == "NA" + - create_geo_continent is changed + - create_geo_continent is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_continent.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.ContinentCode == "NA" - name: Create a record with geo_location - continent_code (idempotency) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-1.{{ zone_one }}' - identifier: "geohost1@www" + record: geo-test-1.{{ zone_one }} + identifier: geohost1@www type: A value: 127.0.0.1 ttl: 30 @@ -749,16 +749,16 @@ register: create_geo_continent_idem - assert: that: - - create_geo_continent_idem is not changed - - create_geo_continent_idem is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem.resource_actions' + - create_geo_continent_idem is not changed + - create_geo_continent_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem.resource_actions' - name: Create a record with geo_location - continent_code (idempotency - check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-1.{{ zone_one }}' - identifier: "geohost1@www" + record: geo-test-1.{{ zone_one }} + identifier: geohost1@www type: A value: 127.0.0.1 ttl: 30 @@ -769,17 +769,17 @@ - assert: that: - - create_geo_continent_idem_check is not changed - - create_geo_continent_idem_check is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem_check.resource_actions' + - create_geo_continent_idem_check is not changed + - create_geo_continent_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_continent_idem_check.resource_actions' #Test Geo Location - Country Code - name: Create a record with geo_location - country_code (check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-2.{{ zone_one }}' - identifier: "geohost2@www" + record: geo-test-2.{{ zone_one }} + identifier: geohost2@www type: A value: 127.0.0.1 ttl: 30 @@ -789,16 +789,16 @@ register: create_geo_country_check_mode - assert: that: - - create_geo_country_check_mode is changed - - create_geo_country_check_mode is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_country_check_mode.resource_actions' + - create_geo_country_check_mode is changed + - create_geo_country_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_check_mode.resource_actions' - name: Create a record with geo_location - country_code route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-2.{{ zone_one }}' - identifier: "geohost2@www" + record: geo-test-2.{{ zone_one }} + identifier: geohost2@www type: A value: 127.0.0.1 ttl: 30 @@ -806,27 +806,27 @@ country_code: US register: create_geo_country # Get resulting A record and geo_location parameters are applied - - name: 'get Route53 A record information' + - name: get Route53 A record information route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' - start_record_name: 'geo-test-2.{{ zone_one }}' + start_record_name: geo-test-2.{{ zone_one }} max_items: 1 register: result - assert: that: - - create_geo_country is changed - - create_geo_country is not failed - - '"route53:ChangeResourceRecordSets" in create_geo_country.resource_actions' - - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + - create_geo_country is changed + - create_geo_country is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_country.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" - name: Create a record with geo_location - country_code (idempotency) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-2.{{ zone_one }}' - identifier: "geohost2@www" + record: geo-test-2.{{ zone_one }} + identifier: geohost2@www type: A value: 127.0.0.1 ttl: 30 @@ -835,16 +835,16 @@ register: create_geo_country_idem - assert: that: - - create_geo_country_idem is not changed - - create_geo_country_idem is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem.resource_actions' + - create_geo_country_idem is not changed + - create_geo_country_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem.resource_actions' - name: Create a record with geo_location - country_code (idempotency - check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-2.{{ zone_one }}' - identifier: "geohost2@www" + record: geo-test-2.{{ zone_one }} + identifier: geohost2@www type: A value: 127.0.0.1 ttl: 30 @@ -855,17 +855,17 @@ - assert: that: - - create_geo_country_idem_check is not changed - - create_geo_country_idem_check is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem_check.resource_actions' + - create_geo_country_idem_check is not changed + - create_geo_country_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_country_idem_check.resource_actions' #Test Geo Location - Subdivision Code - name: Create a record with geo_location - subdivision_code (check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-3.{{ zone_one }}' - identifier: "geohost3@www" + record: geo-test-3.{{ zone_one }} + identifier: geohost3@www type: A value: 127.0.0.1 ttl: 30 @@ -876,16 +876,16 @@ register: create_geo_subdivision_check_mode - assert: that: - - create_geo_subdivision_check_mode is changed - - create_geo_subdivision_check_mode is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_check_mode.resource_actions' + - create_geo_subdivision_check_mode is changed + - create_geo_subdivision_check_mode is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_check_mode.resource_actions' - name: Create a record with geo_location - subdivision_code route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-3.{{ zone_one }}' - identifier: "geohost3@www" + record: geo-test-3.{{ zone_one }} + identifier: geohost3@www type: A value: 127.0.0.1 ttl: 30 @@ -894,28 +894,28 @@ subdivision_code: TX register: create_geo_subdivision # Get resulting A record and geo_location parameters are applied - - name: 'get Route53 A record information' + - name: get Route53 A record information route53_info: type: A query: record_sets hosted_zone_id: '{{ z1.zone_id }}' - start_record_name: 'geo-test-3.{{ zone_one }}' + start_record_name: geo-test-3.{{ zone_one }} max_items: 1 register: result - assert: that: - - create_geo_subdivision is changed - - create_geo_subdivision is not failed - - '"route53:ChangeResourceRecordSets" in create_geo_subdivision.resource_actions' - - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" - - result.ResourceRecordSets[0].GeoLocation.SubdivisionCode == "TX" + - create_geo_subdivision is changed + - create_geo_subdivision is not failed + - '"route53:ChangeResourceRecordSets" in create_geo_subdivision.resource_actions' + - result.ResourceRecordSets[0].GeoLocation.CountryCode == "US" + - result.ResourceRecordSets[0].GeoLocation.SubdivisionCode == "TX" - name: Create a record with geo_location - subdivision_code (idempotency) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-3.{{ zone_one }}' - identifier: "geohost3@www" + record: geo-test-3.{{ zone_one }} + identifier: geohost3@www type: A value: 127.0.0.1 ttl: 30 @@ -925,16 +925,16 @@ register: create_geo_subdivision_idem - assert: that: - - create_geo_subdivision_idem is not changed - - create_geo_subdivision_idem is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem.resource_actions' + - create_geo_subdivision_idem is not changed + - create_geo_subdivision_idem is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem.resource_actions' - name: Create a record with geo_location - subdivision_code (idempotency - check_mode) route53: state: present zone: '{{ zone_one }}' - record: 'geo-test-3.{{ zone_one }}' - identifier: "geohost3@www" + record: geo-test-3.{{ zone_one }} + identifier: geohost3@www type: A value: 127.0.0.1 ttl: 30 @@ -946,9 +946,9 @@ - assert: that: - - create_geo_subdivision_idem_check is not changed - - create_geo_subdivision_idem_check is not failed - - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem_check.resource_actions' + - create_geo_subdivision_idem_check is not changed + - create_geo_subdivision_idem_check is not failed + - '"route53:ChangeResourceRecordSets" not in create_geo_subdivision_idem_check.resource_actions' #Cleanup------------------------------------------------------ @@ -958,8 +958,8 @@ route53: state: absent zone: '{{ zone_one }}' - record: 'geo-test-1.{{ zone_one }}' - identifier: "geohost1@www" + record: geo-test-1.{{ zone_one }} + identifier: geohost1@www type: A value: 127.0.0.1 ttl: 30 @@ -971,8 +971,8 @@ route53: state: absent zone: '{{ zone_one }}' - record: 'geo-test-2.{{ zone_one }}' - identifier: "geohost2@www" + record: geo-test-2.{{ zone_one }} + identifier: geohost2@www type: A value: 127.0.0.1 ttl: 30 @@ -984,8 +984,8 @@ route53: state: absent zone: '{{ zone_one }}' - record: 'geo-test-3.{{ zone_one }}' - identifier: "geohost3@www" + record: geo-test-3.{{ zone_one }} + identifier: geohost3@www type: A value: 127.0.0.1 ttl: 30 @@ -999,21 +999,22 @@ hosted_zone_id: '{{ z1.zone_id }}' register: z1_records - - name: 'Loop over A/AAAA/CNAME Alias records and delete them' + - name: Loop over A/AAAA/CNAME Alias records and delete them route53: state: absent - alias: True + alias: true alias_hosted_zone_id: '{{ item.AliasTarget.HostedZoneId }}' zone: '{{ zone_one }}' record: '{{ item.Name }}' type: '{{ item.Type }}' value: '{{ item.AliasTarget.DNSName }}' - ignore_errors: True - loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' - - name: 'Loop over A/AAAA/CNAME records and delete them' + - name: Loop over A/AAAA/CNAME records and delete them route53: state: absent zone: '{{ zone_one }}' @@ -1023,21 +1024,23 @@ weight: '{{ item.Weight | default(omit) }}' identifier: '{{ item.SetIdentifier }}' region: '{{ omit }}' - ignore_errors: True - loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"ResourceRecords" in item' - '"SetIdentifier" in item' - - name: 'Loop over A/AAAA/CNAME records and delete them' + - name: Loop over A/AAAA/CNAME records and delete them route53: state: absent zone: '{{ zone_one }}' record: '{{ item.Name }}' type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' - ignore_errors: True - loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z1_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"ResourceRecords" in item' @@ -1046,22 +1049,23 @@ hosted_zone_id: '{{ z2.zone_id }}' register: z2_records - - name: 'Loop over A/AAAA/CNAME Alias records and delete them' + - name: Loop over A/AAAA/CNAME Alias records and delete them route53: state: absent - alias: True + alias: true alias_hosted_zone_id: '{{ item.AliasTarget.HostedZoneId }}' zone: '{{ zone_two }}' record: '{{ item.Name }}' type: '{{ item.Type }}' value: '{{ item.AliasTarget.DNSName }}' private_zone: true - ignore_errors: True - loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"AliasTarget" in item' - - name: 'Loop over A/AAAA/CNAME records and delete them' + - name: Loop over A/AAAA/CNAME records and delete them route53: state: absent zone: '{{ zone_two }}' @@ -1071,13 +1075,14 @@ identifier: '{{ item.SetIdentifier }}' region: '{{ omit }}' private_zone: true - ignore_errors: True - loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"ResourceRecords" in item' - '"SetIdentifier" in item' - - name: 'Loop over A/AAAA/CNAME records and delete them' + - name: Loop over A/AAAA/CNAME records and delete them route53: state: absent zone: '{{ zone_two }}' @@ -1085,12 +1090,13 @@ type: '{{ item.Type }}' value: '{{ item.ResourceRecords | map(attribute="Value") | join(",") }}' private_zone: true - ignore_errors: True - loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", "CNAME", "CAA"]) | list }}' + ignore_errors: true + loop: '{{ z2_records.ResourceRecordSets | selectattr("Type", "in", ["A", "AAAA", + "CNAME", "CAA"]) | list }}' when: - '"ResourceRecords" in item' - - name: 'Delete test zone one {{ zone_one }}' + - name: Delete test zone one {{ zone_one }} route53_zone: state: absent zone: '{{ zone_one }}' @@ -1099,12 +1105,12 @@ retries: 10 until: delete_one is not failed - - name: 'Delete test zone two {{ zone_two }}' + - name: Delete test zone two {{ zone_two }} route53_zone: state: absent zone: '{{ zone_two }}' register: delete_two - ignore_errors: True + ignore_errors: true retries: 10 until: delete_two is not failed From 34add39d807bb0555ebee6d66051604b981a1ef8 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 16 Sep 2022 17:25:55 +0200 Subject: [PATCH 77/81] Add changelog fragment --- changelogs/fragments/migrate_route53.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 changelogs/fragments/migrate_route53.yml diff --git a/changelogs/fragments/migrate_route53.yml b/changelogs/fragments/migrate_route53.yml new file mode 100644 index 00000000000..123d1bb2d20 --- /dev/null +++ b/changelogs/fragments/migrate_route53.yml @@ -0,0 +1,13 @@ +major_changes: +- route53 - 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.route53``. +- route53_health_check - 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.route53_health_check``. +- route53_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.route53_info``. +- route53_zone - 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.route53_zone``. From 3f48acb12e836b8ce5c0bd70c731eecd2a6864d1 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Fri, 16 Sep 2022 17:25:55 +0200 Subject: [PATCH 78/81] Update ignore files --- tests/sanity/ignore-2.10.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index e69de29bb2d..9f9adc33ce1 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -0,0 +1 @@ +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this \ No newline at end of file From d22ac1c2f3cb93e64152ae1e56b5fe47e8acc512 Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Tue, 20 Sep 2022 13:55:52 +0200 Subject: [PATCH 79/81] Fix FQDN Signed-off-by: Alina Buzachis --- tests/integration/targets/route53/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/route53/tasks/main.yml b/tests/integration/targets/route53/tasks/main.yml index 9f592bf31ad..08ec59d9364 100644 --- a/tests/integration/targets/route53/tasks/main.yml +++ b/tests/integration/targets/route53/tasks/main.yml @@ -13,7 +13,7 @@ aws_secret_key: '{{ aws_secret_key }}' security_token: '{{ security_token | default(omit) }}' region: '{{ aws_region }}' - route53: + amazon.aws.route53: # Route53 is explicitly a global service region: block: From fad37b363a6fe4ab314811f614e5a54ce4bf129a Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Wed, 21 Sep 2022 15:03:50 +0200 Subject: [PATCH 80/81] Fix FQDN tests Signed-off-by: Alina Buzachis --- .../tasks/create_multiple_health_checks.yml | 4 ++-- .../route53_health_check/tasks/update_delete_by_id.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml b/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml index 41713673d67..42bdb6562a9 100644 --- a/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml +++ b/tests/integration/targets/route53_health_check/tasks/create_multiple_health_checks.yml @@ -43,14 +43,14 @@ health_check_2_id: "{{ create_result.results[1].health_check.id }}" - name: Get health_check 1 info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_1_id }}" health_check_method: details register: health_check_1_info - name: Get health_check 2 info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_2_id }}" health_check_method: details diff --git a/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml b/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml index 8bb4fb870f9..e4d242a2021 100644 --- a/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml +++ b/tests/integration/targets/route53_health_check/tasks/update_delete_by_id.yml @@ -24,7 +24,7 @@ health_check_id: "{{ create_result.health_check.id }}" - name: Get health_check info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_id }}" health_check_method: details @@ -52,7 +52,7 @@ register: update_result - name: Get health_check info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_id }}" health_check_method: details @@ -117,7 +117,7 @@ register: update_result - name: Get health_check info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_id }}" health_check_method: details @@ -192,7 +192,7 @@ register: update_result - name: Get health_check info - community.aws.route53_info: + amazon.aws.route53_info: query: health_check health_check_id: "{{ health_check_id }}" health_check_method: details From c0b80e4a40c64adff21a6eb7a42fc5a18fe01aba Mon Sep 17 00:00:00 2001 From: Alina Buzachis Date: Wed, 21 Sep 2022 15:07:50 +0200 Subject: [PATCH 81/81] Update sanity Signed-off-by: Alina Buzachis --- tests/sanity/ignore-2.11.txt | 1 + tests/sanity/ignore-2.12.txt | 1 + tests/sanity/ignore-2.13.txt | 1 + tests/sanity/ignore-2.14.txt | 1 + tests/sanity/ignore-2.9.txt | 3 ++- 5 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index e69de29bb2d..9f9adc33ce1 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -0,0 +1 @@ +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this \ No newline at end of file diff --git a/tests/sanity/ignore-2.12.txt b/tests/sanity/ignore-2.12.txt index e69de29bb2d..9f9adc33ce1 100644 --- a/tests/sanity/ignore-2.12.txt +++ b/tests/sanity/ignore-2.12.txt @@ -0,0 +1 @@ +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this \ No newline at end of file diff --git a/tests/sanity/ignore-2.13.txt b/tests/sanity/ignore-2.13.txt index e69de29bb2d..9f9adc33ce1 100644 --- a/tests/sanity/ignore-2.13.txt +++ b/tests/sanity/ignore-2.13.txt @@ -0,0 +1 @@ +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this \ No newline at end of file diff --git a/tests/sanity/ignore-2.14.txt b/tests/sanity/ignore-2.14.txt index e69de29bb2d..9f9adc33ce1 100644 --- a/tests/sanity/ignore-2.14.txt +++ b/tests/sanity/ignore-2.14.txt @@ -0,0 +1 @@ +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this \ No newline at end of file diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 730f6f17a12..ec052980186 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -2,4 +2,5 @@ plugins/modules/ec2_vpc_dhcp_option.py pylint:ansible-deprecated-no-version # W plugins/modules/ec2_vpc_endpoint.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability plugins/modules/ec2_vpc_endpoint_info.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability plugins/modules/ec2_instance.py pylint:ansible-deprecated-no-version # We use dates for deprecations, Ansible 2.9 only supports this for compatability -plugins/modules/iam_policy.py pylint:ansible-deprecated-no-version \ No newline at end of file +plugins/modules/iam_policy.py pylint:ansible-deprecated-no-version +plugins/modules/route53.py validate-modules:parameter-state-invalid-choice # route53_info needs improvements before we can deprecate this