From 55a1bcb5c10e42966853531f909d7c78212e655b Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 29 Jun 2021 15:37:50 +0200 Subject: [PATCH 1/3] ec2_vpc_route_table - use ensure_ec2_tags --- plugins/modules/ec2_vpc_route_table.py | 64 ++++---------------------- 1 file changed, 8 insertions(+), 56 deletions(-) diff --git a/plugins/modules/ec2_vpc_route_table.py b/plugins/modules/ec2_vpc_route_table.py index 6549f78881b..049e81056f1 100644 --- a/plugins/modules/ec2_vpc_route_table.py +++ b/plugins/modules/ec2_vpc_route_table.py @@ -238,10 +238,9 @@ 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 ansible_dict_to_boto3_filter_list -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import describe_ec2_tags +from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ensure_ec2_tags from ansible_collections.amazon.aws.plugins.module_utils.waiters import get_waiter @@ -257,14 +256,6 @@ def describe_igws_with_backoff(connection, **params): return paginator.paginate(**params).build_full_result()['InternetGateways'] -@AWSRetry.jittered_backoff() -def describe_tags_with_backoff(connection, resource_id): - filters = ansible_dict_to_boto3_filter_list({'resource-id': resource_id}) - paginator = connection.get_paginator('describe_tags') - tags = paginator.paginate(Filters=filters).build_full_result()['Tags'] - return boto3_tag_list_to_ansible_dict(tags) - - @AWSRetry.jittered_backoff() def describe_route_tables_with_backoff(connection, **params): try: @@ -349,45 +340,6 @@ def tags_match(match_tags, candidate_tags): for k, v in match_tags.items())) -def ensure_tags(connection=None, module=None, resource_id=None, tags=None, purge_tags=None, check_mode=None): - try: - cur_tags = describe_tags_with_backoff(connection, resource_id) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Unable to list tags for VPC') - - to_add, to_delete = compare_aws_tags(cur_tags, tags, purge_tags) - - if not to_add and not to_delete: - return {'changed': False, 'tags': cur_tags} - if check_mode: - if not purge_tags: - tags = cur_tags.update(tags) - return {'changed': True, 'tags': tags} - - if to_delete: - try: - connection.delete_tags( - aws_retry=True, - Resources=[resource_id], - Tags=[{'Key': k} for k in to_delete]) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't delete tags") - if to_add: - try: - connection.create_tags( - aws_retry=True, - Resources=[resource_id], - Tags=ansible_dict_to_boto3_tag_list(to_add)) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Couldn't create tags") - - try: - latest_tags = describe_tags_with_backoff(connection, resource_id) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Unable to list tags for VPC') - return {'changed': True, 'tags': latest_tags} - - def get_route_table_by_id(connection, module, route_table_id): route_table = None @@ -410,7 +362,7 @@ def get_route_table_by_tags(connection, module, vpc_id, tags): except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get route table") for table in route_tables: - this_tags = describe_tags_with_backoff(connection, table['RouteTableId']) + this_tags = describe_ec2_tags(connection, module, table['RouteTableId']) if tags_match(tags, this_tags): route_table = table count += 1 @@ -625,7 +577,7 @@ def ensure_route_table_absent(connection, module): def get_route_table_info(connection, module, route_table): result = get_route_table_by_id(connection, module, route_table['RouteTableId']) try: - result['Tags'] = describe_tags_with_backoff(connection, route_table['RouteTableId']) + result['Tags'] = describe_ec2_tags(connection, module, route_table['RouteTableId']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get tags for route table") result = camel_dict_to_snake_dict(result, ignore_list=['Tags']) @@ -711,10 +663,10 @@ def ensure_route_table_present(connection, module): changed = changed or result['changed'] if not tags_valid and tags is not None: - result = ensure_tags(connection=connection, module=module, resource_id=route_table['RouteTableId'], tags=tags, - purge_tags=purge_tags, check_mode=module.check_mode) - route_table['Tags'] = result['tags'] - changed = changed or result['changed'] + changed |= ensure_ec2_tags(connection, module, route_table['RouteTableId'], + tags=tags, purge_tags=purge_tags, + retry_codes=['InvalidRouteTableID.NotFound']) + route_table['Tags'] = describe_ec2_tags(connection, module, route_table['RouteTableId']) if subnets is not None: associated_subnets = find_subnets(connection, module, vpc_id, subnets) From fbdd232c3466e6e659ddd0e1f4910619a5c8ba20 Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 29 Jun 2021 15:48:53 +0200 Subject: [PATCH 2/3] Retry on InvalidRouteTableID.NotFound, to work around race conditions --- plugins/modules/ec2_vpc_route_table.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/modules/ec2_vpc_route_table.py b/plugins/modules/ec2_vpc_route_table.py index 049e81056f1..afc3487110a 100644 --- a/plugins/modules/ec2_vpc_route_table.py +++ b/plugins/modules/ec2_vpc_route_table.py @@ -703,7 +703,9 @@ def main(): ['state', 'present', ['vpc_id']]], supports_check_mode=True) - retry_decorator = AWSRetry.jittered_backoff(retries=10) + # The tests for RouteTable existing uses its own decorator, we can safely + # retry on InvalidRouteTableID.NotFound + retry_decorator = AWSRetry.jittered_backoff(retries=10, catch_extra_error_codes=['InvalidRouteTableID.NotFound']) connection = module.client('ec2', retry_decorator=retry_decorator) state = module.params.get('state') From 9ccd089eddaf85838ba479554a8bda2d963a910b Mon Sep 17 00:00:00 2001 From: Mark Chappell Date: Tue, 29 Jun 2021 15:54:31 +0200 Subject: [PATCH 3/3] changelog --- changelogs/fragments/616-ec2_vpc_route_table-tagging.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelogs/fragments/616-ec2_vpc_route_table-tagging.yml diff --git a/changelogs/fragments/616-ec2_vpc_route_table-tagging.yml b/changelogs/fragments/616-ec2_vpc_route_table-tagging.yml new file mode 100644 index 00000000000..44ff49b8b38 --- /dev/null +++ b/changelogs/fragments/616-ec2_vpc_route_table-tagging.yml @@ -0,0 +1,4 @@ +bugfixes: +- ec2_vpc_route_table - automatically retry when attempting to modify freshly created route tables (https://github.com/ansible-collections/community.aws/pull/616). +minor_changes: +- ec2_vpc_route_table - use shared code for tagging route tables (https://github.com/ansible-collections/community.aws/pull/616).