From c8ef07e015428a7959428c98dfd7ee8525e3434b Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Mon, 5 Mar 2018 17:22:16 -0500 Subject: [PATCH] Route custom waiter (#36922) This creates a way for us to use boto3's data-driven waiter support to use custom waiters where Boto3 hasn't implemented them yet. The only waiter implemented so far is for VPC Route Tables to check that they exist, and this replaces some custom retry code. --- lib/ansible/module_utils/aws/waiters.py | 52 +++++++++++++++++++ .../cloud/amazon/ec2_vpc_route_table.py | 11 ++-- 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 lib/ansible/module_utils/aws/waiters.py diff --git a/lib/ansible/module_utils/aws/waiters.py b/lib/ansible/module_utils/aws/waiters.py new file mode 100644 index 00000000000..c8f9157dbc2 --- /dev/null +++ b/lib/ansible/module_utils/aws/waiters.py @@ -0,0 +1,52 @@ +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +try: + import botocore.waiter as core_waiter +except ImportError: + pass # caught by HAS_BOTO3 + + +ec2_data = { + "version": 2, + "waiters": { + "RouteTableExists": { + "delay": 5, + "maxAttempts": 40, + "operation": "DescribeRouteTables", + "acceptors": [ + { + "matcher": "path", + "expected": True, + "argument": "length(RouteTables[]) > `0`", + "state": "success" + }, + { + "matcher": "error", + "expected": "InvalidRouteTableID.NotFound", + "state": "retry" + } + ] + } + } +} + + +def model_for(name): + ec2_models = core_waiter.WaiterModel(waiter_config=ec2_data) + return ec2_models.get_waiter(name) + + +waiters_by_name = { + ('EC2', 'route_table_exists'): lambda ec2: core_waiter.Waiter( + 'route_table_exists', + model_for('RouteTableExists'), + ec2.describe_route_tables) +} + + +def get_waiter(client, waiter_name): + try: + return waiters_by_name[(client.__class__.__name__, waiter_name)](client) + except KeyError: + raise NotImplementedError("Waiter {0} could not be found for client {1}. Available waiters: {2}".format( + waiter_name, type(client), ', '.join(repr(k) for k in waiters_by_name.keys()))) diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py index 0222dd579ee..29c33c7fc17 100644 --- a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py +++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py @@ -226,6 +226,7 @@ route_table: import re from time import sleep from ansible.module_utils.aws.core import AnsibleAWSModule +from ansible.module_utils.aws.waiters import get_waiter from ansible.module_utils.ec2 import ec2_argument_spec, boto3_conn, get_aws_connection_info from ansible.module_utils.ec2 import ansible_dict_to_boto3_filter_list from ansible.module_utils.ec2 import camel_dict_to_snake_dict, snake_dict_to_camel_dict @@ -649,11 +650,11 @@ def ensure_route_table_present(connection, module): try: route_table = connection.create_route_table(VpcId=vpc_id)['RouteTable'] # try to wait for route table to be present before moving on - for attempt in range(5): - if not get_route_table_by_id(connection, module, route_table['RouteTableId']): - sleep(2) - else: - break + get_waiter( + connection, 'route_table_exists' + ).wait( + RouteTableIds=[route_table['RouteTableId']], + ) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Error creating route table") else: