Support 429 Too Many Requests for acme module_utils. (#508)

pull/514/head
Felix Fontein 2022-09-19 20:10:03 +02:00 committed by GitHub
parent 1dcc135da5
commit a0d862e1f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 5 deletions

View File

@ -0,0 +1,2 @@
minor_changes:
- "acme* modules - support the HTTP 429 Too Many Requests response status (https://github.com/ansible-collections/community.crypto/pull/508)."

View File

@ -13,6 +13,7 @@ import copy
import datetime import datetime
import json import json
import locale import locale
import time
import traceback import traceback
from ansible.module_utils.basic import missing_required_lib from ansible.module_utils.basic import missing_required_lib
@ -51,6 +52,23 @@ else:
IPADDRESS_IMPORT_ERROR = None IPADDRESS_IMPORT_ERROR = None
def _decode_retry(module, response, info, retry_count):
if info['status'] != 429:
return False
if retry_count >= 5:
raise ACMEProtocolException(module, msg='Giving up after 5 retries', info=info, response=response)
try:
retry_after = min(max(1, int(info.get('retry-after'))), 60)
except (TypeError, ValueError) as dummy:
retry_after = 10
module.log('Retrieved a 429 Too Many Requests on %s, retrying in %s seconds' % (info['url'], retry_after))
time.sleep(retry_after)
return True
def _assert_fetch_url_success(module, response, info, allow_redirect=False, allow_client_error=True, allow_server_error=True): def _assert_fetch_url_success(module, response, info, allow_redirect=False, allow_client_error=True, allow_server_error=True):
if info['status'] < 0: if info['status'] < 0:
raise NetworkException(msg="Failure downloading %s, %s" % (info['url'], info['msg'])) raise NetworkException(msg="Failure downloading %s, %s" % (info['url'], info['msg']))
@ -107,7 +125,12 @@ class ACMEDirectory(object):
url = self.directory_root if self.version == 1 else self.directory['newNonce'] url = self.directory_root if self.version == 1 else self.directory['newNonce']
if resource is not None: if resource is not None:
url = resource url = resource
dummy, info = fetch_url(self.module, url, method='HEAD', timeout=self.request_timeout) retry_count = 0
while True:
response, info = fetch_url(self.module, url, method='HEAD', timeout=self.request_timeout)
if _decode_retry(self.module, response, info, retry_count):
retry_count += 1
continue
if info['status'] not in (200, 204): if info['status'] not in (200, 204):
raise NetworkException("Failed to get replay-nonce, got status {0}".format(info['status'])) raise NetworkException("Failed to get replay-nonce, got status {0}".format(info['status']))
return info['replay-nonce'] return info['replay-nonce']
@ -242,6 +265,9 @@ class ACMEClient(object):
'Content-Type': 'application/jose+json', 'Content-Type': 'application/jose+json',
} }
resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST', timeout=self.request_timeout) resp, info = fetch_url(self.module, url, data=data, headers=headers, method='POST', timeout=self.request_timeout)
if _decode_retry(self.module, resp, info, failed_tries):
failed_tries += 1
continue
_assert_fetch_url_success(self.module, resp, info) _assert_fetch_url_success(self.module, resp, info)
result = {} result = {}
@ -300,7 +326,12 @@ class ACMEClient(object):
if get_only: if get_only:
# Perform unauthenticated GET # Perform unauthenticated GET
retry_count = 0
while True:
resp, info = fetch_url(self.module, uri, method='GET', headers=headers, timeout=self.request_timeout) resp, info = fetch_url(self.module, uri, method='GET', headers=headers, timeout=self.request_timeout)
if not _decode_retry(self.module, resp, info, retry_count):
break
retry_count += 1
_assert_fetch_url_success(self.module, resp, info) _assert_fetch_url_success(self.module, resp, info)