From 06d65fd3da8788066c859640ba16f6173c46ea3f Mon Sep 17 00:00:00 2001 From: Abhinav I Date: Fri, 28 Apr 2017 21:26:32 +0530 Subject: [PATCH] Added test cases that covers route53 client's function. Also added validation to throw a ClientError when the record set does not match the hosted zone's config --- moto/route53/responses.py | 11 ++ tests/test_route53/test_route53.py | 156 +++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+) diff --git a/moto/route53/responses.py b/moto/route53/responses.py index 984f305ab..2419f896d 100644 --- a/moto/route53/responses.py +++ b/moto/route53/responses.py @@ -112,6 +112,17 @@ class Route53(BaseResponse): for value in change_list: action = value['Action'] record_set = value['ResourceRecordSet'] + + cleaned_record_name = record_set['Name'].strip('.') + cleaned_hosted_zone_name = the_zone.name.strip('.') + + if not cleaned_record_name.endswith(cleaned_hosted_zone_name): + error_msg = """ + An error occurred (InvalidChangeBatch) when calling the ChangeResourceRecordSets operation: + RRSet with DNS name %s is not permitted in zone %s + """ % (record_set['Name'], the_zone.name) + return 400, headers, error_msg + if action in ('CREATE', 'UPSERT'): if 'ResourceRecords' in record_set: resource_records = list( diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py index b64c63a30..ac8d6e7ad 100644 --- a/tests/test_route53/test_route53.py +++ b/tests/test_route53/test_route53.py @@ -9,6 +9,9 @@ import sure # noqa import uuid +import botocore +from nose.tools import assert_raises + from moto import mock_route53, mock_route53_deprecated @@ -491,3 +494,156 @@ def test_list_hosted_zones_by_name(): zones["HostedZones"][0]["Name"].should.equal("test.b.com.") zones["HostedZones"][1]["Name"].should.equal("test.a.org.") zones["HostedZones"][2]["Name"].should.equal("test.a.org.") + + +@mock_route53 +def test_change_resource_record_sets_crud_valid(): + conn = boto3.client('route53', region_name='us-east-1') + conn.create_hosted_zone( + Name="db.", + CallerReference=str(hash('foo')), + HostedZoneConfig=dict( + PrivateZone=True, + Comment="db", + ) + ) + + zones = conn.list_hosted_zones_by_name(DNSName="db.") + len(zones["HostedZones"]).should.equal(1) + zones["HostedZones"][0]["Name"].should.equal("db.") + hosted_zone_id = zones["HostedZones"][0]["Id"] + + # Create A Record. + a_record_endpoint_payload = { + 'Comment': 'create A record prod.redis.db', + 'Changes': [ + { + 'Action': 'CREATE', + 'ResourceRecordSet': { + 'Name': 'prod.redis.db', + 'Type': 'A', + 'TTL': 10, + 'ResourceRecords': [{ + 'Value': '127.0.0.1' + }] + } + } + ] + } + conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=a_record_endpoint_payload) + + response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id) + len(response['ResourceRecordSets']).should.equal(1) + a_record_detail = response['ResourceRecordSets'][0] + a_record_detail['Name'].should.equal('prod.redis.db') + a_record_detail['Type'].should.equal('A') + a_record_detail['TTL'].should.equal(10) + a_record_detail['ResourceRecords'].should.equal([{'Value': '127.0.0.1'}]) + + # Update type to CNAME + cname_record_endpoint_payload = { + 'Comment': 'Update to CNAME prod.redis.db', + 'Changes': [ + { + 'Action': 'UPSERT', + 'ResourceRecordSet': { + 'Name': 'prod.redis.db', + 'Type': 'CNAME', + 'TTL': 60, + 'ResourceRecords': [{ + 'Value': '192.168.1.1' + }] + } + } + ] + } + conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=cname_record_endpoint_payload) + + response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id) + len(response['ResourceRecordSets']).should.equal(1) + cname_record_detail = response['ResourceRecordSets'][0] + cname_record_detail['Name'].should.equal('prod.redis.db') + cname_record_detail['Type'].should.equal('CNAME') + cname_record_detail['TTL'].should.equal(60) + cname_record_detail['ResourceRecords'].should.equal([{'Value': '192.168.1.1'}]) + + # Delete record. + delete_payload = { + 'Comment': 'delete prod.redis.db', + 'Changes': [ + { + 'Action': 'DELETE', + 'ResourceRecordSet': { + 'Name': 'prod.redis.db', + 'Type': 'CNAME', + } + } + ] + } + conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=delete_payload) + response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id) + len(response['ResourceRecordSets']).should.equal(0) + + +@mock_route53 +def test_change_resource_record_invalid(): + conn = boto3.client('route53', region_name='us-east-1') + conn.create_hosted_zone( + Name="db.", + CallerReference=str(hash('foo')), + HostedZoneConfig=dict( + PrivateZone=True, + Comment="db", + ) + ) + + zones = conn.list_hosted_zones_by_name(DNSName="db.") + len(zones["HostedZones"]).should.equal(1) + zones["HostedZones"][0]["Name"].should.equal("db.") + hosted_zone_id = zones["HostedZones"][0]["Id"] + + invalid_a_record_payload = { + 'Comment': 'this should fail', + 'Changes': [ + { + 'Action': 'CREATE', + 'ResourceRecordSet': { + 'Name': 'prod.scooby.doo', + 'Type': 'A', + 'TTL': 10, + 'ResourceRecords': [{ + 'Value': '127.0.0.1' + }] + } + } + ] + } + + with assert_raises(botocore.exceptions.ClientError): + conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=invalid_a_record_payload) + + response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id) + len(response['ResourceRecordSets']).should.equal(0) + + invalid_cname_record_payload = { + 'Comment': 'this should also fail', + 'Changes': [ + { + 'Action': 'UPSERT', + 'ResourceRecordSet': { + 'Name': 'prod.scooby.doo', + 'Type': 'CNAME', + 'TTL': 10, + 'ResourceRecords': [{ + 'Value': '127.0.0.1' + }] + } + } + ] + } + + with assert_raises(botocore.exceptions.ClientError): + conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=invalid_cname_record_payload) + + response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id) + len(response['ResourceRecordSets']).should.equal(0)