diff --git a/plugins/module_utils/ec2.py b/plugins/module_utils/ec2.py index 4947e36b47f..ea2e21f383b 100644 --- a/plugins/module_utils/ec2.py +++ b/plugins/module_utils/ec2.py @@ -48,7 +48,6 @@ from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict # pylint: disable=unused-import from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict # pylint: disable=unused-import -from .cloud import CloudRetry # Used to live here, moved into ansible_collections.amazon.aws.plugins.module_utils.tagging from .tagging import ansible_dict_to_boto3_tag_list from .tagging import boto3_tag_list_to_ansible_dict @@ -59,6 +58,9 @@ from .policy import compare_policies # pylint: disable=unused-import from .policy import sort_json_policy_dict # pylint: disable=unused-import +# Used to live here, moved into # ansible_collections.amazon.aws.plugins.module_utils.retries +from .retries import AWSRetry # pylint: disable=unused-import + BOTO_IMP_ERR = None try: import boto @@ -82,46 +84,6 @@ class AnsibleAWSError(Exception): pass -def _botocore_exception_maybe(): - """ - Allow for boto3 not being installed when using these utils by wrapping - botocore.exceptions instead of assigning from it directly. - """ - if HAS_BOTO3: - return botocore.exceptions.ClientError - return type(None) - - -class AWSRetry(CloudRetry): - base_class = _botocore_exception_maybe() - - @staticmethod - def status_code_from_exception(error): - return error.response['Error']['Code'] - - @staticmethod - def found(response_code, catch_extra_error_codes=None): - # This list of failures is based on this API Reference - # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html - # - # TooManyRequestsException comes from inside botocore when it - # does retrys, unfortunately however it does not try long - # enough to allow some services such as API Gateway to - # complete configuration. At the moment of writing there is a - # botocore/boto3 bug open to fix this. - # - # https://github.com/boto/boto3/issues/876 (and linked PRs etc) - retry_on = [ - 'RequestLimitExceeded', 'Unavailable', 'ServiceUnavailable', - 'InternalFailure', 'InternalError', 'TooManyRequestsException', - 'Throttling' - ] - if catch_extra_error_codes: - retry_on.extend(catch_extra_error_codes) - - return response_code in retry_on - - def boto3_conn(module, conn_type=None, resource=None, region=None, endpoint=None, **params): try: return _boto3_conn(conn_type=conn_type, resource=resource, region=region, endpoint=endpoint, **params) diff --git a/plugins/module_utils/retries.py b/plugins/module_utils/retries.py new file mode 100644 index 00000000000..1bd214b6bf9 --- /dev/null +++ b/plugins/module_utils/retries.py @@ -0,0 +1,78 @@ +# This code is part of Ansible, but is an independent component. +# This particular file snippet, and this file snippet only, is BSD licensed. +# Modules you write using this snippet, which is embedded dynamically by Ansible +# still belong to the author of the module, and may assign their own license +# to the complete work. +# +# Copyright (c), Michael DeHaan , 2012-2013 +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +try: + from botocore.exceptions import ClientError + HAS_BOTO3 = True +except ImportError: + HAS_BOTO3 = False + +from .cloud import CloudRetry + + +def _botocore_exception_maybe(): + """ + Allow for boto3 not being installed when using these utils by wrapping + botocore.exceptions instead of assigning from it directly. + """ + if HAS_BOTO3: + return ClientError + return type(None) + + +class AWSRetry(CloudRetry): + base_class = _botocore_exception_maybe() + + @staticmethod + def status_code_from_exception(error): + return error.response['Error']['Code'] + + @staticmethod + def found(response_code, catch_extra_error_codes=None): + # This list of failures is based on this API Reference + # http://docs.aws.amazon.com/AWSEC2/latest/APIReference/errors-overview.html + # + # TooManyRequestsException comes from inside botocore when it + # does retrys, unfortunately however it does not try long + # enough to allow some services such as API Gateway to + # complete configuration. At the moment of writing there is a + # botocore/boto3 bug open to fix this. + # + # https://github.com/boto/boto3/issues/876 (and linked PRs etc) + retry_on = [ + 'RequestLimitExceeded', 'Unavailable', 'ServiceUnavailable', + 'InternalFailure', 'InternalError', 'TooManyRequestsException', + 'Throttling' + ] + if catch_extra_error_codes: + retry_on.extend(catch_extra_error_codes) + + return response_code in retry_on diff --git a/tests/unit/module_utils/ec2/test_aws.py b/tests/unit/module_utils/test_retries.py similarity index 97% rename from tests/unit/module_utils/ec2/test_aws.py rename to tests/unit/module_utils/test_retries.py index 91170b9e693..2655bcd0692 100644 --- a/tests/unit/module_utils/ec2/test_aws.py +++ b/tests/unit/module_utils/test_retries.py @@ -19,7 +19,7 @@ import pytest from ansible_collections.amazon.aws.tests.unit.compat import unittest -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry +from ansible_collections.amazon.aws.plugins.module_utils.retries import AWSRetry if not HAS_BOTO3: pytestmark = pytest.mark.skip("test_aws.py requires the python modules 'boto3' and 'botocore'")