Browse Source

Implement healthcheck protocol and port for Dyn

pull/67/head
Ross McFarland 8 years ago
parent
commit
e6d8669611
No known key found for this signature in database GPG Key ID: 61C10C4FC8FE4A89
3 changed files with 110 additions and 22 deletions
  1. +53
    -14
      octodns/provider/dyn.py
  2. +38
    -8
      tests/test_octodns_provider_dyn.py
  3. +19
    -0
      tests/test_octodns_record.py

+ 53
- 14
octodns/provider/dyn.py View File

@ -63,15 +63,46 @@ def _monitor_path_set(self, value):
DSFMonitor.path = DSFMonitor.path.setter(_monitor_path_set) DSFMonitor.path = DSFMonitor.path.setter(_monitor_path_set)
def _monitor_update(self, host, path):
def _monitor_protocol_get(self):
return self._protocol
DSFMonitor.protocol = property(_monitor_protocol_get)
def _monitor_protocol_set(self, value):
self._protocol = value
DSFMonitor.protocol = DSFMonitor.protocol.setter(_monitor_protocol_set)
def _monitor_port_get(self):
return self._port or self._options['port']
DSFMonitor.port = property(_monitor_port_get)
def _monitor_port_set(self, value):
if self._options is None:
self._options = {}
self._port = self._options['port'] = value
DSFMonitor.port = DSFMonitor.port.setter(_monitor_port_set)
def _monitor_update(self, host, path, protocol, port):
# I can't see how to actually do this with the client lib so # I can't see how to actually do this with the client lib so
# I'm having to hack around it. Have to provide all the # I'm having to hack around it. Have to provide all the
# options or else things complain # options or else things complain
return self._update({ return self._update({
'protocol': protocol,
'options': { 'options': {
'host': host, 'host': host,
'path': path, 'path': path,
'port': DynProvider.MONITOR_PORT,
'port': port,
'timeout': DynProvider.MONITOR_TIMEOUT, 'timeout': DynProvider.MONITOR_TIMEOUT,
'header': DynProvider.MONITOR_HEADER, 'header': DynProvider.MONITOR_HEADER,
} }
@ -82,6 +113,11 @@ DSFMonitor.update = _monitor_update
############################################################################### ###############################################################################
def _monitor_matches(monitor, host, path, protocol, port):
return monitor.host != host or monitor.path != path or \
monitor.protocol != protocol or int(monitor.port) != port
class _CachingDynZone(DynZone): class _CachingDynZone(DynZone):
log = getLogger('_CachingDynZone') log = getLogger('_CachingDynZone')
@ -142,10 +178,6 @@ class _CachingDynZone(DynZone):
self.flush_cache() self.flush_cache()
def _monitor_matches(monitor, host, path):
return monitor.host != host or monitor.path != path
class DynProvider(BaseProvider): class DynProvider(BaseProvider):
''' '''
Dynect Managed DNS provider Dynect Managed DNS provider
@ -202,7 +234,6 @@ class DynProvider(BaseProvider):
} }
MONITOR_HEADER = 'User-Agent: Dyn Monitor' MONITOR_HEADER = 'User-Agent: Dyn Monitor'
MONITOR_PORT = 443
MONITOR_TIMEOUT = 10 MONITOR_TIMEOUT = 10
_sess_create_lock = Lock() _sess_create_lock = Lock()
@ -479,7 +510,9 @@ class DynProvider(BaseProvider):
# Heh, when pulled from the API host & path live under options, but # Heh, when pulled from the API host & path live under options, but
# when you create with the constructor they're top-level :-( # when you create with the constructor they're top-level :-(
if _monitor_matches(monitor, record.healthcheck_host, if _monitor_matches(monitor, record.healthcheck_host,
record.healthcheck_path):
record.healthcheck_path,
record.healthcheck_protocol,
record.healthcheck_port):
self.log.info('_extra_changes: health-check mis-match for %s', self.log.info('_extra_changes: health-check mis-match for %s',
label) label)
extra.append(Update(record, record)) extra.append(Update(record, record))
@ -586,6 +619,8 @@ class DynProvider(BaseProvider):
try: try:
try: try:
monitor = self.traffic_director_monitors[label] monitor = self.traffic_director_monitors[label]
self.log.debug('_traffic_director_monitor: existing for %s',
label)
except KeyError: except KeyError:
# UNTIL 1.0 We don't have one for the new label format, see if # UNTIL 1.0 We don't have one for the new label format, see if
# we still have one for the old and update it # we still have one for the old and update it
@ -597,19 +632,23 @@ class DynProvider(BaseProvider):
self.traffic_director_monitors[fqdn] self.traffic_director_monitors[fqdn]
del self.traffic_director_monitors[fqdn] del self.traffic_director_monitors[fqdn]
if _monitor_matches(monitor, record.healthcheck_host, if _monitor_matches(monitor, record.healthcheck_host,
record.healthcheck_path):
record.healthcheck_path,
record.healthcheck_protocol,
record.healthcheck_port):
self.log.info('_traffic_director_monitor: updating monitor ' self.log.info('_traffic_director_monitor: updating monitor '
'for %s', label) 'for %s', label)
monitor.update(record.healthcheck_host, monitor.update(record.healthcheck_host,
record.healthcheck_path)
record.healthcheck_path,
record.healthcheck_protocol,
record.healthcheck_port)
return monitor return monitor
except KeyError: except KeyError:
self.log.info('_traffic_director_monitor: creating monitor ' self.log.info('_traffic_director_monitor: creating monitor '
'for %s', label) 'for %s', label)
monitor = DSFMonitor(label, protocol='HTTPS', response_count=2,
probe_interval=60, retries=2,
port=self.MONITOR_PORT, active='Y',
host=record.healthcheck_host,
monitor = DSFMonitor(label, protocol=record.healthcheck_protocol,
response_count=2, probe_interval=60,
retries=2, port=record.healthcheck_port,
active='Y', host=record.healthcheck_host,
timeout=self.MONITOR_TIMEOUT, timeout=self.MONITOR_TIMEOUT,
header=self.MONITOR_HEADER, header=self.MONITOR_HEADER,
path=record.healthcheck_path) path=record.healthcheck_path)


+ 38
- 8
tests/test_octodns_provider_dyn.py View File

@ -768,7 +768,9 @@ class TestDynProviderGeo(TestCase):
'octodns': { 'octodns': {
'healthcheck': { 'healthcheck': {
'host': 'bleep.bloop', 'host': 'bleep.bloop',
'path': '/_nope'
'path': '/_nope',
'protocol': 'HTTP',
'port': 8080,
} }
}, },
'ttl': 60, 'ttl': 60,
@ -787,10 +789,10 @@ class TestDynProviderGeo(TestCase):
'header': 'User-Agent: Dyn Monitor', 'header': 'User-Agent: Dyn Monitor',
'host': 'bleep.bloop', 'host': 'bleep.bloop',
'path': '/_nope', 'path': '/_nope',
'port': '443',
'port': '8080',
'timeout': '10', 'timeout': '10',
'probe_interval': '60', 'probe_interval': '60',
'protocol': 'HTTPS',
'protocol': 'HTTP',
'response_count': '2', 'response_count': '2',
'retries': '2' 'retries': '2'
}, },
@ -806,11 +808,12 @@ class TestDynProviderGeo(TestCase):
# should have resulted an update # should have resulted an update
mock.assert_has_calls([ mock.assert_has_calls([
call('/DSFMonitor/42a/', 'PUT', { call('/DSFMonitor/42a/', 'PUT', {
'protocol': 'HTTP',
'options': { 'options': {
'path': '/_nope', 'path': '/_nope',
'host': 'bleep.bloop', 'host': 'bleep.bloop',
'header': 'User-Agent: Dyn Monitor', 'header': 'User-Agent: Dyn Monitor',
'port': 443,
'port': 8080,
'timeout': 10 'timeout': 10
} }
}) })
@ -821,6 +824,8 @@ class TestDynProviderGeo(TestCase):
monitor = provider._traffic_director_monitors['unit.tests.:A'] monitor = provider._traffic_director_monitors['unit.tests.:A']
self.assertEquals('bleep.bloop', monitor.host) self.assertEquals('bleep.bloop', monitor.host)
self.assertEquals('/_nope', monitor.path) self.assertEquals('/_nope', monitor.path)
self.assertEquals('HTTP', monitor.protocol)
self.assertEquals('8080', monitor.port)
# test upgrading an old label # test upgrading an old label
record = Record.new(existing, 'old-label', { record = Record.new(existing, 'old-label', {
@ -1510,15 +1515,20 @@ class TestDynProviderAlias(TestCase):
# patching # patching
class DummyDSFMonitor(DSFMonitor): class DummyDSFMonitor(DSFMonitor):
def __init__(self, host=None, path=None, options_host=None,
options_path=None):
def __init__(self, host=None, path=None, protocol=None, port=None,
options_host=None, options_path=None, options_protocol=None,
options_port=None):
# not calling super on purpose # not calling super on purpose
self._host = host self._host = host
self._path = path self._path = path
self._protocol = protocol
self._port = port
if options_host: if options_host:
self._options = { self._options = {
'host': options_host, 'host': options_host,
'path': options_path, 'path': options_path,
'protocol': options_protocol,
'port': options_port,
} }
else: else:
self._options = None self._options = None
@ -1527,12 +1537,16 @@ class DummyDSFMonitor(DSFMonitor):
class TestDSFMonitorMonkeyPatching(TestCase): class TestDSFMonitorMonkeyPatching(TestCase):
def test_host(self): def test_host(self):
monitor = DummyDSFMonitor(host='host.com', path='/path')
monitor = DummyDSFMonitor(host='host.com', path='/path',
protocol='HTTP', port=8080)
self.assertEquals('host.com', monitor.host) self.assertEquals('host.com', monitor.host)
self.assertEquals('/path', monitor.path) self.assertEquals('/path', monitor.path)
self.assertEquals('HTTP', monitor.protocol)
self.assertEquals(8080, monitor.port)
monitor = DummyDSFMonitor(options_host='host.com', monitor = DummyDSFMonitor(options_host='host.com',
options_path='/path')
options_path='/path',
options_protocol='HTTP', options_port=8080)
self.assertEquals('host.com', monitor.host) self.assertEquals('host.com', monitor.host)
self.assertEquals('/path', monitor.path) self.assertEquals('/path', monitor.path)
@ -1540,6 +1554,10 @@ class TestDSFMonitorMonkeyPatching(TestCase):
self.assertEquals('other.com', monitor.host) self.assertEquals('other.com', monitor.host)
monitor.path = '/other-path' monitor.path = '/other-path'
self.assertEquals('/other-path', monitor.path) self.assertEquals('/other-path', monitor.path)
monitor.protocol = 'HTTPS'
self.assertEquals('HTTPS', monitor.protocol)
monitor.port = 8081
self.assertEquals(8081, monitor.port)
monitor = DummyDSFMonitor() monitor = DummyDSFMonitor()
monitor.host = 'other.com' monitor.host = 'other.com'
@ -1547,3 +1565,15 @@ class TestDSFMonitorMonkeyPatching(TestCase):
monitor = DummyDSFMonitor() monitor = DummyDSFMonitor()
monitor.path = '/other-path' monitor.path = '/other-path'
self.assertEquals('/other-path', monitor.path) self.assertEquals('/other-path', monitor.path)
monitor.protocol = 'HTTP'
self.assertEquals('HTTP', monitor.protocol)
monitor.port = 8080
self.assertEquals(8080, monitor.port)
# Just to exercise the _options init
monitor = DummyDSFMonitor()
monitor.protocol = 'HTTP'
self.assertEquals('HTTP', monitor.protocol)
monitor = DummyDSFMonitor()
monitor.port = 8080
self.assertEquals(8080, monitor.port)

+ 19
- 0
tests/test_octodns_record.py View File

@ -1020,6 +1020,25 @@ class TestRecordValidation(TestCase):
'invalid ip address "goodbye"' 'invalid ip address "goodbye"'
], ctx.exception.reasons) ], ctx.exception.reasons)
# invalid healthcheck protocol
with self.assertRaises(ValidationError) as ctx:
Record.new(self.zone, 'a', {
'geo': {
'NA': ['1.2.3.5'],
'NA-US': ['1.2.3.5', '1.2.3.6']
},
'type': 'A',
'ttl': 600,
'value': '1.2.3.4',
'octodns': {
'healthcheck': {
'protocol': 'FTP',
}
}
})
self.assertEquals(['invalid healthcheck protocol'],
ctx.exception.reasons)
def test_AAAA(self): def test_AAAA(self):
# doesn't blow up # doesn't blow up
Record.new(self.zone, '', { Record.new(self.zone, '', {


Loading…
Cancel
Save