Add route53 health checks.
This commit is contained in:
parent
adb26986eb
commit
585ef7b768
@ -37,6 +37,7 @@ MODEL_MAP = {
|
|||||||
"AWS::RDS::DBInstance": rds_models.Database,
|
"AWS::RDS::DBInstance": rds_models.Database,
|
||||||
"AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup,
|
"AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup,
|
||||||
"AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup,
|
"AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup,
|
||||||
|
"AWS::Route53::HealthCheck": route53_models.HealthCheck,
|
||||||
"AWS::Route53::HostedZone": route53_models.FakeZone,
|
"AWS::Route53::HostedZone": route53_models.FakeZone,
|
||||||
"AWS::Route53::RecordSet": route53_models.RecordSet,
|
"AWS::Route53::RecordSet": route53_models.RecordSet,
|
||||||
"AWS::Route53::RecordSetGroup": route53_models.RecordSetGroup,
|
"AWS::Route53::RecordSetGroup": route53_models.RecordSetGroup,
|
||||||
|
@ -1,11 +1,65 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import uuid
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
|
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
from moto.core.utils import get_random_hex
|
from moto.core.utils import get_random_hex
|
||||||
|
|
||||||
|
|
||||||
|
class HealthCheck(object):
|
||||||
|
def __init__(self, health_check_id, health_check_args):
|
||||||
|
self.id = health_check_id
|
||||||
|
self.ip_address = health_check_args.get("ip_address")
|
||||||
|
self.port = health_check_args.get("port", 80)
|
||||||
|
self._type = health_check_args.get("type")
|
||||||
|
self.resource_path = health_check_args.get("resource_path")
|
||||||
|
self.fqdn = health_check_args.get("fqdn")
|
||||||
|
self.search_string = health_check_args.get("search_string")
|
||||||
|
self.request_interval = health_check_args.get("request_interval", 30)
|
||||||
|
self.failure_threshold = health_check_args.get("failure_threshold", 3)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def physical_resource_id(self):
|
||||||
|
return self.id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||||
|
properties = cloudformation_json['Properties']['HealthCheckConfig']
|
||||||
|
health_check_args = {
|
||||||
|
"ip_address": properties.get('IPAddress'),
|
||||||
|
"port": properties.get('Port'),
|
||||||
|
"type": properties['Type'],
|
||||||
|
"resource_path": properties.get('ResourcePath'),
|
||||||
|
"fqdn": properties.get('FullyQualifiedDomainName'),
|
||||||
|
"search_string": properties.get('SearchString'),
|
||||||
|
"request_interval": properties.get('RequestInterval'),
|
||||||
|
"failure_threshold": properties.get('FailureThreshold'),
|
||||||
|
}
|
||||||
|
health_check = route53_backend.create_health_check(health_check_args)
|
||||||
|
return health_check
|
||||||
|
|
||||||
|
def to_xml(self):
|
||||||
|
template = Template("""<HealthCheck>
|
||||||
|
<Id>{{ health_check.id }}</Id>
|
||||||
|
<CallerReference>example.com 192.0.2.17</CallerReference>
|
||||||
|
<HealthCheckConfig>
|
||||||
|
<IPAddress>{{ health_check.ip_address }}</IPAddress>
|
||||||
|
<Port>{{ health_check.port }}</Port>
|
||||||
|
<Type>{{ health_check._type }}</Type>
|
||||||
|
<ResourcePath>{{ health_check.resource_path }}</ResourcePath>
|
||||||
|
<FullyQualifiedDomainName>{{ health_check.fqdn }}</FullyQualifiedDomainName>
|
||||||
|
<RequestInterval>{{ health_check.request_interval }}</RequestInterval>
|
||||||
|
<FailureThreshold>{{ health_check.failure_threshold }}</FailureThreshold>
|
||||||
|
{% if health_check.search_string %}
|
||||||
|
<SearchString>{{ health_check.search_string }}</SearchString>
|
||||||
|
{% endif %}
|
||||||
|
</HealthCheckConfig>
|
||||||
|
<HealthCheckVersion>1</HealthCheckVersion>
|
||||||
|
</HealthCheck>""")
|
||||||
|
return template.render(health_check=self)
|
||||||
|
|
||||||
|
|
||||||
class RecordSet(object):
|
class RecordSet(object):
|
||||||
def __init__(self, kwargs):
|
def __init__(self, kwargs):
|
||||||
self.name = kwargs.get('Name')
|
self.name = kwargs.get('Name')
|
||||||
@ -14,6 +68,7 @@ class RecordSet(object):
|
|||||||
self.records = kwargs.get('ResourceRecords', [])
|
self.records = kwargs.get('ResourceRecords', [])
|
||||||
self.set_identifier = kwargs.get('SetIdentifier')
|
self.set_identifier = kwargs.get('SetIdentifier')
|
||||||
self.weight = kwargs.get('Weight')
|
self.weight = kwargs.get('Weight')
|
||||||
|
self.health_check = kwargs.get('HealthCheckId')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||||
@ -42,7 +97,9 @@ class RecordSet(object):
|
|||||||
</ResourceRecord>
|
</ResourceRecord>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ResourceRecords>
|
</ResourceRecords>
|
||||||
<HealthCheckId></HealthCheckId>
|
{% if record_set.health_check %}
|
||||||
|
<HealthCheckId>{{ record_set.health_check }}</HealthCheckId>
|
||||||
|
{% endif %}
|
||||||
</ResourceRecordSet>""")
|
</ResourceRecordSet>""")
|
||||||
return template.render(record_set=self)
|
return template.render(record_set=self)
|
||||||
|
|
||||||
@ -111,6 +168,7 @@ class Route53Backend(BaseBackend):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.zones = {}
|
self.zones = {}
|
||||||
|
self.health_checks = {}
|
||||||
|
|
||||||
def create_hosted_zone(self, name):
|
def create_hosted_zone(self, name):
|
||||||
new_id = get_random_hex()
|
new_id = get_random_hex()
|
||||||
@ -135,5 +193,16 @@ class Route53Backend(BaseBackend):
|
|||||||
del self.zones[id_]
|
del self.zones[id_]
|
||||||
return zone
|
return zone
|
||||||
|
|
||||||
|
def create_health_check(self, health_check_args):
|
||||||
|
health_check_id = str(uuid.uuid4())
|
||||||
|
health_check = HealthCheck(health_check_id, health_check_args)
|
||||||
|
self.health_checks[health_check_id] = health_check
|
||||||
|
return health_check
|
||||||
|
|
||||||
|
def get_health_checks(self):
|
||||||
|
return self.health_checks.values()
|
||||||
|
|
||||||
|
def delete_health_check(self, health_check_id):
|
||||||
|
return self.health_checks.pop(health_check_id, None)
|
||||||
|
|
||||||
route53_backend = Route53Backend()
|
route53_backend = Route53Backend()
|
||||||
|
@ -70,6 +70,35 @@ def rrset_response(request, full_url, headers):
|
|||||||
return 200, headers, template.render(record_sets=record_sets)
|
return 200, headers, template.render(record_sets=record_sets)
|
||||||
|
|
||||||
|
|
||||||
|
def health_check_response(request, full_url, headers):
|
||||||
|
parsed_url = urlparse(full_url)
|
||||||
|
method = request.method
|
||||||
|
|
||||||
|
if method == "POST":
|
||||||
|
properties = xmltodict.parse(request.body)['CreateHealthCheckRequest']['HealthCheckConfig']
|
||||||
|
health_check_args = {
|
||||||
|
"ip_address": properties.get('IPAddress'),
|
||||||
|
"port": properties.get('Port'),
|
||||||
|
"type": properties['Type'],
|
||||||
|
"resource_path": properties.get('ResourcePath'),
|
||||||
|
"fqdn": properties.get('FullyQualifiedDomainName'),
|
||||||
|
"search_string": properties.get('SearchString'),
|
||||||
|
"request_interval": properties.get('RequestInterval'),
|
||||||
|
"failure_threshold": properties.get('FailureThreshold'),
|
||||||
|
}
|
||||||
|
health_check = route53_backend.create_health_check(health_check_args)
|
||||||
|
template = Template(CREATE_HEALTH_CHECK_RESPONSE)
|
||||||
|
return 201, headers, template.render(health_check=health_check)
|
||||||
|
elif method == "DELETE":
|
||||||
|
health_check_id = parsed_url.path.split("/")[-1]
|
||||||
|
route53_backend.delete_health_check(health_check_id)
|
||||||
|
return 200, headers, DELETE_HEALTH_CHECK_REPONSE
|
||||||
|
elif method == "GET":
|
||||||
|
template = Template(LIST_HEALTH_CHECKS_REPONSE)
|
||||||
|
health_checks = route53_backend.get_health_checks()
|
||||||
|
return 200, headers, template.render(health_checks=health_checks)
|
||||||
|
|
||||||
|
|
||||||
LIST_RRSET_REPONSE = """<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
|
LIST_RRSET_REPONSE = """<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
|
||||||
<ResourceRecordSets>
|
<ResourceRecordSets>
|
||||||
{% for record_set in record_sets %}
|
{% for record_set in record_sets %}
|
||||||
@ -126,3 +155,23 @@ LIST_HOSTED_ZONES_RESPONSE = """<ListHostedZonesResponse xmlns="https://route53.
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</HostedZones>
|
</HostedZones>
|
||||||
</ListHostedZonesResponse>"""
|
</ListHostedZonesResponse>"""
|
||||||
|
|
||||||
|
CREATE_HEALTH_CHECK_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CreateHealthCheckResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||||
|
{{ health_check.to_xml() }}
|
||||||
|
</CreateHealthCheckResponse>"""
|
||||||
|
|
||||||
|
LIST_HEALTH_CHECKS_REPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ListHealthChecksResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||||
|
<HealthChecks>
|
||||||
|
{% for health_check in health_checks %}
|
||||||
|
{{ health_check.to_xml() }}
|
||||||
|
{% endfor %}
|
||||||
|
</HealthChecks>
|
||||||
|
<IsTruncated>false</IsTruncated>
|
||||||
|
<MaxItems>{{ health_checks|length }}</MaxItems>
|
||||||
|
</ListHealthChecksResponse>"""
|
||||||
|
|
||||||
|
DELETE_HEALTH_CHECK_REPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<DeleteHealthCheckResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||||
|
</DeleteHealthCheckResponse>"""
|
||||||
|
@ -2,11 +2,12 @@ from __future__ import unicode_literals
|
|||||||
from . import responses
|
from . import responses
|
||||||
|
|
||||||
url_bases = [
|
url_bases = [
|
||||||
"https://route53.amazonaws.com/201.-..-../hostedzone",
|
"https://route53.amazonaws.com/201.-..-../",
|
||||||
]
|
]
|
||||||
|
|
||||||
url_paths = {
|
url_paths = {
|
||||||
'{0}$': responses.list_or_create_hostzone_response,
|
'{0}hostedzone$': responses.list_or_create_hostzone_response,
|
||||||
'{0}/[^/]+$': responses.get_or_delete_hostzone_response,
|
'{0}hostedzone/[^/]+$': responses.get_or_delete_hostzone_response,
|
||||||
'{0}/[^/]+/rrset$': responses.rrset_response,
|
'{0}hostedzone/[^/]+/rrset$': responses.rrset_response,
|
||||||
|
'{0}healthcheck': responses.health_check_response,
|
||||||
}
|
}
|
||||||
|
39
tests/test_cloudformation/fixtures/route53_health_check.py
Normal file
39
tests/test_cloudformation/fixtures/route53_health_check.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
template = {
|
||||||
|
"Resources" : {
|
||||||
|
"HostedZone": {
|
||||||
|
"Type" : "AWS::Route53::HostedZone",
|
||||||
|
"Properties" : {
|
||||||
|
"Name" : "my_zone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"my_health_check": {
|
||||||
|
"Type": "AWS::Route53::HealthCheck",
|
||||||
|
"Properties" : {
|
||||||
|
"HealthCheckConfig" : {
|
||||||
|
"FailureThreshold" : 3,
|
||||||
|
"IPAddress" : "10.0.0.4",
|
||||||
|
"Port" : 80,
|
||||||
|
"RequestInterval" : 10,
|
||||||
|
"ResourcePath" : "/",
|
||||||
|
"Type" : "HTTP",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"myDNSRecord" : {
|
||||||
|
"Type" : "AWS::Route53::RecordSet",
|
||||||
|
"Properties" : {
|
||||||
|
"HostedZoneName" : { "Ref" : "HostedZone" },
|
||||||
|
"Comment" : "DNS name for my instance.",
|
||||||
|
"Name" : "my_record_set",
|
||||||
|
"Type" : "A",
|
||||||
|
"TTL" : "900",
|
||||||
|
"ResourceRecords" : ["my.example.com"],
|
||||||
|
"HealthCheckId": {"Ref": "my_health_check"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
@ -28,6 +28,7 @@ from .fixtures import (
|
|||||||
fn_join,
|
fn_join,
|
||||||
rds_mysql_with_read_replica,
|
rds_mysql_with_read_replica,
|
||||||
route53_ec2_instance_with_public_ip,
|
route53_ec2_instance_with_public_ip,
|
||||||
|
route53_health_check,
|
||||||
route53_roundrobin,
|
route53_roundrobin,
|
||||||
single_instance_with_ebs_volume,
|
single_instance_with_ebs_volume,
|
||||||
vpc_eip,
|
vpc_eip,
|
||||||
@ -845,3 +846,38 @@ def test_route53_ec2_instance_with_public_ip():
|
|||||||
record_set1.ttl.should.equal('900')
|
record_set1.ttl.should.equal('900')
|
||||||
record_set1.weight.should.equal(None)
|
record_set1.weight.should.equal(None)
|
||||||
record_set1.resource_records[0].should.equal("10.0.0.25")
|
record_set1.resource_records[0].should.equal("10.0.0.25")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_cloudformation()
|
||||||
|
@mock_route53()
|
||||||
|
def test_route53_associate_health_check():
|
||||||
|
route53_conn = boto.connect_route53()
|
||||||
|
|
||||||
|
template_json = json.dumps(route53_health_check.template)
|
||||||
|
conn = boto.cloudformation.connect_to_region("us-west-1")
|
||||||
|
stack = conn.create_stack(
|
||||||
|
"test_stack",
|
||||||
|
template_body=template_json,
|
||||||
|
)
|
||||||
|
|
||||||
|
checks = route53_conn.get_list_health_checks()['ListHealthChecksResponse']['HealthChecks']
|
||||||
|
list(checks).should.have.length_of(1)
|
||||||
|
check = checks[0]
|
||||||
|
health_check_id = check['Id']
|
||||||
|
config = check['HealthCheckConfig']
|
||||||
|
config["FailureThreshold"].should.equal("3")
|
||||||
|
config["IPAddress"].should.equal("10.0.0.4")
|
||||||
|
config["Port"].should.equal("80")
|
||||||
|
config["RequestInterval"].should.equal("10")
|
||||||
|
config["ResourcePath"].should.equal("/")
|
||||||
|
config["Type"].should.equal("HTTP")
|
||||||
|
|
||||||
|
zones = route53_conn.get_all_hosted_zones()['ListHostedZonesResponse']['HostedZones']
|
||||||
|
list(zones).should.have.length_of(1)
|
||||||
|
zone_id = zones[0]['Id']
|
||||||
|
|
||||||
|
rrsets = route53_conn.get_all_rrsets(zone_id)
|
||||||
|
rrsets.should.have.length_of(1)
|
||||||
|
|
||||||
|
record_set = rrsets[0]
|
||||||
|
record_set.health_check.should.equal(health_check_id)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import boto
|
import boto
|
||||||
|
from boto.route53.healthcheck import HealthCheck
|
||||||
from boto.route53.record import ResourceRecordSets
|
from boto.route53.record import ResourceRecordSets
|
||||||
|
|
||||||
import sure # noqa
|
import sure # noqa
|
||||||
@ -89,3 +90,79 @@ def test_rrset():
|
|||||||
|
|
||||||
rrsets = conn.get_all_rrsets(zoneid, name="foo.foo.testdns.aws.com", type="A")
|
rrsets = conn.get_all_rrsets(zoneid, name="foo.foo.testdns.aws.com", type="A")
|
||||||
rrsets.should.have.length_of(0)
|
rrsets.should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_route53
|
||||||
|
def test_create_health_check():
|
||||||
|
conn = boto.connect_route53('the_key', 'the_secret')
|
||||||
|
|
||||||
|
check = HealthCheck(
|
||||||
|
ip_addr="10.0.0.25",
|
||||||
|
port=80,
|
||||||
|
hc_type="HTTP",
|
||||||
|
resource_path="/",
|
||||||
|
fqdn="example.com",
|
||||||
|
string_match="a good response",
|
||||||
|
request_interval=10,
|
||||||
|
failure_threshold=2,
|
||||||
|
)
|
||||||
|
conn.create_health_check(check)
|
||||||
|
|
||||||
|
checks = conn.get_list_health_checks()['ListHealthChecksResponse']['HealthChecks']
|
||||||
|
list(checks).should.have.length_of(1)
|
||||||
|
check = checks[0]
|
||||||
|
config = check['HealthCheckConfig']
|
||||||
|
config['IPAddress'].should.equal("10.0.0.25")
|
||||||
|
config['Port'].should.equal("80")
|
||||||
|
config['Type'].should.equal("HTTP")
|
||||||
|
config['ResourcePath'].should.equal("/")
|
||||||
|
config['FullyQualifiedDomainName'].should.equal("example.com")
|
||||||
|
config['SearchString'].should.equal("a good response")
|
||||||
|
config['RequestInterval'].should.equal("10")
|
||||||
|
config['FailureThreshold'].should.equal("2")
|
||||||
|
|
||||||
|
|
||||||
|
@mock_route53
|
||||||
|
def test_delete_health_check():
|
||||||
|
conn = boto.connect_route53('the_key', 'the_secret')
|
||||||
|
|
||||||
|
check = HealthCheck(
|
||||||
|
ip_addr="10.0.0.25",
|
||||||
|
port=80,
|
||||||
|
hc_type="HTTP",
|
||||||
|
resource_path="/",
|
||||||
|
)
|
||||||
|
conn.create_health_check(check)
|
||||||
|
|
||||||
|
checks = conn.get_list_health_checks()['ListHealthChecksResponse']['HealthChecks']
|
||||||
|
list(checks).should.have.length_of(1)
|
||||||
|
health_check_id = checks[0]['Id']
|
||||||
|
|
||||||
|
conn.delete_health_check(health_check_id)
|
||||||
|
checks = conn.get_list_health_checks()['ListHealthChecksResponse']['HealthChecks']
|
||||||
|
list(checks).should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_route53
|
||||||
|
def test_use_health_check_in_resource_record_set():
|
||||||
|
conn = boto.connect_route53('the_key', 'the_secret')
|
||||||
|
|
||||||
|
check = HealthCheck(
|
||||||
|
ip_addr="10.0.0.25",
|
||||||
|
port=80,
|
||||||
|
hc_type="HTTP",
|
||||||
|
resource_path="/",
|
||||||
|
)
|
||||||
|
check = conn.create_health_check(check)['CreateHealthCheckResponse']['HealthCheck']
|
||||||
|
check_id = check['Id']
|
||||||
|
|
||||||
|
zone = conn.create_hosted_zone("testdns.aws.com")
|
||||||
|
zone_id = zone["CreateHostedZoneResponse"]["HostedZone"]["Id"].split("/")[-1]
|
||||||
|
|
||||||
|
changes = ResourceRecordSets(conn, zone_id)
|
||||||
|
change = changes.add_change("CREATE", "foo.bar.testdns.aws.com", "A", health_check=check_id)
|
||||||
|
change.add_value("1.2.3.4")
|
||||||
|
changes.commit()
|
||||||
|
|
||||||
|
record_sets = conn.get_all_rrsets(zone_id)
|
||||||
|
record_sets[0].health_check.should.equal(check_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user