Permalink
Browse files

Merge pull request #2542 from cloudregistry/r53_list_rrsets_paging_fix

Truncated Response Handling in Route53 ListResourceRecordSets. Fixes #2542.
  • Loading branch information...
danielgtaylor committed Sep 2, 2014
2 parents ac2b311 + 0e76262 commit 3ba380f0e2098d7d475225e3b75deb3cb4b59384
Showing with 108 additions and 5 deletions.
  1. +1 −1 boto/route53/connection.py
  2. +8 −3 boto/route53/record.py
  3. +99 −1 tests/unit/route53/test_connection.py
@@ -383,7 +383,7 @@ def get_all_rrsets(self, hosted_zone_id, type=None,
"""
params = {'type': type, 'name': name,
'Identifier': identifier, 'maxitems': maxitems}
'identifier': identifier, 'maxitems': maxitems}
uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id)
response = self.make_request('GET', uri, params=params)
body = response.read()
View
@@ -54,6 +54,7 @@ def __init__(self, connection=None, hosted_zone_id=None, comment=None):
self.changes = []
self.next_record_name = None
self.next_record_type = None
self.next_record_identifier = None
super(ResourceRecordSets, self).__init__([('ResourceRecordSet', Record)])
def __repr__(self):
@@ -165,12 +166,14 @@ def commit(self):
return self.connection.change_rrsets(self.hosted_zone_id, self.to_xml())
def endElement(self, name, value, connection):
"""Overwritten to also add the NextRecordName and
NextRecordType to the base object"""
"""Overwritten to also add the NextRecordName,
NextRecordType and NextRecordIdentifier to the base object"""
if name == 'NextRecordName':
self.next_record_name = value
elif name == 'NextRecordType':
self.next_record_type = value
elif name == 'NextRecordIdentifier':
self.next_record_identifier = value
else:
return super(ResourceRecordSets, self).endElement(name, value, connection)
@@ -183,7 +186,9 @@ def __iter__(self):
yield obj
if self.is_truncated:
self.is_truncated = False
results = self.connection.get_all_rrsets(self.hosted_zone_id, name=self.next_record_name, type=self.next_record_type)
results = self.connection.get_all_rrsets(self.hosted_zone_id, name=self.next_record_name,
type=self.next_record_type,
identifier=self.next_record_identifier)
else:
results = None
self.is_truncated = truncated
@@ -23,7 +23,6 @@
from tests.compat import mock, unittest
import re
import xml.dom.minidom
from boto.exception import BotoServerError
from boto.route53.connection import Route53Connection
from boto.route53.exception import DNSServerError
@@ -33,6 +32,8 @@
from nose.plugins.attrib import attr
from tests.unit import AWSMockServiceTestCase
from boto.compat import six
urllib = six.moves.urllib
@attr(route53=True)
class TestRoute53Connection(AWSMockServiceTestCase):
@@ -371,6 +372,103 @@ def test_get_all_rr_sets(self):
self.assertEqual(healthcheck_record.identifier, 'latency-example-us-west-2-evaluate-health-healthcheck')
self.assertEqual(healthcheck_record.alias_dns_name, 'example-123456-evaluate-health-healthcheck.us-west-2.elb.amazonaws.com.')
@attr(route53=True)
class TestTruncatedGetAllRRSetsRoute53(AWSMockServiceTestCase):
connection_class = Route53Connection
def setUp(self):
super(TestTruncatedGetAllRRSetsRoute53, self).setUp()
def default_body(self):
return b"""
<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
<ResourceRecordSets>
<ResourceRecordSet>
<Name>example.com.</Name>
<Type>NS</Type>
<TTL>900</TTL>
<ResourceRecords>
<ResourceRecord>
<Value>ns-91.awsdns-41.co.uk.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-1929.awsdns-93.net.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-12.awsdns-21.org.</Value>
</ResourceRecord>
<ResourceRecord>
<Value>ns-102.awsdns-96.com.</Value>
</ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
<ResourceRecordSet>
<Name>example.com.</Name>
<Type>SOA</Type>
<TTL>1800</TTL>
<ResourceRecords>
<ResourceRecord>
<Value>ns-1929.awsdns-93.net. hostmaster.awsdns.net. 1 10800 3600 604800 1800</Value>
</ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
<ResourceRecordSet>
<Name>wrr.example.com.</Name>
<Type>A</Type>
<SetIdentifier>primary</SetIdentifier>
<Weight>100</Weight>
<TTL>300</TTL>
<ResourceRecords>
<ResourceRecord><Value>127.0.0.1</Value></ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
</ResourceRecordSets>
<IsTruncated>true</IsTruncated>
<NextRecordName>wrr.example.com.</NextRecordName>
<NextRecordType>A</NextRecordType>
<NextRecordIdentifier>secondary</NextRecordIdentifier>
<MaxItems>3</MaxItems>
</ListResourceRecordSetsResponse>"""
def paged_body(self):
return b"""
<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
<ResourceRecordSets>
<ResourceRecordSet>
<Name>wrr.example.com.</Name>
<Type>A</Type>
<SetIdentifier>secondary</SetIdentifier>
<Weight>50</Weight>
<TTL>300</TTL>
<ResourceRecords>
<ResourceRecord><Value>127.0.0.2</Value></ResourceRecord>
</ResourceRecords>
</ResourceRecordSet>
</ResourceRecordSets>
<IsTruncated>false</IsTruncated>
<MaxItems>3</MaxItems>
</ListResourceRecordSetsResponse>"""
def test_get_all_rr_sets(self):
self.set_http_response(status_code=200)
response = self.service_connection.get_all_rrsets("Z1111", maxitems=3)
# made first request
self.assertEqual(self.actual_request.path, '/2013-04-01/hostedzone/Z1111/rrset?maxitems=3')
# anticipate a second request when we page it
self.set_http_response(status_code=200, body=self.paged_body())
# this should trigger another call to get_all_rrsets
self.assertEqual(len(list(response)), 4)
url_parts = urllib.parse.urlparse(self.actual_request.path)
self.assertEqual(url_parts.path, '/2013-04-01/hostedzone/Z1111/rrset')
self.assertEqual(urllib.parse.parse_qs(url_parts.query),
dict(type=['A'], name=['wrr.example.com.'], identifier=['secondary']))
@attr(route53=True)
class TestCreateHealthCheckRoute53IpAddress(AWSMockServiceTestCase):
connection_class = Route53Connection

0 comments on commit 3ba380f

Please sign in to comment.