Hello community,
here is the log from the commit of package python-certbot-dns-route53 for
openSUSE:Factory checked in at 2020-01-03 17:39:25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-certbot-dns-route53 (Old)
and /work/SRC/openSUSE:Factory/.python-certbot-dns-route53.new.6675 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-certbot-dns-route53"
Fri Jan 3 17:39:25 2020 rev:15 rq:760666 version:1.0.0
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-certbot-dns-route53/python-certbot-dns-route53.changes
2019-11-15 00:29:24.243760314 +0100
+++
/work/SRC/openSUSE:Factory/.python-certbot-dns-route53.new.6675/python-certbot-dns-route53.changes
2020-01-03 17:39:43.907380286 +0100
@@ -1,0 +2,6 @@
+Fri Jan 3 13:18:46 UTC 2020 - Marketa Calabkova <[email protected]>
+
+- update to version 1.0.0
+ * sync with main certbot package
+
+-------------------------------------------------------------------
Old:
----
certbot-dns-route53-0.40.1.tar.gz
New:
----
certbot-dns-route53-1.0.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-certbot-dns-route53.spec ++++++
--- /var/tmp/diff_new_pack.LU2AbC/_old 2020-01-03 17:39:44.547380615 +0100
+++ /var/tmp/diff_new_pack.LU2AbC/_new 2020-01-03 17:39:44.547380615 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-certbot-dns-route53
#
-# 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,24 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-certbot-dns-route53
-Version: 0.40.1
+Version: 1.0.0
Release: 0
Summary: Route53 DNS Authenticator plugin for Certbot
License: Apache-2.0
URL: https://github.com/certbot/certbot
Source:
https://files.pythonhosted.org/packages/source/c/certbot-dns-route53/certbot-dns-route53-%{version}.tar.gz
-BuildRequires: %{python_module acme >= 0.25.0}
+BuildRequires: %{python_module acme >= 0.29.0}
BuildRequires: %{python_module boto3}
-BuildRequires: %{python_module certbot >= 0.34.0}
+BuildRequires: %{python_module certbot >= 1.0.0}
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.29.0
Requires: python-boto3
-Requires: python-certbot >= 0.34.0
+Requires: python-certbot >= 1.0.0
Requires: python-zope.interface
BuildArch: noarch
%python_subpackages
++++++ certbot-dns-route53-0.40.1.tar.gz -> certbot-dns-route53-1.0.0.tar.gz
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-route53-0.40.1/MANIFEST.in
new/certbot-dns-route53-1.0.0/MANIFEST.in
--- old/certbot-dns-route53-0.40.1/MANIFEST.in 2019-11-06 03:24:51.000000000
+0100
+++ new/certbot-dns-route53-1.0.0/MANIFEST.in 2019-12-03 18:20:30.000000000
+0100
@@ -1,3 +1,6 @@
include LICENSE.txt
include README
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-route53-0.40.1/PKG-INFO
new/certbot-dns-route53-1.0.0/PKG-INFO
--- old/certbot-dns-route53-0.40.1/PKG-INFO 2019-11-06 03:25:26.000000000
+0100
+++ new/certbot-dns-route53-1.0.0/PKG-INFO 2019-12-03 18:21:17.000000000
+0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: certbot-dns-route53
-Version: 0.40.1
+Version: 1.0.0
Summary: Route53 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-route53-0.40.1/certbot_dns_route53/_internal/__init__.py
new/certbot-dns-route53-1.0.0/certbot_dns_route53/_internal/__init__.py
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53/_internal/__init__.py
1970-01-01 01:00:00.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53/_internal/__init__.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1 @@
+"""Internal implementation of `~certbot_dns_route53.dns_route53` plugin."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53/_internal/dns_route53.py
new/certbot-dns-route53-1.0.0/certbot_dns_route53/_internal/dns_route53.py
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53/_internal/dns_route53.py
1970-01-01 01:00:00.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53/_internal/dns_route53.py
2019-12-03 18:20:30.000000000 +0100
@@ -0,0 +1,151 @@
+"""Certbot Route53 authenticator plugin."""
+import collections
+import logging
+import time
+
+import boto3
+import zope.interface
+from botocore.exceptions import NoCredentialsError, ClientError
+
+from certbot import errors
+from certbot import interfaces
+from certbot.plugins import dns_common
+
+from acme.magic_typing import DefaultDict, List, Dict # pylint:
disable=unused-import, no-name-in-module
+
+logger = logging.getLogger(__name__)
+
+INSTRUCTIONS = (
+ "To use certbot-dns-route53, configure credentials as described at "
+
"https://boto3.readthedocs.io/en/latest/guide/configuration.html#best-practices-for-configuring-credentials
" # pylint: disable=line-too-long
+ "and add the necessary permissions for Route53 access.")
+
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
+class Authenticator(dns_common.DNSAuthenticator):
+ """Route53 Authenticator
+
+ This authenticator solves a DNS01 challenge by uploading the answer to AWS
+ Route53.
+ """
+
+ description = ("Obtain certificates using a DNS TXT record (if you are
using AWS Route53 for "
+ "DNS).")
+ ttl = 10
+
+ def __init__(self, *args, **kwargs):
+ super(Authenticator, self).__init__(*args, **kwargs)
+ self.r53 = boto3.client("route53")
+ self._resource_records = collections.defaultdict(list) # type:
DefaultDict[str, List[Dict[str, str]]]
+
+ def more_info(self): # pylint: disable=missing-docstring,no-self-use
+ return "Solve a DNS01 challenge using AWS Route53"
+
+ def _setup_credentials(self):
+ pass
+
+ def _perform(self, domain, validation_name, validation): # pylint:
disable=missing-docstring
+ pass
+
+ def perform(self, achalls):
+ self._attempt_cleanup = True
+
+ try:
+ change_ids = [
+ self._change_txt_record("UPSERT",
+ achall.validation_domain_name(achall.domain),
+ achall.validation(achall.account_key))
+ for achall in achalls
+ ]
+
+ for change_id in change_ids:
+ self._wait_for_change(change_id)
+ except (NoCredentialsError, ClientError) as e:
+ logger.debug('Encountered error during perform: %s', e,
exc_info=True)
+ raise errors.PluginError("\n".join([str(e), INSTRUCTIONS]))
+ return [achall.response(achall.account_key) for achall in achalls]
+
+ def _cleanup(self, domain, validation_name, validation):
+ try:
+ self._change_txt_record("DELETE", validation_name, validation)
+ except (NoCredentialsError, ClientError) as e:
+ logger.debug('Encountered error during cleanup: %s', e,
exc_info=True)
+
+ def _find_zone_id_for_domain(self, domain):
+ """Find the zone id responsible a given FQDN.
+
+ That is, the id for the zone whose name is the longest parent of the
+ domain.
+ """
+ paginator = self.r53.get_paginator("list_hosted_zones")
+ zones = []
+ target_labels = domain.rstrip(".").split(".")
+ for page in paginator.paginate():
+ for zone in page["HostedZones"]:
+ if zone["Config"]["PrivateZone"]:
+ continue
+
+ candidate_labels = zone["Name"].rstrip(".").split(".")
+ if candidate_labels == target_labels[-len(candidate_labels):]:
+ zones.append((zone["Name"], zone["Id"]))
+
+ if not zones:
+ raise errors.PluginError(
+ "Unable to find a Route53 hosted zone for {0}".format(domain)
+ )
+
+ # Order the zones that are suffixes for our desired to domain by
+ # length, this puts them in an order like:
+ # ["foo.bar.baz.com", "bar.baz.com", "baz.com", "com"]
+ # And then we choose the first one, which will be the most specific.
+ zones.sort(key=lambda z: len(z[0]), reverse=True)
+ return zones[0][1]
+
+ def _change_txt_record(self, action, validation_domain_name, validation):
+ zone_id = self._find_zone_id_for_domain(validation_domain_name)
+
+ rrecords = self._resource_records[validation_domain_name]
+ challenge = {"Value": '"{0}"'.format(validation)}
+ if action == "DELETE":
+ # Remove the record being deleted from the list of tracked records
+ rrecords.remove(challenge)
+ if rrecords:
+ # Need to update instead, as we're not deleting the rrset
+ action = "UPSERT"
+ else:
+ # Create a new list containing the record to use with DELETE
+ rrecords = [challenge]
+ else:
+ rrecords.append(challenge)
+
+ response = self.r53.change_resource_record_sets(
+ HostedZoneId=zone_id,
+ ChangeBatch={
+ "Comment": "certbot-dns-route53 certificate validation " +
action,
+ "Changes": [
+ {
+ "Action": action,
+ "ResourceRecordSet": {
+ "Name": validation_domain_name,
+ "Type": "TXT",
+ "TTL": self.ttl,
+ "ResourceRecords": rrecords,
+ }
+ }
+ ]
+ }
+ )
+ return response["ChangeInfo"]["Id"]
+
+ def _wait_for_change(self, change_id):
+ """Wait for a change to be propagated to all Route53 DNS servers.
+
https://docs.aws.amazon.com/Route53/latest/APIReference/API_GetChange.html
+ """
+ for unused_n in range(0, 120):
+ response = self.r53.get_change(Id=change_id)
+ if response["ChangeInfo"]["Status"] == "INSYNC":
+ return
+ time.sleep(5)
+ raise errors.PluginError(
+ "Timed out waiting for Route53 change. Current status: %s" %
+ response["ChangeInfo"]["Status"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53/authenticator.py
new/certbot-dns-route53-1.0.0/certbot_dns_route53/authenticator.py
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53/authenticator.py
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53/authenticator.py
2019-12-03 18:20:30.000000000 +0100
@@ -1,16 +1,17 @@
-"""Shim around `~certbot_dns_route53.dns_route53` for backwards
compatibility."""
+"""Shim around `~certbot_dns_route53._internal.dns_route53` for backwards
compatibility."""
import warnings
import zope.interface
from certbot import interfaces
-from certbot_dns_route53 import dns_route53
+from certbot_dns_route53._internal import dns_route53
@zope.interface.implementer(interfaces.IAuthenticator)
@zope.interface.provider(interfaces.IPluginFactory)
class Authenticator(dns_route53.Authenticator):
- """Shim around `~certbot_dns_route53.dns_route53.Authenticator` for
backwards compatibility."""
+ """Shim around `~certbot_dns_route53._internal.dns_route53.Authenticator`
+ for backwards compatibility."""
hidden = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53/dns_route53.py
new/certbot-dns-route53-1.0.0/certbot_dns_route53/dns_route53.py
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53/dns_route53.py
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53/dns_route53.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,151 +0,0 @@
-"""Certbot Route53 authenticator plugin."""
-import collections
-import logging
-import time
-
-import boto3
-import zope.interface
-from botocore.exceptions import NoCredentialsError, ClientError
-
-from certbot import errors
-from certbot import interfaces
-from certbot.plugins import dns_common
-
-from acme.magic_typing import DefaultDict, List, Dict # pylint:
disable=unused-import, no-name-in-module
-
-logger = logging.getLogger(__name__)
-
-INSTRUCTIONS = (
- "To use certbot-dns-route53, configure credentials as described at "
-
"https://boto3.readthedocs.io/en/latest/guide/configuration.html#best-practices-for-configuring-credentials
" # pylint: disable=line-too-long
- "and add the necessary permissions for Route53 access.")
-
[email protected](interfaces.IAuthenticator)
[email protected](interfaces.IPluginFactory)
-class Authenticator(dns_common.DNSAuthenticator):
- """Route53 Authenticator
-
- This authenticator solves a DNS01 challenge by uploading the answer to AWS
- Route53.
- """
-
- description = ("Obtain certificates using a DNS TXT record (if you are
using AWS Route53 for "
- "DNS).")
- ttl = 10
-
- def __init__(self, *args, **kwargs):
- super(Authenticator, self).__init__(*args, **kwargs)
- self.r53 = boto3.client("route53")
- self._resource_records = collections.defaultdict(list) # type:
DefaultDict[str, List[Dict[str, str]]]
-
- def more_info(self): # pylint: disable=missing-docstring,no-self-use
- return "Solve a DNS01 challenge using AWS Route53"
-
- def _setup_credentials(self):
- pass
-
- def _perform(self, domain, validation_name, validation): # pylint:
disable=missing-docstring
- pass
-
- def perform(self, achalls):
- self._attempt_cleanup = True
-
- try:
- change_ids = [
- self._change_txt_record("UPSERT",
- achall.validation_domain_name(achall.domain),
- achall.validation(achall.account_key))
- for achall in achalls
- ]
-
- for change_id in change_ids:
- self._wait_for_change(change_id)
- except (NoCredentialsError, ClientError) as e:
- logger.debug('Encountered error during perform: %s', e,
exc_info=True)
- raise errors.PluginError("\n".join([str(e), INSTRUCTIONS]))
- return [achall.response(achall.account_key) for achall in achalls]
-
- def _cleanup(self, domain, validation_name, validation):
- try:
- self._change_txt_record("DELETE", validation_name, validation)
- except (NoCredentialsError, ClientError) as e:
- logger.debug('Encountered error during cleanup: %s', e,
exc_info=True)
-
- def _find_zone_id_for_domain(self, domain):
- """Find the zone id responsible a given FQDN.
-
- That is, the id for the zone whose name is the longest parent of the
- domain.
- """
- paginator = self.r53.get_paginator("list_hosted_zones")
- zones = []
- target_labels = domain.rstrip(".").split(".")
- for page in paginator.paginate():
- for zone in page["HostedZones"]:
- if zone["Config"]["PrivateZone"]:
- continue
-
- candidate_labels = zone["Name"].rstrip(".").split(".")
- if candidate_labels == target_labels[-len(candidate_labels):]:
- zones.append((zone["Name"], zone["Id"]))
-
- if not zones:
- raise errors.PluginError(
- "Unable to find a Route53 hosted zone for {0}".format(domain)
- )
-
- # Order the zones that are suffixes for our desired to domain by
- # length, this puts them in an order like:
- # ["foo.bar.baz.com", "bar.baz.com", "baz.com", "com"]
- # And then we choose the first one, which will be the most specific.
- zones.sort(key=lambda z: len(z[0]), reverse=True)
- return zones[0][1]
-
- def _change_txt_record(self, action, validation_domain_name, validation):
- zone_id = self._find_zone_id_for_domain(validation_domain_name)
-
- rrecords = self._resource_records[validation_domain_name]
- challenge = {"Value": '"{0}"'.format(validation)}
- if action == "DELETE":
- # Remove the record being deleted from the list of tracked records
- rrecords.remove(challenge)
- if rrecords:
- # Need to update instead, as we're not deleting the rrset
- action = "UPSERT"
- else:
- # Create a new list containing the record to use with DELETE
- rrecords = [challenge]
- else:
- rrecords.append(challenge)
-
- response = self.r53.change_resource_record_sets(
- HostedZoneId=zone_id,
- ChangeBatch={
- "Comment": "certbot-dns-route53 certificate validation " +
action,
- "Changes": [
- {
- "Action": action,
- "ResourceRecordSet": {
- "Name": validation_domain_name,
- "Type": "TXT",
- "TTL": self.ttl,
- "ResourceRecords": rrecords,
- }
- }
- ]
- }
- )
- return response["ChangeInfo"]["Id"]
-
- def _wait_for_change(self, change_id):
- """Wait for a change to be propagated to all Route53 DNS servers.
-
https://docs.aws.amazon.com/Route53/latest/APIReference/API_GetChange.html
- """
- for unused_n in range(0, 120):
- response = self.r53.get_change(Id=change_id)
- if response["ChangeInfo"]["Status"] == "INSYNC":
- return
- time.sleep(5)
- raise errors.PluginError(
- "Timed out waiting for Route53 change. Current status: %s" %
- response["ChangeInfo"]["Status"])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53/dns_route53_test.py
new/certbot-dns-route53-1.0.0/certbot_dns_route53/dns_route53_test.py
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53/dns_route53_test.py
2019-11-06 03:24:51.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53/dns_route53_test.py
1970-01-01 01:00:00.000000000 +0100
@@ -1,263 +0,0 @@
-"""Tests for certbot_dns_route53.dns_route53.Authenticator"""
-
-import unittest
-
-import mock
-from botocore.exceptions import NoCredentialsError, ClientError
-
-from certbot import errors
-from certbot.compat import os
-from certbot.plugins import dns_test_common
-from certbot.plugins.dns_test_common import DOMAIN
-
-
-class AuthenticatorTest(unittest.TestCase,
dns_test_common.BaseAuthenticatorTest):
- # pylint: disable=protected-access
-
- def setUp(self):
- from certbot_dns_route53.dns_route53 import Authenticator
-
- super(AuthenticatorTest, self).setUp()
-
- self.config = mock.MagicMock()
-
- # Set up dummy credentials for testing
- os.environ["AWS_ACCESS_KEY_ID"] = "dummy_access_key"
- os.environ["AWS_SECRET_ACCESS_KEY"] = "dummy_secret_access_key"
-
- self.auth = Authenticator(self.config, "route53")
-
- def tearDown(self):
- # Remove the dummy credentials from env vars
- del os.environ["AWS_ACCESS_KEY_ID"]
- del os.environ["AWS_SECRET_ACCESS_KEY"]
- super(AuthenticatorTest, self).tearDown()
-
- def test_perform(self):
- self.auth._change_txt_record = mock.MagicMock()
- self.auth._wait_for_change = mock.MagicMock()
-
- self.auth.perform([self.achall])
-
- self.auth._change_txt_record.assert_called_once_with("UPSERT",
-
'_acme-challenge.' + DOMAIN,
- mock.ANY)
- self.assertEqual(self.auth._wait_for_change.call_count, 1)
-
- def test_perform_no_credentials_error(self):
- self.auth._change_txt_record =
mock.MagicMock(side_effect=NoCredentialsError)
-
- self.assertRaises(errors.PluginError,
- self.auth.perform,
- [self.achall])
-
- def test_perform_client_error(self):
- self.auth._change_txt_record = mock.MagicMock(
- side_effect=ClientError({"Error": {"Code": "foo"}}, "bar"))
-
- self.assertRaises(errors.PluginError,
- self.auth.perform,
- [self.achall])
-
- def test_cleanup(self):
- self.auth._attempt_cleanup = True
-
- self.auth._change_txt_record = mock.MagicMock()
-
- self.auth.cleanup([self.achall])
-
- self.auth._change_txt_record.assert_called_once_with("DELETE",
-
'_acme-challenge.'+DOMAIN,
- mock.ANY)
-
- def test_cleanup_no_credentials_error(self):
- self.auth._attempt_cleanup = True
-
- self.auth._change_txt_record =
mock.MagicMock(side_effect=NoCredentialsError)
-
- self.auth.cleanup([self.achall])
-
- def test_cleanup_client_error(self):
- self.auth._attempt_cleanup = True
-
- self.auth._change_txt_record = mock.MagicMock(
- side_effect=ClientError({"Error": {"Code": "foo"}}, "bar"))
-
- self.auth.cleanup([self.achall])
-
-
-class ClientTest(unittest.TestCase):
- # pylint: disable=protected-access
-
- PRIVATE_ZONE = {
- "Id": "BAD-PRIVATE",
- "Name": "example.com",
- "Config": {
- "PrivateZone": True
- }
- }
-
- EXAMPLE_NET_ZONE = {
- "Id": "BAD-WRONG-TLD",
- "Name": "example.net",
- "Config": {
- "PrivateZone": False
- }
- }
-
- EXAMPLE_COM_ZONE = {
- "Id": "EXAMPLE",
- "Name": "example.com",
- "Config": {
- "PrivateZone": False
- }
- }
-
- FOO_EXAMPLE_COM_ZONE = {
- "Id": "FOO",
- "Name": "foo.example.com",
- "Config": {
- "PrivateZone": False
- }
- }
-
- def setUp(self):
- from certbot_dns_route53.dns_route53 import Authenticator
-
- super(ClientTest, self).setUp()
-
- self.config = mock.MagicMock()
-
- # Set up dummy credentials for testing
- os.environ["AWS_ACCESS_KEY_ID"] = "dummy_access_key"
- os.environ["AWS_SECRET_ACCESS_KEY"] = "dummy_secret_access_key"
-
- self.client = Authenticator(self.config, "route53")
-
- def tearDown(self):
- # Remove the dummy credentials from env vars
- del os.environ["AWS_ACCESS_KEY_ID"]
- del os.environ["AWS_SECRET_ACCESS_KEY"]
- super(ClientTest, self).tearDown()
-
- def test_find_zone_id_for_domain(self):
- self.client.r53.get_paginator = mock.MagicMock()
- self.client.r53.get_paginator().paginate.return_value = [
- {
- "HostedZones": [
- self.EXAMPLE_NET_ZONE,
- self.EXAMPLE_COM_ZONE,
- ]
- }
- ]
-
- result = self.client._find_zone_id_for_domain("foo.example.com")
- self.assertEqual(result, "EXAMPLE")
-
- def test_find_zone_id_for_domain_pagination(self):
- self.client.r53.get_paginator = mock.MagicMock()
- self.client.r53.get_paginator().paginate.return_value = [
- {
- "HostedZones": [
- self.PRIVATE_ZONE,
- self.EXAMPLE_COM_ZONE,
- ]
- },
- {
- "HostedZones": [
- self.PRIVATE_ZONE,
- self.FOO_EXAMPLE_COM_ZONE,
- ]
- }
- ]
-
- result = self.client._find_zone_id_for_domain("foo.example.com")
- self.assertEqual(result, "FOO")
-
- def test_find_zone_id_for_domain_no_results(self):
- self.client.r53.get_paginator = mock.MagicMock()
- self.client.r53.get_paginator().paginate.return_value = []
-
- self.assertRaises(errors.PluginError,
- self.client._find_zone_id_for_domain,
- "foo.example.com")
-
- def test_find_zone_id_for_domain_no_correct_results(self):
- self.client.r53.get_paginator = mock.MagicMock()
- self.client.r53.get_paginator().paginate.return_value = [
- {
- "HostedZones": [
- self.PRIVATE_ZONE,
- self.EXAMPLE_NET_ZONE,
- ]
- },
- ]
-
- self.assertRaises(errors.PluginError,
- self.client._find_zone_id_for_domain,
- "foo.example.com")
-
- def test_change_txt_record(self):
- self.client._find_zone_id_for_domain = mock.MagicMock()
- self.client.r53.change_resource_record_sets = mock.MagicMock(
- return_value={"ChangeInfo": {"Id": 1}})
-
- self.client._change_txt_record("FOO", DOMAIN, "foo")
-
- call_count = self.client.r53.change_resource_record_sets.call_count
- self.assertEqual(call_count, 1)
-
- def test_change_txt_record_delete(self):
- self.client._find_zone_id_for_domain = mock.MagicMock()
- self.client.r53.change_resource_record_sets = mock.MagicMock(
- return_value={"ChangeInfo": {"Id": 1}})
-
- validation = "some-value"
- validation_record = {"Value": '"{0}"'.format(validation)}
- self.client._resource_records[DOMAIN] = [validation_record]
-
- self.client._change_txt_record("DELETE", DOMAIN, validation)
-
- call_count = self.client.r53.change_resource_record_sets.call_count
- self.assertEqual(call_count, 1)
- call_args =
self.client.r53.change_resource_record_sets.call_args_list[0][1]
- call_args_batch = call_args["ChangeBatch"]["Changes"][0]
- self.assertEqual(call_args_batch["Action"], "DELETE")
- self.assertEqual(
- call_args_batch["ResourceRecordSet"]["ResourceRecords"],
- [validation_record])
-
- def test_change_txt_record_multirecord(self):
- self.client._find_zone_id_for_domain = mock.MagicMock()
- self.client._get_validation_rrset = mock.MagicMock()
- self.client._resource_records[DOMAIN] = [
- {"Value": "\"pre-existing-value\""},
- {"Value": "\"pre-existing-value-two\""},
- ]
- self.client.r53.change_resource_record_sets = mock.MagicMock(
- return_value={"ChangeInfo": {"Id": 1}})
-
- self.client._change_txt_record("DELETE", DOMAIN, "pre-existing-value")
-
- call_count = self.client.r53.change_resource_record_sets.call_count
- call_args =
self.client.r53.change_resource_record_sets.call_args_list[0][1]
- call_args_batch = call_args["ChangeBatch"]["Changes"][0]
- self.assertEqual(call_args_batch["Action"], "UPSERT")
- self.assertEqual(
- call_args_batch["ResourceRecordSet"]["ResourceRecords"],
- [{"Value": "\"pre-existing-value-two\""}])
-
- self.assertEqual(call_count, 1)
-
- def test_wait_for_change(self):
- self.client.r53.get_change = mock.MagicMock(
- side_effect=[{"ChangeInfo": {"Status": "PENDING"}},
- {"ChangeInfo": {"Status": "INSYNC"}}])
-
- self.client._wait_for_change(1)
-
- self.assertTrue(self.client.r53.get_change.called)
-
-
-if __name__ == "__main__":
- unittest.main() # pragma: no cover
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53.egg-info/PKG-INFO
new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/PKG-INFO
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53.egg-info/PKG-INFO
2019-11-06 03:25:26.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/PKG-INFO
2019-12-03 18:21:17.000000000 +0100
@@ -1,6 +1,6 @@
Metadata-Version: 1.2
Name: certbot-dns-route53
-Version: 0.40.1
+Version: 1.0.0
Summary: Route53 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-route53-0.40.1/certbot_dns_route53.egg-info/SOURCES.txt
new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/SOURCES.txt
--- old/certbot-dns-route53-0.40.1/certbot_dns_route53.egg-info/SOURCES.txt
2019-11-06 03:25:26.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/SOURCES.txt
2019-12-03 18:21:17.000000000 +0100
@@ -5,19 +5,18 @@
setup.py
certbot_dns_route53/__init__.py
certbot_dns_route53/authenticator.py
-certbot_dns_route53/dns_route53.py
-certbot_dns_route53/dns_route53_test.py
certbot_dns_route53.egg-info/PKG-INFO
certbot_dns_route53.egg-info/SOURCES.txt
certbot_dns_route53.egg-info/dependency_links.txt
certbot_dns_route53.egg-info/entry_points.txt
certbot_dns_route53.egg-info/requires.txt
certbot_dns_route53.egg-info/top_level.txt
+certbot_dns_route53/_internal/__init__.py
+certbot_dns_route53/_internal/dns_route53.py
docs/.gitignore
docs/Makefile
docs/api.rst
docs/conf.py
docs/index.rst
docs/make.bat
-docs/api/authenticator.rst
-docs/api/dns_route53.rst
\ No newline at end of file
+tests/dns_route53_test.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/certbot_dns_route53.egg-info/entry_points.txt
new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/entry_points.txt
---
old/certbot-dns-route53-0.40.1/certbot_dns_route53.egg-info/entry_points.txt
2019-11-06 03:25:26.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/certbot_dns_route53.egg-info/entry_points.txt
2019-12-03 18:21:17.000000000 +0100
@@ -1,4 +1,4 @@
[certbot.plugins]
certbot-route53:auth = certbot_dns_route53.authenticator:Authenticator
-dns-route53 = certbot_dns_route53.dns_route53:Authenticator
+dns-route53 = certbot_dns_route53._internal.dns_route53:Authenticator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/certbot-dns-route53-0.40.1/docs/api/authenticator.rst
new/certbot-dns-route53-1.0.0/docs/api/authenticator.rst
--- old/certbot-dns-route53-0.40.1/docs/api/authenticator.rst 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/docs/api/authenticator.rst 1970-01-01
01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-:mod:`certbot_dns_route53.authenticator`
-----------------------------------------
-
-.. automodule:: certbot_dns_route53.authenticator
- :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-route53-0.40.1/docs/api/dns_route53.rst
new/certbot-dns-route53-1.0.0/docs/api/dns_route53.rst
--- old/certbot-dns-route53-0.40.1/docs/api/dns_route53.rst 2019-11-06
03:24:51.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/docs/api/dns_route53.rst 1970-01-01
01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-:mod:`certbot_dns_route53.dns_route53`
---------------------------------------
-
-.. automodule:: certbot_dns_route53.dns_route53
- :members:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-route53-0.40.1/docs/api.rst
new/certbot-dns-route53-1.0.0/docs/api.rst
--- old/certbot-dns-route53-0.40.1/docs/api.rst 2019-11-06 03:24:51.000000000
+0100
+++ new/certbot-dns-route53-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-route53-0.40.1/setup.py
new/certbot-dns-route53-1.0.0/setup.py
--- old/certbot-dns-route53-0.40.1/setup.py 2019-11-06 03:24:52.000000000
+0100
+++ new/certbot-dns-route53-1.0.0/setup.py 2019-12-03 18:20:32.000000000
+0100
@@ -1,7 +1,9 @@
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.
@@ -14,6 +16,20 @@
'zope.interface',
]
+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-route53',
version=version,
@@ -51,9 +67,11 @@
keywords=['certbot', 'route53', 'aws'],
entry_points={
'certbot.plugins': [
- 'dns-route53 = certbot_dns_route53.dns_route53:Authenticator',
+ 'dns-route53 =
certbot_dns_route53._internal.dns_route53:Authenticator',
'certbot-route53:auth =
certbot_dns_route53.authenticator:Authenticator'
],
},
+ tests_require=["pytest"],
test_suite='certbot_dns_route53',
+ cmdclass={"test": PyTest},
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/certbot-dns-route53-0.40.1/tests/dns_route53_test.py
new/certbot-dns-route53-1.0.0/tests/dns_route53_test.py
--- old/certbot-dns-route53-0.40.1/tests/dns_route53_test.py 1970-01-01
01:00:00.000000000 +0100
+++ new/certbot-dns-route53-1.0.0/tests/dns_route53_test.py 2019-12-03
18:20:30.000000000 +0100
@@ -0,0 +1,263 @@
+"""Tests for certbot_dns_route53._internal.dns_route53.Authenticator"""
+
+import unittest
+
+import mock
+from botocore.exceptions import NoCredentialsError, ClientError
+
+from certbot import errors
+from certbot.compat import os
+from certbot.plugins import dns_test_common
+from certbot.plugins.dns_test_common import DOMAIN
+
+
+class AuthenticatorTest(unittest.TestCase,
dns_test_common.BaseAuthenticatorTest):
+ # pylint: disable=protected-access
+
+ def setUp(self):
+ from certbot_dns_route53._internal.dns_route53 import Authenticator
+
+ super(AuthenticatorTest, self).setUp()
+
+ self.config = mock.MagicMock()
+
+ # Set up dummy credentials for testing
+ os.environ["AWS_ACCESS_KEY_ID"] = "dummy_access_key"
+ os.environ["AWS_SECRET_ACCESS_KEY"] = "dummy_secret_access_key"
+
+ self.auth = Authenticator(self.config, "route53")
+
+ def tearDown(self):
+ # Remove the dummy credentials from env vars
+ del os.environ["AWS_ACCESS_KEY_ID"]
+ del os.environ["AWS_SECRET_ACCESS_KEY"]
+ super(AuthenticatorTest, self).tearDown()
+
+ def test_perform(self):
+ self.auth._change_txt_record = mock.MagicMock()
+ self.auth._wait_for_change = mock.MagicMock()
+
+ self.auth.perform([self.achall])
+
+ self.auth._change_txt_record.assert_called_once_with("UPSERT",
+
'_acme-challenge.' + DOMAIN,
+ mock.ANY)
+ self.assertEqual(self.auth._wait_for_change.call_count, 1)
+
+ def test_perform_no_credentials_error(self):
+ self.auth._change_txt_record =
mock.MagicMock(side_effect=NoCredentialsError)
+
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ def test_perform_client_error(self):
+ self.auth._change_txt_record = mock.MagicMock(
+ side_effect=ClientError({"Error": {"Code": "foo"}}, "bar"))
+
+ self.assertRaises(errors.PluginError,
+ self.auth.perform,
+ [self.achall])
+
+ def test_cleanup(self):
+ self.auth._attempt_cleanup = True
+
+ self.auth._change_txt_record = mock.MagicMock()
+
+ self.auth.cleanup([self.achall])
+
+ self.auth._change_txt_record.assert_called_once_with("DELETE",
+
'_acme-challenge.'+DOMAIN,
+ mock.ANY)
+
+ def test_cleanup_no_credentials_error(self):
+ self.auth._attempt_cleanup = True
+
+ self.auth._change_txt_record =
mock.MagicMock(side_effect=NoCredentialsError)
+
+ self.auth.cleanup([self.achall])
+
+ def test_cleanup_client_error(self):
+ self.auth._attempt_cleanup = True
+
+ self.auth._change_txt_record = mock.MagicMock(
+ side_effect=ClientError({"Error": {"Code": "foo"}}, "bar"))
+
+ self.auth.cleanup([self.achall])
+
+
+class ClientTest(unittest.TestCase):
+ # pylint: disable=protected-access
+
+ PRIVATE_ZONE = {
+ "Id": "BAD-PRIVATE",
+ "Name": "example.com",
+ "Config": {
+ "PrivateZone": True
+ }
+ }
+
+ EXAMPLE_NET_ZONE = {
+ "Id": "BAD-WRONG-TLD",
+ "Name": "example.net",
+ "Config": {
+ "PrivateZone": False
+ }
+ }
+
+ EXAMPLE_COM_ZONE = {
+ "Id": "EXAMPLE",
+ "Name": "example.com",
+ "Config": {
+ "PrivateZone": False
+ }
+ }
+
+ FOO_EXAMPLE_COM_ZONE = {
+ "Id": "FOO",
+ "Name": "foo.example.com",
+ "Config": {
+ "PrivateZone": False
+ }
+ }
+
+ def setUp(self):
+ from certbot_dns_route53._internal.dns_route53 import Authenticator
+
+ super(ClientTest, self).setUp()
+
+ self.config = mock.MagicMock()
+
+ # Set up dummy credentials for testing
+ os.environ["AWS_ACCESS_KEY_ID"] = "dummy_access_key"
+ os.environ["AWS_SECRET_ACCESS_KEY"] = "dummy_secret_access_key"
+
+ self.client = Authenticator(self.config, "route53")
+
+ def tearDown(self):
+ # Remove the dummy credentials from env vars
+ del os.environ["AWS_ACCESS_KEY_ID"]
+ del os.environ["AWS_SECRET_ACCESS_KEY"]
+ super(ClientTest, self).tearDown()
+
+ def test_find_zone_id_for_domain(self):
+ self.client.r53.get_paginator = mock.MagicMock()
+ self.client.r53.get_paginator().paginate.return_value = [
+ {
+ "HostedZones": [
+ self.EXAMPLE_NET_ZONE,
+ self.EXAMPLE_COM_ZONE,
+ ]
+ }
+ ]
+
+ result = self.client._find_zone_id_for_domain("foo.example.com")
+ self.assertEqual(result, "EXAMPLE")
+
+ def test_find_zone_id_for_domain_pagination(self):
+ self.client.r53.get_paginator = mock.MagicMock()
+ self.client.r53.get_paginator().paginate.return_value = [
+ {
+ "HostedZones": [
+ self.PRIVATE_ZONE,
+ self.EXAMPLE_COM_ZONE,
+ ]
+ },
+ {
+ "HostedZones": [
+ self.PRIVATE_ZONE,
+ self.FOO_EXAMPLE_COM_ZONE,
+ ]
+ }
+ ]
+
+ result = self.client._find_zone_id_for_domain("foo.example.com")
+ self.assertEqual(result, "FOO")
+
+ def test_find_zone_id_for_domain_no_results(self):
+ self.client.r53.get_paginator = mock.MagicMock()
+ self.client.r53.get_paginator().paginate.return_value = []
+
+ self.assertRaises(errors.PluginError,
+ self.client._find_zone_id_for_domain,
+ "foo.example.com")
+
+ def test_find_zone_id_for_domain_no_correct_results(self):
+ self.client.r53.get_paginator = mock.MagicMock()
+ self.client.r53.get_paginator().paginate.return_value = [
+ {
+ "HostedZones": [
+ self.PRIVATE_ZONE,
+ self.EXAMPLE_NET_ZONE,
+ ]
+ },
+ ]
+
+ self.assertRaises(errors.PluginError,
+ self.client._find_zone_id_for_domain,
+ "foo.example.com")
+
+ def test_change_txt_record(self):
+ self.client._find_zone_id_for_domain = mock.MagicMock()
+ self.client.r53.change_resource_record_sets = mock.MagicMock(
+ return_value={"ChangeInfo": {"Id": 1}})
+
+ self.client._change_txt_record("FOO", DOMAIN, "foo")
+
+ call_count = self.client.r53.change_resource_record_sets.call_count
+ self.assertEqual(call_count, 1)
+
+ def test_change_txt_record_delete(self):
+ self.client._find_zone_id_for_domain = mock.MagicMock()
+ self.client.r53.change_resource_record_sets = mock.MagicMock(
+ return_value={"ChangeInfo": {"Id": 1}})
+
+ validation = "some-value"
+ validation_record = {"Value": '"{0}"'.format(validation)}
+ self.client._resource_records[DOMAIN] = [validation_record]
+
+ self.client._change_txt_record("DELETE", DOMAIN, validation)
+
+ call_count = self.client.r53.change_resource_record_sets.call_count
+ self.assertEqual(call_count, 1)
+ call_args =
self.client.r53.change_resource_record_sets.call_args_list[0][1]
+ call_args_batch = call_args["ChangeBatch"]["Changes"][0]
+ self.assertEqual(call_args_batch["Action"], "DELETE")
+ self.assertEqual(
+ call_args_batch["ResourceRecordSet"]["ResourceRecords"],
+ [validation_record])
+
+ def test_change_txt_record_multirecord(self):
+ self.client._find_zone_id_for_domain = mock.MagicMock()
+ self.client._get_validation_rrset = mock.MagicMock()
+ self.client._resource_records[DOMAIN] = [
+ {"Value": "\"pre-existing-value\""},
+ {"Value": "\"pre-existing-value-two\""},
+ ]
+ self.client.r53.change_resource_record_sets = mock.MagicMock(
+ return_value={"ChangeInfo": {"Id": 1}})
+
+ self.client._change_txt_record("DELETE", DOMAIN, "pre-existing-value")
+
+ call_count = self.client.r53.change_resource_record_sets.call_count
+ call_args =
self.client.r53.change_resource_record_sets.call_args_list[0][1]
+ call_args_batch = call_args["ChangeBatch"]["Changes"][0]
+ self.assertEqual(call_args_batch["Action"], "UPSERT")
+ self.assertEqual(
+ call_args_batch["ResourceRecordSet"]["ResourceRecords"],
+ [{"Value": "\"pre-existing-value-two\""}])
+
+ self.assertEqual(call_count, 1)
+
+ def test_wait_for_change(self):
+ self.client.r53.get_change = mock.MagicMock(
+ side_effect=[{"ChangeInfo": {"Status": "PENDING"}},
+ {"ChangeInfo": {"Status": "INSYNC"}}])
+
+ self.client._wait_for_change(1)
+
+ self.assertTrue(self.client.r53.get_change.called)
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover