diff options
author | Kyle Knapp <kyleknap@amazon.com> | 2014-10-07 12:28:44 -0700 |
---|---|---|
committer | Kyle Knapp <kyleknap@amazon.com> | 2014-10-07 12:28:44 -0700 |
commit | 2de87163741f92094f7c85c95930dd9796b45b83 (patch) | |
tree | 7ad53c1e989c9dd2afe02f548257d13d3ecebac4 | |
parent | 63bd53bbb90e01597e5ca394cc1b65269f58203e (diff) | |
parent | cea3e26e4c8a1c078251bc184d93c59530ab7044 (diff) | |
download | boto-2de87163741f92094f7c85c95930dd9796b45b83.tar.gz |
Merge pull request #2631 from kyleknap/route53
Inserted break when iterating Route53 records.
-rw-r--r-- | boto/route53/connection.py | 10 | ||||
-rw-r--r-- | boto/route53/zone.py | 9 | ||||
-rw-r--r-- | tests/unit/route53/test_connection.py | 17 | ||||
-rw-r--r-- | tests/unit/route53/test_zone.py | 63 |
4 files changed, 93 insertions, 6 deletions
diff --git a/boto/route53/connection.py b/boto/route53/connection.py index 9f17781a..c13ab2e0 100644 --- a/boto/route53/connection.py +++ b/boto/route53/connection.py @@ -521,12 +521,18 @@ class Route53Connection(AWSAuthConnection): if response.status == 400: code = response.getheader('Code') - if code and 'PriorRequestNotComplete' in code: + if code: # This is a case where we need to ignore a 400 error, as # Route53 returns this. See # http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html + if 'PriorRequestNotComplete' in code: + error = 'PriorRequestNotComplete' + elif 'Throttling' in code: + error = 'Throttling' + else: + return status msg = "%s, retry attempt %s" % ( - 'PriorRequestNotComplete', + error, i ) next_sleep = min(random.random() * (2 ** i), diff --git a/boto/route53/zone.py b/boto/route53/zone.py index 167a0891..b21c8de4 100644 --- a/boto/route53/zone.py +++ b/boto/route53/zone.py @@ -233,7 +233,14 @@ class Zone(object): # name/type for get_all_rrsets sets the starting record; they # are not a filter - results = [r for r in returned if r.name == name and r.type == type] + results = [] + for r in returned: + if r.name == name and r.type == type: + results.append(r) + # Is at the end of the list of matched records. No need to continue + # since the records are sorted by name and type. + else: + break weight = None region = None diff --git a/tests/unit/route53/test_connection.py b/tests/unit/route53/test_connection.py index a748f307..3c696c7a 100644 --- a/tests/unit/route53/test_connection.py +++ b/tests/unit/route53/test_connection.py @@ -54,7 +54,7 @@ class TestRoute53Connection(AWSMockServiceTestCase): def test_typical_400(self): self.set_http_response(status_code=400, header=[ - ['Code', 'Throttling'], + ['Code', 'AccessDenied'], ]) with self.assertRaises(DNSServerError) as err: @@ -62,11 +62,22 @@ class TestRoute53Connection(AWSMockServiceTestCase): self.assertTrue('It failed.' in str(err.exception)) - @mock.patch('time.sleep') - def test_retryable_400(self, sleep_mock): + def test_retryable_400_prior_request_not_complete(self): + # Test ability to retry on ``PriorRequestNotComplete``. self.set_http_response(status_code=400, header=[ ['Code', 'PriorRequestNotComplete'], ]) + self.do_retry_handler() + + def test_retryable_400_throttling(self): + # Test ability to rety on ``Throttling``. + self.set_http_response(status_code=400, header=[ + ['Code', 'Throttling'], + ]) + self.do_retry_handler() + + @mock.patch('time.sleep') + def do_retry_handler(self, sleep_mock): def incr_retry_handler(func): def _wrapper(*args, **kwargs): diff --git a/tests/unit/route53/test_zone.py b/tests/unit/route53/test_zone.py new file mode 100644 index 00000000..12d1d254 --- /dev/null +++ b/tests/unit/route53/test_zone.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, dis- +# tribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the fol- +# lowing conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- +# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +from boto.route53.zone import Zone +from tests.compat import mock, unittest + + +class TestZone(unittest.TestCase): + def test_find_records(self): + mock_connection = mock.Mock() + zone = Zone(mock_connection, {}) + zone.id = None + rr_names = ['amazon.com', 'amazon.com', 'aws.amazon.com', + 'aws.amazon.com'] + mock_rrs = [] + # Create some mock resource records. + for rr_name in rr_names: + mock_rr = mock.Mock() + mock_rr.name = rr_name + mock_rr.type = 'A' + mock_rr.weight = None + mock_rr.region = None + mock_rrs.append(mock_rr) + + # Set the last resource record to ``None``. The ``find_records`` loop + # should never hit this. + mock_rrs[3] = None + + mock_connection.get_all_rrsets.return_value = mock_rrs + mock_connection._make_qualified.return_value = 'amazon.com' + + # Ensure that the ``None`` type object was not iterated over. + try: + result_rrs = zone.find_records('amazon.com', 'A', all=True) + except AttributeError as e: + self.fail("find_records() iterated too far into resource" + " record list.") + + # Determine that the resulting records are correct. + self.assertEqual(result_rrs, [mock_rrs[0], mock_rrs[1]]) + + +if __name__ == "__main__": + unittest.main() |