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

Merge branch 'master' into master

This commit is contained in:
Ross McFarland
2018-01-08 14:12:03 -08:00
committed by GitHub
16 changed files with 786 additions and 176 deletions

View File

@@ -0,0 +1,7 @@
manager:
plan_outputs:
'bad':
class: octodns.provider.plan.PlanLogger
invalid: config
providers: {}
zones: {}

View File

@@ -0,0 +1,5 @@
manager:
plan_outputs:
'bad': {}
providers: {}
zones: {}

View File

@@ -180,7 +180,7 @@
"per_page": 10,
"total_pages": 2,
"count": 10,
"total_count": 17
"total_count": 19
},
"success": true,
"errors": [],

View File

@@ -163,7 +163,7 @@
"per_page": 10,
"total_pages": 2,
"count": 9,
"total_count": 20
"total_count": 19
},
"success": true,
"errors": [],

View File

@@ -83,6 +83,19 @@ class TestManager(TestCase):
.sync(['unknown.target.'])
self.assertTrue('unknown target' in ctx.exception.message)
def test_bad_plan_output_class(self):
with self.assertRaises(Exception) as ctx:
name = 'bad-plan-output-missing-class.yaml'
Manager(get_config_filename(name)).sync()
self.assertEquals('plan_output bad is missing class',
ctx.exception.message)
def test_bad_plan_output_config(self):
with self.assertRaises(Exception) as ctx:
Manager(get_config_filename('bad-plan-output-config.yaml')).sync()
self.assertEqual('Incorrect plan_output config for bad',
ctx.exception.message)
def test_source_only_as_a_target(self):
with self.assertRaises(Exception) as ctx:
Manager(get_config_filename('unknown-provider.yaml')) \

View File

@@ -0,0 +1,92 @@
#
#
#
from __future__ import absolute_import, division, print_function, \
unicode_literals
from StringIO import StringIO
from logging import getLogger
from unittest import TestCase
from octodns.provider.plan import Plan, PlanHtml, PlanLogger, PlanMarkdown
from octodns.record import Create, Delete, Record, Update
from octodns.zone import Zone
from helpers import SimpleProvider
class TestPlanLogger(TestCase):
def test_invalid_level(self):
with self.assertRaises(Exception) as ctx:
PlanLogger('invalid', 'not-a-level')
self.assertEquals('Unsupported level: not-a-level',
ctx.exception.message)
simple = SimpleProvider()
zone = Zone('unit.tests.', [])
existing = Record.new(zone, 'a', {
'ttl': 300,
'type': 'A',
# This matches the zone data above, one to swap, one to leave
'values': ['1.1.1.1', '2.2.2.2'],
})
new = Record.new(zone, 'a', {
'geo': {
'AF': ['5.5.5.5'],
'NA-US': ['6.6.6.6']
},
'ttl': 300,
'type': 'A',
# This leaves one, swaps ones, and adds one
'values': ['2.2.2.2', '3.3.3.3', '4.4.4.4'],
}, simple)
create = Create(Record.new(zone, 'b', {
'ttl': 60,
'type': 'CNAME',
'value': 'foo.unit.tests.'
}, simple))
update = Update(existing, new)
delete = Delete(new)
changes = [create, delete, update]
plans = [
(simple, Plan(zone, zone, changes)),
(simple, Plan(zone, zone, changes)),
]
class TestPlanHtml(TestCase):
log = getLogger('TestPlanHtml')
def test_empty(self):
out = StringIO()
PlanHtml('html').run([], fh=out)
self.assertEquals('<b>No changes were planned</b>', out.getvalue())
def test_simple(self):
out = StringIO()
PlanHtml('html').run(plans, fh=out)
out = out.getvalue()
self.assertTrue(' <td colspan=6>Summary: Creates=1, Updates=1, '
'Deletes=1, Existing Records=0</td>' in out)
class TestPlanMarkdown(TestCase):
log = getLogger('TestPlanMarkdown')
def test_empty(self):
out = StringIO()
PlanMarkdown('markdown').run([], fh=out)
self.assertEquals('## No changes were planned\n', out.getvalue())
def test_simple(self):
out = StringIO()
PlanMarkdown('markdown').run(plans, fh=out)
out = out.getvalue()
self.assertTrue('## unit.tests.' in out)
self.assertTrue('Create | b | CNAME | 60 | foo.unit.tests.' in out)
self.assertTrue('Update | a | A | 300 | 1.1.1.1;' in out)
self.assertTrue('NA-US: 6.6.6.6 | test' in out)
self.assertTrue('Delete | a | A | 300 | 2.2.2.2;' in out)

