mirror of
https://github.com/certbot/certbot.git
synced 2026-07-16 13:13:18 -04:00
Merge e49ab564a2 into f9ed58e616
This commit is contained in:
commit
13bf141ea5
2 changed files with 45 additions and 0 deletions
|
|
@ -111,6 +111,10 @@ class _CloudflareClient:
|
|||
"""
|
||||
Add a TXT record using the supplied information.
|
||||
|
||||
If a TXT record with the same name and content already exists (e.g. from a
|
||||
previous failed attempt or a reused authorization), the existing record is
|
||||
left in place and this method returns successfully.
|
||||
|
||||
:param str domain: The domain to use to look up the Cloudflare zone.
|
||||
:param str record_name: The record name (typically beginning with '_acme-challenge.').
|
||||
:param str record_content: The record content (typically the challenge validation).
|
||||
|
|
@ -120,6 +124,14 @@ class _CloudflareClient:
|
|||
|
||||
zone_id = self._find_zone_id(domain)
|
||||
|
||||
# Check if the record already exists (e.g. from a prior unclean termination
|
||||
# or the ACME server reusing a pending authorization).
|
||||
existing_record_id = self._find_txt_record_id(zone_id, record_name, record_content)
|
||||
if existing_record_id:
|
||||
logger.debug('TXT record already exists with record_id: %s; no action needed.',
|
||||
existing_record_id)
|
||||
return
|
||||
|
||||
data = {'type': 'TXT',
|
||||
'name': record_name,
|
||||
'content': record_content,
|
||||
|
|
@ -130,6 +142,15 @@ class _CloudflareClient:
|
|||
self.cf.zones.dns_records.post(zone_id, data=data) # zones | pylint: disable=no-member
|
||||
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||
code = int(e)
|
||||
|
||||
# Error 81057 means "Record already exists" — this can happen due to a
|
||||
# race condition between the pre-check above and a concurrent create
|
||||
# (e.g. parallel certbot invocations for the same domain).
|
||||
if code == 81057:
|
||||
logger.debug('TXT record was created concurrently (error 81057); '
|
||||
'treating as success.')
|
||||
return
|
||||
|
||||
hint = None
|
||||
|
||||
if code == 1009:
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ class CloudflareClientTest(unittest.TestCase):
|
|||
|
||||
def test_add_txt_record(self):
|
||||
self.cf.zones.get.return_value = [{'id': self.zone_id}]
|
||||
self.cf.zones.dns_records.get.return_value = []
|
||||
|
||||
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content,
|
||||
self.record_ttl)
|
||||
|
|
@ -125,8 +126,31 @@ class CloudflareClientTest(unittest.TestCase):
|
|||
assert self.record_content == post_data['content']
|
||||
assert self.record_ttl == post_data['ttl']
|
||||
|
||||
def test_add_txt_record_already_exists(self):
|
||||
self.cf.zones.get.return_value = [{'id': self.zone_id}]
|
||||
self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}]
|
||||
|
||||
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content,
|
||||
self.record_ttl)
|
||||
|
||||
# Should not attempt to create since it already exists
|
||||
self.cf.zones.dns_records.post.assert_not_called()
|
||||
|
||||
def test_add_txt_record_duplicate_race_condition(self):
|
||||
self.cf.zones.get.return_value = [{'id': self.zone_id}]
|
||||
# Pre-check finds nothing (record doesn't exist yet)
|
||||
self.cf.zones.dns_records.get.return_value = []
|
||||
# But create fails because another process created it concurrently
|
||||
self.cf.zones.dns_records.post.side_effect = \
|
||||
CloudFlare.exceptions.CloudFlareAPIError(81057, '', '')
|
||||
|
||||
# Should not raise — treat duplicate as success
|
||||
self.cloudflare_client.add_txt_record(DOMAIN, self.record_name, self.record_content,
|
||||
self.record_ttl)
|
||||
|
||||
def test_add_txt_record_error(self):
|
||||
self.cf.zones.get.return_value = [{'id': self.zone_id}]
|
||||
self.cf.zones.dns_records.get.return_value = []
|
||||
|
||||
self.cf.zones.dns_records.post.side_effect = CloudFlare.exceptions.CloudFlareAPIError(1009, '', '')
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue