diff --git a/certbot_dns_hetzner/dns_hetzner.py b/certbot_dns_hetzner/dns_hetzner.py index cea003b..2d8e4c2 100644 --- a/certbot_dns_hetzner/dns_hetzner.py +++ b/certbot_dns_hetzner/dns_hetzner.py @@ -1,7 +1,8 @@ """DNS Authenticator for Hetzner DNS.""" import tldextract -from certbot.plugins import dns_common, dns_common_lexicon -from lexicon.providers import hetzner +from certbot.plugins import dns_common +from lexicon.client import Client +from lexicon.config import ConfigResolver TTL = 60 @@ -47,40 +48,27 @@ def _get_zone(domain): return '.'.join([zone_name.domain, zone_name.suffix]) def _perform(self, domain, validation_name, validation): - self._get_hetzner_client().add_txt_record( - self._get_zone(domain), - self._fqdn_format(validation_name), - validation - ) + with self._get_hetzner_client(domain) as client: + client.create_record("TXT", self._fqdn_format(validation_name), validation) def _cleanup(self, domain, validation_name, validation): - self._get_hetzner_client().del_txt_record( - self._get_zone(domain), - self._fqdn_format(validation_name), - validation - ) + with self._get_hetzner_client(domain) as client: + client.delete_record(None, "TXT", self._fqdn_format(validation_name), validation) + + def _get_hetzner_client(self, domain): + config = ConfigResolver().with_env().with_dict({ + "provider_name": "hetzner", + "hetzner": { + "auth_token": self.credentials.conf("api_token") + }, - def _get_hetzner_client(self): - return _HetznerClient(self.credentials.conf("api_token")) + "ttl": TTL, + "domain": self._get_zone(domain), + }) + return Client(config) @staticmethod def _fqdn_format(name): if not name.endswith("."): return f"{name}." return name - - -class _HetznerClient(dns_common_lexicon.LexiconClient): - """ - Encapsulates all communication with the Hetzner via Lexicon. - """ - def __init__(self, auth_token): - super().__init__() - - config = dns_common_lexicon.build_lexicon_config('hetzner', { - 'ttl': TTL, - }, { - 'auth_token': auth_token, - }) - - self.provider = hetzner.Provider(config) diff --git a/certbot_dns_hetzner/dns_hetzner_test.py b/certbot_dns_hetzner/dns_hetzner_test.py index 9e47cdb..8c1d0bb 100644 --- a/certbot_dns_hetzner/dns_hetzner_test.py +++ b/certbot_dns_hetzner/dns_hetzner_test.py @@ -13,7 +13,6 @@ from certbot_dns_hetzner.fakes import FAKE_API_TOKEN, FAKE_RECORD - patch_display_util = test_util.patch_display_util @@ -44,43 +43,50 @@ def setUp(self): self.auth = Authenticator(self.config, "hetzner") self.mock_client = mock.MagicMock() + + mock_client_wrapper = mock.MagicMock() + mock_client_wrapper.__enter__ = mock.MagicMock( + return_value=self.mock_client + ) + # _get_ispconfig_client | pylint: disable=protected-access self.auth._get_hetzner_client = mock.MagicMock( - return_value=self.mock_client) + return_value=mock_client_wrapper + ) @patch_display_util() - def test_perform(self, unused_mock_get_utility): - self.mock_client.add_txt_record.return_value = FAKE_RECORD + def test_perform(self, _unused_mock_get_utility): + self.mock_client.create_record.return_value = FAKE_RECORD self.auth.perform([self.achall]) - self.mock_client.add_txt_record.assert_called_with( - DOMAIN, "_acme-challenge." + DOMAIN + ".", mock.ANY + self.mock_client.create_record.assert_called_with( + "TXT", "_acme-challenge." + DOMAIN + ".", mock.ANY ) def test_perform_but_raises_plugin_error(self): - self.mock_client.add_txt_record.side_effect = mock.MagicMock( + self.mock_client.create_record.side_effect = mock.MagicMock( side_effect=PluginError() ) self.assertRaises(PluginError, self.auth.perform, [self.achall]) - self.mock_client.add_txt_record.assert_called_with( - DOMAIN, "_acme-challenge." + DOMAIN + ".", mock.ANY + self.mock_client.create_record.assert_called_with( + "TXT", "_acme-challenge." + DOMAIN + ".", mock.ANY ) @patch_display_util() - def test_cleanup(self, unused_mock_get_utility): - self.mock_client.add_txt_record.return_value = FAKE_RECORD + def test_cleanup(self, _unused_mock_get_utility): + self.mock_client.create_record.return_value = FAKE_RECORD # _attempt_cleanup | pylint: disable=protected-access self.auth.perform([self.achall]) self.auth._attempt_cleanup = True self.auth.cleanup([self.achall]) - self.mock_client.del_txt_record.assert_called_with( - DOMAIN, "_acme-challenge." + DOMAIN + ".", mock.ANY + self.mock_client.delete_record.assert_called_with( + None, "TXT", "_acme-challenge." + DOMAIN + ".", mock.ANY ) @patch_display_util() - def test_cleanup_but_raises_plugin_error(self, unused_mock_get_utility): - self.mock_client.add_txt_record.return_value = FAKE_RECORD - self.mock_client.del_txt_record.side_effect = mock.MagicMock( + def test_cleanup_but_raises_plugin_error(self, _unused_mock_get_utility): + self.mock_client.create_record.return_value = FAKE_RECORD + self.mock_client.delete_record.side_effect = mock.MagicMock( side_effect=PluginError() ) # _attempt_cleanup | pylint: disable=protected-access @@ -88,8 +94,8 @@ def test_cleanup_but_raises_plugin_error(self, unused_mock_get_utility): self.auth._attempt_cleanup = True self.assertRaises(PluginError, self.auth.cleanup, [self.achall]) - self.mock_client.del_txt_record.assert_called_with( - DOMAIN, "_acme-challenge." + DOMAIN + ".", mock.ANY + self.mock_client.delete_record.assert_called_with( + None, "TXT", "_acme-challenge." + DOMAIN + ".", mock.ANY )