View File

@@ -9,7 +9,8 @@ from logging import getLogger
from unittest import TestCase
from octodns.record import Create, Delete, Record, Update
from octodns.provider.base import BaseProvider, Plan, UnsafePlan
from octodns.provider.base import BaseProvider
from octodns.provider.plan import Plan, UnsafePlan
from octodns.zone import Zone

View File

@@ -11,7 +11,8 @@ from requests import HTTPError
from requests_mock import ANY, mock as requests_mock
from unittest import TestCase
from octodns.record import Record
from octodns.record import Record, Update
from octodns.provider.base import Plan
from octodns.provider.cloudflare import CloudflareProvider
from octodns.provider.yaml import YamlProvider
from octodns.zone import Zone
@@ -267,15 +268,219 @@ class TestCloudflareProvider(TestCase):
self.assertEquals(2, provider.apply(plan))
# recreate for update, and deletes for the 2 parts of the other
provider._request.assert_has_calls([
call('POST', '/zones/42/dns_records', data={
'content': '3.2.3.4',
'type': 'A',
'name': 'ttl.unit.tests',
'ttl': 300}),
call('DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
'dns_records/fc12ab34cd5611334422ab3322997655'),
call('PUT', '/zones/ff12ab34cd5611334422ab3322997650/dns_records/'
'fc12ab34cd5611334422ab3322997655',
data={'content': '3.2.3.4',
'type': 'A',
'name': 'ttl.unit.tests',
'ttl': 300}),
call('DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
'dns_records/fc12ab34cd5611334422ab3322997653'),
call('DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
'dns_records/fc12ab34cd5611334422ab3322997654')
])
def test_update_add_swap(self):
provider = CloudflareProvider('test', 'email', 'token')
provider.zone_records = Mock(return_value=[
{
"id": "fc12ab34cd5611334422ab3322997653",
"type": "A",
"name": "a.unit.tests",
"content": "1.1.1.1",
"proxiable": True,
"proxied": False,
"ttl": 300,
"locked": False,
"zone_id": "ff12ab34cd5611334422ab3322997650",
"zone_name": "unit.tests",
"modified_on": "2017-03-11T18:01:43.420689Z",
"created_on": "2017-03-11T18:01:43.420689Z",
"meta": {
"auto_added": False
}
},
{
"id": "fc12ab34cd5611334422ab3322997654",
"type": "A",
"name": "a.unit.tests",
"content": "2.2.2.2",
"proxiable": True,
"proxied": False,
"ttl": 300,
"locked": False,
"zone_id": "ff12ab34cd5611334422ab3322997650",
"zone_name": "unit.tests",
"modified_on": "2017-03-11T18:01:43.420689Z",
"created_on": "2017-03-11T18:01:43.420689Z",
"meta": {
"auto_added": False
}
},
])
provider._request = Mock()
provider._request.side_effect = [
self.empty, # no zones
{
'result': {
'id': 42,
}
}, # zone create
None,
None,
]
# Add something and delete something
zone = Zone('unit.tests.', [])
existing = Record.new(zone, 'a', {
'ttl': 300,
'type': 'A',
# This matches the zone data above, one to swap, one to leave
'values': ['1.1.1.1', '2.2.2.2'],
})
new = Record.new(zone, 'a', {
'ttl': 300,
'type': 'A',
# This leaves one, swaps ones, and adds one
'values': ['2.2.2.2', '3.3.3.3', '4.4.4.4'],
})
change = Update(existing, new)
plan = Plan(zone, zone, [change])
provider._apply(plan)
provider._request.assert_has_calls([
call('GET', '/zones', params={'page': 1}),
call('POST', '/zones', data={'jump_start': False,
'name': 'unit.tests'}),
call('PUT', '/zones/ff12ab34cd5611334422ab3322997650/dns_records/'
'fc12ab34cd5611334422ab3322997653',
data={'content': '4.4.4.4', 'type': 'A', 'name':
'a.unit.tests', 'ttl': 300}),
call('POST', '/zones/42/dns_records',
data={'content': '3.3.3.3', 'type': 'A',
'name': 'a.unit.tests', 'ttl': 300})
])
def test_update_delete(self):
# We need another run so that we can delete, we can't both add and
# delete in one go b/c of swaps
provider = CloudflareProvider('test', 'email', 'token')
provider.zone_records = Mock(return_value=[
{
"id": "fc12ab34cd5611334422ab3322997653",
"type": "NS",
"name": "unit.tests",
"content": "ns1.foo.bar",
"proxiable": True,
"proxied": False,
"ttl": 300,
"locked": False,
"zone_id": "ff12ab34cd5611334422ab3322997650",
"zone_name": "unit.tests",
"modified_on": "2017-03-11T18:01:43.420689Z",
"created_on": "2017-03-11T18:01:43.420689Z",
"meta": {
"auto_added": False
}
},
{
"id": "fc12ab34cd5611334422ab3322997654",
"type": "NS",
"name": "unit.tests",
"content": "ns2.foo.bar",
"proxiable": True,
"proxied": False,
"ttl": 300,
"locked": False,
"zone_id": "ff12ab34cd5611334422ab3322997650",
"zone_name": "unit.tests",
"modified_on": "2017-03-11T18:01:43.420689Z",
"created_on": "2017-03-11T18:01:43.420689Z",
"meta": {
"auto_added": False
}
},
])
provider._request = Mock()
provider._request.side_effect = [
self.empty, # no zones
{
'result': {
'id': 42,
}
}, # zone create
None,
None,
]
# Add something and delete something
zone = Zone('unit.tests.', [])
existing = Record.new(zone, '', {
'ttl': 300,
'type': 'NS',
# This matches the zone data above, one to delete, one to leave
'values': ['ns1.foo.bar.', 'ns2.foo.bar.'],
})
new = Record.new(zone, '', {
'ttl': 300,
'type': 'NS',
# This leaves one and deletes one
'value': 'ns2.foo.bar.',
})
change = Update(existing, new)
plan = Plan(zone, zone, [change])
provider._apply(plan)
provider._request.assert_has_calls([
call('GET', '/zones', params={'page': 1}),
call('POST', '/zones',
data={'jump_start': False, 'name': 'unit.tests'}),
call('DELETE', '/zones/ff12ab34cd5611334422ab3322997650/'
'dns_records/fc12ab34cd5611334422ab3322997653')
])
def test_alias(self):
provider = CloudflareProvider('test', 'email', 'token')
# A CNAME for us to transform to ALIAS
provider.zone_records = Mock(return_value=[
{
"id": "fc12ab34cd5611334422ab3322997642",
"type": "CNAME",
"name": "unit.tests",
"content": "www.unit.tests",
"proxiable": True,
"proxied": False,
"ttl": 300,
"locked": False,
"zone_id": "ff12ab34cd5611334422ab3322997650",
"zone_name": "unit.tests",
"modified_on": "2017-03-11T18:01:43.420689Z",
"created_on": "2017-03-11T18:01:43.420689Z",
"meta": {
"auto_added": False
}
},
])
zone = Zone('unit.tests.', [])
provider.populate(zone)
self.assertEquals(1, len(zone.records))
record = list(zone.records)[0]
self.assertEquals('', record.name)
self.assertEquals('unit.tests.', record.fqdn)
self.assertEquals('ALIAS', record._type)
self.assertEquals('www.unit.tests.', record.value)
# Make sure we transform back to CNAME going the other way
contents = provider._gen_contents(record)
self.assertEquals({
'content': u'www.unit.tests.',
'name': 'unit.tests',
'ttl': 300,
'type': 'CNAME'
}, list(contents)[0])