From 7a32e4c912e4578864e2ad23592de467543f2f1f Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Thu, 9 Dec 2021 10:24:15 -0800 Subject: [PATCH] Add value status support to Route53 dynamic --- octodns/provider/route53.py | 57 +++++++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/octodns/provider/route53.py b/octodns/provider/route53.py index a6a3105..67fab21 100644 --- a/octodns/provider/route53.py +++ b/octodns/provider/route53.py @@ -80,9 +80,11 @@ class _Route53Record(EqualityTupleMixin): # be served out according to their weights for i, value in enumerate(pool.data['values']): weight = value['weight'] + status = value['status'] value = value['value'] ret.add(_Route53DynamicValue(provider, record, pool_name, - value, weight, i, creating)) + value, weight, status, i, + creating)) # Rules for i, rule in enumerate(record.dynamic.rules): @@ -345,19 +347,21 @@ class _Route53DynamicRule(_Route53Record): class _Route53DynamicValue(_Route53Record): - def __init__(self, provider, record, pool_name, value, weight, index, - creating): + def __init__(self, provider, record, pool_name, value, weight, status, + index, creating): fqdn_override = f'_octodns-{pool_name}-value.{record.fqdn}' super(_Route53DynamicValue, self).__init__(provider, record, creating, fqdn_override=fqdn_override) self.pool_name = pool_name + self.status = status self.index = index value_convert = getattr(self, f'_value_convert_{record._type}') self.value = value_convert(value, record) self.weight = weight self.health_check_id = provider.get_health_check_id(record, self.value, + self.status, creating) @property @@ -433,7 +437,7 @@ class _Route53GeoRecord(_Route53Record): value = geo.values[0] self.health_check_id = provider.get_health_check_id(record, value, - creating) + 'obey', creating) def mod(self, action, existing_rrsets): geo = self.geo @@ -599,6 +603,7 @@ class Route53Provider(BaseProvider): ''' SUPPORTS_GEO = True SUPPORTS_DYNAMIC = True + SUPPORTS_POOL_VALUE_STATUS = True SUPPORTS = set(('A', 'AAAA', 'CAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', 'SRV', 'TXT')) @@ -914,7 +919,25 @@ class Route53Provider(BaseProvider): # it's ignored only used to make sure the value is unique pool_name = rrset['SetIdentifier'][:-4] value = rrset['ResourceRecords'][0]['Value'] + try: + health_check_id = rrset.get('HealthCheckId', None) + health_check = self.health_checks[health_check_id] + health_check_config = health_check['HealthCheckConfig'] + if health_check_config['Disabled']: + if health_check_config['Inverted']: + # disabled and inverted means down + status = 'down' + else: + # disabled means always up + status = 'up' + else: + # otherwise obey + status = 'obey' + except KeyError: + # No healthcheck implies status is always up + status = 'up' pools[pool_name]['values'].append({ + 'status': status, 'value': value, 'weight': rrset['Weight'], }) @@ -1092,7 +1115,8 @@ class Route53Provider(BaseProvider): def _health_check_equivalent(self, host, path, protocol, port, measure_latency, request_interval, - health_check, value=None): + health_check, value=None, disabled=None, + inverted=None): config = health_check['HealthCheckConfig'] # So interestingly Route53 normalizes IPv6 addresses to a funky, but @@ -1115,16 +1139,18 @@ class Route53Provider(BaseProvider): port == config['Port'] and \ measure_latency == config['MeasureLatency'] and \ request_interval == config['RequestInterval'] and \ + (disabled is None or disabled == config['Disabled']) and \ + (inverted is None or inverted == config['Inverted']) and \ value == config_ip_address - def get_health_check_id(self, record, value, create): + def get_health_check_id(self, record, value, status, create): # fqdn & the first value are special, we use them to match up health # checks to their records. Route53 health checks check a single ip and # we're going to assume that ips are interchangeable to avoid # health-checking each one independently fqdn = record.fqdn - self.log.debug('get_health_check_id: fqdn=%s, type=%s, value=%s', - fqdn, record._type, value) + self.log.debug('get_health_check_id: fqdn=%s, type=%s, value=%s, ' + 'status=%s', fqdn, record._type, value, status) try: ip_address(str(value)) @@ -1140,6 +1166,15 @@ class Route53Provider(BaseProvider): healthcheck_port = record.healthcheck_port healthcheck_latency = self._healthcheck_measure_latency(record) healthcheck_interval = self._healthcheck_request_interval(record) + if status == 'up': + healthcheck_disabled = True + healthcheck_inverted = False + elif status == 'down': + healthcheck_disabled = True + healthcheck_inverted = True + else: + healthcheck_disabled = False + healthcheck_inverted = False # we're looking for a healthcheck with the current version & our record # type, we'll ignore anything else @@ -1156,7 +1191,9 @@ class Route53Provider(BaseProvider): healthcheck_latency, healthcheck_interval, health_check, - value=value): + value=value, + disabled=healthcheck_disabled, + inverted=healthcheck_inverted): # this is the health check we're looking for self.log.debug('get_health_check_id: found match id=%s', id) return id @@ -1168,6 +1205,8 @@ class Route53Provider(BaseProvider): # no existing matches, we need to create a new health check config = { + 'Disabled': healthcheck_disabled, + 'Inverted': healthcheck_inverted, 'EnableSNI': healthcheck_protocol == 'HTTPS', 'FailureThreshold': 6, 'MeasureLatency': healthcheck_latency,