add route53

This commit is contained in:
jjofseattle 2013-11-14 11:14:14 -08:00
parent d5b3af202e
commit a11c80fe20
8 changed files with 276 additions and 1 deletions

View File

@ -11,3 +11,4 @@ from .s3bucket_path import mock_s3bucket_path
from .ses import mock_ses
from .sqs import mock_sqs
from .sts import mock_sts
from .route53 import mock_route53

View File

@ -8,6 +8,7 @@ from moto.s3bucket_path import s3bucket_path_backend
from moto.ses import ses_backend
from moto.sqs import sqs_backend
from moto.sts import sts_backend
from moto.route53 import route53_backend
BACKENDS = {
'autoscaling': autoscaling_backend,
@ -20,4 +21,5 @@ BACKENDS = {
'ses': ses_backend,
'sqs': sqs_backend,
'sts': sts_backend,
'route53': route53_backend
}

2
moto/route53/__init__.py Normal file
View File

@ -0,0 +1,2 @@
from .models import route53_backend
mock_route53 = route53_backend.decorator

57
moto/route53/models.py Normal file
View File

@ -0,0 +1,57 @@
from moto.core import BaseBackend
from moto.core.utils import get_random_hex
class FakeZone:
def __init__(self, name, id):
self.name = name
self.id = id
self.rrsets = {}
def add_rrset(self, name, rrset):
self.rrsets[name] = rrset
def delete_rrset(self, name):
del self.rrsets[name]
class FakeResourceRecord:
def __init__(self, value):
pass
class FakeResourceRecordSet:
def __init__(self, name, type, ttl, rrlist):
self.name = name
self.type = type
self.ttl = ttl
self.rrList = rrlist
class Route53Backend(BaseBackend):
def __init__(self):
self.zones = {}
def create_hosted_zone(self, name):
new_id = get_random_hex()
new_zone = FakeZone(name, new_id)
self.zones[new_id] = new_zone
return new_zone
def get_all_hosted_zones(self):
return self.zones.values()
def get_hosted_zone(self, id):
return self.zones.get(id)
def delete_hosted_zone(self, id):
zone = self.zones.get(id)
if zone:
del self.zones[id]
return zone
return None
route53_backend = Route53Backend()

130
moto/route53/responses.py Normal file
View File

@ -0,0 +1,130 @@
from jinja2 import Template
from urlparse import parse_qs, urlparse
from .models import route53_backend
import xmltodict
import dicttoxml
def list_or_create_hostzone_response(request, full_url, headers):
if request.method == "POST":
r = xmltodict.parse(request.body)
new_zone = route53_backend.create_hosted_zone(r["CreateHostedZoneRequest"]["Name"])
template = Template(CREATE_HOSTED_ZONE_RESPONSE)
return 201, headers, template.render(zone=new_zone)
elif request.method == "GET":
all_zones = route53_backend.get_all_hosted_zones()
template = Template(LIST_HOSTED_ZONES_RESPONSE)
return 200, headers, template.render(zones=all_zones)
def get_or_delete_hostzone_response(request, full_url, headers):
parsed_url = urlparse(full_url)
zoneid = parsed_url.path.rstrip('/').rsplit('/', 1)[1]
the_zone = route53_backend.get_hosted_zone(zoneid)
if not the_zone:
return 404, headers, "Zone %s not Found" % zoneid
if request.method == "GET":
template = Template(GET_HOSTED_ZONE_RESPONSE)
return 200, headers, template.render(zone=the_zone)
elif request.method == "DELETE":
route53_backend.delete_hosted_zone(zoneid)
return 200, headers, DELETE_HOSTED_ZONE_RESPONSE
def rrset_response(request, full_url, headers):
parsed_url = urlparse(full_url)
method = request.method
zoneid = parsed_url.path.rstrip('/').rsplit('/', 2)[1]
the_zone = route53_backend.get_hosted_zone(zoneid)
if not the_zone:
return 404, headers, "Zone %s Not Found" % zoneid
if method == "POST":
r = xmltodict.parse(request.body)
for k, v in r['ChangeResourceRecordSetsRequest']['ChangeBatch']['Changes'].items():
action = v['Action']
rrset = v['ResourceRecordSet']
if action == 'CREATE':
the_zone.add_rrset(rrset["Name"], rrset)
elif action == "DELETE":
the_zone.delete_rrset(rrset["Name"])
return 200, headers, CHANGE_RRSET_RESPONSE
elif method == "GET":
querystring = parse_qs(parsed_url.query)
template = Template(LIST_RRSET_REPONSE)
rrset_list = []
for key, value in the_zone.rrsets.items():
if 'type' not in querystring or querystring["type"][0] == value["Type"]:
rrset_list.append(dicttoxml.dicttoxml({"ResourceRecordSet": value}, root=False))
return 200, headers, template.render(rrsets=rrset_list)
def not_implemented_response(request, full_url, headers):
parsed_url = urlparse(full_url)
raise NotImplementedError('handling of %s is not yet implemented' % parsed_url.path)
LIST_RRSET_REPONSE = """<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<ResourceRecordSets>
{% for rrset in rrsets %}
{{ rrset }}
{% endfor %}
</ResourceRecordSets>
</ListResourceRecordSetsResponse>"""
CHANGE_RRSET_RESPONSE = """<ChangeResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<ChangeInfo>
<Status>PENDING</Status>
<SubmittedAt>2010-09-10T01:36:41.958Z</SubmittedAt>
</ChangeInfo>
</ChangeResourceRecordSetsResponse>"""
DELETE_HOSTED_ZONE_RESPONSE = """<DeleteHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<ChangeInfo>
</ChangeInfo>
</DeleteHostedZoneResponse>"""
GET_HOSTED_ZONE_RESPONSE = """<GetHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<HostedZone>
<Id>/hostedzone/{{ zone.id }}</Id>
<Name>{{ zone.name }}</Name>
<ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
</HostedZone>
<DelegationSet>
<NameServer>moto.test.com</NameServer>
</DelegationSet>
</GetHostedZoneResponse>"""
CREATE_HOSTED_ZONE_RESPONSE = """<CreateHostedZoneResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<HostedZone>
<Id>/hostedzone/{{ zone.id }}</Id>
<Name>{{ zone.name }}</Name>
<ResourceRecordSetCount>0</ResourceRecordSetCount>
</HostedZone>
<DelegationSet>
<NameServers>
<NameServer>moto.test.com</NameServer>
</NameServers>
</DelegationSet>
</CreateHostedZoneResponse>"""
LIST_HOSTED_ZONES_RESPONSE = """<ListHostedZonesResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
<HostedZones>
{% for zone in zones %}
<HostedZone>
<Id>{{ zone.id }}</Id>
<Name>{{ zone.name }}</Name>
<ResourceRecordSetCount>{{ zone.rrsets|count }}</ResourceRecordSetCount>
</HostedZone>
{% endfor %}
</HostedZones>
</ListHostedZonesResponse>"""

12
moto/route53/urls.py Normal file
View File

@ -0,0 +1,12 @@
import responses
url_bases = [
#"https://route53.amazonaws.com/201\d-\d\d-\d\d/hostedzone",
"https://route53.amazonaws.com/201.-..-../hostedzone",
]
url_paths = {
'{0}$': responses.list_or_create_hostzone_response,
'{0}/.+$': responses.get_or_delete_hostzone_response,
'{0}/.+/rrset$': responses.rrset_response,
}

View File

@ -4,4 +4,6 @@ mock
nose
https://github.com/spulec/python-coveralls/tarball/796d9dba34b759664e42ba39e6414209a0f319ad
requests
sure
sure
xmltodict
dicttoxml

View File

@ -0,0 +1,69 @@
import urllib2
import boto
from boto.exception import S3ResponseError
from boto.s3.key import Key
from boto.route53.record import ResourceRecordSets
from freezegun import freeze_time
import requests
import sure # noqa
from moto import mock_route53
@mock_route53
def test_hosted_zone():
conn = boto.connect_route53('the_key', 'the_secret')
firstzone = conn.create_hosted_zone("testdns.aws.com")
zones = conn.get_all_hosted_zones()
len(zones["ListHostedZonesResponse"]["HostedZones"]).should.equal(1)
secondzone = conn.create_hosted_zone("testdns1.aws.com")
zones = conn.get_all_hosted_zones()
len(zones["ListHostedZonesResponse"]["HostedZones"]).should.equal(2)
id1 = firstzone["CreateHostedZoneResponse"]["HostedZone"]["Id"]
zone = conn.get_hosted_zone(id1)
zone["GetHostedZoneResponse"]["HostedZone"]["Name"].should.equal("testdns.aws.com")
conn.delete_hosted_zone(id1)
zones = conn.get_all_hosted_zones()
len(zones["ListHostedZonesResponse"]["HostedZones"]).should.equal(1)
conn.get_hosted_zone.when.called_with("abcd").should.throw(boto.route53.exception.DNSServerError, "404 Not Found")
@mock_route53
def test_rrset():
conn = boto.connect_route53('the_key', 'the_secret')
zone = conn.create_hosted_zone("testdns.aws.com")
zoneid = zone["CreateHostedZoneResponse"]["HostedZone"]["Id"]
changes = ResourceRecordSets(conn, zoneid)
change = changes.add_change("CREATE", "foo.bar.testdns.aws.com", "A")
change.add_value("1.2.3.4")
changes.commit()
rrsets = conn.get_all_rrsets(zoneid, type="A")
rrsets.should.have.length_of(1)
rrsets[0].resource_records[0].should.equal('1.2.3.4')
rrsets = conn.get_all_rrsets(zoneid, type="CNAME")
rrsets.should.have.length_of(0)
changes = ResourceRecordSets(conn, zoneid)
changes.add_change("DELETE", "foo.bar.testdns.aws.com", "A")
changes.commit()
rrsets = conn.get_all_rrsets(zoneid)
rrsets.should.have.length_of(0)