Browse Source

Add healthcheck option 'measure_latency' for Route53 provider

Route53 allows to monitor latency information on the dashboard
and using CloudWatch. While that is a nice to have function,
it is not necessary for a DNS failover scenario and increases
Route 53 costs.
To maintain backward compatibility, the default for this option
when ommited is true.
pull/321/head
Jörg Runkel 7 years ago
parent
commit
1794f5ccd6
5 changed files with 72 additions and 10 deletions
  1. +31
    -3
      docs/records.md
  2. +16
    -7
      octodns/provider/route53.py
  3. +7
    -0
      octodns/record/__init__.py
  4. +15
    -0
      tests/test_octodns_provider_route53.py
  5. +3
    -0
      tests/test_octodns_record.py

+ 31
- 3
docs/records.md View File

@ -72,8 +72,36 @@ So the example is saying:
- Europe: gets an "A" record of 111.111.111.4 - Europe: gets an "A" record of 111.111.111.4
- Everyone else gets an "A" record of 111.111.111.5 - Everyone else gets an "A" record of 111.111.111.5
### Health Checks
Octodns will automatically set up a monitor and check for **https://<ip_address>/_dns** and check for a 200 response.
Octodns will automatically set up monitors for each IP and check for a 200 response for **https://<ip_address>/_dns**.
These checks can be configured by adding a `healthcheck` configuration to the record:
```yaml
---
test:
geo:
AS:
- 1.2.3.4
EU:
- 2.3.4.5
octodns:
healthcheck:
host: my-host-name
measure_latency: 0
path: /dns-health-check
port: 443
protocol: HTTPS
```
| Key | Description | Default |
|--|--|--|
| host | FQDN for host header and SNI | - |
| path | path to check | _dns |
| port | port to check | 443 |
| protocol | HTTP/HTTPS | HTTPS |
| measure_latency | Route53 only: Show latency in AWS console | true |
## Config (`YamlProvider`) ## Config (`YamlProvider`)
@ -83,7 +111,7 @@ OctoDNS records and `YamlProvider`'s schema is essentially a 1:1 match. Properti
Each top-level key in the yaml file is a record name. Two common special cases are the root record `''`, and a wildcard `'*'`. Each top-level key in the yaml file is a record name. Two common special cases are the root record `''`, and a wildcard `'*'`.
```
```yaml
--- ---
'': '':
type: A type: A
@ -111,7 +139,7 @@ The above config lays out 4 records, `A`s for `example.com.`, `www.example.com.`
In the above example each name had a single record, but there are cases where a name will need to have multiple records associated with it. This can be accomplished by using a list. In the above example each name had a single record, but there are cases where a name will need to have multiple records associated with it. This can be accomplished by using a list.
```
```yaml
--- ---
'': '':
- type: A - type: A


+ 16
- 7
octodns/provider/route53.py View File

@ -546,11 +546,13 @@ class Route53Provider(BaseProvider):
return self._health_checks return self._health_checks
def _health_check_equivilent(self, host, path, protocol, port, def _health_check_equivilent(self, host, path, protocol, port,
health_check, first_value=None):
measure_latency, health_check,
first_value=None):
config = health_check['HealthCheckConfig'] config = health_check['HealthCheckConfig']
return host == config['FullyQualifiedDomainName'] and \ return host == config['FullyQualifiedDomainName'] and \
path == config['ResourcePath'] and protocol == config['Type'] \ path == config['ResourcePath'] and protocol == config['Type'] \
and port == config['Port'] and \ and port == config['Port'] and \
measure_latency == config['MeasureLatency'] and \
(first_value is None or first_value == config['IPAddress']) (first_value is None or first_value == config['IPAddress'])
def get_health_check_id(self, record, ident, geo, create): def get_health_check_id(self, record, ident, geo, create):
@ -568,6 +570,7 @@ class Route53Provider(BaseProvider):
healthcheck_path = record.healthcheck_path healthcheck_path = record.healthcheck_path
healthcheck_protocol = record.healthcheck_protocol healthcheck_protocol = record.healthcheck_protocol
healthcheck_port = record.healthcheck_port healthcheck_port = record.healthcheck_port
healthcheck_measure_latency = record.healthcheck_measure_latency
# we're looking for a healthcheck with the current version & our record # we're looking for a healthcheck with the current version & our record
# type, we'll ignore anything else # type, we'll ignore anything else
@ -580,7 +583,9 @@ class Route53Provider(BaseProvider):
if self._health_check_equivilent(healthcheck_host, if self._health_check_equivilent(healthcheck_host,
healthcheck_path, healthcheck_path,
healthcheck_protocol, healthcheck_protocol,
healthcheck_port, health_check,
healthcheck_port,
healthcheck_measure_latency,
health_check,
first_value=first_value): first_value=first_value):
# this is the health check we're looking for # this is the health check we're looking for
self.log.debug('get_health_check_id: found match id=%s', id) self.log.debug('get_health_check_id: found match id=%s', id)
@ -597,7 +602,7 @@ class Route53Provider(BaseProvider):
'FailureThreshold': 6, 'FailureThreshold': 6,
'FullyQualifiedDomainName': healthcheck_host, 'FullyQualifiedDomainName': healthcheck_host,
'IPAddress': first_value, 'IPAddress': first_value,
'MeasureLatency': True,
'MeasureLatency': healthcheck_measure_latency,
'Port': healthcheck_port, 'Port': healthcheck_port,
'RequestInterval': 10, 'RequestInterval': 10,
'ResourcePath': healthcheck_path, 'ResourcePath': healthcheck_path,
@ -612,10 +617,12 @@ class Route53Provider(BaseProvider):
# store the new health check so that we'll be able to find it in the # store the new health check so that we'll be able to find it in the
# future # future
self._health_checks[id] = health_check self._health_checks[id] = health_check
self.log.info('get_health_check_id: created id=%s, host=%s, path=%s, '
'protocol=%s, port=%d, first_value=%s', id,
healthcheck_host, healthcheck_path, healthcheck_protocol,
healthcheck_port, first_value)
self.log.info('get_health_check_id: created id=%s, host=%s, '
'path=%s, protocol=%s, port=%d, measure_latency=%r, '
'first_value=%s',
id, healthcheck_host, healthcheck_path,
healthcheck_protocol, healthcheck_port,
healthcheck_measure_latency, first_value)
return id return id
def _gc_health_checks(self, record, new): def _gc_health_checks(self, record, new):
@ -728,6 +735,7 @@ class Route53Provider(BaseProvider):
healthcheck_path = record.healthcheck_path healthcheck_path = record.healthcheck_path
healthcheck_protocol = record.healthcheck_protocol healthcheck_protocol = record.healthcheck_protocol
healthcheck_port = record.healthcheck_port healthcheck_port = record.healthcheck_port
healthcheck_latency = record.healthcheck_measure_latency
fqdn = record.fqdn fqdn = record.fqdn
# loop through all the r53 rrsets # loop through all the r53 rrsets
@ -749,6 +757,7 @@ class Route53Provider(BaseProvider):
healthcheck_path, healthcheck_path,
healthcheck_protocol, healthcheck_protocol,
healthcheck_port, healthcheck_port,
healthcheck_latency,
health_check): health_check):
# it has the right health check # it has the right health check
continue continue


