Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add route53_wait module #1904

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/1904-route53_wait.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
trivial:
- "Add route53_wait module to community.aws.aws action group (https://github.com/ansible-collections/community.aws/pull/1904)."
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ action_groups:
- redshift_cross_region_snapshots
- redshift_info
- redshift_subnet_group
- route53_wait
- s3_bucket_notification
- s3_bucket_info
- s3_cors
Expand Down
185 changes: 185 additions & 0 deletions plugins/modules/route53_wait.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2023, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = r"""
---
module: route53_wait
version_added: 6.2.0
short_description: wait for changes in Amazons Route 53 DNS service to propagate
description:
- When using M(amazon.aws.route53) with I(wait=false), this module allows to wait for the
module's propagation to finish at a later point of time.
options:
result:
aliases:
- results
description:
- The registered result of one or multiple M(amazon.aws.route53) invocations.
required: true
type: dict
wait_timeout:
description:
- How long to wait for the changes to be replicated, in seconds.
- This timeout will be used for every changed result in I(result).
default: 300
type: int
region:
description:
- This setting is ignored by the module. It is only present to make it possible to
have I(region) present in the module default group.
type: str
author:
- Felix Fontein (@felixfontein)
extends_documentation_fragment:
- amazon.aws.common.modules
- amazon.aws.boto3
"""

RETURN = r"""
#
"""

EXAMPLES = r"""
# Example when using a single route53 invocation:

- name: Add new.foo.com as an A record with 3 IPs
amazon.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
register: module_result

# do something else

- name: Wait for the changes of the above route53 invocation to propagate
community.aws.route53_wait:
result: "{{ module_result }}"

#########################################################################
# Example when using a loop over amazon.aws.route53:

- name: Add various A records
amazon.aws.route53:
state: present
zone: foo.com
record: "{{ item.record }}"
type: A
ttl: 300
value: "{{ item.value }}"
loop:
- record: new.foo.com
value: 1.1.1.1
- record: foo.foo.com
value: 2.2.2.2
- record: bar.foo.com
value:
- 3.3.3.3
- 4.4.4.4
register: module_results

# do something else

- name: Wait for the changes of the above three route53 invocations to propagate
community.aws.route53_wait:
results: "{{ module_results }}"
"""

try:
import botocore
except ImportError:
pass # Handled by AnsibleAWSModule

from ansible.module_utils._text import to_native

from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter

from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule

WAIT_RETRY = 5 # how many seconds to wait between propagation status polls


def detect_task_results(results):
if "results" in results:
# This must be the registered result of a loop of route53 tasks
for key in ("changed", "msg", "skipped"):
if key not in results:
raise ValueError(f"missing {key} key")
if not isinstance(results["results"], list):
raise ValueError("results is present, but not a list")
for index, result in enumerate(results["results"]):
if not isinstance(result, dict):
raise ValueError(f"result {index + 1} is not a dictionary")
for key in ("changed", "failed", "ansible_loop_var", "invocation"):
if key not in result:
raise ValueError(f"missing {key} key for result {index + 1}")
yield f" for result #{index + 1}", result
return
# This must be a single route53 task
for key in ("changed", "failed"):
if key not in results:
raise ValueError(f"missing {key} key")
yield "", results


def main():
argument_spec = dict(
result=dict(type="dict", required=True, aliases=["results"]),
wait_timeout=dict(type="int", default=300),
region=dict(type="str"), # ignored
)

module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)

result_in = module.params["result"]
wait_timeout_in = module.params.get("wait_timeout")

changed_results = []
try:
for id, result in detect_task_results(result_in):
if result.get("wait_id"):
changed_results.append((id, result["wait_id"]))
except ValueError as exc:
module.fail_json(
msg=f"The value passed as result does not seem to be a registered route53 result: {to_native(exc)}"
)

# connect to the route53 endpoint
try:
route53 = module.client("route53")
except botocore.exceptions.HTTPClientError as e:
module.fail_json_aws(e, msg="Failed to connect to AWS")

for what, wait_id in changed_results:
try:
waiter = get_waiter(route53, "resource_record_sets_changed")
waiter.wait(
Id=wait_id,
WaiterConfig=dict(
Delay=WAIT_RETRY,
MaxAttempts=wait_timeout_in // WAIT_RETRY,
),
)
except botocore.exceptions.WaiterError as e:
module.fail_json_aws(e, msg=f"Timeout waiting for resource records changes{what} 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=f"Unhandled exception. ({to_native(e)})")

module.exit_json(changed=False)


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions tests/integration/targets/route53_wait/aliases
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cloud/aws
Loading