Hello community,
here is the log from the commit of package python-certbot-dns-digitalocean for
openSUSE:Factory checked in at 2020-01-03 17:39:01
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot-dns-digitalocean (Old)
and /work/SRC/openSUSE:Factory/.python-certbot-dns-digitalocean.new.6675
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-certbot-dns-digitalocean"
Fri Jan 3 17:39:01 2020 rev:14 rq:760652 version:1.0.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-certbot-dns-digitalocean/python-certbot-dns-digitalocean.changes
2019-11-15 00:23:52.087875724 +0100
+++
/work/SRC/openSUSE:Factory/.python-certbot-dns-digitalocean.new.6675/python-certbot-dns-digitalocean.changes
2020-01-03 17:39:18.251367116 +0100
@@ -1,0 +2,6 @@
+Fri Jan 3 11:48:56 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- update to version 1.0.0
+ * sync with main certbot package
+
+-------------------------------------------------------------------
Old:
----
certbot-dns-digitalocean-0.40.1.tar.gz
New:
----
certbot-dns-digitalocean-1.0.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-certbot-dns-digitalocean.spec ++++++
--- /var/tmp/diff_new_pack.A9utVB/_old 2020-01-03 17:39:19.275367641 +0100
+++ /var/tmp/diff_new_pack.A9utVB/_new 2020-01-03 17:39:19.275367641 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-certbot-dns-digitalocean
#
-# 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,23 +18,22 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-certbot-dns-digitalocean
-Version: 0.40.1
+Version: 1.0.0
Release: 0
Summary: DigitalOcean Authenticator plugin for Certbot
License: Apache-2.0
URL: https://github.com/certbot/certbot
Source:
https://files.pythonhosted.org/packages/source/c/certbot-dns-digitalocean/certbot-dns-digitalocean-%{version}.tar.gz
-BuildRequires: %{python_module certbot >= 0.34.0}
-BuildRequires: %{python_module digitalocean}
-BuildRequires: %{python_module dns-lexicon >= 2.2.1}
+BuildRequires: %{python_module certbot >= 1.0.0}
+BuildRequires: %{python_module digitalocean >= 1.11}
BuildRequires: %{python_module mock}
+BuildRequires: %{python_module pytest}
BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
Requires: python-acme >= 0.29.0
-Requires: python-certbot >= 0.34.0
-Requires: python-digitalocean
-Requires: python-dns-lexicon >= 2.2.1
+Requires: python-certbot >= 1.0.0
+Requires: python-digitalocean >= 1.11
Requires: python-six
Requires: python-zope.interface
BuildArch: noarch
++++++ certbot-dns-digitalocean-0.40.1.tar.gz ->
certbot-dns-digitalocean-1.0.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-digitalocean-0.40.1/MANIFEST.in
new/certbot-dns-digitalocean-1.0.0/MANIFEST.in
--- old/certbot-dns-digitalocean-0.40.1/MANIFEST.in 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-digitalocean-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-digitalocean-0.40.1/PKG-INFO
new/certbot-dns-digitalocean-1.0.0/PKG-INFO
--- old/certbot-dns-digitalocean-0.40.1/PKG-INFO 2019-11-06
03:25:05.000000000 +0100
+++ new/certbot-dns-digitalocean-1.0.0/PKG-INFO 2019-12-03 18:20:54.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-digitalocean
-Version: 0.40.1
+Version: 1.0.0
Summary: DigitalOcean 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-digitalocean-0.40.1/certbot_dns_digitalocean/_internal/__init__.py
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/_internal/__init__.py
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/_internal/__init__.py
1970-01-01 01:00:00.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/_internal/__init__.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1 @@
+"""Internal implementation of `~certbot_dns_digitalocean.dns_digitalocean`
plugin."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/_internal/dns_digitalocean.py
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/_internal/dns_digitalocean.py
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/_internal/dns_digitalocean.py
1970-01-01 01:00:00.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/_internal/dns_digitalocean.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1,168 @@
+"""DNS Authenticator for DigitalOcean."""
+import logging
+
+import digitalocean
+import zope.interface
+
+from certbot import errors
+from certbot import interfaces
+from certbot.plugins import dns_common
+
+logger = logging.getLogger(__name__)
+
+
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
+class Authenticator(dns_common.DNSAuthenticator):
+ """DNS Authenticator for DigitalOcean
+
+ This Authenticator uses the DigitalOcean API to fulfill a dns-01 challenge.
+ """
+
+ description = 'Obtain certs using a DNS TXT record (if you are using
DigitalOcean for DNS).'
+
+ 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='DigitalOcean 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 DigitalOcean API.'
+
+ def _setup_credentials(self):
+ self.credentials = self._configure_credentials(
+ 'credentials',
+ 'DigitalOcean credentials INI file',
+ {
+ 'token': 'API token for DigitalOcean account'
+ }
+ )
+
+ def _perform(self, domain, validation_name, validation):
+ self._get_digitalocean_client().add_txt_record(domain,
validation_name, validation)
+
+ def _cleanup(self, domain, validation_name, validation):
+ self._get_digitalocean_client().del_txt_record(domain,
validation_name, validation)
+
+ def _get_digitalocean_client(self):
+ return _DigitalOceanClient(self.credentials.conf('token'))
+
+
+class _DigitalOceanClient(object):
+ """
+ Encapsulates all communication with the DigitalOcean API.
+ """
+
+ def __init__(self, token):
+ self.manager = digitalocean.Manager(token=token)
+
+ def add_txt_record(self, domain_name, record_name, record_content):
+ """
+ Add a TXT record using the supplied information.
+
+ :param str domain_name: The domain to use to associate the record with.
+ :param str record_name: The record name (typically beginning with
'_acme-challenge.').
+ :param str record_content: The record content (typically the challenge
validation).
+ :raises certbot.errors.PluginError: if an error occurs communicating
with the DigitalOcean
+ API
+ """
+
+ try:
+ domain = self._find_domain(domain_name)
+ except digitalocean.Error as e:
+ hint = None
+
+ if str(e).startswith("Unable to authenticate"):
+ hint = 'Did you provide a valid API token?'
+
+ logger.debug('Error finding domain using the DigitalOcean API:
%s', e)
+ raise errors.PluginError('Error finding domain using the
DigitalOcean API: {0}{1}'
+ .format(e, ' ({0})'.format(hint) if hint
else ''))
+
+ try:
+ result = domain.create_new_domain_record(
+ type='TXT',
+ name=self._compute_record_name(domain, record_name),
+ data=record_content)
+
+ record_id = result['domain_record']['id']
+
+ logger.debug('Successfully added TXT record with id: %d',
record_id)
+ except digitalocean.Error as e:
+ logger.debug('Error adding TXT record using the DigitalOcean API:
%s', e)
+ raise errors.PluginError('Error adding TXT record using the
DigitalOcean API: {0}'
+ .format(e))
+
+ def del_txt_record(self, domain_name, 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_name: The domain to use to associate the record with.
+ :param str record_name: The record name (typically beginning with
'_acme-challenge.').
+ :param str record_content: The record content (typically the challenge
validation).
+ """
+
+ try:
+ domain = self._find_domain(domain_name)
+ except digitalocean.Error as e:
+ logger.debug('Error finding domain using the DigitalOcean API:
%s', e)
+ return
+
+ try:
+ domain_records = domain.get_records()
+
+ matching_records = [record for record in domain_records
+ if record.type == 'TXT'
+ and record.name ==
self._compute_record_name(domain, record_name)
+ and record.data == record_content]
+ except digitalocean.Error as e:
+ logger.debug('Error getting DNS records using the DigitalOcean
API: %s', e)
+ return
+
+ for record in matching_records:
+ try:
+ logger.debug('Removing TXT record with id: %s', record.id)
+ record.destroy()
+ except digitalocean.Error as e:
+ logger.warning('Error deleting TXT record %s using the
DigitalOcean API: %s',
+ record.id, e)
+
+ def _find_domain(self, domain_name):
+ """
+ Find the domain object for a given domain name.
+
+ :param str domain_name: The domain name for which to find the
corresponding Domain.
+ :returns: The Domain, if found.
+ :rtype: `~digitalocean.Domain`
+ :raises certbot.errors.PluginError: if no matching Domain is found.
+ """
+
+ domain_name_guesses = dns_common.base_domain_name_guesses(domain_name)
+
+ domains = self.manager.get_all_domains()
+
+ for guess in domain_name_guesses:
+ matches = [domain for domain in domains if domain.name == guess]
+
+ if matches:
+ domain = matches[0]
+ logger.debug('Found base domain for %s using name %s',
domain_name, guess)
+ return domain
+
+ raise errors.PluginError('Unable to determine base domain for {0}
using names: {1}.'
+ .format(domain_name, domain_name_guesses))
+
+ @staticmethod
+ def _compute_record_name(domain, full_record_name):
+ # The domain, from DigitalOcean's point of view, is automatically
appended.
+ return full_record_name.rpartition("." + domain.name)[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/dns_digitalocean.py
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/dns_digitalocean.py
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/dns_digitalocean.py
2019-11-06 03:24:51.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/dns_digitalocean.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,168 +0,0 @@
-"""DNS Authenticator for DigitalOcean."""
-import logging
-
-import digitalocean
-import zope.interface
-
-from certbot import errors
-from certbot import interfaces
-from certbot.plugins import dns_common
-
-logger = logging.getLogger(__name__)
-
-
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
-class Authenticator(dns_common.DNSAuthenticator):
- """DNS Authenticator for DigitalOcean
-
- This Authenticator uses the DigitalOcean API to fulfill a dns-01 challenge.
- """
-
- description = 'Obtain certs using a DNS TXT record (if you are using
DigitalOcean for DNS).'
-
- 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='DigitalOcean 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 DigitalOcean API.'
-
- def _setup_credentials(self):
- self.credentials = self._configure_credentials(
- 'credentials',
- 'DigitalOcean credentials INI file',
- {
- 'token': 'API token for DigitalOcean account'
- }
- )
-
- def _perform(self, domain, validation_name, validation):
- self._get_digitalocean_client().add_txt_record(domain,
validation_name, validation)
-
- def _cleanup(self, domain, validation_name, validation):
- self._get_digitalocean_client().del_txt_record(domain,
validation_name, validation)
-
- def _get_digitalocean_client(self):
- return _DigitalOceanClient(self.credentials.conf('token'))
-
-
-class _DigitalOceanClient(object):
- """
- Encapsulates all communication with the DigitalOcean API.
- """
-
- def __init__(self, token):
- self.manager = digitalocean.Manager(token=token)
-
- def add_txt_record(self, domain_name, record_name, record_content):
- """
- Add a TXT record using the supplied information.
-
- :param str domain_name: The domain to use to associate the record with.
- :param str record_name: The record name (typically beginning with
'_acme-challenge.').
- :param str record_content: The record content (typically the challenge
validation).
- :raises certbot.errors.PluginError: if an error occurs communicating
with the DigitalOcean
- API
- """
-
- try:
- domain = self._find_domain(domain_name)
- except digitalocean.Error as e:
- hint = None
-
- if str(e).startswith("Unable to authenticate"):
- hint = 'Did you provide a valid API token?'
-
- logger.debug('Error finding domain using the DigitalOcean API:
%s', e)
- raise errors.PluginError('Error finding domain using the
DigitalOcean API: {0}{1}'
- .format(e, ' ({0})'.format(hint) if hint
else ''))
-
- try:
- result = domain.create_new_domain_record(
- type='TXT',
- name=self._compute_record_name(domain, record_name),
- data=record_content)
-
- record_id = result['domain_record']['id']
-
- logger.debug('Successfully added TXT record with id: %d',
record_id)
- except digitalocean.Error as e:
- logger.debug('Error adding TXT record using the DigitalOcean API:
%s', e)
- raise errors.PluginError('Error adding TXT record using the
DigitalOcean API: {0}'
- .format(e))
-
- def del_txt_record(self, domain_name, 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_name: The domain to use to associate the record with.
- :param str record_name: The record name (typically beginning with
'_acme-challenge.').
- :param str record_content: The record content (typically the challenge
validation).
- """
-
- try:
- domain = self._find_domain(domain_name)
- except digitalocean.Error as e:
- logger.debug('Error finding domain using the DigitalOcean API:
%s', e)
- return
-
- try:
- domain_records = domain.get_records()
-
- matching_records = [record for record in domain_records
- if record.type == 'TXT'
- and record.name ==
self._compute_record_name(domain, record_name)
- and record.data == record_content]
- except digitalocean.Error as e:
- logger.debug('Error getting DNS records using the DigitalOcean
API: %s', e)
- return
-
- for record in matching_records:
- try:
- logger.debug('Removing TXT record with id: %s', record.id)
- record.destroy()
- except digitalocean.Error as e:
- logger.warning('Error deleting TXT record %s using the
DigitalOcean API: %s',
- record.id, e)
-
- def _find_domain(self, domain_name):
- """
- Find the domain object for a given domain name.
-
- :param str domain_name: The domain name for which to find the
corresponding Domain.
- :returns: The Domain, if found.
- :rtype: `~digitalocean.Domain`
- :raises certbot.errors.PluginError: if no matching Domain is found.
- """
-
- domain_name_guesses = dns_common.base_domain_name_guesses(domain_name)
-
- domains = self.manager.get_all_domains()
-
- for guess in domain_name_guesses:
- matches = [domain for domain in domains if domain.name == guess]
-
- if matches:
- domain = matches[0]
- logger.debug('Found base domain for %s using name %s',
domain_name, guess)
- return domain
-
- raise errors.PluginError('Unable to determine base domain for {0}
using names: {1}.'
- .format(domain_name, domain_name_guesses))
-
- @staticmethod
- def _compute_record_name(domain, full_record_name):
- # The domain, from DigitalOcean's point of view, is automatically
appended.
- return full_record_name.rpartition("." + domain.name)[0]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/dns_digitalocean_test.py
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/dns_digitalocean_test.py
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean/dns_digitalocean_test.py
2019-11-06 03:24:51.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean/dns_digitalocean_test.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,171 +0,0 @@
-"""Tests for certbot_dns_digitalocean.dns_digitalocean."""
-
-import unittest
-
-import digitalocean
-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 = digitalocean.DataReadError()
-TOKEN = 'a-token'
-
-
-class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common.BaseAuthenticatorTest):
-
- def setUp(self):
- from certbot_dns_digitalocean.dns_digitalocean import Authenticator
-
- super(AuthenticatorTest, self).setUp()
-
- path = os.path.join(self.tempdir, 'file.ini')
- dns_test_common.write({"digitalocean_token": TOKEN}, path)
-
- self.config = mock.MagicMock(digitalocean_credentials=path,
- digitalocean_propagation_seconds=0) #
don't wait during tests
-
- self.auth = Authenticator(self.config, "digitalocean")
-
- self.mock_client = mock.MagicMock()
- # _get_digitalocean_client | pylint: disable=protected-access
- self.auth._get_digitalocean_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)]
- 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 DigitalOceanClientTest(unittest.TestCase):
-
- id_num = 1
- record_prefix = "_acme-challenge"
- record_name = record_prefix + "." + DOMAIN
- record_content = "bar"
-
- def setUp(self):
- from certbot_dns_digitalocean.dns_digitalocean import
_DigitalOceanClient
-
- self.digitalocean_client = _DigitalOceanClient(TOKEN)
-
- self.manager = mock.MagicMock()
- self.digitalocean_client.manager = self.manager
-
- def test_add_txt_record(self):
- wrong_domain_mock = mock.MagicMock()
- wrong_domain_mock.name = "other.invalid"
- wrong_domain_mock.create_new_domain_record.side_effect =
AssertionError('Wrong Domain')
-
- domain_mock = mock.MagicMock()
- domain_mock.name = DOMAIN
- domain_mock.create_new_domain_record.return_value = {'domain_record':
{'id': self.id_num}}
-
- self.manager.get_all_domains.return_value = [wrong_domain_mock,
domain_mock]
-
- self.digitalocean_client.add_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- domain_mock.create_new_domain_record.assert_called_with(type='TXT',
-
name=self.record_prefix,
-
data=self.record_content)
-
- def test_add_txt_record_fail_to_find_domain(self):
- self.manager.get_all_domains.return_value = []
-
- self.assertRaises(errors.PluginError,
- self.digitalocean_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content)
-
- def test_add_txt_record_error_finding_domain(self):
- self.manager.get_all_domains.side_effect = API_ERROR
-
- self.assertRaises(errors.PluginError,
- self.digitalocean_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content)
-
- def test_add_txt_record_error_creating_record(self):
- domain_mock = mock.MagicMock()
- domain_mock.name = DOMAIN
- domain_mock.create_new_domain_record.side_effect = API_ERROR
-
- self.manager.get_all_domains.return_value = [domain_mock]
-
- self.assertRaises(errors.PluginError,
- self.digitalocean_client.add_txt_record,
- DOMAIN, self.record_name, self.record_content)
-
- def test_del_txt_record(self):
- first_record_mock = mock.MagicMock()
- first_record_mock.type = 'TXT'
- first_record_mock.name = "DIFFERENT"
- first_record_mock.data = self.record_content
-
- correct_record_mock = mock.MagicMock()
- correct_record_mock.type = 'TXT'
- correct_record_mock.name = self.record_prefix
- correct_record_mock.data = self.record_content
-
- last_record_mock = mock.MagicMock()
- last_record_mock.type = 'TXT'
- last_record_mock.name = self.record_prefix
- last_record_mock.data = "DIFFERENT"
-
- domain_mock = mock.MagicMock()
- domain_mock.name = DOMAIN
- domain_mock.get_records.return_value = [first_record_mock,
- correct_record_mock,
- last_record_mock]
-
- self.manager.get_all_domains.return_value = [domain_mock]
-
- self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- self.assertTrue(correct_record_mock.destroy.called)
-
- self.assertFalse(first_record_mock.destroy.call_args_list)
- self.assertFalse(last_record_mock.destroy.call_args_list)
-
- def test_del_txt_record_error_finding_domain(self):
- self.manager.get_all_domains.side_effect = API_ERROR
-
- self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- def test_del_txt_record_error_finding_record(self):
- domain_mock = mock.MagicMock()
- domain_mock.name = DOMAIN
- domain_mock.get_records.side_effect = API_ERROR
-
- self.manager.get_all_domains.return_value = [domain_mock]
-
- self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
- def test_del_txt_record_error_deleting_record(self):
- record_mock = mock.MagicMock()
- record_mock.type = 'TXT'
- record_mock.name = self.record_prefix
- record_mock.data = self.record_content
- record_mock.destroy.side_effect = API_ERROR
-
- domain_mock = mock.MagicMock()
- domain_mock.name = DOMAIN
- domain_mock.get_records.return_value = [record_mock]
-
- self.manager.get_all_domains.return_value = [domain_mock]
-
- self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
-
-
-if __name__ == "__main__":
- unittest.main() # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/PKG-INFO
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/PKG-INFO
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/PKG-INFO
2019-11-06 03:25:05.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/PKG-INFO
2019-12-03 18:20:54.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: certbot-dns-digitalocean
-Version: 0.40.1
+Version: 1.0.0
Summary: DigitalOcean 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-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/SOURCES.txt
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/SOURCES.txt
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/SOURCES.txt
2019-11-06 03:25:05.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/SOURCES.txt
2019-12-03 18:20:54.000000000 +0100
@@ -4,18 +4,18 @@
setup.cfg
setup.py
certbot_dns_digitalocean/__init__.py
-certbot_dns_digitalocean/dns_digitalocean.py
-certbot_dns_digitalocean/dns_digitalocean_test.py
certbot_dns_digitalocean.egg-info/PKG-INFO
certbot_dns_digitalocean.egg-info/SOURCES.txt
certbot_dns_digitalocean.egg-info/dependency_links.txt
certbot_dns_digitalocean.egg-info/entry_points.txt
certbot_dns_digitalocean.egg-info/requires.txt
certbot_dns_digitalocean.egg-info/top_level.txt
+certbot_dns_digitalocean/_internal/__init__.py
+certbot_dns_digitalocean/_internal/dns_digitalocean.py
docs/.gitignore
docs/Makefile
docs/api.rst
docs/conf.py
docs/index.rst
docs/make.bat
-docs/api/dns_digitalocean.rst
\ No newline at end of file
+tests/dns_digitalocean_test.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/entry_points.txt
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/entry_points.txt
---
old/certbot-dns-digitalocean-0.40.1/certbot_dns_digitalocean.egg-info/entry_points.txt
2019-11-06 03:25:05.000000000 +0100
+++
new/certbot-dns-digitalocean-1.0.0/certbot_dns_digitalocean.egg-info/entry_points.txt
2019-12-03 18:20:54.000000000 +0100
@@ -1,3 +1,3 @@
[certbot.plugins]
-dns-digitalocean = certbot_dns_digitalocean.dns_digitalocean:Authenticator
+dns-digitalocean =
certbot_dns_digitalocean._internal.dns_digitalocean:Authenticator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/docs/api/dns_digitalocean.rst
new/certbot-dns-digitalocean-1.0.0/docs/api/dns_digitalocean.rst
--- old/certbot-dns-digitalocean-0.40.1/docs/api/dns_digitalocean.rst
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-digitalocean-1.0.0/docs/api/dns_digitalocean.rst
1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-:mod:`certbot_dns_digitalocean.dns_digitalocean`
-------------------------------------------------
-
-.. automodule:: certbot_dns_digitalocean.dns_digitalocean
- :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-digitalocean-0.40.1/docs/api.rst
new/certbot-dns-digitalocean-1.0.0/docs/api.rst
--- old/certbot-dns-digitalocean-0.40.1/docs/api.rst 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-digitalocean-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-digitalocean-0.40.1/setup.py
new/certbot-dns-digitalocean-1.0.0/setup.py
--- old/certbot-dns-digitalocean-0.40.1/setup.py 2019-11-06
03:24:52.000000000 +0100
+++ new/certbot-dns-digitalocean-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.
@@ -21,6 +23,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-digitalocean',
version=version,
@@ -61,8 +77,10 @@
},
entry_points={
'certbot.plugins': [
- 'dns-digitalocean =
certbot_dns_digitalocean.dns_digitalocean:Authenticator',
+ 'dns-digitalocean =
certbot_dns_digitalocean._internal.dns_digitalocean:Authenticator',
],
},
+ tests_require=["pytest"],
test_suite='certbot_dns_digitalocean',
+ cmdclass={"test": PyTest},
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-digitalocean-0.40.1/tests/dns_digitalocean_test.py
new/certbot-dns-digitalocean-1.0.0/tests/dns_digitalocean_test.py
--- old/certbot-dns-digitalocean-0.40.1/tests/dns_digitalocean_test.py
1970-01-01 01:00:00.000000000 +0100
+++ new/certbot-dns-digitalocean-1.0.0/tests/dns_digitalocean_test.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1,171 @@
+"""Tests for certbot_dns_digitalocean._internal.dns_digitalocean."""
+
+import unittest
+
+import digitalocean
+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 = digitalocean.DataReadError()
+TOKEN = 'a-token'
+
+
+class AuthenticatorTest(test_util.TempDirTestCase,
dns_test_common.BaseAuthenticatorTest):
+
+ def setUp(self):
+ from certbot_dns_digitalocean._internal.dns_digitalocean import
Authenticator
+
+ super(AuthenticatorTest, self).setUp()
+
+ path = os.path.join(self.tempdir, 'file.ini')
+ dns_test_common.write({"digitalocean_token": TOKEN}, path)
+
+ self.config = mock.MagicMock(digitalocean_credentials=path,
+ digitalocean_propagation_seconds=0) #
don't wait during tests
+
+ self.auth = Authenticator(self.config, "digitalocean")
+
+ self.mock_client = mock.MagicMock()
+ # _get_digitalocean_client | pylint: disable=protected-access
+ self.auth._get_digitalocean_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)]
+ 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 DigitalOceanClientTest(unittest.TestCase):
+
+ id_num = 1
+ record_prefix = "_acme-challenge"
+ record_name = record_prefix + "." + DOMAIN
+ record_content = "bar"
+
+ def setUp(self):
+ from certbot_dns_digitalocean._internal.dns_digitalocean import
_DigitalOceanClient
+
+ self.digitalocean_client = _DigitalOceanClient(TOKEN)
+
+ self.manager = mock.MagicMock()
+ self.digitalocean_client.manager = self.manager
+
+ def test_add_txt_record(self):
+ wrong_domain_mock = mock.MagicMock()
+ wrong_domain_mock.name = "other.invalid"
+ wrong_domain_mock.create_new_domain_record.side_effect =
AssertionError('Wrong Domain')
+
+ domain_mock = mock.MagicMock()
+ domain_mock.name = DOMAIN
+ domain_mock.create_new_domain_record.return_value = {'domain_record':
{'id': self.id_num}}
+
+ self.manager.get_all_domains.return_value = [wrong_domain_mock,
domain_mock]
+
+ self.digitalocean_client.add_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ domain_mock.create_new_domain_record.assert_called_with(type='TXT',
+
name=self.record_prefix,
+
data=self.record_content)
+
+ def test_add_txt_record_fail_to_find_domain(self):
+ self.manager.get_all_domains.return_value = []
+
+ self.assertRaises(errors.PluginError,
+ self.digitalocean_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content)
+
+ def test_add_txt_record_error_finding_domain(self):
+ self.manager.get_all_domains.side_effect = API_ERROR
+
+ self.assertRaises(errors.PluginError,
+ self.digitalocean_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content)
+
+ def test_add_txt_record_error_creating_record(self):
+ domain_mock = mock.MagicMock()
+ domain_mock.name = DOMAIN
+ domain_mock.create_new_domain_record.side_effect = API_ERROR
+
+ self.manager.get_all_domains.return_value = [domain_mock]
+
+ self.assertRaises(errors.PluginError,
+ self.digitalocean_client.add_txt_record,
+ DOMAIN, self.record_name, self.record_content)
+
+ def test_del_txt_record(self):
+ first_record_mock = mock.MagicMock()
+ first_record_mock.type = 'TXT'
+ first_record_mock.name = "DIFFERENT"
+ first_record_mock.data = self.record_content
+
+ correct_record_mock = mock.MagicMock()
+ correct_record_mock.type = 'TXT'
+ correct_record_mock.name = self.record_prefix
+ correct_record_mock.data = self.record_content
+
+ last_record_mock = mock.MagicMock()
+ last_record_mock.type = 'TXT'
+ last_record_mock.name = self.record_prefix
+ last_record_mock.data = "DIFFERENT"
+
+ domain_mock = mock.MagicMock()
+ domain_mock.name = DOMAIN
+ domain_mock.get_records.return_value = [first_record_mock,
+ correct_record_mock,
+ last_record_mock]
+
+ self.manager.get_all_domains.return_value = [domain_mock]
+
+ self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ self.assertTrue(correct_record_mock.destroy.called)
+
+ self.assertFalse(first_record_mock.destroy.call_args_list)
+ self.assertFalse(last_record_mock.destroy.call_args_list)
+
+ def test_del_txt_record_error_finding_domain(self):
+ self.manager.get_all_domains.side_effect = API_ERROR
+
+ self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ def test_del_txt_record_error_finding_record(self):
+ domain_mock = mock.MagicMock()
+ domain_mock.name = DOMAIN
+ domain_mock.get_records.side_effect = API_ERROR
+
+ self.manager.get_all_domains.return_value = [domain_mock]
+
+ self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+ def test_del_txt_record_error_deleting_record(self):
+ record_mock = mock.MagicMock()
+ record_mock.type = 'TXT'
+ record_mock.name = self.record_prefix
+ record_mock.data = self.record_content
+ record_mock.destroy.side_effect = API_ERROR
+
+ domain_mock = mock.MagicMock()
+ domain_mock.name = DOMAIN
+ domain_mock.get_records.return_value = [record_mock]
+
+ self.manager.get_all_domains.return_value = [domain_mock]
+
+ self.digitalocean_client.del_txt_record(DOMAIN, self.record_name,
self.record_content)
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover