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

Move RetryingBotoClientWrapper into module_utils.retries #1230

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
4 changes: 4 additions & 0 deletions changelogs/fragments/1230-move-RetryingBotoClientWrapper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
minor_changes:
- "module_utils - move RetryingBotoClientWrapper into module_utils.retries for reuse with other plugin types (https://github.com/ansible-collections/amazon.aws/pull/1230)."
bugfixes:
- "module_utils - fixes ``TypeError: deciding_wrapper() got multiple values for argument 'aws_retry'`` when passing positional arguments to functions wrapped by AnsibleAWSModule.client (https://github.com/ansible-collections/amazon.aws/pull/1230)."
38 changes: 2 additions & 36 deletions plugins/module_utils/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from functools import wraps
import logging
import os
import re
Expand All @@ -77,7 +76,7 @@
from .botocore import get_aws_connection_info
from .botocore import get_aws_region
from .botocore import gather_sdk_versions

from .retries import RetryingBotoClientWrapper
from .version import LooseVersion

# Currently only AnsibleAWSModule. However we have a lot of Copy and Paste code
Expand Down Expand Up @@ -215,7 +214,7 @@ def client(self, service, retry_decorator=None):
region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
conn = boto3_conn(self, conn_type='client', resource=service,
region=region, endpoint=endpoint_url, **aws_connect_kwargs)
return conn if retry_decorator is None else _RetryingBotoClientWrapper(conn, retry_decorator)
return conn if retry_decorator is None else RetryingBotoClientWrapper(conn, retry_decorator)

def resource(self, service):
region, endpoint_url, aws_connect_kwargs = get_aws_connection_info(self, boto3=True)
Expand Down Expand Up @@ -335,39 +334,6 @@ def botocore_at_least(self, desired):
return LooseVersion(existing['botocore_version']) >= LooseVersion(desired)


class _RetryingBotoClientWrapper(object):
__never_wait = (
'get_paginator', 'can_paginate',
'get_waiter', 'generate_presigned_url',
)

def __init__(self, client, retry):
self.client = client
self.retry = retry

def _create_optional_retry_wrapper_function(self, unwrapped):
retrying_wrapper = self.retry(unwrapped)

@wraps(unwrapped)
def deciding_wrapper(aws_retry=False, *args, **kwargs):
if aws_retry:
return retrying_wrapper(*args, **kwargs)
else:
return unwrapped(*args, **kwargs)
return deciding_wrapper

def __getattr__(self, name):
unwrapped = getattr(self.client, name)
if name in self.__never_wait:
return unwrapped
elif callable(unwrapped):
wrapped = self._create_optional_retry_wrapper_function(unwrapped)
setattr(self, name, wrapped)
return wrapped
else:
return unwrapped


def _aws_common_argument_spec():
"""
This does not include 'region' as some AWS APIs don't require a
Expand Down
35 changes: 35 additions & 0 deletions plugins/module_utils/retries.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

from functools import wraps

try:
from botocore.exceptions import ClientError
HAS_BOTO3 = True
Expand Down Expand Up @@ -76,3 +78,36 @@ def found(response_code, catch_extra_error_codes=None):
retry_on.extend(catch_extra_error_codes)

return response_code in retry_on


class RetryingBotoClientWrapper(object):
__never_wait = (
'get_paginator', 'can_paginate',
'get_waiter', 'generate_presigned_url',
)

def __init__(self, client, retry):
self.client = client
self.retry = retry

def _create_optional_retry_wrapper_function(self, unwrapped):
retrying_wrapper = self.retry(unwrapped)

@wraps(unwrapped)
def deciding_wrapper(*args, aws_retry=False, **kwargs):
if aws_retry:
return retrying_wrapper(*args, **kwargs)
else:
return unwrapped(*args, **kwargs)
return deciding_wrapper

def __getattr__(self, name):
unwrapped = getattr(self.client, name)
if name in self.__never_wait:
return unwrapped
elif callable(unwrapped):
wrapped = self._create_optional_retry_wrapper_function(unwrapped)
setattr(self, name, wrapped)
return wrapped
else:
return unwrapped
4 changes: 2 additions & 2 deletions plugins/module_utils/waiters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
except ImportError:
pass # caught by HAS_BOTO3

from ansible_collections.amazon.aws.plugins.module_utils.modules import _RetryingBotoClientWrapper
from ansible_collections.amazon.aws.plugins.module_utils.retries import RetryingBotoClientWrapper


ec2_data = {
Expand Down Expand Up @@ -1256,7 +1256,7 @@ def route53_model(name):


def get_waiter(client, waiter_name):
if isinstance(client, _RetryingBotoClientWrapper):
if isinstance(client, RetryingBotoClientWrapper):
return get_waiter(client.client, waiter_name)
try:
return waiters_by_name[(client.__class__.__name__, waiter_name)](client)
Expand Down
21 changes: 21 additions & 0 deletions tests/unit/module_utils/retries/test_botocore_exception_maybe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# (c) 2022 Red Hat Inc.
#
# This file is part of Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

try:
import botocore
except ImportError:
pass

import ansible_collections.amazon.aws.plugins.module_utils.retries as util_retries


def test_botocore_exception_maybe(monkeypatch):
none_type = type(None)
assert util_retries._botocore_exception_maybe() is botocore.exceptions.ClientError
monkeypatch.setattr(util_retries, 'HAS_BOTO3', False)
assert util_retries._botocore_exception_maybe() is none_type
Loading