def create_hosted_zone(self, caller_reference, name): """ @see: L{txaws.route53.client._Route53Client.create_hosted_zone} """ zone = HostedZone( name=name, reference=caller_reference, identifier=self._state.next_id(), # Hosted zones start with SOA and NS rrsets. rrset_count=2, ) self._state.zones = self._state.zones.append(zone) self.change_resource_record_sets( zone.identifier, [ create_rrset( RRSet( label=Name(name), type=u"SOA", ttl=900, records=self._state.soa_records, ), ), create_rrset( RRSet( label=Name(name), type=u"NS", ttl=172800, records=self._state.ns_records, ), ), ], ) return succeed(zone)
def create_hosted_zone(self, caller_reference, name): """ @see: L{txaws.route53.client._Route53Client.create_hosted_zone} """ zone = HostedZone( name=name, reference=caller_reference, identifier=self._state.next_id(), # Hosted zones start with SOA and NS rrsets. rrset_count=2, ) self._state.zones = self._state.zones.append(zone) self.change_resource_record_sets( zone.identifier, [ create_rrset( RRSet( label=Name(name), type="SOA", ttl=900, records=self._state.soa_records, ), ), create_rrset( RRSet( label=Name(name), type="NS", ttl=172800, records=self._state.ns_records, ), ), ], ) return succeed(zone)
def created_zone(zone): self.addCleanup(lambda: self._cleanup(client, zone.identifier)) d = client.change_resource_record_sets(zone.identifier, [ create_rrset(extra), create_rrset(expected), ]) d.addCallback(lambda ignored: zone) return d
def test_error_changes(self): duplicate_resource = POSTableData( sample_create_resource_record_sets_error_result.xml, b"text/xml", BAD_REQUEST, ) zone_id = u"1234ABCDEF" agent = RequestTraversalAgent(static_resource({ b"2013-04-01": { b"hostedzone": { zone_id.encode("ascii"): { b"rrset": duplicate_resource, }, }, }, })) aws = AWSServiceRegion(access_key="abc", secret_key="def") client = get_route53_client(agent, aws, uncooperator()) err = self.failureResultOf(client.change_resource_record_sets( zone_id=zone_id, changes=[create_rrset(sample_create_resource_record_sets_error_result.rrset)], ), Route53Error) expected = { u'Code': 'InvalidChangeBatch', u'Message': "[Tried to create resource record set [name='duplicate.example.invalid.', type='CNAME'] but it already exists]", u'Type': 'Sender', } self.assertEqual(err.value.errors, [expected])
def test_error_changes(self): duplicate_resource = POSTableData( sample_create_resource_record_sets_error_result.xml, b"text/xml", BAD_REQUEST, ) zone_id = "1234ABCDEF" agent = RequestTraversalAgent( static_resource({ b"2013-04-01": { b"hostedzone": { zone_id.encode("ascii"): { b"rrset": duplicate_resource, }, }, }, })) aws = AWSServiceRegion(access_key="abc", secret_key="def") client = get_route53_client(agent, aws, uncooperator()) err = self.failureResultOf( client.change_resource_record_sets( zone_id=zone_id, changes=[ create_rrset( sample_create_resource_record_sets_error_result.rrset) ], ), Route53Error) expected = { 'Code': 'InvalidChangeBatch', 'Message': "[Tried to create resource record set [name='duplicate.example.invalid.', type='CNAME'] but it already exists]", 'Type': 'Sender', } self.assertEqual(err.value.errors, [expected])
def create_route53_rrsets(route53, zone, subscriptions): a = start_action(action_type=u"create-route53") with a.context(): d = route53.change_resource_record_sets(zone.identifier, list( create_rrset(_rrset_for_subscription(subscription.subscription_id, zone.name)) for subscription in subscriptions )) d = DeferredContext(d) return d.addActionFinish()
def test_change_resource_record_sets_nonexistent_zone(self): """ You cannot interact with resource record sets for a non-existent zone. """ rrset = RRSet( label=Name(u"foo.example.invalid."), type=u"CNAME", ttl=60, records={CNAME(canonical_name=Name(u"bar.example.invalid."))}, ) client = get_client(self) d = client.change_resource_record_sets(u"abcdefg12345678", [create_rrset(rrset)]) self.assertFailure(d, Route53Error) def got_error(error): self.assertEqual(NOT_FOUND, int(error.status)) d.addCallback(got_error) return d
def test_some_changes(self): change_resource = POSTableData( sample_change_resource_record_sets_result.xml, b"text/xml", ) zone_id = u"ABCDEF1234" agent = RequestTraversalAgent( static_resource({ b"2013-04-01": { b"hostedzone": { zone_id.encode("ascii"): { b"rrset": change_resource, } }, }, })) aws = AWSServiceRegion(access_key="abc", secret_key="def") client = get_route53_client(agent, aws, uncooperator()) self.successResultOf( client.change_resource_record_sets( zone_id=zone_id, changes=[ create_rrset( sample_change_resource_record_sets_result.rrset), delete_rrset( sample_change_resource_record_sets_result.rrset), upsert_rrset( sample_change_resource_record_sets_result.rrset), ], )) # Ack, what a pathetic assertion. change_template = u"<Change><Action>{action}</Action><ResourceRecordSet><Name>example.invalid.</Name><Type>NS</Type><TTL>86400</TTL><ResourceRecords><ResourceRecord><Value>ns1.example.invalid.</Value></ResourceRecord><ResourceRecord><Value>ns2.example.invalid.</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change>" changes = [ change_template.format(action=u"CREATE"), change_template.format(action=u"DELETE"), change_template.format(action=u"UPSERT"), ] expected = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2013-04-01/"><ChangeBatch><Changes>{changes}</Changes></ChangeBatch></ChangeResourceRecordSetsRequest>""".format( changes=u"".join(changes)).encode("utf-8") self.assertEqual((expected, ), change_resource.posted)
def test_some_changes(self): change_resource = POSTableData( sample_change_resource_record_sets_result.xml, b"text/xml", ) zone_id = u"ABCDEF1234" agent = RequestTraversalAgent(static_resource({ b"2013-04-01": { b"hostedzone": { zone_id.encode("ascii"): { b"rrset": change_resource, } }, }, })) aws = AWSServiceRegion(access_key="abc", secret_key="def") client = get_route53_client(agent, aws, uncooperator()) self.successResultOf(client.change_resource_record_sets( zone_id=zone_id, changes=[ create_rrset(sample_change_resource_record_sets_result.rrset), delete_rrset(sample_change_resource_record_sets_result.rrset), upsert_rrset(sample_change_resource_record_sets_result.rrset), ], )) # Ack, what a pathetic assertion. change_template = u"<Change><Action>{action}</Action><ResourceRecordSet><Name>example.invalid.</Name><Type>NS</Type><TTL>86400</TTL><ResourceRecords><ResourceRecord><Value>ns1.example.invalid.</Value></ResourceRecord><ResourceRecord><Value>ns2.example.invalid.</Value></ResourceRecord></ResourceRecords></ResourceRecordSet></Change>" changes = [ change_template.format(action=u"CREATE"), change_template.format(action=u"DELETE"), change_template.format(action=u"UPSERT"), ] expected = u"""\ <?xml version="1.0" encoding="UTF-8"?> <ChangeResourceRecordSetsRequest xmlns="https://route53.amazonaws.com/doc/2013-04-01/"><ChangeBatch><Changes>{changes}</Changes></ChangeBatch></ChangeResourceRecordSetsRequest>""".format(changes=u"".join(changes)).encode("utf-8") self.assertEqual((expected,), change_resource.posted)
def test_resource_record_sets(self): zone_name = u"{}.example.invalid.".format(uuid4()) cname = CNAME(canonical_name=Name(u"example.invalid.")) client = get_client(self) zone = yield client.create_hosted_zone(u"{}".format(time()), zone_name) # At least try to clean up, to be as nice as possible. # This might fail and someone else might have to do the # cleanup - but it might not! self.addCleanup(lambda: self._cleanup(client, zone.identifier)) cname_label = Name(u"foo.\N{SNOWMAN}.{}".format(zone_name)) create = create_rrset(RRSet( label=cname_label, type=u"CNAME", ttl=60, records={cname}, )) yield client.change_resource_record_sets(zone.identifier, [create]) initial = yield client.list_resource_record_sets(zone.identifier) key = RRSetKey(cname_label, u"CNAME") self.assertIn(key, initial) cname_rrset = initial[key] self.assertEqual( RRSet(label=cname_label, type=u"CNAME", ttl=60, records={cname}), cname_rrset, ) # Zones start with an SOA and some NS records. key = RRSetKey(Name(zone_name), u"SOA") self.assertIn(key, initial) soa = initial[key] self.assertEqual( len(soa.records), 1, "Expected one SOA record, got {}".format(soa.records) ) key = RRSetKey(Name(zone_name), u"NS") self.assertIn(key, initial) ns = initial[key] self.assertNotEqual( set(), ns.records, "Expected some NS records, got none" ) # Unrecognized change type # XXX This depends on _ChangeRRSet using attrs. bogus = attr.assoc(create, action=u"BOGUS") d = client.change_resource_record_sets(zone.identifier, [bogus]) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) created_a = A(IPv4Address(u"10.0.0.1")) upsert_label = Name(u"upsert.{}".format(zone_name)) upsert_create = upsert_rrset(RRSet( upsert_label, u"A", 60, {created_a}, )) updated_a = A(IPv4Address(u"10.0.0.2")) upsert_update = upsert_rrset(RRSet( upsert_create.rrset.label, upsert_create.rrset.type, upsert_create.rrset.ttl, {updated_a}, )) yield client.change_resource_record_sets(zone.identifier, [upsert_create]) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records, {created_a}) yield client.change_resource_record_sets(zone.identifier, [upsert_update]) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records, {updated_a}) # Use the name and maxitems parameters to select exactly one resource record. rrsets = yield client.list_resource_record_sets( zone.identifier, maxitems=1, name=upsert_label, type=u"A", ) self.assertEqual(1, len(rrsets), "Expected 1 rrset") self.assertEqual({updated_a}, rrsets[RRSetKey(upsert_label, u"A")].records) # It's invalid to specify type without name. d = client.list_resource_record_sets(zone.identifier, type=u"A") error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # It's invalid to delete the SOA record. d = client.change_resource_record_sets( zone.identifier, [delete_rrset(soa)], ) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # Likewise, the NS records. d = client.change_resource_record_sets( zone.identifier, [delete_rrset(ns)], ) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # Test deletion at the end so the zone is clean for the # naive cleanup logic. yield client.change_resource_record_sets( zone.identifier, [ delete_rrset(cname_rrset), delete_rrset(upsert_update.rrset), ], ) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertNotIn(cname_label, rrsets) self.assertNotIn(upsert_label, rrsets)
def created_rrset(zone): d = client.change_resource_record_sets(zone.identifier, [create_rrset(rrset)]) self.assertFailure(d, Route53Error) return d
def test_resource_record_sets(self): zone_name = u"{}.example.invalid.".format(uuid4()) cname = CNAME(canonical_name=Name(u"example.invalid.")) client = get_client(self) zone = yield client.create_hosted_zone(u"{}".format(time()), zone_name) # At least try to clean up, to be as nice as possible. # This might fail and someone else might have to do the # cleanup - but it might not! self.addCleanup(lambda: self._cleanup(client, zone.identifier)) cname_label = Name(u"foo.\N{SNOWMAN}.{}".format(zone_name)) create = create_rrset( RRSet( label=cname_label, type=u"CNAME", ttl=60, records={cname}, )) yield client.change_resource_record_sets(zone.identifier, [create]) initial = yield client.list_resource_record_sets(zone.identifier) key = RRSetKey(cname_label, u"CNAME") self.assertIn(key, initial) cname_rrset = initial[key] self.assertEqual( RRSet(label=cname_label, type=u"CNAME", ttl=60, records={cname}), cname_rrset, ) # Zones start with an SOA and some NS records. key = RRSetKey(Name(zone_name), u"SOA") self.assertIn(key, initial) soa = initial[key] self.assertEqual( len(soa.records), 1, "Expected one SOA record, got {}".format(soa.records)) key = RRSetKey(Name(zone_name), u"NS") self.assertIn(key, initial) ns = initial[key] self.assertNotEqual(set(), ns.records, "Expected some NS records, got none") # Unrecognized change type # XXX This depends on _ChangeRRSet using attrs. bogus = attr.assoc(create, action=u"BOGUS") d = client.change_resource_record_sets(zone.identifier, [bogus]) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) created_a = A(IPv4Address(u"10.0.0.1")) upsert_label = Name(u"upsert.{}".format(zone_name)) upsert_create = upsert_rrset( RRSet( upsert_label, u"A", 60, {created_a}, )) updated_a = A(IPv4Address(u"10.0.0.2")) upsert_update = upsert_rrset( RRSet( upsert_create.rrset.label, upsert_create.rrset.type, upsert_create.rrset.ttl, {updated_a}, )) yield client.change_resource_record_sets(zone.identifier, [upsert_create]) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records, {created_a}) yield client.change_resource_record_sets(zone.identifier, [upsert_update]) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertEqual(rrsets[RRSetKey(upsert_label, u"A")].records, {updated_a}) # Use the name and maxitems parameters to select exactly one resource record. rrsets = yield client.list_resource_record_sets( zone.identifier, maxitems=1, name=upsert_label, type=u"A", ) self.assertEqual(1, len(rrsets), "Expected 1 rrset") self.assertEqual({updated_a}, rrsets[RRSetKey(upsert_label, u"A")].records) # It's invalid to specify type without name. d = client.list_resource_record_sets(zone.identifier, type=u"A") error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # It's invalid to delete the SOA record. d = client.change_resource_record_sets( zone.identifier, [delete_rrset(soa)], ) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # Likewise, the NS records. d = client.change_resource_record_sets( zone.identifier, [delete_rrset(ns)], ) error = yield self.assertFailure(d, Route53Error) self.assertEqual(BAD_REQUEST, int(error.status)) # Test deletion at the end so the zone is clean for the # naive cleanup logic. yield client.change_resource_record_sets( zone.identifier, [ delete_rrset(cname_rrset), delete_rrset(upsert_update.rrset), ], ) rrsets = yield client.list_resource_record_sets(zone.identifier) self.assertNotIn(cname_label, rrsets) self.assertNotIn(upsert_label, rrsets)