add route53
This commit is contained in:
		
							parent
							
								
									d5b3af202e
								
							
						
					
					
						commit
						a11c80fe20
					
				| @ -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 | ||||
|  | ||||
| @ -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
									
								
							
							
						
						
									
										2
									
								
								moto/route53/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| from .models import route53_backend | ||||
| mock_route53 = route53_backend.decorator | ||||
							
								
								
									
										57
									
								
								moto/route53/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								moto/route53/models.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										130
									
								
								moto/route53/responses.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										12
									
								
								moto/route53/urls.py
									
									
									
									
									
										Normal 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, | ||||
| } | ||||
| @ -5,3 +5,5 @@ nose | ||||
| https://github.com/spulec/python-coveralls/tarball/796d9dba34b759664e42ba39e6414209a0f319ad | ||||
| requests | ||||
| sure | ||||
| xmltodict | ||||
| dicttoxml | ||||
|  | ||||
							
								
								
									
										69
									
								
								tests/test_route53/test_route53.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tests/test_route53/test_route53.py
									
									
									
									
									
										Normal 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) | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user