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

Implement healthcheck protocol and port for Dyn

This commit is contained in:
Ross McFarland
2018-03-31 14:31:15 -07:00
parent 849a97f161
commit e6d8669611
3 changed files with 110 additions and 22 deletions

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, monitor = DSFMonitor(label, protocol=record.healthcheck_protocol,
probe_interval=60, retries=2, response_count=2, probe_interval=60,
port=self.MONITOR_PORT, active='Y', retries=2, port=record.healthcheck_port,
host=record.healthcheck_host, 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)

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, def __init__(self, host=None, path=None, protocol=None, port=None,
options_path=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)

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, '', {