+ 7
- 0
octodns/record/__init__.py View File

@ -189,6 +189,13 @@ class Record(object):
except KeyError: except KeyError:
return 443 return 443
@property
def healthcheck_measure_latency(self):
try:
return bool(self._octodns['healthcheck']['measure_latency'])
except KeyError:
return True
def changes(self, other, target): def changes(self, other, target):
# We're assuming we have the same name and type if we're being compared # We're assuming we have the same name and type if we're being compared
if self.ttl != other.ttl: if self.ttl != other.ttl:


+ 15
- 0
tests/test_octodns_provider_route53.py View File

@ -105,6 +105,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -117,6 +118,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 42, 'HealthCheckVersion': 42,
}, { }, {
@ -129,6 +131,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -141,6 +144,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -154,6 +158,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}] }]
@ -704,6 +709,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -716,6 +722,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}] }]
@ -738,6 +745,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}] }]
@ -785,6 +793,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -797,6 +806,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}] }]
@ -947,6 +957,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -959,6 +970,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}, { }, {
@ -971,6 +983,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}] }]
@ -1147,6 +1160,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True,
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}], }],
@ -1250,6 +1264,7 @@ class TestRoute53Provider(TestCase):
'ResourcePath': '/_dns', 'ResourcePath': '/_dns',
'Type': 'HTTPS', 'Type': 'HTTPS',
'Port': 443, 'Port': 443,
'MeasureLatency': True
}, },
'HealthCheckVersion': 2, 'HealthCheckVersion': 2,
}], }],


+ 3
- 0
tests/test_octodns_record.py View File

@ -757,6 +757,7 @@ class TestRecord(TestCase):
'host': 'bleep.bloop', 'host': 'bleep.bloop',
'protocol': 'HTTP', 'protocol': 'HTTP',
'port': 8080, 'port': 8080,
'measure_latency': False
} }
} }
}) })
@ -764,6 +765,7 @@ class TestRecord(TestCase):
self.assertEquals('bleep.bloop', new.healthcheck_host) self.assertEquals('bleep.bloop', new.healthcheck_host)
self.assertEquals('HTTP', new.healthcheck_protocol) self.assertEquals('HTTP', new.healthcheck_protocol)
self.assertEquals(8080, new.healthcheck_port) self.assertEquals(8080, new.healthcheck_port)
self.assertEquals(False, new.healthcheck_measure_latency)
new = Record.new(self.zone, 'a', { new = Record.new(self.zone, 'a', {
'ttl': 44, 'ttl': 44,
@ -774,6 +776,7 @@ class TestRecord(TestCase):
self.assertEquals('a.unit.tests', new.healthcheck_host) self.assertEquals('a.unit.tests', new.healthcheck_host)
self.assertEquals('HTTPS', new.healthcheck_protocol) self.assertEquals('HTTPS', new.healthcheck_protocol)
self.assertEquals(443, new.healthcheck_port) self.assertEquals(443, new.healthcheck_port)
self.assertEquals(True, new.healthcheck_measure_latency)
def test_inored(self): def test_inored(self):
new = Record.new(self.zone, 'txt', { new = Record.new(self.zone, 'txt', {


Loading…
Cancel
Save