Merge remote-tracking branch 'upstream/master' into adding_rds2_support

This commit is contained in:
Mike Fuller 2015-01-19 08:22:27 +11:00
commit e2ed5ed733
11 changed files with 626 additions and 26 deletions

View file

@ -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)

View file

@ -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]

View file

@ -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()

View file

@ -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>"""

View file

@ -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,
}

View file

@ -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):