From 72a389e8351dbc55e8bef92a075f5129d5e553d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Runkel?= Date: Mon, 4 Mar 2019 14:48:18 +0100 Subject: [PATCH 1/3] Add healthcheck option 'request_interval' for Route53 provider Route53 allows to specify an interval for its health checks. To maintain backward compatibility, the default for this option when ommited is 10 (fast check). --- docs/records.md | 2 + octodns/provider/route53.py | 22 +++++++--- tests/test_octodns_provider_route53.py | 58 +++++++++++++++++++------- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/docs/records.md b/docs/records.md index 1bfc7fd..3c10e2f 100644 --- a/docs/records.md +++ b/docs/records.md @@ -106,6 +106,7 @@ test: | Key | Description | Default | |--|--|--| | measure_latency | Show latency in AWS console | true | +| request_interval | Healthcheck interval [10\|30] seconds | 10 | ```yaml --- @@ -118,6 +119,7 @@ test: route53: healthcheck: measure_latency: false + request_interval: 30 ``` diff --git a/octodns/provider/route53.py b/octodns/provider/route53.py index d02cab7..e010ae2 100644 --- a/octodns/provider/route53.py +++ b/octodns/provider/route53.py @@ -550,14 +550,21 @@ class Route53Provider(BaseProvider): .get('healthcheck', {}) \ .get('measure_latency', True) + def _healthcheck_request_interval(self, record): + interval = record._octodns.get('route53', {}) \ + .get('healthcheck', {}) \ + .get('request_interval') + return interval if (interval in [10, 30]) else 10 + def _health_check_equivilent(self, host, path, protocol, port, - measure_latency, health_check, - first_value=None): + measure_latency, request_interval, + health_check, first_value=None): config = health_check['HealthCheckConfig'] return host == config['FullyQualifiedDomainName'] and \ path == config['ResourcePath'] and protocol == config['Type'] \ and port == config['Port'] and \ measure_latency == config['MeasureLatency'] and \ + request_interval == config['RequestInterval'] and \ (first_value is None or first_value == config['IPAddress']) def get_health_check_id(self, record, ident, geo, create): @@ -576,6 +583,7 @@ class Route53Provider(BaseProvider): healthcheck_protocol = record.healthcheck_protocol healthcheck_port = record.healthcheck_port healthcheck_latency = self._healthcheck_measure_latency(record) + healthcheck_interval = self._healthcheck_request_interval(record) # we're looking for a healthcheck with the current version & our record # type, we'll ignore anything else @@ -590,6 +598,7 @@ class Route53Provider(BaseProvider): healthcheck_protocol, healthcheck_port, healthcheck_latency, + healthcheck_interval, health_check, first_value=first_value): # this is the health check we're looking for @@ -609,7 +618,7 @@ class Route53Provider(BaseProvider): 'IPAddress': first_value, 'MeasureLatency': healthcheck_latency, 'Port': healthcheck_port, - 'RequestInterval': 10, + 'RequestInterval': healthcheck_interval, 'ResourcePath': healthcheck_path, 'Type': healthcheck_protocol, } @@ -624,10 +633,11 @@ class Route53Provider(BaseProvider): self._health_checks[id] = health_check self.log.info('get_health_check_id: created id=%s, host=%s, ' 'path=%s, protocol=%s, port=%d, measure_latency=%r, ' - 'first_value=%s', + 'request_interval=%d, first_value=%s', id, healthcheck_host, healthcheck_path, healthcheck_protocol, healthcheck_port, - healthcheck_latency, first_value) + healthcheck_latency, healthcheck_interval, + first_value) return id def _gc_health_checks(self, record, new): @@ -741,6 +751,7 @@ class Route53Provider(BaseProvider): healthcheck_protocol = record.healthcheck_protocol healthcheck_port = record.healthcheck_port healthcheck_latency = self._healthcheck_measure_latency(record) + healthcheck_interval = self._healthcheck_request_interval(record) fqdn = record.fqdn # loop through all the r53 rrsets @@ -763,6 +774,7 @@ class Route53Provider(BaseProvider): healthcheck_protocol, healthcheck_port, healthcheck_latency, + healthcheck_interval, health_check): # it has the right health check continue diff --git a/tests/test_octodns_provider_route53.py b/tests/test_octodns_provider_route53.py index eaff0e7..227fb71 100644 --- a/tests/test_octodns_provider_route53.py +++ b/tests/test_octodns_provider_route53.py @@ -106,6 +106,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -119,6 +120,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 42, }, { @@ -132,6 +134,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -145,6 +148,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -159,6 +163,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }] @@ -710,6 +715,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -723,6 +729,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }] @@ -746,6 +753,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }] @@ -794,6 +802,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -807,6 +816,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }] @@ -868,9 +878,9 @@ class TestRoute53Provider(TestCase): self.assertEquals('42', id) stubber.assert_no_pending_responses() - def test_health_check_measure_latency(self): + def test_health_check_provider_options(self): provider, stubber = self._get_stubbed_provider() - record_true = Record.new(self.expected, 'a', { + record = Record.new(self.expected, 'a', { 'ttl': 61, 'type': 'A', 'value': '1.2.3.4', @@ -879,23 +889,28 @@ class TestRoute53Provider(TestCase): }, 'route53': { 'healthcheck': { - 'measure_latency': True + 'measure_latency': True, + 'request_interval': 10, } } } }) - measure_latency = provider._healthcheck_measure_latency(record_true) - self.assertTrue(measure_latency) + latency = provider._healthcheck_measure_latency(record) + interval = provider._healthcheck_request_interval(record) + self.assertTrue(latency) + self.assertEquals(10, interval) record_default = Record.new(self.expected, 'a', { 'ttl': 61, 'type': 'A', 'value': '1.2.3.4', }) - measure_latency = provider._healthcheck_measure_latency(record_default) - self.assertTrue(measure_latency) + latency = provider._healthcheck_measure_latency(record_default) + interval = provider._healthcheck_request_interval(record_default) + self.assertTrue(latency) + self.assertEquals(10, interval) - record_false = Record.new(self.expected, 'a', { + record = Record.new(self.expected, 'a', { 'ttl': 61, 'type': 'A', 'value': '1.2.3.4', @@ -904,15 +919,18 @@ class TestRoute53Provider(TestCase): }, 'route53': { 'healthcheck': { - 'measure_latency': False + 'measure_latency': False, + 'request_interval': 30, } } } }) - measure_latency = provider._healthcheck_measure_latency(record_false) - self.assertFalse(measure_latency) + latency = provider._healthcheck_measure_latency(record) + interval = provider._healthcheck_request_interval(record) + self.assertFalse(latency) + self.assertEquals(30, interval) - def test_create_health_checks_measure_latency(self): + def test_create_health_checks_provider_options(self): provider, stubber = self._get_stubbed_provider() health_check_config = { @@ -922,7 +940,7 @@ class TestRoute53Provider(TestCase): 'IPAddress': '1.2.3.4', 'MeasureLatency': False, 'Port': 443, - 'RequestInterval': 10, + 'RequestInterval': 30, 'ResourcePath': '/_dns', 'Type': 'HTTPS' } @@ -959,7 +977,8 @@ class TestRoute53Provider(TestCase): }, 'route53': { 'healthcheck': { - 'measure_latency': False + 'measure_latency': False, + 'request_interval': 30 } } } @@ -967,7 +986,9 @@ class TestRoute53Provider(TestCase): id = provider.get_health_check_id(record, 'AF', record.geo['AF'], True) ml = provider.health_checks[id]['HealthCheckConfig']['MeasureLatency'] - self.assertEqual(False, ml) + ri = provider.health_checks[id]['HealthCheckConfig']['RequestInterval'] + self.assertFalse(ml) + self.assertEquals(30, ri) def test_health_check_gc(self): provider, stubber = self._get_stubbed_provider() @@ -1059,6 +1080,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -1072,6 +1094,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }, { @@ -1085,6 +1108,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }] @@ -1262,6 +1286,7 @@ class TestRoute53Provider(TestCase): 'Type': 'HTTPS', 'Port': 443, 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }], @@ -1365,7 +1390,8 @@ class TestRoute53Provider(TestCase): 'ResourcePath': '/_dns', 'Type': 'HTTPS', 'Port': 443, - 'MeasureLatency': True + 'MeasureLatency': True, + 'RequestInterval': 10, }, 'HealthCheckVersion': 2, }], From 07b7f1e8ef73fb38851a61373309444ca074f5c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Runkel?= Date: Mon, 4 Mar 2019 17:10:29 +0100 Subject: [PATCH 2/3] Throw exception on invalid route53 interval option value --- octodns/provider/route53.py | 8 ++++++-- tests/test_octodns_provider_route53.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/octodns/provider/route53.py b/octodns/provider/route53.py index e010ae2..f971397 100644 --- a/octodns/provider/route53.py +++ b/octodns/provider/route53.py @@ -553,8 +553,12 @@ class Route53Provider(BaseProvider): def _healthcheck_request_interval(self, record): interval = record._octodns.get('route53', {}) \ .get('healthcheck', {}) \ - .get('request_interval') - return interval if (interval in [10, 30]) else 10 + .get('request_interval', 10) + if (interval in [10, 30]): + return interval + else: + raise Exception('route53.healthcheck.request_interval ' + 'parameter must be either 10 or 30.') def _health_check_equivilent(self, host, path, protocol, port, measure_latency, request_interval, diff --git a/tests/test_octodns_provider_route53.py b/tests/test_octodns_provider_route53.py index 227fb71..50dbbee 100644 --- a/tests/test_octodns_provider_route53.py +++ b/tests/test_octodns_provider_route53.py @@ -930,6 +930,23 @@ class TestRoute53Provider(TestCase): self.assertFalse(latency) self.assertEquals(30, interval) + record_invalid = Record.new(self.expected, 'a', { + 'ttl': 61, + 'type': 'A', + 'value': '1.2.3.4', + 'octodns': { + 'healthcheck': { + }, + 'route53': { + 'healthcheck': { + 'request_interval': 20, + } + } + } + }) + with self.assertRaises(Exception): + interval = provider._healthcheck_request_interval(record_invalid) + def test_create_health_checks_provider_options(self): provider, stubber = self._get_stubbed_provider() From 00ee5053c7dc5a820cdd63c99301d283bce6ca58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Runkel?= Date: Fri, 22 Mar 2019 17:30:49 +0100 Subject: [PATCH 3/3] Use specific Route53Provider Exception --- octodns/provider/route53.py | 9 +++++++-- tests/test_octodns_provider_route53.py | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/octodns/provider/route53.py b/octodns/provider/route53.py index f971397..b645772 100644 --- a/octodns/provider/route53.py +++ b/octodns/provider/route53.py @@ -211,6 +211,10 @@ class _Route53GeoRecord(_Route53Record): self.values) +class Route53ProviderException(Exception): + pass + + class Route53Provider(BaseProvider): ''' AWS Route53 Provider @@ -557,8 +561,9 @@ class Route53Provider(BaseProvider): if (interval in [10, 30]): return interval else: - raise Exception('route53.healthcheck.request_interval ' - 'parameter must be either 10 or 30.') + raise Route53ProviderException( + 'route53.healthcheck.request_interval ' + 'parameter must be either 10 or 30.') def _health_check_equivilent(self, host, path, protocol, port, measure_latency, request_interval, diff --git a/tests/test_octodns_provider_route53.py b/tests/test_octodns_provider_route53.py index 50dbbee..a569f7c 100644 --- a/tests/test_octodns_provider_route53.py +++ b/tests/test_octodns_provider_route53.py @@ -12,7 +12,7 @@ from mock import patch from octodns.record import Create, Delete, Record, Update from octodns.provider.route53 import Route53Provider, _Route53GeoDefault, \ - _Route53GeoRecord, _Route53Record, _octal_replace + _Route53GeoRecord, _Route53Record, _octal_replace, Route53ProviderException from octodns.zone import Zone from helpers import GeoProvider @@ -944,7 +944,7 @@ class TestRoute53Provider(TestCase): } } }) - with self.assertRaises(Exception): + with self.assertRaises(Route53ProviderException): interval = provider._healthcheck_request_interval(record_invalid) def test_create_health_checks_provider_options(self):