Merge remote-tracking branch 'upstream/master' into adding_rds2_support
This commit is contained in:
commit
e2ed5ed733
11 changed files with 626 additions and 26 deletions
|
|
@ -8,6 +8,8 @@ from moto.ec2 import models as ec2_models
|
|||
from moto.elb import models as elb_models
|
||||
from moto.iam import models as iam_models
|
||||
from moto.rds import models as rds_models
|
||||
from moto.route53 import models as route53_models
|
||||
from moto.sns import models as sns_models
|
||||
from moto.sqs import models as sqs_models
|
||||
from .utils import random_suffix
|
||||
from .exceptions import MissingParameterError, UnformattedGetAttTemplateException
|
||||
|
|
@ -36,6 +38,11 @@ MODEL_MAP = {
|
|||
"AWS::RDS::DBInstance": rds_models.Database,
|
||||
"AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup,
|
||||
"AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup,
|
||||
"AWS::Route53::HealthCheck": route53_models.HealthCheck,
|
||||
"AWS::Route53::HostedZone": route53_models.FakeZone,
|
||||
"AWS::Route53::RecordSet": route53_models.RecordSet,
|
||||
"AWS::Route53::RecordSetGroup": route53_models.RecordSetGroup,
|
||||
"AWS::SNS::Topic": sns_models.Topic,
|
||||
"AWS::SQS::Queue": sqs_models.Queue,
|
||||
}
|
||||
|
||||
|
|
@ -144,8 +151,6 @@ def resource_class_from_type(resource_type):
|
|||
|
||||
|
||||
def resource_name_property_from_type(resource_type):
|
||||
if resource_type not in NAME_TYPE_MAP:
|
||||
return None
|
||||
return NAME_TYPE_MAP.get(resource_type)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||
self.subnet_id = kwargs.get("subnet_id")
|
||||
self.key_name = kwargs.get("key_name")
|
||||
self.source_dest_check = "true"
|
||||
self.private_ip_address = kwargs.get('private_ip_address')
|
||||
|
||||
self.block_device_mapping = BlockDeviceMapping()
|
||||
self.block_device_mapping['/dev/sda1'] = BlockDeviceType(volume_id=random_volume_id())
|
||||
|
|
@ -344,6 +345,7 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||
instance_type=properties.get("InstanceType", "m1.small"),
|
||||
subnet_id=properties.get("SubnetId"),
|
||||
key_name=properties.get("KeyName"),
|
||||
private_ip_address=properties.get('PrivateIpAddress'),
|
||||
)
|
||||
return reservation.instances[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +1,174 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import uuid
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.utils import get_random_hex
|
||||
|
||||
|
||||
class HealthCheck(object):
|
||||
def __init__(self, health_check_id, health_check_args):
|
||||
self.id = health_check_id
|
||||
self.ip_address = health_check_args.get("ip_address")
|
||||
self.port = health_check_args.get("port", 80)
|
||||
self._type = health_check_args.get("type")
|
||||
self.resource_path = health_check_args.get("resource_path")
|
||||
self.fqdn = health_check_args.get("fqdn")
|
||||
self.search_string = health_check_args.get("search_string")
|
||||
self.request_interval = health_check_args.get("request_interval", 30)
|
||||
self.failure_threshold = health_check_args.get("failure_threshold", 3)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']['HealthCheckConfig']
|
||||
health_check_args = {
|
||||
"ip_address": properties.get('IPAddress'),
|
||||
"port": properties.get('Port'),
|
||||
"type": properties['Type'],
|
||||
"resource_path": properties.get('ResourcePath'),
|
||||
"fqdn": properties.get('FullyQualifiedDomainName'),
|
||||
"search_string": properties.get('SearchString'),
|
||||
"request_interval": properties.get('RequestInterval'),
|
||||
"failure_threshold": properties.get('FailureThreshold'),
|
||||
}
|
||||
health_check = route53_backend.create_health_check(health_check_args)
|
||||
return health_check
|
||||
|
||||
def to_xml(self):
|
||||
template = Template("""<HealthCheck>
|
||||
<Id>{{ health_check.id }}</Id>
|
||||
<CallerReference>example.com 192.0.2.17</CallerReference>
|
||||
<HealthCheckConfig>
|
||||
<IPAddress>{{ health_check.ip_address }}</IPAddress>
|
||||
<Port>{{ health_check.port }}</Port>
|
||||
<Type>{{ health_check._type }}</Type>
|
||||
<ResourcePath>{{ health_check.resource_path }}</ResourcePath>
|
||||
<FullyQualifiedDomainName>{{ health_check.fqdn }}</FullyQualifiedDomainName>
|
||||
<RequestInterval>{{ health_check.request_interval }}</RequestInterval>
|
||||
<FailureThreshold>{{ health_check.failure_threshold }}</FailureThreshold>
|
||||
{% if health_check.search_string %}
|
||||
<SearchString>{{ health_check.search_string }}</SearchString>
|
||||
{% endif %}
|
||||
</HealthCheckConfig>
|
||||
<HealthCheckVersion>1</HealthCheckVersion>
|
||||
</HealthCheck>""")
|
||||
return template.render(health_check=self)
|
||||
|
||||
|
||||
class RecordSet(object):
|
||||
def __init__(self, kwargs):
|
||||
self.name = kwargs.get('Name')
|
||||
self._type = kwargs.get('Type')
|
||||
self.ttl = kwargs.get('TTL')
|
||||
self.records = kwargs.get('ResourceRecords', [])
|
||||
self.set_identifier = kwargs.get('SetIdentifier')
|
||||
self.weight = kwargs.get('Weight')
|
||||
self.health_check = kwargs.get('HealthCheckId')
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
zone_name = properties["HostedZoneName"]
|
||||
hosted_zone = route53_backend.get_hosted_zone_by_name(zone_name)
|
||||
record_set = hosted_zone.add_rrset(properties)
|
||||
return record_set
|
||||
|
||||
def to_xml(self):
|
||||
template = Template("""<ResourceRecordSet>
|
||||
<Name>{{ record_set.name }}</Name>
|
||||
<Type>{{ record_set._type }}</Type>
|
||||
{% if record_set.set_identifier %}
|
||||
<SetIdentifier>{{ record_set.set_identifier }}</SetIdentifier>
|
||||
{% endif %}
|
||||
{% if record_set.weight %}
|
||||
<Weight>{{ record_set.weight }}</Weight>
|
||||
{% endif %}
|
||||
<TTL>{{ record_set.ttl }}</TTL>
|
||||
<ResourceRecords>
|
||||
{% for record in record_set.records %}
|
||||
<ResourceRecord>
|
||||
<Value>{{ record }}</Value>
|
||||
</ResourceRecord>
|
||||
{% endfor %}
|
||||
</ResourceRecords>
|
||||
{% if record_set.health_check %}
|
||||
<HealthCheckId>{{ record_set.health_check }}</HealthCheckId>
|
||||
{% endif %}
|
||||
</ResourceRecordSet>""")
|
||||
return template.render(record_set=self)
|
||||
|
||||
|
||||
class FakeZone(object):
|
||||
|
||||
def __init__(self, name, id_):
|
||||
self.name = name
|
||||
self.id = id_
|
||||
self.rrsets = {}
|
||||
self.rrsets = []
|
||||
|
||||
def add_rrset(self, name, rrset):
|
||||
self.rrsets[name] = rrset
|
||||
def add_rrset(self, record_set):
|
||||
record_set = RecordSet(record_set)
|
||||
self.rrsets.append(record_set)
|
||||
return record_set
|
||||
|
||||
def delete_rrset(self, name):
|
||||
self.rrsets.pop(name, None)
|
||||
self.rrsets = [record_set for record_set in self.rrsets if record_set.name != name]
|
||||
|
||||
def get_record_sets(self, type_filter, name_filter):
|
||||
record_sets = list(self.rrsets) # Copy the list
|
||||
if type_filter:
|
||||
record_sets = [record_set for record_set in record_sets if record_set._type == type_filter]
|
||||
if name_filter:
|
||||
record_sets = [record_set for record_set in record_sets if record_set.name == name_filter]
|
||||
|
||||
return record_sets
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
name = properties["Name"]
|
||||
|
||||
hosted_zone = route53_backend.create_hosted_zone(name)
|
||||
return hosted_zone
|
||||
|
||||
|
||||
class RecordSetGroup(object):
|
||||
def __init__(self, hosted_zone_id, record_sets):
|
||||
self.hosted_zone_id = hosted_zone_id
|
||||
self.record_sets = record_sets
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return "arn:aws:route53:::hostedzone/{0}".format(self.hosted_zone_id)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
zone_name = properties["HostedZoneName"]
|
||||
hosted_zone = route53_backend.get_hosted_zone_by_name(zone_name)
|
||||
record_sets = properties["RecordSets"]
|
||||
for record_set in record_sets:
|
||||
hosted_zone.add_rrset(record_set)
|
||||
|
||||
record_set_group = RecordSetGroup(hosted_zone.id, record_sets)
|
||||
return record_set_group
|
||||
|
||||
|
||||
class Route53Backend(BaseBackend):
|
||||
|
||||
def __init__(self):
|
||||
self.zones = {}
|
||||
self.health_checks = {}
|
||||
|
||||
def create_hosted_zone(self, name):
|
||||
new_id = get_random_hex()
|
||||
|
|
@ -34,12 +182,27 @@ class Route53Backend(BaseBackend):
|
|||
def get_hosted_zone(self, id_):
|
||||
return self.zones.get(id_)
|
||||
|
||||
def get_hosted_zone_by_name(self, name):
|
||||
for zone in self.get_all_hosted_zones():
|
||||
if zone.name == name:
|
||||
return zone
|
||||
|
||||
def delete_hosted_zone(self, id_):
|
||||
zone = self.zones.get(id_)
|
||||
if zone:
|
||||
del self.zones[id_]
|
||||
return zone
|
||||
return None
|
||||
|
||||
def create_health_check(self, health_check_args):
|
||||
health_check_id = str(uuid.uuid4())
|
||||
health_check = HealthCheck(health_check_id, health_check_args)
|
||||
self.health_checks[health_check_id] = health_check
|
||||
return health_check
|
||||
|
||||
def get_health_checks(self):
|
||||
return self.health_checks.values()
|
||||
|
||||
def delete_health_check(self, health_check_id):
|
||||
return self.health_checks.pop(health_check_id, None)
|
||||
|
||||
route53_backend = Route53Backend()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ from jinja2 import Template
|
|||
from six.moves.urllib.parse import parse_qs, urlparse
|
||||
from .models import route53_backend
|
||||
import xmltodict
|
||||
import dicttoxml
|
||||
|
||||
|
||||
def list_or_create_hostzone_response(request, full_url, headers):
|
||||
|
|
@ -53,33 +52,57 @@ def rrset_response(request, full_url, headers):
|
|||
|
||||
for value in change_list:
|
||||
action = value['Action']
|
||||
rrset = value['ResourceRecordSet']
|
||||
|
||||
record_set = value['ResourceRecordSet']
|
||||
if action == 'CREATE':
|
||||
the_zone.add_rrset(rrset["Name"], rrset)
|
||||
record_set['ResourceRecords'] = [x['Value'] for x in record_set['ResourceRecords'].values()]
|
||||
the_zone.add_rrset(record_set)
|
||||
elif action == "DELETE":
|
||||
the_zone.delete_rrset(rrset["Name"])
|
||||
the_zone.delete_rrset(record_set["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' in querystring and querystring["type"][0] != value["Type"]:
|
||||
continue
|
||||
if 'name' in querystring and querystring["name"][0] != value["Name"]:
|
||||
continue
|
||||
rrset_list.append(dicttoxml.dicttoxml({"ResourceRecordSet": value}, root=False))
|
||||
type_filter = querystring.get("type", [None])[0]
|
||||
name_filter = querystring.get("name", [None])[0]
|
||||
record_sets = the_zone.get_record_sets(type_filter, name_filter)
|
||||
return 200, headers, template.render(record_sets=record_sets)
|
||||
|
||||
return 200, headers, template.render(rrsets=rrset_list)
|
||||
|
||||
def health_check_response(request, full_url, headers):
|
||||
parsed_url = urlparse(full_url)
|
||||
method = request.method
|
||||
|
||||
if method == "POST":
|
||||
properties = xmltodict.parse(request.body)['CreateHealthCheckRequest']['HealthCheckConfig']
|
||||
health_check_args = {
|
||||
"ip_address": properties.get('IPAddress'),
|
||||
"port": properties.get('Port'),
|
||||
"type": properties['Type'],
|
||||
"resource_path": properties.get('ResourcePath'),
|
||||
"fqdn": properties.get('FullyQualifiedDomainName'),
|
||||
"search_string": properties.get('SearchString'),
|
||||
"request_interval": properties.get('RequestInterval'),
|
||||
"failure_threshold": properties.get('FailureThreshold'),
|
||||
}
|
||||
health_check = route53_backend.create_health_check(health_check_args)
|
||||
template = Template(CREATE_HEALTH_CHECK_RESPONSE)
|
||||
return 201, headers, template.render(health_check=health_check)
|
||||
elif method == "DELETE":
|
||||
health_check_id = parsed_url.path.split("/")[-1]
|
||||
route53_backend.delete_health_check(health_check_id)
|
||||
return 200, headers, DELETE_HEALTH_CHECK_REPONSE
|
||||
elif method == "GET":
|
||||
template = Template(LIST_HEALTH_CHECKS_REPONSE)
|
||||
health_checks = route53_backend.get_health_checks()
|
||||
return 200, headers, template.render(health_checks=health_checks)
|
||||
|
||||
|
||||
LIST_RRSET_REPONSE = """<ListResourceRecordSetsResponse xmlns="https://route53.amazonaws.com/doc/2012-12-12/">
|
||||
<ResourceRecordSets>
|
||||
{% for rrset in rrsets %}
|
||||
{{ rrset }}
|
||||
{% for record_set in record_sets %}
|
||||
{{ record_set.to_xml() }}
|
||||
{% endfor %}
|
||||
</ResourceRecordSets>
|
||||
</ListResourceRecordSetsResponse>"""
|
||||
|
|
@ -132,3 +155,23 @@ LIST_HOSTED_ZONES_RESPONSE = """<ListHostedZonesResponse xmlns="https://route53.
|
|||
{% endfor %}
|
||||
</HostedZones>
|
||||
</ListHostedZonesResponse>"""
|
||||
|
||||
CREATE_HEALTH_CHECK_RESPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CreateHealthCheckResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||
{{ health_check.to_xml() }}
|
||||
</CreateHealthCheckResponse>"""
|
||||
|
||||
LIST_HEALTH_CHECKS_REPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ListHealthChecksResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||
<HealthChecks>
|
||||
{% for health_check in health_checks %}
|
||||
{{ health_check.to_xml() }}
|
||||
{% endfor %}
|
||||
</HealthChecks>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<MaxItems>{{ health_checks|length }}</MaxItems>
|
||||
</ListHealthChecksResponse>"""
|
||||
|
||||
DELETE_HEALTH_CHECK_REPONSE = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<DeleteHealthCheckResponse xmlns="https://route53.amazonaws.com/doc/2013-04-01/">
|
||||
</DeleteHealthCheckResponse>"""
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ from __future__ import unicode_literals
|
|||
from . import responses
|
||||
|
||||
url_bases = [
|
||||
"https://route53.amazonaws.com/201.-..-../hostedzone",
|
||||
"https://route53.amazonaws.com/201.-..-../",
|
||||
]
|
||||
|
||||
url_paths = {
|
||||
'{0}$': responses.list_or_create_hostzone_response,
|
||||
'{0}/[^/]+$': responses.get_or_delete_hostzone_response,
|
||||
'{0}/[^/]+/rrset$': responses.rrset_response,
|
||||
'{0}hostedzone$': responses.list_or_create_hostzone_response,
|
||||
'{0}hostedzone/[^/]+$': responses.get_or_delete_hostzone_response,
|
||||
'{0}hostedzone/[^/]+/rrset$': responses.rrset_response,
|
||||
'{0}healthcheck': responses.health_check_response,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,22 @@ class Topic(object):
|
|||
return self.name
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
sns_backend = sns_backends[region_name]
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
topic = sns_backend.create_topic(
|
||||
properties.get("TopicName")
|
||||
)
|
||||
for subscription in properties.get("Subscription", []):
|
||||
sns_backend.subscribe(topic.arn, subscription['Endpoint'], subscription['Protocol'])
|
||||
return topic
|
||||
|
||||
|
||||
class Subscription(object):
|
||||
def __init__(self, topic, endpoint, protocol):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue