Skip to content

Commit

Permalink
Move AWSRetry out of module_utils.ec2 (#642)
Browse files Browse the repository at this point in the history
Move AWSRetry out of module_utils.ec2

SUMMARY
AWSRetry living in the module_utils.ec2 is primarily a quirk of the AWS utility code living solely in ec2.py at the time it was written (no other aws utility code existed at the time).  Since it isn't specific to the EC2 service split it out.
Long term I'd like to get to a state where module_utils.ec2 only includes code specific to the EC2 service.
ISSUE TYPE

Feature Pull Request

COMPONENT NAME
plugins/module_utils/ec2.py
ADDITIONAL INFORMATION

Reviewed-by: Alina Buzachis <None>
Reviewed-by: Mark Chappell <None>
  • Loading branch information
tremble authored Jan 28, 2022
1 parent 4bed875 commit eaec924
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 42 deletions.
44 changes: 3 additions & 41 deletions plugins/module_utils/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down
78 changes: 78 additions & 0 deletions plugins/module_utils/retries.py
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>, 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
Original file line number Diff line number Diff line change
Expand Up @@ -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'")
Expand Down

0 comments on commit eaec924

Please sign in to comment.