1
0
mirror of https://github.com/github/octodns.git synced 2024-05-11 05:55:00 +00:00

Handle domains not registred at Gandi or not using Gandi's DNS

This commit is contained in:
Jonathan Leroy
2020-10-27 11:23:22 +01:00
parent 7161baa262
commit 6d17b4671a
3 changed files with 61 additions and 10 deletions

View File

@@ -41,6 +41,12 @@ class GandiClientNotFound(GandiClientException):
super(GandiClientNotFound, self).__init__(r.text)
class GandiClientUnknownDomainName(GandiClientException):
def __init__(self, msg):
super(GandiClientUnknownDomainName, self).__init__(msg)
class GandiClient(object):
def __init__(self, token):
@@ -63,6 +69,16 @@ class GandiClient(object):
r.raise_for_status()
return r
def zone(self, zone_name):
return self._request('GET', '/livedns/domains/{}'
.format(zone_name)).json()
def zone_create(self, zone_name):
return self._request('POST', '/livedns/domains', data={
'fqdn': zone_name,
'zone': {}
}).json()
def zone_records(self, zone_name):
records = self._request('GET', '/livedns/domains/{}/records'
.format(zone_name)).json()
@@ -318,9 +334,25 @@ class GandiProvider(BaseProvider):
def _apply(self, plan):
desired = plan.desired
changes = plan.changes
zone = desired.name[:-1]
self.log.debug('_apply: zone=%s, len(changes)=%d', desired.name,
len(changes))
try:
self._client.zone(zone)
except GandiClientNotFound:
self.log.info('_apply: no existing zone, trying to create it')
try:
self._client.zone_create(zone)
self.log.info('_apply: zone has been successfully created')
except GandiClientNotFound:
raise GandiClientUnknownDomainName('This domain is not '
'registred at Gandi. '
'Please register or '
'transfer it here '
'to be able to manage its '
'DNS zone.')
# Force records deletion to be done before creation in order to avoid
# "CNAME record must be the only record" error when an existing CNAME
# record is replaced by an A/AAAA record.

7
tests/fixtures/gandi-zone.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"domain_keys_href": "https://api.gandi.net/v5/livedns/domains/unit.tests/keys",
"fqdn": "unit.tests",
"automatic_snapshots": true,
"domain_records_href": "https://api.gandi.net/v5/livedns/domains/unit.tests/records",
"domain_href": "https://api.gandi.net/v5/livedns/domains/unit.tests"
}

View File

@@ -86,6 +86,18 @@ class TestGandiProvider(TestCase):
provider.populate(zone)
self.assertIn('"cause":"Forbidden"', text_type(ctx.exception))
# 404 - Not Found.
with requests_mock() as mock:
mock.get(ANY, status_code=404,
text='{"code": 404, "message": "The resource could not '
'be found.", "object": "HTTPNotFound", "cause": '
'"Not Found"}')
with self.assertRaises(GandiClientNotFound) as ctx:
zone = Zone('unit.tests.', [])
provider._client.zone(zone)
self.assertIn('"cause": "Not Found"', text_type(ctx.exception))
# General error
with requests_mock() as mock:
mock.get(ANY, status_code=502, text='Things caught fire')
@@ -95,15 +107,6 @@ class TestGandiProvider(TestCase):
provider.populate(zone)
self.assertEquals(502, ctx.exception.response.status_code)
# Non-existent zone doesn't populate anything
with requests_mock() as mock:
mock.get(ANY, status_code=404,
text='{"message": "Domain `foo.bar` not found"}')
zone = Zone('unit.tests.', [])
provider.populate(zone)
self.assertEquals(set(), zone.records)
# No diffs == no changes
with requests_mock() as mock:
base = 'https://api.gandi.net/v5/livedns/domains/unit.tests' \
@@ -147,10 +150,14 @@ class TestGandiProvider(TestCase):
resp.json = Mock()
provider._client._request = Mock(return_value=resp)
with open('tests/fixtures/gandi-zone.json') as fh:
zone = fh.read()
# non-existent domain
resp.json.side_effect = [
GandiClientNotFound(resp), # no zone in populate
GandiClientNotFound(resp), # no domain during apply
zone
]
plan = provider.plan(self.expected)
@@ -162,6 +169,11 @@ class TestGandiProvider(TestCase):
provider._client._request.assert_has_calls([
call('GET', '/livedns/domains/unit.tests/records'),
call('GET', '/livedns/domains/unit.tests'),
call('POST', '/livedns/domains', data={
'fqdn': 'unit.tests',
'zone': {}
}),
call('POST', '/livedns/domains/unit.tests/records', data={
'rrset_name': 'www.sub',
'rrset_ttl': 300,
@@ -258,7 +270,7 @@ class TestGandiProvider(TestCase):
})
])
# expected number of total calls
self.assertEquals(14, provider._client._request.call_count)
self.assertEquals(16, provider._client._request.call_count)
provider._client._request.reset_mock()