diff --git a/moto/autoscaling/models.py b/moto/autoscaling/models.py index bcd40e892..09110b016 100644 --- a/moto/autoscaling/models.py +++ b/moto/autoscaling/models.py @@ -47,12 +47,13 @@ class FakeLaunchConfiguration(object): self.block_device_mapping_dict = block_device_mapping_dict @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] instance_profile_name = properties.get("IamInstanceProfile") - config = default_autoscaling_backend.create_launch_configuration( + backend = autoscaling_backends[region_name] + config = backend.create_launch_configuration( name=resource_name, image_id=properties.get("ImageId"), key_name=properties.get("KeyName"), @@ -128,13 +129,14 @@ class FakeAutoScalingGroup(object): self.set_desired_capacity(desired_capacity) @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] launch_config_name = properties.get("LaunchConfigurationName") load_balancer_names = properties.get("LoadBalancerNames", []) - group = default_autoscaling_backend.create_autoscaling_group( + backend = autoscaling_backends[region_name] + group = backend.create_autoscaling_group( name=resource_name, availability_zones=properties.get("AvailabilityZones", []), desired_capacity=properties.get("DesiredCapacity"), diff --git a/moto/cloudformation/__init__.py b/moto/cloudformation/__init__.py index 17d520ba7..1d7ff041f 100644 --- a/moto/cloudformation/__init__.py +++ b/moto/cloudformation/__init__.py @@ -1,3 +1,10 @@ from __future__ import unicode_literals -from .models import cloudformation_backend -mock_cloudformation = cloudformation_backend.decorator +from .models import cloudformation_backends, cloudformation_backend # flake8: noqa +from ..core.models import MockAWS + + +def mock_cloudformation(func=None): + if func: + return MockAWS(cloudformation_backends)(func) + else: + return MockAWS(cloudformation_backends) diff --git a/moto/cloudformation/models.py b/moto/cloudformation/models.py index 81fa8f840..aeb130e23 100644 --- a/moto/cloudformation/models.py +++ b/moto/cloudformation/models.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import json +import boto.cloudformation from moto.core import BaseBackend from .parsing import ResourceMap, OutputMap @@ -9,9 +10,10 @@ from .exceptions import ValidationError class FakeStack(object): - def __init__(self, stack_id, name, template, notification_arns=None): + def __init__(self, stack_id, name, template, region_name, notification_arns=None): self.stack_id = stack_id self.name = name + self.region_name = region_name self.notification_arns = notification_arns if notification_arns else [] self.template = template self.status = 'CREATE_COMPLETE' @@ -19,7 +21,7 @@ class FakeStack(object): template_dict = json.loads(self.template) self.description = template_dict.get('Description') - self.resource_map = ResourceMap(stack_id, name, template_dict) + self.resource_map = ResourceMap(stack_id, name, region_name, template_dict) self.resource_map.create() self.output_map = OutputMap(self.resource_map, template_dict) @@ -40,9 +42,15 @@ class CloudFormationBackend(BaseBackend): self.stacks = {} self.deleted_stacks = {} - def create_stack(self, name, template, notification_arns=None): + def create_stack(self, name, template, region_name, notification_arns=None): stack_id = generate_stack_id(name) - new_stack = FakeStack(stack_id=stack_id, name=name, template=template, notification_arns=notification_arns) + new_stack = FakeStack( + stack_id=stack_id, + name=name, + template=template, + region_name=region_name, + notification_arns=notification_arns, + ) self.stacks[stack_id] = new_stack return new_stack @@ -90,4 +98,8 @@ class CloudFormationBackend(BaseBackend): self.delete_stack(stack_to_delete.stack_id) -cloudformation_backend = CloudFormationBackend() +cloudformation_backends = {} +for region in boto.cloudformation.regions(): + cloudformation_backends[region.name] = CloudFormationBackend() + +cloudformation_backend = cloudformation_backends['us-east-1'] diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index dc07aa4e3..07bf72e35 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -120,7 +120,7 @@ def resource_name_property_from_type(resource_type): return NAME_TYPE_MAP.get(resource_type) -def parse_resource(logical_id, resource_json, resources_map): +def parse_resource(logical_id, resource_json, resources_map, region_name): resource_type = resource_json['Type'] resource_class = resource_class_from_type(resource_type) if not resource_class: @@ -142,7 +142,7 @@ def parse_resource(logical_id, resource_json, resources_map): logical_id, random_suffix()) - resource = resource_class.create_from_cloudformation_json(resource_name, resource_json) + resource = resource_class.create_from_cloudformation_json(resource_name, resource_json, region_name) resource.type = resource_type resource.logical_resource_id = logical_id return resource @@ -164,9 +164,10 @@ class ResourceMap(collections.Mapping): each resources is passed this lazy map that it can grab dependencies from. """ - def __init__(self, stack_id, stack_name, template): + def __init__(self, stack_id, stack_name, region_name, template): self._template = template self._resource_json_map = template['Resources'] + self._region_name = region_name # Create the default resources self._parsed_resources = { @@ -183,7 +184,7 @@ class ResourceMap(collections.Mapping): return self._parsed_resources[resource_logical_id] else: resource_json = self._resource_json_map.get(resource_logical_id) - new_resource = parse_resource(resource_logical_id, resource_json, self) + new_resource = parse_resource(resource_logical_id, resource_json, self, self._region_name) self._parsed_resources[resource_logical_id] = new_resource return new_resource diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index a9f2cafee..48e8686de 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -4,19 +4,24 @@ import json from jinja2 import Template from moto.core.responses import BaseResponse -from .models import cloudformation_backend +from .models import cloudformation_backends class CloudFormationResponse(BaseResponse): + @property + def cloudformation_backend(self): + return cloudformation_backends[self.region] + def create_stack(self): stack_name = self._get_param('StackName') stack_body = self._get_param('TemplateBody') stack_notification_arns = self._get_multi_param('NotificationARNs.member') - stack = cloudformation_backend.create_stack( + stack = self.cloudformation_backend.create_stack( name=stack_name, template=stack_body, + region_name=self.region, notification_arns=stack_notification_arns ) stack_body = { @@ -32,34 +37,34 @@ class CloudFormationResponse(BaseResponse): stack_name_or_id = None if self._get_param('StackName'): stack_name_or_id = self.querystring.get('StackName')[0] - stacks = cloudformation_backend.describe_stacks(stack_name_or_id) + stacks = self.cloudformation_backend.describe_stacks(stack_name_or_id) template = Template(DESCRIBE_STACKS_TEMPLATE) return template.render(stacks=stacks) def describe_stack_resources(self): stack_name = self._get_param('StackName') - stack = cloudformation_backend.get_stack(stack_name) + stack = self.cloudformation_backend.get_stack(stack_name) template = Template(LIST_STACKS_RESOURCES_RESPONSE) return template.render(stack=stack) def list_stacks(self): - stacks = cloudformation_backend.list_stacks() + stacks = self.cloudformation_backend.list_stacks() template = Template(LIST_STACKS_RESPONSE) return template.render(stacks=stacks) def get_template(self): name_or_stack_id = self.querystring.get('StackName')[0] - stack = cloudformation_backend.get_stack(name_or_stack_id) + stack = self.cloudformation_backend.get_stack(name_or_stack_id) return stack.template # def update_stack(self): # stack_name = self._get_param('StackName') # stack_body = self._get_param('TemplateBody') - # stack = cloudformation_backend.update_stack( + # stack = self.cloudformation_backend.update_stack( # name=stack_name, # template=stack_body, # ) @@ -75,7 +80,7 @@ class CloudFormationResponse(BaseResponse): def delete_stack(self): name_or_stack_id = self.querystring.get('StackName')[0] - cloudformation_backend.delete_stack(name_or_stack_id) + self.cloudformation_backend.delete_stack(name_or_stack_id) return json.dumps({ 'DeleteStackResponse': { 'DeleteStackResult': {}, diff --git a/moto/ec2/models.py b/moto/ec2/models.py index 1a367ebd8..cf380d3fa 100644 --- a/moto/ec2/models.py +++ b/moto/ec2/models.py @@ -165,12 +165,13 @@ class NetworkInterface(object): self._group_set.append(group) @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] security_group_ids = properties.get('SecurityGroups', []) subnet_id = properties['SubnetId'] + ec2_backend = ec2_backends[region_name] subnet = ec2_backend.get_subnet(subnet_id) private_ip_address = properties.get('PrivateIpAddress', None) @@ -324,9 +325,10 @@ class Instance(BotoInstance, TaggedEC2Resource): associate_public_ip=kwargs.get("associate_public_ip")) @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] security_group_ids = properties.get('SecurityGroups', []) group_names = [ec2_backend.get_security_group_from_id(group_id).name for group_id in security_group_ids] @@ -975,9 +977,10 @@ class SecurityGroup(object): self.vpc_id = vpc_id @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] vpc_id = properties.get('VpcId') security_group = ec2_backend.create_security_group( name=resource_name, @@ -1203,12 +1206,13 @@ class VolumeAttachment(object): self.device = device @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] instance_id = properties['InstanceId'] volume_id = properties['VolumeId'] + ec2_backend = ec2_backends[region_name] attachment = ec2_backend.attach_volume( volume_id=volume_id, instance_id=instance_id, @@ -1226,9 +1230,10 @@ class Volume(TaggedEC2Resource): self.ec2_backend = ec2_backend @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] volume = ec2_backend.create_volume( size=properties.get('Size'), zone_name=properties.get('AvailabilityZone'), @@ -1360,9 +1365,10 @@ class VPC(TaggedEC2Resource): self.state = 'available' @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] vpc = ec2_backend.create_vpc( cidr_block=properties['CidrBlock'], ) @@ -1479,9 +1485,10 @@ class VPCPeeringConnection(TaggedEC2Resource): self._status = VPCPeeringConnectionStatus() @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] vpc = ec2_backend.get_vpc(properties['VpcId']) peer_vpc = ec2_backend.get_vpc(properties['PeerVpcId']) @@ -1543,10 +1550,11 @@ class Subnet(TaggedEC2Resource): self.cidr_block = cidr_block @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] vpc_id = properties['VpcId'] + ec2_backend = ec2_backends[region_name] subnet = ec2_backend.create_subnet( vpc_id=vpc_id, cidr_block=properties['CidrBlock'] @@ -1615,12 +1623,13 @@ class SubnetRouteTableAssociation(object): self.subnet_id = subnet_id @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] route_table_id = properties['RouteTableId'] subnet_id = properties['SubnetId'] + ec2_backend = ec2_backends[region_name] subnet_association = ec2_backend.create_subnet_association( route_table_id=route_table_id, subnet_id=subnet_id, @@ -1649,10 +1658,11 @@ class RouteTable(TaggedEC2Resource): self.routes = {} @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] vpc_id = properties['VpcId'] + ec2_backend = ec2_backends[region_name] route_table = ec2_backend.create_route_table( vpc_id=vpc_id, ) @@ -1781,7 +1791,7 @@ class Route(object): self.vpc_pcx = vpc_pcx @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] gateway_id = properties.get('GatewayId') @@ -1790,6 +1800,7 @@ class Route(object): pcx_id = properties.get('VpcPeeringConnectionId') route_table_id = properties['RouteTableId'] + ec2_backend = ec2_backends[region_name] route_table = ec2_backend.create_route( route_table_id=route_table_id, destination_cidr_block=properties['DestinationCidrBlock'], @@ -1860,7 +1871,8 @@ class InternetGateway(TaggedEC2Resource): self.vpc = None @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): + ec2_backend = ec2_backends[region_name] return ec2_backend.create_internet_gateway() @property @@ -1935,9 +1947,10 @@ class VPCGatewayAttachment(object): self.vpc_id = vpc_id @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + ec2_backend = ec2_backends[region_name] return ec2_backend.create_vpc_gateway_attachment( gateway_id=properties['InternetGatewayId'], vpc_id=properties['VpcId'], @@ -2056,7 +2069,9 @@ class ElasticAddress(object): self.association_id = None @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): + ec2_backend = ec2_backends[region_name] + properties = cloudformation_json.get('Properties') instance_id = None if properties: diff --git a/moto/elb/__init__.py b/moto/elb/__init__.py index fbee07220..e2efcde4d 100644 --- a/moto/elb/__init__.py +++ b/moto/elb/__init__.py @@ -1,3 +1,10 @@ from __future__ import unicode_literals -from .models import elb_backend -mock_elb = elb_backend.decorator +from .models import elb_backends, elb_backend # flake8: noqa +from ..core.models import MockAWS + + +def mock_elb(func=None): + if func: + return MockAWS(elb_backends)(func) + else: + return MockAWS(elb_backends) diff --git a/moto/elb/models.py b/moto/elb/models.py index c0eb4e686..f1d71f959 100644 --- a/moto/elb/models.py +++ b/moto/elb/models.py @@ -1,4 +1,6 @@ from __future__ import unicode_literals + +import boto.ec2.elb from moto.core import BaseBackend @@ -38,9 +40,10 @@ class FakeLoadBalancer(object): self.listeners.append(listener) @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + elb_backend = elb_backends[region_name] new_elb = elb_backend.create_load_balancer( name=properties.get('LoadBalancerName', resource_name), zones=properties.get('AvailabilityZones'), @@ -148,4 +151,9 @@ class ELBBackend(BaseBackend): load_balancer.instance_ids = new_instance_ids return load_balancer -elb_backend = ELBBackend() + +elb_backends = {} +for region in boto.ec2.elb.regions(): + elb_backends[region.name] = ELBBackend() + +elb_backend = elb_backends['us-east-1'] diff --git a/moto/elb/responses.py b/moto/elb/responses.py index 1e777bec0..8895b75f9 100644 --- a/moto/elb/responses.py +++ b/moto/elb/responses.py @@ -2,11 +2,15 @@ from __future__ import unicode_literals from jinja2 import Template from moto.core.responses import BaseResponse -from .models import elb_backend +from .models import elb_backends class ELBResponse(BaseResponse): + @property + def elb_backend(self): + return elb_backends[self.region] + def create_load_balancer(self): """ u'Scheme': [u'internet-facing'], @@ -26,7 +30,7 @@ class ELBResponse(BaseResponse): ports.append([protocol, lb_port, instance_port, ssl_certificate_id]) port_index += 1 - elb_backend.create_load_balancer( + self.elb_backend.create_load_balancer( name=load_balancer_name, zones=availability_zones, ports=ports, @@ -49,14 +53,14 @@ class ELBResponse(BaseResponse): ports.append([protocol, lb_port, instance_port, ssl_certificate_id]) port_index += 1 - elb_backend.create_load_balancer_listeners(name=load_balancer_name, ports=ports) + self.elb_backend.create_load_balancer_listeners(name=load_balancer_name, ports=ports) template = Template(CREATE_LOAD_BALANCER_LISTENERS_TEMPLATE) return template.render() def describe_load_balancers(self): names = [value[0] for key, value in self.querystring.items() if "LoadBalancerNames.member" in key] - load_balancers = elb_backend.describe_load_balancers(names) + load_balancers = self.elb_backend.describe_load_balancers(names) template = Template(DESCRIBE_LOAD_BALANCERS_TEMPLATE) return template.render(load_balancers=load_balancers) @@ -73,18 +77,18 @@ class ELBResponse(BaseResponse): port_index += 1 ports.append(int(port)) - elb_backend.delete_load_balancer_listeners(load_balancer_name, ports) + self.elb_backend.delete_load_balancer_listeners(load_balancer_name, ports) template = Template(DELETE_LOAD_BALANCER_LISTENERS) return template.render() def delete_load_balancer(self): load_balancer_name = self.querystring.get('LoadBalancerName')[0] - elb_backend.delete_load_balancer(load_balancer_name) + self.elb_backend.delete_load_balancer(load_balancer_name) template = Template(DELETE_LOAD_BALANCER_TEMPLATE) return template.render() def configure_health_check(self): - check = elb_backend.configure_health_check( + check = self.elb_backend.configure_health_check( load_balancer_name=self.querystring.get('LoadBalancerName')[0], timeout=self.querystring.get('HealthCheck.Timeout')[0], healthy_threshold=self.querystring.get('HealthCheck.HealthyThreshold')[0], @@ -99,7 +103,7 @@ class ELBResponse(BaseResponse): load_balancer_name = self.querystring.get('LoadBalancerName')[0] instance_ids = [value[0] for key, value in self.querystring.items() if "Instances.member" in key] template = Template(REGISTER_INSTANCES_TEMPLATE) - load_balancer = elb_backend.register_instances(load_balancer_name, instance_ids) + load_balancer = self.elb_backend.register_instances(load_balancer_name, instance_ids) return template.render(load_balancer=load_balancer) def set_load_balancer_listener_sslcertificate(self): @@ -107,7 +111,7 @@ class ELBResponse(BaseResponse): ssl_certificate_id = self.querystring['SSLCertificateId'][0] lb_port = self.querystring['LoadBalancerPort'][0] - elb_backend.set_load_balancer_listener_sslcertificate(load_balancer_name, lb_port, ssl_certificate_id) + self.elb_backend.set_load_balancer_listener_sslcertificate(load_balancer_name, lb_port, ssl_certificate_id) template = Template(SET_LOAD_BALANCER_SSL_CERTIFICATE) return template.render() @@ -116,7 +120,7 @@ class ELBResponse(BaseResponse): load_balancer_name = self.querystring.get('LoadBalancerName')[0] instance_ids = [value[0] for key, value in self.querystring.items() if "Instances.member" in key] template = Template(DEREGISTER_INSTANCES_TEMPLATE) - load_balancer = elb_backend.deregister_instances(load_balancer_name, instance_ids) + load_balancer = self.elb_backend.deregister_instances(load_balancer_name, instance_ids) return template.render(load_balancer=load_balancer) CREATE_LOAD_BALANCER_TEMPLATE = """ diff --git a/moto/iam/models.py b/moto/iam/models.py index 7c4f2ec8f..a9bd4eb71 100644 --- a/moto/iam/models.py +++ b/moto/iam/models.py @@ -16,7 +16,7 @@ class Role(object): self.policies = policies @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] return iam_backend.create_role( @@ -45,7 +45,7 @@ class InstanceProfile(object): self.roles = roles if roles else [] @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] role_ids = properties['Roles'] diff --git a/moto/sqs/__init__.py b/moto/sqs/__init__.py index 2b934b6c7..18d6caa27 100644 --- a/moto/sqs/__init__.py +++ b/moto/sqs/__init__.py @@ -1,3 +1,10 @@ from __future__ import unicode_literals -from .models import sqs_backend -mock_sqs = sqs_backend.decorator +from .models import sqs_backends, sqs_backend # flake8: noqa +from ..core.models import MockAWS + + +def mock_sqs(func=None): + if func: + return MockAWS(sqs_backends)(func) + else: + return MockAWS(sqs_backends) diff --git a/moto/sqs/models.py b/moto/sqs/models.py index cbcb61892..a31e4b046 100644 --- a/moto/sqs/models.py +++ b/moto/sqs/models.py @@ -5,6 +5,7 @@ import time import re from xml.sax.saxutils import escape +import boto.sqs from moto.core import BaseBackend from moto.core.utils import camelcase_to_underscores, get_random_message_id @@ -120,9 +121,10 @@ class Queue(object): self.receive_message_wait_time_seconds = 0 @classmethod - def create_from_cloudformation_json(cls, resource_name, cloudformation_json): + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): properties = cloudformation_json['Properties'] + sqs_backend = sqs_backends[region_name] return sqs_backend.create_queue( name=properties['QueueName'], visibility_timeout=properties.get('VisibilityTimeout'), @@ -272,4 +274,8 @@ class SQSBackend(BaseBackend): return raise ReceiptHandleIsInvalid -sqs_backend = SQSBackend() +sqs_backends = {} +for region in boto.sqs.regions(): + sqs_backends[region.name] = SQSBackend() + +sqs_backend = sqs_backends['us-east-1'] diff --git a/moto/sqs/responses.py b/moto/sqs/responses.py index 71d68c72f..a151d62ca 100644 --- a/moto/sqs/responses.py +++ b/moto/sqs/responses.py @@ -4,7 +4,7 @@ from jinja2 import Template from moto.core.responses import BaseResponse from moto.core.utils import camelcase_to_underscores from .utils import parse_message_attributes -from .models import sqs_backend +from .models import sqs_backends from .exceptions import ( MessageAttributesInvalid, MessageNotInflight, @@ -16,19 +16,23 @@ MAXIMUM_VISIBILTY_TIMEOUT = 43200 class QueuesResponse(BaseResponse): + @property + def sqs_backend(self): + return sqs_backends[self.region] + def create_queue(self): visibility_timeout = None if 'Attribute.1.Name' in self.querystring and self.querystring.get('Attribute.1.Name')[0] == 'VisibilityTimeout': visibility_timeout = self.querystring.get("Attribute.1.Value")[0] queue_name = self.querystring.get("QueueName")[0] - queue = sqs_backend.create_queue(queue_name, visibility_timeout=visibility_timeout) + queue = self.sqs_backend.create_queue(queue_name, visibility_timeout=visibility_timeout) template = Template(CREATE_QUEUE_RESPONSE) return template.render(queue=queue) def get_queue_url(self): queue_name = self.querystring.get("QueueName")[0] - queue = sqs_backend.get_queue(queue_name) + queue = self.sqs_backend.get_queue(queue_name) if queue: template = Template(GET_QUEUE_URL_RESPONSE) return template.render(queue=queue) @@ -37,12 +41,17 @@ class QueuesResponse(BaseResponse): def list_queues(self): queue_name_prefix = self.querystring.get("QueueNamePrefix", [None])[0] - queues = sqs_backend.list_queues(queue_name_prefix) + queues = self.sqs_backend.list_queues(queue_name_prefix) template = Template(LIST_QUEUES_RESPONSE) return template.render(queues=queues) class QueueResponse(BaseResponse): + + @property + def sqs_backend(self): + return sqs_backends[self.region] + def change_message_visibility(self): queue_name = self.path.split("/")[-1] receipt_handle = self.querystring.get("ReceiptHandle")[0] @@ -54,7 +63,7 @@ class QueueResponse(BaseResponse): ), dict(status=400) try: - sqs_backend.change_message_visibility( + self.sqs_backend.change_message_visibility( queue_name=queue_name, receipt_handle=receipt_handle, visibility_timeout=visibility_timeout @@ -67,7 +76,7 @@ class QueueResponse(BaseResponse): def get_queue_attributes(self): queue_name = self.path.split("/")[-1] - queue = sqs_backend.get_queue(queue_name) + queue = self.sqs_backend.get_queue(queue_name) template = Template(GET_QUEUE_ATTRIBUTES_RESPONSE) return template.render(queue=queue) @@ -75,12 +84,12 @@ class QueueResponse(BaseResponse): queue_name = self.path.split("/")[-1] key = camelcase_to_underscores(self.querystring.get('Attribute.Name')[0]) value = self.querystring.get('Attribute.Value')[0] - sqs_backend.set_queue_attribute(queue_name, key, value) + self.sqs_backend.set_queue_attribute(queue_name, key, value) return SET_QUEUE_ATTRIBUTE_RESPONSE def delete_queue(self): queue_name = self.path.split("/")[-1] - queue = sqs_backend.delete_queue(queue_name) + queue = self.sqs_backend.delete_queue(queue_name) if not queue: return "A queue with name {0} does not exist".format(queue_name), dict(status=404) template = Template(DELETE_QUEUE_RESPONSE) @@ -101,7 +110,7 @@ class QueueResponse(BaseResponse): return e.description, dict(status=e.status_code) queue_name = self.path.split("/")[-1] - message = sqs_backend.send_message( + message = self.sqs_backend.send_message( queue_name, message, message_attributes=message_attributes, @@ -137,7 +146,7 @@ class QueueResponse(BaseResponse): message_user_id = self.querystring.get(message_user_id_key)[0] delay_key = 'SendMessageBatchRequestEntry.{0}.DelaySeconds'.format(index) delay_seconds = self.querystring.get(delay_key, [None])[0] - message = sqs_backend.send_message(queue_name, message_body[0], delay_seconds=delay_seconds) + message = self.sqs_backend.send_message(queue_name, message_body[0], delay_seconds=delay_seconds) message.user_id = message_user_id message_attributes = parse_message_attributes(self.querystring, base='SendMessageBatchRequestEntry.{0}.'.format(index), value_namespace='') @@ -153,7 +162,7 @@ class QueueResponse(BaseResponse): def delete_message(self): queue_name = self.path.split("/")[-1] receipt_handle = self.querystring.get("ReceiptHandle")[0] - sqs_backend.delete_message(queue_name, receipt_handle) + self.sqs_backend.delete_message(queue_name, receipt_handle) template = Template(DELETE_MESSAGE_RESPONSE) return template.render() @@ -178,7 +187,7 @@ class QueueResponse(BaseResponse): # Found all messages break - sqs_backend.delete_message(queue_name, receipt_handle[0]) + self.sqs_backend.delete_message(queue_name, receipt_handle[0]) message_user_id_key = 'DeleteMessageBatchRequestEntry.{0}.Id'.format(index) message_user_id = self.querystring.get(message_user_id_key)[0] @@ -190,7 +199,7 @@ class QueueResponse(BaseResponse): def receive_message(self): queue_name = self.path.split("/")[-1] message_count = int(self.querystring.get("MaxNumberOfMessages")[0]) - messages = sqs_backend.receive_messages(queue_name, message_count) + messages = self.sqs_backend.receive_messages(queue_name, message_count) template = Template(RECEIVE_MESSAGE_RESPONSE) output = template.render(messages=messages) return output diff --git a/tests/test_cloudformation/test_cloudformation_stack_crud.py b/tests/test_cloudformation/test_cloudformation_stack_crud.py index 4ab7b2062..f97542391 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_crud.py +++ b/tests/test_cloudformation/test_cloudformation_stack_crud.py @@ -1,9 +1,12 @@ from __future__ import unicode_literals + import json + import boto +import boto.cloudformation import sure # noqa # Ensure 'assert_raises' context manager support for Python 2.6 -import tests.backport_assert_raises +import tests.backport_assert_raises # noqa from nose.tools import assert_raises from moto import mock_cloudformation @@ -38,6 +41,18 @@ def test_create_stack(): stack.get_template().should.equal(dummy_template) +@mock_cloudformation +def test_creating_stacks_across_regions(): + west1_conn = boto.cloudformation.connect_to_region("us-west-1") + west1_conn.create_stack("test_stack", template_body=dummy_template_json) + + west2_conn = boto.cloudformation.connect_to_region("us-west-2") + west2_conn.create_stack("test_stack", template_body=dummy_template_json) + + list(west1_conn.describe_stacks()).should.have.length_of(1) + list(west2_conn.describe_stacks()).should.have.length_of(1) + + @mock_cloudformation def test_create_stack_with_notification_arn(): conn = boto.connect_cloudformation() diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 3cda40928..7fb06cf20 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -2,6 +2,8 @@ from __future__ import unicode_literals import json import boto +import boto.cloudformation +import boto.ec2 import sure # noqa from moto import ( @@ -464,13 +466,13 @@ def test_iam_roles(): def test_single_instance_with_ebs_volume(): template_json = json.dumps(single_instance_with_ebs_volume.template) - conn = boto.connect_cloudformation() + conn = boto.cloudformation.connect_to_region("us-west-1") conn.create_stack( "test_stack", template_body=template_json, ) - ec2_conn = boto.connect_ec2() + ec2_conn = boto.ec2.connect_to_region("us-west-1") reservation = ec2_conn.get_all_instances()[0] ec2_instance = reservation.instances[0] @@ -490,10 +492,7 @@ def test_classic_eip(): template_json = json.dumps(ec2_classic_eip.template) conn = boto.connect_cloudformation() - conn.create_stack( - "test_stack", - template_body=template_json, - ) + conn.create_stack("test_stack", template_body=template_json) ec2_conn = boto.connect_ec2() eip = ec2_conn.get_all_addresses()[0] @@ -509,10 +508,7 @@ def test_vpc_eip(): template_json = json.dumps(vpc_eip.template) conn = boto.connect_cloudformation() - conn.create_stack( - "test_stack", - template_body=template_json, - ) + conn.create_stack("test_stack", template_body=template_json) ec2_conn = boto.connect_ec2() eip = ec2_conn.get_all_addresses()[0] @@ -528,10 +524,7 @@ def test_fn_join(): template_json = json.dumps(fn_join.template) conn = boto.connect_cloudformation() - conn.create_stack( - "test_stack", - template_body=template_json, - ) + conn.create_stack("test_stack", template_body=template_json) ec2_conn = boto.connect_ec2() eip = ec2_conn.get_all_addresses()[0] diff --git a/tests/test_cloudformation/test_stack_parsing.py b/tests/test_cloudformation/test_stack_parsing.py index 286d5de74..548c6fd66 100644 --- a/tests/test_cloudformation/test_stack_parsing.py +++ b/tests/test_cloudformation/test_stack_parsing.py @@ -82,7 +82,7 @@ def test_parse_stack_resources(): stack_id="test_id", name="test_stack", template=dummy_template_json, - ) + region_name='us-west-1') stack.resource_map.should.have.length_of(1) list(stack.resource_map.keys())[0].should.equal('Queue') @@ -101,7 +101,8 @@ def test_parse_stack_with_name_type_resource(): stack = FakeStack( stack_id="test_id", name="test_stack", - template=name_type_template_json) + template=name_type_template_json, + region_name='us-west-1') stack.resource_map.should.have.length_of(1) list(stack.resource_map.keys())[0].should.equal('Queue') @@ -113,7 +114,8 @@ def test_parse_stack_with_outputs(): stack = FakeStack( stack_id="test_id", name="test_stack", - template=output_type_template_json) + template=output_type_template_json, + region_name='us-west-1') stack.output_map.should.have.length_of(1) list(stack.output_map.keys())[0].should.equal('Output1') @@ -126,7 +128,8 @@ def test_parse_stack_with_get_attribute_outputs(): stack = FakeStack( stack_id="test_id", name="test_stack", - template=get_attribute_outputs_template_json) + template=get_attribute_outputs_template_json, + region_name='us-west-1') stack.output_map.should.have.length_of(1) list(stack.output_map.keys())[0].should.equal('Output1') @@ -137,4 +140,4 @@ def test_parse_stack_with_get_attribute_outputs(): def test_parse_stack_with_bad_get_attribute_outputs(): FakeStack.when.called_with( - "test_id", "test_stack", bad_output_template_json).should.throw(BotoServerError) \ No newline at end of file + "test_id", "test_stack", bad_output_template_json, "us-west-1").should.throw(BotoServerError)