Hello community,
here is the log from the commit of package python-certbot-dns-cloudflare for
openSUSE:Factory checked in at 2020-01-03 17:38:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot-dns-cloudflare (Old)
and /work/SRC/openSUSE:Factory/.python-certbot-dns-cloudflare.new.6675
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-certbot-dns-cloudflare"
Fri Jan 3 17:38:57 2020 rev:14 rq:760650 version:1.0.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-certbot-dns-cloudflare/python-certbot-dns-cloudflare.changes
2019-11-15 00:22:24.127906286 +0100
+++
/work/SRC/openSUSE:Factory/.python-certbot-dns-cloudflare.new.6675/python-certbot-dns-cloudflare.changes
2020-01-03 17:39:10.731363255 +0100
@@ -1,0 +2,6 @@
+Fri Jan 3 11:37:04 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- update to version 1.0.0
+ * sync with main certbot package
+
+-------------------------------------------------------------------
Old:
----
certbot-dns-cloudflare-0.40.1.tar.gz
New:
----
certbot-dns-cloudflare-1.0.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-certbot-dns-cloudflare.spec ++++++
--- /var/tmp/diff_new_pack.UDJmyE/_old 2020-01-03 17:39:11.311363553 +0100
+++ /var/tmp/diff_new_pack.UDJmyE/_new 2020-01-03 17:39:11.335363566 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-certbot-dns-cloudflare
#
-# Copyright (c) 2019 SUSE LLC.
+# Copyright (c) 2020 SUSE LLC
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,24 +18,24 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-certbot-dns-cloudflare
-Version: 0.40.1
+Version: 1.0.0
Release: 0
Summary: Cloudflare Authenticator plugin for Certbot
License: Apache-2.0
URL: https://github.com/certbot/certbot
Source:
https://files.pythonhosted.org/packages/source/c/certbot-dns-cloudflare/certbot-dns-cloudflare-%{version}.tar.gz
BuildRequires: %{python_module acme >= 0.29.0}
-BuildRequires: %{python_module certbot >= 0.34.0}
-BuildRequires: %{python_module cloudflare}
+BuildRequires: %{python_module certbot >= 1.0.0}
+BuildRequires: %{python_module cloudflare >= 1.5.1}
BuildRequires: %{python_module mock}
+BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: %{python_module zope.interface}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
-Requires: python-acme >= 0.25.0
-Requires: python-certbot >= 0.34.0
-Requires: python-cloudflare
-Requires: python-dns-lexicon >= 2.2.1
+Requires: python-acme >= 0.29.0
+Requires: python-certbot >= 1.0.0
+Requires: python-cloudflare >= 1.5.1
Requires: python-zope.interface
BuildArch: noarch
%python_subpackages
++++++ certbot-dns-cloudflare-0.40.1.tar.gz ->
certbot-dns-cloudflare-1.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-0.40.1/MANIFEST.in
new/certbot-dns-cloudflare-1.0.0/MANIFEST.in
--- old/certbot-dns-cloudflare-0.40.1/MANIFEST.in 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/MANIFEST.in 2019-12-03
18:20:30.000000000 +0100
@@ -1,3 +1,6 @@
include LICENSE.txt
include README.rst
recursive-include docs *
+recursive-include tests *
+global-exclude __pycache__
+global-exclude *.py[cod]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-0.40.1/PKG-INFO
new/certbot-dns-cloudflare-1.0.0/PKG-INFO
--- old/certbot-dns-cloudflare-0.40.1/PKG-INFO 2019-11-06 03:25:01.000000000
+0100
+++ new/certbot-dns-cloudflare-1.0.0/PKG-INFO 2019-12-03 18:20:49.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-cloudflare
-Version: 0.40.1
+Version: 1.0.0
Summary: Cloudflare DNS Authenticator plugin for Certbot
Home-page: https://github.com/certbot/certbot
Author: Certbot Project
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/_internal/__init__.py
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/_internal/__init__.py
---
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/_internal/__init__.py
1970-01-01 01:00:00.000000000 +0100
+++
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/_internal/__init__.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1 @@
+"""Internal implementation of `~certbot_dns_cloudflare.dns_cloudflare`
plugin."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/_internal/dns_cloudflare.py
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
---
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/_internal/dns_cloudflare.py
1970-01-01 01:00:00.000000000 +0100
+++
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/_internal/dns_cloudflare.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1,199 @@
+"""DNS Authenticator for Cloudflare."""
+import logging
+
+import CloudFlare
+import zope.interface
+
+from certbot import errors
+from certbot import interfaces
+from certbot.plugins import dns_common
+
+logger = logging.getLogger(__name__)
+
+ACCOUNT_URL = 'https://dash.cloudflare.com/profile/api-tokens'
+
+
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
+class Authenticator(dns_common.DNSAuthenticator):
+ """DNS Authenticator for Cloudflare
+
+ This Authenticator uses the Cloudflare API to fulfill a dns-01 challenge.
+ """
+
+ description = ('Obtain certificates using a DNS TXT record (if you are
using Cloudflare for '
+ 'DNS).')
+ ttl = 120
+
+ def __init__(self, *args, **kwargs):
+ super(Authenticator, self).__init__(*args, **kwargs)
+ self.credentials = None
+
+ @classmethod
+ def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
+ super(Authenticator, cls).add_parser_arguments(add)
+ add('credentials', help='Cloudflare credentials INI file.')
+
+ def more_info(self): # pylint: disable=missing-docstring,no-self-use
+ return 'This plugin configures a DNS TXT record to respond to a dns-01
challenge using ' + \
+ 'the Cloudflare API.'
+
+ def _setup_credentials(self):
+ self.credentials = self._configure_credentials(
+ 'credentials',
+ 'Cloudflare credentials INI file',
+ {
+ 'email': 'email address associated with Cloudflare account',
+ 'api-key': 'API key for Cloudflare account, obtained from
{0}'.format(ACCOUNT_URL)
+ }
+ )
+
+ def _perform(self, domain, validation_name, validation):
+ self._get_cloudflare_client().add_txt_record(domain, validation_name,
validation, self.ttl)
+
+ def _cleanup(self, domain, validation_name, validation):
+ self._get_cloudflare_client().del_txt_record(domain, validation_name,
validation)
+
+ def _get_cloudflare_client(self):
+ return _CloudflareClient(self.credentials.conf('email'),
self.credentials.conf('api-key'))
+
+
+class _CloudflareClient(object):
+ """
+ Encapsulates all communication with the Cloudflare API.
+ """
+
+ def __init__(self, email, api_key):
+ self.cf = CloudFlare.CloudFlare(email, api_key)
+
+ def add_txt_record(self, domain, record_name, record_content, record_ttl):
+ """
+ Add a TXT record using the supplied information.
+
+ :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).
+ :param int record_ttl: The record TTL (number of seconds that the
record may be cached).
+ :raises certbot.errors.PluginError: if an error occurs communicating
with the Cloudflare API
+ """
+
+ zone_id = self._find_zone_id(domain)
+
+ data = {'type': 'TXT',
+ 'name': record_name,
+ 'content': record_content,
+ 'ttl': record_ttl}
+
+ try:
+ logger.debug('Attempting to add record to zone %s: %s', zone_id,
data)
+ self.cf.zones.dns_records.post(zone_id, data=data) # zones |
pylint: disable=no-member
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ logger.error('Encountered CloudFlareAPIError adding TXT record: %d
%s', e, e)
+ raise errors.PluginError('Error communicating with the Cloudflare
API: {0}'.format(e))
+
+ record_id = self._find_txt_record_id(zone_id, record_name,
record_content)
+ logger.debug('Successfully added TXT record with record_id: %s',
record_id)
+
+ def del_txt_record(self, domain, record_name, record_content):
+ """
+ Delete a TXT record using the supplied information.
+
+ Note that both the record's name and content are used to ensure that
similar records
+ created concurrently (e.g., due to concurrent invocations of this
plugin) are not deleted.
+
+ Failures are logged, but not raised.
+
+ :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).
+ """
+
+ try:
+ zone_id = self._find_zone_id(domain)
+ except errors.PluginError as e:
+ logger.debug('Encountered error finding zone_id during deletion:
%s', e)
+ return
+
+ if zone_id:
+ record_id = self._find_txt_record_id(zone_id, record_name,
record_content)
+ if record_id:
+ try:
+ # zones | pylint: disable=no-member
+ self.cf.zones.dns_records.delete(zone_id, record_id)
+ logger.debug('Successfully deleted TXT record.')
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ logger.warning('Encountered CloudFlareAPIError deleting
TXT record: %s', e)
+ else:
+ logger.debug('TXT record not found; no cleanup needed.')
+ else:
+ logger.debug('Zone not found; no cleanup needed.')
+
+ def _find_zone_id(self, domain):
+ """
+ Find the zone_id for a given domain.
+
+ :param str domain: The domain for which to find the zone_id.
+ :returns: The zone_id, if found.
+ :rtype: str
+ :raises certbot.errors.PluginError: if no zone_id is found.
+ """
+
+ zone_name_guesses = dns_common.base_domain_name_guesses(domain)
+
+ for zone_name in zone_name_guesses:
+ params = {'name': zone_name,
+ 'per_page': 1}
+
+ try:
+ zones = self.cf.zones.get(params=params) # zones | pylint:
disable=no-member
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ code = int(e)
+ hint = None
+
+ if code == 6003:
+ hint = 'Did you copy your entire API key?'
+ elif code == 9103:
+ hint = 'Did you enter the correct email address?'
+
+ raise errors.PluginError('Error determining zone_id: {0} {1}.
Please confirm that '
+ 'you have supplied valid Cloudflare
API credentials.{2}'
+ .format(code, e, '
({0})'.format(hint) if hint else ''))
+
+ if zones:
+ zone_id = zones[0]['id']
+ logger.debug('Found zone_id of %s for %s using name %s',
zone_id, domain, zone_name)
+ return zone_id
+
+ raise errors.PluginError('Unable to determine zone_id for {0} using
zone names: {1}. '
+ 'Please confirm that the domain name has been
entered correctly '
+ 'and is already associated with the supplied
Cloudflare account.'
+ .format(domain, zone_name_guesses))
+
+ def _find_txt_record_id(self, zone_id, record_name, record_content):
+ """
+ Find the record_id for a TXT record with the given name and content.
+
+ :param str zone_id: The zone_id which contains the record.
+ :param str record_name: The record name (typically beginning with
'_acme-challenge.').
+ :param str record_content: The record content (typically the challenge
validation).
+ :returns: The record_id, if found.
+ :rtype: str
+ """
+
+ params = {'type': 'TXT',
+ 'name': record_name,
+ 'content': record_content,
+ 'per_page': 1}
+ try:
+ # zones | pylint: disable=no-member
+ records = self.cf.zones.dns_records.get(zone_id, params=params)
+ except CloudFlare.exceptions.CloudFlareAPIError as e:
+ logger.debug('Encountered CloudFlareAPIError getting TXT
record_id: %s', e)
+ records = []
+
+ if records:
+ # Cleanup is returning the system to the state we found it. If,
for some reason,
+ # there are multiple matching records, we only delete one because
we only added one.
+ return records[0]['id']
+ logger.debug('Unable to find TXT record.')
+ return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/dns_cloudflare.py
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/dns_cloudflare.py
--- old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/dns_cloudflare.py
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/dns_cloudflare.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,199 +0,0 @@
-"""DNS Authenticator for Cloudflare."""
-import logging
-
-import CloudFlare
-import zope.interface
-
-from certbot import errors
-from certbot import interfaces
-from certbot.plugins import dns_common
-
-logger = logging.getLogger(__name__)
-
-ACCOUNT_URL = 'https://dash.cloudflare.com/profile/api-tokens'
-
-
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
-class Authenticator(dns_common.DNSAuthenticator):
- """DNS Authenticator for Cloudflare
-
- This Authenticator uses the Cloudflare API to fulfill a dns-01 challenge.
- """
-
- description = ('Obtain certificates using a DNS TXT record (if you are
using Cloudflare for '
- 'DNS).')
- ttl = 120
-
- def __init__(self, *args, **kwargs):
- super(Authenticator, self).__init__(*args, **kwargs)
- self.credentials = None
-
- @classmethod
- def add_parser_arguments(cls, add): # pylint: disable=arguments-differ
- super(Authenticator, cls).add_parser_arguments(add)
- add('credentials', help='Cloudflare credentials INI file.')
-
- def more_info(self): # pylint: disable=missing-docstring,no-self-use
- return 'This plugin configures a DNS TXT record to respond to a dns-01
challenge using ' + \
- 'the Cloudflare API.'
-
- def _setup_credentials(self):
- self.credentials = self._configure_credentials(
- 'credentials',
- 'Cloudflare credentials INI file',
- {
- 'email': 'email address associated with Cloudflare account',
- 'api-key': 'API key for Cloudflare account, obtained from
{0}'.format(ACCOUNT_URL)
- }
- )
-
- def _perform(self, domain, validation_name, validation):
- self._get_cloudflare_client().add_txt_record(domain, validation_name,
validation, self.ttl)
-
- def _cleanup(self, domain, validation_name, validation):
- self._get_cloudflare_client().del_txt_record(domain, validation_name,
validation)
-
- def _get_cloudflare_client(self):
- return _CloudflareClient(self.credentials.conf('email'),
self.credentials.conf('api-key'))
-
-
-class _CloudflareClient(object):
- """
- Encapsulates all communication with the Cloudflare API.
- """
-
- def __init__(self, email, api_key):
- self.cf = CloudFlare.CloudFlare(email, api_key)
-
- def add_txt_record(self, domain, record_name, record_content, record_ttl):
- """
- Add a TXT record using the supplied information.
-
- :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).
- :param int record_ttl: The record TTL (number of seconds that the
record may be cached).
- :raises certbot.errors.PluginError: if an error occurs communicating
with the Cloudflare API
- """
-
- zone_id = self._find_zone_id(domain)
-
- data = {'type': 'TXT',
- 'name': record_name,
- 'content': record_content,
- 'ttl': record_ttl}
-
- try:
- logger.debug('Attempting to add record to zone %s: %s', zone_id,
data)
- self.cf.zones.dns_records.post(zone_id, data=data) # zones |
pylint: disable=no-member
- except CloudFlare.exceptions.CloudFlareAPIError as e:
- logger.error('Encountered CloudFlareAPIError adding TXT record: %d
%s', e, e)
- raise errors.PluginError('Error communicating with the Cloudflare
API: {0}'.format(e))
-
- record_id = self._find_txt_record_id(zone_id, record_name,
record_content)
- logger.debug('Successfully added TXT record with record_id: %s',
record_id)
-
- def del_txt_record(self, domain, record_name, record_content):
- """
- Delete a TXT record using the supplied information.
-
- Note that both the record's name and content are used to ensure that
similar records
- created concurrently (e.g., due to concurrent invocations of this
plugin) are not deleted.
-
- Failures are logged, but not raised.
-
- :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).
- """
-
- try:
- zone_id = self._find_zone_id(domain)
- except errors.PluginError as e:
- logger.debug('Encountered error finding zone_id during deletion:
%s', e)
- return
-
- if zone_id:
- record_id = self._find_txt_record_id(zone_id, record_name,
record_content)
- if record_id:
- try:
- # zones | pylint: disable=no-member
- self.cf.zones.dns_records.delete(zone_id, record_id)
- logger.debug('Successfully deleted TXT record.')
- except CloudFlare.exceptions.CloudFlareAPIError as e:
- logger.warning('Encountered CloudFlareAPIError deleting
TXT record: %s', e)
- else:
- logger.debug('TXT record not found; no cleanup needed.')
- else:
- logger.debug('Zone not found; no cleanup needed.')
-
- def _find_zone_id(self, domain):
- """
- Find the zone_id for a given domain.
-
- :param str domain: The domain for which to find the zone_id.
- :returns: The zone_id, if found.
- :rtype: str
- :raises certbot.errors.PluginError: if no zone_id is found.
- """
-
- zone_name_guesses = dns_common.base_domain_name_guesses(domain)
-
- for zone_name in zone_name_guesses:
- params = {'name': zone_name,
- 'per_page': 1}
-
- try:
- zones = self.cf.zones.get(params=params) # zones | pylint:
disable=no-member
- except CloudFlare.exceptions.CloudFlareAPIError as e:
- code = int(e)
- hint = None
-
- if code == 6003:
- hint = 'Did you copy your entire API key?'
- elif code == 9103:
- hint = 'Did you enter the correct email address?'
-
- raise errors.PluginError('Error determining zone_id: {0} {1}.
Please confirm that '
- 'you have supplied valid Cloudflare
API credentials.{2}'
- .format(code, e, '
({0})'.format(hint) if hint else ''))
-
- if zones:
- zone_id = zones[0]['id']
- logger.debug('Found zone_id of %s for %s using name %s',
zone_id, domain, zone_name)
- return zone_id
-
- raise errors.PluginError('Unable to determine zone_id for {0} using
zone names: {1}. '
- 'Please confirm that the domain name has been
entered correctly '
- 'and is already associated with the supplied
Cloudflare account.'
- .format(domain, zone_name_guesses))
-
- def _find_txt_record_id(self, zone_id, record_name, record_content):
- """
- Find the record_id for a TXT record with the given name and content.
-
- :param str zone_id: The zone_id which contains the record.
- :param str record_name: The record name (typically beginning with
'_acme-challenge.').
- :param str record_content: The record content (typically the challenge
validation).
- :returns: The record_id, if found.
- :rtype: str
- """
-
- params = {'type': 'TXT',
- 'name': record_name,
- 'content': record_content,
- 'per_page': 1}
- try:
- # zones | pylint: disable=no-member
- records = self.cf.zones.dns_records.get(zone_id, params=params)
- except CloudFlare.exceptions.CloudFlareAPIError as e:
- logger.debug('Encountered CloudFlareAPIError getting TXT
record_id: %s', e)
- records = []
-
- if records:
- # Cleanup is returning the system to the state we found it. If,
for some reason,
- # there are multiple matching records, we only delete one because
we only added one.
- return records[0]['id']
- logger.debug('Unable to find TXT record.')
- return None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/dns_cloudflare_test.py
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/dns_cloudflare_test.py
---
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare/dns_cloudflare_test.py
2019-11-06 03:24:51.000000000 +0100
+++
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare/dns_cloudflare_test.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,174 +0,0 @@
-"""Tests for certbot_dns_cloudflare.dns_cloudflare."""
-
-import unittest
-
-import CloudFlare
-import mock
-
-from certbot import errors
-from certbot.compat import os
-from certbot.plugins import dns_test_common
-from certbot.plugins.dns_test_common import DOMAIN
-from certbot.tests import util as test_util
-
-API_ERROR = CloudFlare.exceptions.CloudFlareAPIError(1000, '', '')
-API_KEY = 'an-api-key'
-EMAIL = '[email protected]'
-
-
-class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common.BaseAuthenticatorTest):
-
- def setUp(self):
- from certbot_dns_cloudflare.dns_cloudflare import Authenticator
-
- super(AuthenticatorTest, self).setUp()
-
- path = os.path.join(self.tempdir, 'file.ini')
- dns_test_common.write({"cloudflare_email": EMAIL,
"cloudflare_api_key": API_KEY}, path)
-
- self.config = mock.MagicMock(cloudflare_credentials=path,
- cloudflare_propagation_seconds=0) #
don't wait during tests
-
- self.auth = Authenticator(self.config, "cloudflare")
-
- self.mock_client = mock.MagicMock()
- # _get_cloudflare_client | pylint: disable=protected-access
- self.auth._get_cloudflare_client =
mock.MagicMock(return_value=self.mock_client)
-
- def test_perform(self):
- self.auth.perform([self.achall])
-
- expected = [mock.call.add_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
- self.assertEqual(expected, self.mock_client.mock_calls)
-
- def test_cleanup(self):
- # _attempt_cleanup | pylint: disable=protected-access
- self.auth._attempt_cleanup = True
- self.auth.cleanup([self.achall])
-
- expected = [mock.call.del_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY)]
- self.assertEqual(expected, self.mock_client.mock_calls)
-
-
-class CloudflareClientTest(unittest.TestCase):
- record_name = "foo"
- record_content = "bar"
- record_ttl = 42
- zone_id = 1
- record_id = 2
-
- def setUp(self):
- from certbot_dns_cloudflare.dns_cloudflare import _CloudflareClient
-
- self.cloudflare_client = _CloudflareClient(EMAIL, API_KEY)
-
- self.cf = mock.MagicMock()
- self.cloudflare_client.cf = self.cf
-
- def test_add_txt_record(self):
- self.cf.zones.get.return_value = [{'id': self.zone_id}]
-
- self.cloudflare_client.add_txt_record(DOMAIN, self.record_name,
self.record_content,
- self.record_ttl)
-
- self.cf.zones.dns_records.post.assert_called_with(self.zone_id,
data=mock.ANY)
-
- post_data = self.cf.zones.dns_records.post.call_args[1]['data']
-
- self.assertEqual('TXT', post_data['type'])
- self.assertEqual(self.record_name, post_data['name'])
- self.assertEqual(self.record_content, post_data['content'])
- self.assertEqual(self.record_ttl, post_data['ttl'])
-
- def test_add_txt_record_error(self):
- self.cf.zones.get.return_value = [{'id': self.zone_id}]
-
- self.cf.zones.dns_records.post.side_effect = API_ERROR
-
- self.assertRaises(
- errors.PluginError,
- self.cloudflare_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content, self.record_ttl)
-
- def test_add_txt_record_error_during_zone_lookup(self):
- self.cf.zones.get.side_effect = API_ERROR
-
- self.assertRaises(
- errors.PluginError,
- self.cloudflare_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content, self.record_ttl)
-
- def test_add_txt_record_zone_not_found(self):
- self.cf.zones.get.return_value = []
-
- self.assertRaises(
- errors.PluginError,
- self.cloudflare_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content, self.record_ttl)
-
- def test_del_txt_record(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.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- expected = [mock.call.zones.get(params=mock.ANY),
- mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY),
- mock.call.zones.dns_records.delete(self.zone_id,
self.record_id)]
-
- self.assertEqual(expected, self.cf.mock_calls)
-
- get_data = self.cf.zones.dns_records.get.call_args[1]['params']
-
- self.assertEqual('TXT', get_data['type'])
- self.assertEqual(self.record_name, get_data['name'])
- self.assertEqual(self.record_content, get_data['content'])
-
- def test_del_txt_record_error_during_zone_lookup(self):
- self.cf.zones.get.side_effect = API_ERROR
-
- self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- def test_del_txt_record_error_during_delete(self):
- self.cf.zones.get.return_value = [{'id': self.zone_id}]
- self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}]
- self.cf.zones.dns_records.delete.side_effect = API_ERROR
-
- self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
- expected = [mock.call.zones.get(params=mock.ANY),
- mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY),
- mock.call.zones.dns_records.delete(self.zone_id,
self.record_id)]
-
- self.assertEqual(expected, self.cf.mock_calls)
-
- def test_del_txt_record_error_during_get(self):
- self.cf.zones.get.return_value = [{'id': self.zone_id}]
- self.cf.zones.dns_records.get.side_effect = API_ERROR
-
- self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
- expected = [mock.call.zones.get(params=mock.ANY),
- mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY)]
-
- self.assertEqual(expected, self.cf.mock_calls)
-
- def test_del_txt_record_no_record(self):
- self.cf.zones.get.return_value = [{'id': self.zone_id}]
- self.cf.zones.dns_records.get.return_value = []
-
- self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
- expected = [mock.call.zones.get(params=mock.ANY),
- mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY)]
-
- self.assertEqual(expected, self.cf.mock_calls)
-
- def test_del_txt_record_no_zone(self):
- self.cf.zones.get.return_value = [{'id': None}]
-
- self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
- expected = [mock.call.zones.get(params=mock.ANY)]
-
- self.assertEqual(expected, self.cf.mock_calls)
-
-
-if __name__ == "__main__":
- unittest.main() # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/PKG-INFO
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/PKG-INFO
--- old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/PKG-INFO
2019-11-06 03:25:01.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/PKG-INFO
2019-12-03 18:20:49.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-cloudflare
-Version: 0.40.1
+Version: 1.0.0
Summary: Cloudflare DNS Authenticator plugin for Certbot
Home-page: https://github.com/certbot/certbot
Author: Certbot Project
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/SOURCES.txt
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/SOURCES.txt
---
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/SOURCES.txt
2019-11-06 03:25:01.000000000 +0100
+++
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/SOURCES.txt
2019-12-03 18:20:49.000000000 +0100
@@ -4,18 +4,18 @@
setup.cfg
setup.py
certbot_dns_cloudflare/__init__.py
-certbot_dns_cloudflare/dns_cloudflare.py
-certbot_dns_cloudflare/dns_cloudflare_test.py
certbot_dns_cloudflare.egg-info/PKG-INFO
certbot_dns_cloudflare.egg-info/SOURCES.txt
certbot_dns_cloudflare.egg-info/dependency_links.txt
certbot_dns_cloudflare.egg-info/entry_points.txt
certbot_dns_cloudflare.egg-info/requires.txt
certbot_dns_cloudflare.egg-info/top_level.txt
+certbot_dns_cloudflare/_internal/__init__.py
+certbot_dns_cloudflare/_internal/dns_cloudflare.py
docs/.gitignore
docs/Makefile
docs/api.rst
docs/conf.py
docs/index.rst
docs/make.bat
-docs/api/dns_cloudflare.rst
\ No newline at end of file
+tests/dns_cloudflare_test.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/entry_points.txt
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/entry_points.txt
---
old/certbot-dns-cloudflare-0.40.1/certbot_dns_cloudflare.egg-info/entry_points.txt
2019-11-06 03:25:01.000000000 +0100
+++
new/certbot-dns-cloudflare-1.0.0/certbot_dns_cloudflare.egg-info/entry_points.txt
2019-12-03 18:20:49.000000000 +0100
@@ -1,3 +1,3 @@
[certbot.plugins]
-dns-cloudflare = certbot_dns_cloudflare.dns_cloudflare:Authenticator
+dns-cloudflare = certbot_dns_cloudflare._internal.dns_cloudflare:Authenticator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/docs/api/dns_cloudflare.rst
new/certbot-dns-cloudflare-1.0.0/docs/api/dns_cloudflare.rst
--- old/certbot-dns-cloudflare-0.40.1/docs/api/dns_cloudflare.rst
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/docs/api/dns_cloudflare.rst
1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-:mod:`certbot_dns_cloudflare.dns_cloudflare`
---------------------------------------------
-
-.. automodule:: certbot_dns_cloudflare.dns_cloudflare
- :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-0.40.1/docs/api.rst
new/certbot-dns-cloudflare-1.0.0/docs/api.rst
--- old/certbot-dns-cloudflare-0.40.1/docs/api.rst 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/docs/api.rst 2019-12-03
18:20:30.000000000 +0100
@@ -2,7 +2,4 @@
API Documentation
=================
-.. toctree::
- :glob:
-
- api/**
+Certbot plugins implement the Certbot plugins API, and do not otherwise have
an external API.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-cloudflare-0.40.1/setup.py
new/certbot-dns-cloudflare-1.0.0/setup.py
--- old/certbot-dns-cloudflare-0.40.1/setup.py 2019-11-06 03:24:52.000000000
+0100
+++ new/certbot-dns-cloudflare-1.0.0/setup.py 2019-12-03 18:20:32.000000000
+0100
@@ -1,8 +1,10 @@
from setuptools import setup
from setuptools import find_packages
+from setuptools.command.test import test as TestCommand
+import sys
-version = '0.40.1'
+version = '1.0.0'
# Remember to update local-oldest-requirements.txt when changing the minimum
# acme/certbot version.
@@ -20,6 +22,20 @@
'sphinx_rtd_theme',
]
+class PyTest(TestCommand):
+ user_options = []
+
+ def initialize_options(self):
+ TestCommand.initialize_options(self)
+ self.pytest_args = ''
+
+ def run_tests(self):
+ import shlex
+ # import here, cause outside the eggs aren't loaded
+ import pytest
+ errno = pytest.main(shlex.split(self.pytest_args))
+ sys.exit(errno)
+
setup(
name='certbot-dns-cloudflare',
version=version,
@@ -60,8 +76,10 @@
},
entry_points={
'certbot.plugins': [
- 'dns-cloudflare =
certbot_dns_cloudflare.dns_cloudflare:Authenticator',
+ 'dns-cloudflare =
certbot_dns_cloudflare._internal.dns_cloudflare:Authenticator',
],
},
+ tests_require=["pytest"],
test_suite='certbot_dns_cloudflare',
+ cmdclass={"test": PyTest},
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-cloudflare-0.40.1/tests/dns_cloudflare_test.py
new/certbot-dns-cloudflare-1.0.0/tests/dns_cloudflare_test.py
--- old/certbot-dns-cloudflare-0.40.1/tests/dns_cloudflare_test.py
1970-01-01 01:00:00.000000000 +0100
+++ new/certbot-dns-cloudflare-1.0.0/tests/dns_cloudflare_test.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1,174 @@
+"""Tests for certbot_dns_cloudflare._internal.dns_cloudflare."""
+
+import unittest
+
+import CloudFlare
+import mock
+
+from certbot import errors
+from certbot.compat import os
+from certbot.plugins import dns_test_common
+from certbot.plugins.dns_test_common import DOMAIN
+from certbot.tests import util as test_util
+
+API_ERROR = CloudFlare.exceptions.CloudFlareAPIError(1000, '', '')
+API_KEY = 'an-api-key'
+EMAIL = '[email protected]'
+
+
+class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common.BaseAuthenticatorTest):
+
+ def setUp(self):
+ from certbot_dns_cloudflare._internal.dns_cloudflare import
Authenticator
+
+ super(AuthenticatorTest, self).setUp()
+
+ path = os.path.join(self.tempdir, 'file.ini')
+ dns_test_common.write({"cloudflare_email": EMAIL,
"cloudflare_api_key": API_KEY}, path)
+
+ self.config = mock.MagicMock(cloudflare_credentials=path,
+ cloudflare_propagation_seconds=0) #
don't wait during tests
+
+ self.auth = Authenticator(self.config, "cloudflare")
+
+ self.mock_client = mock.MagicMock()
+ # _get_cloudflare_client | pylint: disable=protected-access
+ self.auth._get_cloudflare_client =
mock.MagicMock(return_value=self.mock_client)
+
+ def test_perform(self):
+ self.auth.perform([self.achall])
+
+ expected = [mock.call.add_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY, mock.ANY)]
+ self.assertEqual(expected, self.mock_client.mock_calls)
+
+ def test_cleanup(self):
+ # _attempt_cleanup | pylint: disable=protected-access
+ self.auth._attempt_cleanup = True
+ self.auth.cleanup([self.achall])
+
+ expected = [mock.call.del_txt_record(DOMAIN,
'_acme-challenge.'+DOMAIN, mock.ANY)]
+ self.assertEqual(expected, self.mock_client.mock_calls)
+
+
+class CloudflareClientTest(unittest.TestCase):
+ record_name = "foo"
+ record_content = "bar"
+ record_ttl = 42
+ zone_id = 1
+ record_id = 2
+
+ def setUp(self):
+ from certbot_dns_cloudflare._internal.dns_cloudflare import
_CloudflareClient
+
+ self.cloudflare_client = _CloudflareClient(EMAIL, API_KEY)
+
+ self.cf = mock.MagicMock()
+ self.cloudflare_client.cf = self.cf
+
+ def test_add_txt_record(self):
+ self.cf.zones.get.return_value = [{'id': self.zone_id}]
+
+ self.cloudflare_client.add_txt_record(DOMAIN, self.record_name,
self.record_content,
+ self.record_ttl)
+
+ self.cf.zones.dns_records.post.assert_called_with(self.zone_id,
data=mock.ANY)
+
+ post_data = self.cf.zones.dns_records.post.call_args[1]['data']
+
+ self.assertEqual('TXT', post_data['type'])
+ self.assertEqual(self.record_name, post_data['name'])
+ self.assertEqual(self.record_content, post_data['content'])
+ self.assertEqual(self.record_ttl, post_data['ttl'])
+
+ def test_add_txt_record_error(self):
+ self.cf.zones.get.return_value = [{'id': self.zone_id}]
+
+ self.cf.zones.dns_records.post.side_effect = API_ERROR
+
+ self.assertRaises(
+ errors.PluginError,
+ self.cloudflare_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content, self.record_ttl)
+
+ def test_add_txt_record_error_during_zone_lookup(self):
+ self.cf.zones.get.side_effect = API_ERROR
+
+ self.assertRaises(
+ errors.PluginError,
+ self.cloudflare_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content, self.record_ttl)
+
+ def test_add_txt_record_zone_not_found(self):
+ self.cf.zones.get.return_value = []
+
+ self.assertRaises(
+ errors.PluginError,
+ self.cloudflare_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content, self.record_ttl)
+
+ def test_del_txt_record(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.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ expected = [mock.call.zones.get(params=mock.ANY),
+ mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY),
+ mock.call.zones.dns_records.delete(self.zone_id,
self.record_id)]
+
+ self.assertEqual(expected, self.cf.mock_calls)
+
+ get_data = self.cf.zones.dns_records.get.call_args[1]['params']
+
+ self.assertEqual('TXT', get_data['type'])
+ self.assertEqual(self.record_name, get_data['name'])
+ self.assertEqual(self.record_content, get_data['content'])
+
+ def test_del_txt_record_error_during_zone_lookup(self):
+ self.cf.zones.get.side_effect = API_ERROR
+
+ self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ def test_del_txt_record_error_during_delete(self):
+ self.cf.zones.get.return_value = [{'id': self.zone_id}]
+ self.cf.zones.dns_records.get.return_value = [{'id': self.record_id}]
+ self.cf.zones.dns_records.delete.side_effect = API_ERROR
+
+ self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+ expected = [mock.call.zones.get(params=mock.ANY),
+ mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY),
+ mock.call.zones.dns_records.delete(self.zone_id,
self.record_id)]
+
+ self.assertEqual(expected, self.cf.mock_calls)
+
+ def test_del_txt_record_error_during_get(self):
+ self.cf.zones.get.return_value = [{'id': self.zone_id}]
+ self.cf.zones.dns_records.get.side_effect = API_ERROR
+
+ self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+ expected = [mock.call.zones.get(params=mock.ANY),
+ mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY)]
+
+ self.assertEqual(expected, self.cf.mock_calls)
+
+ def test_del_txt_record_no_record(self):
+ self.cf.zones.get.return_value = [{'id': self.zone_id}]
+ self.cf.zones.dns_records.get.return_value = []
+
+ self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+ expected = [mock.call.zones.get(params=mock.ANY),
+ mock.call.zones.dns_records.get(self.zone_id,
params=mock.ANY)]
+
+ self.assertEqual(expected, self.cf.mock_calls)
+
+ def test_del_txt_record_no_zone(self):
+ self.cf.zones.get.return_value = [{'id': None}]
+
+ self.cloudflare_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+ expected = [mock.call.zones.get(params=mock.ANY)]
+
+ self.assertEqual(expected, self.cf.mock_calls)
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover