Merge pull request #173 from hltbra/multi-region

Add multi-region support to EC2 Instances, Autoscaling Groups & LaunchConfigurations
This commit is contained in:
Steve Pulec 2014-08-26 20:33:14 -04:00
commit 5a20476e74
11 changed files with 234 additions and 51 deletions

View File

@ -1,2 +1,9 @@
from .models import autoscaling_backend from .models import autoscaling_backend, autoscaling_backends
mock_autoscaling = autoscaling_backend.decorator from ..core.models import MockAWS
def mock_autoscaling(func=None):
if func:
return MockAWS(autoscaling_backends)(func)
else:
return MockAWS(autoscaling_backends)

View File

@ -1,6 +1,6 @@
from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
from moto.core import BaseBackend from moto.core import BaseBackend
from moto.ec2 import ec2_backend from moto.ec2 import ec2_backends
# http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/AS_Concepts.html#Cooldown # http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/AS_Concepts.html#Cooldown
DEFAULT_COOLDOWN = 300 DEFAULT_COOLDOWN = 300
@ -8,7 +8,7 @@ DEFAULT_COOLDOWN = 300
class FakeScalingPolicy(object): class FakeScalingPolicy(object):
def __init__(self, name, adjustment_type, as_name, scaling_adjustment, def __init__(self, name, adjustment_type, as_name, scaling_adjustment,
cooldown): cooldown, autoscaling_backend):
self.name = name self.name = name
self.adjustment_type = adjustment_type self.adjustment_type = adjustment_type
self.as_name = as_name self.as_name = as_name
@ -17,14 +17,15 @@ class FakeScalingPolicy(object):
self.cooldown = cooldown self.cooldown = cooldown
else: else:
self.cooldown = DEFAULT_COOLDOWN self.cooldown = DEFAULT_COOLDOWN
self.autoscaling_backend = autoscaling_backend
def execute(self): def execute(self):
if self.adjustment_type == 'ExactCapacity': if self.adjustment_type == 'ExactCapacity':
autoscaling_backend.set_desired_capacity(self.as_name, self.scaling_adjustment) self.autoscaling_backend.set_desired_capacity(self.as_name, self.scaling_adjustment)
elif self.adjustment_type == 'ChangeInCapacity': elif self.adjustment_type == 'ChangeInCapacity':
autoscaling_backend.change_capacity(self.as_name, self.scaling_adjustment) self.autoscaling_backend.change_capacity(self.as_name, self.scaling_adjustment)
elif self.adjustment_type == 'PercentChangeInCapacity': elif self.adjustment_type == 'PercentChangeInCapacity':
autoscaling_backend.change_capacity_percent(self.as_name, self.scaling_adjustment) self.autoscaling_backend.change_capacity_percent(self.as_name, self.scaling_adjustment)
class FakeLaunchConfiguration(object): class FakeLaunchConfiguration(object):
@ -104,7 +105,7 @@ class FakeAutoScalingGroup(object):
def __init__(self, name, availability_zones, desired_capacity, max_size, def __init__(self, name, availability_zones, desired_capacity, max_size,
min_size, launch_config_name, vpc_zone_identifier, min_size, launch_config_name, vpc_zone_identifier,
default_cooldown, health_check_period, health_check_type, default_cooldown, health_check_period, health_check_type,
load_balancers, placement_group, termination_policies): load_balancers, placement_group, termination_policies, autoscaling_backend):
self.name = name self.name = name
self.availability_zones = availability_zones self.availability_zones = availability_zones
self.max_size = max_size self.max_size = max_size
@ -124,6 +125,8 @@ class FakeAutoScalingGroup(object):
self.instances = [] self.instances = []
self.set_desired_capacity(desired_capacity) self.set_desired_capacity(desired_capacity)
self.autoscaling_backend = autoscaling_backend
@classmethod @classmethod
def create_from_cloudformation_json(cls, resource_name, cloudformation_json): def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
properties = cloudformation_json['Properties'] properties = cloudformation_json['Properties']
@ -182,7 +185,7 @@ class FakeAutoScalingGroup(object):
if self.desired_capacity > curr_instance_count: if self.desired_capacity > curr_instance_count:
# Need more instances # Need more instances
count_needed = self.desired_capacity - curr_instance_count count_needed = self.desired_capacity - curr_instance_count
reservation = ec2_backend.add_instances( reservation = autoscaling_backend.ec2_backend.add_instances(
self.launch_config.image_id, self.launch_config.image_id,
count_needed, count_needed,
self.launch_config.user_data, self.launch_config.user_data,
@ -196,16 +199,22 @@ class FakeAutoScalingGroup(object):
count_to_remove = curr_instance_count - self.desired_capacity count_to_remove = curr_instance_count - self.desired_capacity
instances_to_remove = self.instances[:count_to_remove] instances_to_remove = self.instances[:count_to_remove]
instance_ids_to_remove = [instance.id for instance in instances_to_remove] instance_ids_to_remove = [instance.id for instance in instances_to_remove]
ec2_backend.terminate_instances(instance_ids_to_remove) autoscaling_backend.ec2_backend.terminate_instances(instance_ids_to_remove)
self.instances = self.instances[count_to_remove:] self.instances = self.instances[count_to_remove:]
class AutoScalingBackend(BaseBackend): class AutoScalingBackend(BaseBackend):
def __init__(self): def __init__(self, ec2_backend):
self.autoscaling_groups = {} self.autoscaling_groups = {}
self.launch_configurations = {} self.launch_configurations = {}
self.policies = {} self.policies = {}
self.ec2_backend = ec2_backend
def reset(self):
ec2_backend = self.ec2_backend
self.__dict__ = {}
self.__init__(ec2_backend)
def create_launch_configuration(self, name, image_id, key_name, def create_launch_configuration(self, name, image_id, key_name,
security_groups, user_data, instance_type, security_groups, user_data, instance_type,
@ -267,6 +276,7 @@ class AutoScalingBackend(BaseBackend):
load_balancers=load_balancers, load_balancers=load_balancers,
placement_group=placement_group, placement_group=placement_group,
termination_policies=termination_policies, termination_policies=termination_policies,
autoscaling_backend=self,
) )
self.autoscaling_groups[name] = group self.autoscaling_groups[name] = group
return group return group
@ -328,7 +338,7 @@ class AutoScalingBackend(BaseBackend):
def create_autoscaling_policy(self, name, adjustment_type, as_name, def create_autoscaling_policy(self, name, adjustment_type, as_name,
scaling_adjustment, cooldown): scaling_adjustment, cooldown):
policy = FakeScalingPolicy(name, adjustment_type, as_name, policy = FakeScalingPolicy(name, adjustment_type, as_name,
scaling_adjustment, cooldown) scaling_adjustment, cooldown, self)
self.policies[name] = policy self.policies[name] = policy
return policy return policy
@ -343,4 +353,9 @@ class AutoScalingBackend(BaseBackend):
policy = self.policies[group_name] policy = self.policies[group_name]
policy.execute() policy.execute()
autoscaling_backend = AutoScalingBackend()
autoscaling_backends = {}
for region, ec2_backend in ec2_backends.items():
autoscaling_backends[region] = AutoScalingBackend(ec2_backend)
autoscaling_backend = autoscaling_backends['us-east-1']

View File

@ -2,10 +2,15 @@ from jinja2 import Template
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from .models import autoscaling_backend from .models import autoscaling_backends
class AutoScalingResponse(BaseResponse): class AutoScalingResponse(BaseResponse):
@property
def autoscaling_backend(self):
return autoscaling_backends[self.region]
def _get_int_param(self, param_name): def _get_int_param(self, param_name):
value = self._get_param(param_name) value = self._get_param(param_name)
if value is not None: if value is not None:
@ -32,7 +37,7 @@ class AutoScalingResponse(BaseResponse):
instance_monitoring = True instance_monitoring = True
else: else:
instance_monitoring = False instance_monitoring = False
autoscaling_backend.create_launch_configuration( self.autoscaling_backend.create_launch_configuration(
name=self._get_param('LaunchConfigurationName'), name=self._get_param('LaunchConfigurationName'),
image_id=self._get_param('ImageId'), image_id=self._get_param('ImageId'),
key_name=self._get_param('KeyName'), key_name=self._get_param('KeyName'),
@ -51,18 +56,18 @@ class AutoScalingResponse(BaseResponse):
def describe_launch_configurations(self): def describe_launch_configurations(self):
names = self._get_multi_param('LaunchConfigurationNames') names = self._get_multi_param('LaunchConfigurationNames')
launch_configurations = autoscaling_backend.describe_launch_configurations(names) launch_configurations = self.autoscaling_backend.describe_launch_configurations(names)
template = Template(DESCRIBE_LAUNCH_CONFIGURATIONS_TEMPLATE) template = Template(DESCRIBE_LAUNCH_CONFIGURATIONS_TEMPLATE)
return template.render(launch_configurations=launch_configurations) return template.render(launch_configurations=launch_configurations)
def delete_launch_configuration(self): def delete_launch_configuration(self):
launch_configurations_name = self.querystring.get('LaunchConfigurationName')[0] launch_configurations_name = self.querystring.get('LaunchConfigurationName')[0]
autoscaling_backend.delete_launch_configuration(launch_configurations_name) self.autoscaling_backend.delete_launch_configuration(launch_configurations_name)
template = Template(DELETE_LAUNCH_CONFIGURATION_TEMPLATE) template = Template(DELETE_LAUNCH_CONFIGURATION_TEMPLATE)
return template.render() return template.render()
def create_auto_scaling_group(self): def create_auto_scaling_group(self):
autoscaling_backend.create_autoscaling_group( self.autoscaling_backend.create_autoscaling_group(
name=self._get_param('AutoScalingGroupName'), name=self._get_param('AutoScalingGroupName'),
availability_zones=self._get_multi_param('AvailabilityZones.member'), availability_zones=self._get_multi_param('AvailabilityZones.member'),
desired_capacity=self._get_int_param('DesiredCapacity'), desired_capacity=self._get_int_param('DesiredCapacity'),
@ -82,12 +87,12 @@ class AutoScalingResponse(BaseResponse):
def describe_auto_scaling_groups(self): def describe_auto_scaling_groups(self):
names = self._get_multi_param("AutoScalingGroupNames") names = self._get_multi_param("AutoScalingGroupNames")
groups = autoscaling_backend.describe_autoscaling_groups(names) groups = self.autoscaling_backend.describe_autoscaling_groups(names)
template = Template(DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE) template = Template(DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE)
return template.render(groups=groups) return template.render(groups=groups)
def update_auto_scaling_group(self): def update_auto_scaling_group(self):
autoscaling_backend.update_autoscaling_group( self.autoscaling_backend.update_autoscaling_group(
name=self._get_param('AutoScalingGroupName'), name=self._get_param('AutoScalingGroupName'),
availability_zones=self._get_multi_param('AvailabilityZones.member'), availability_zones=self._get_multi_param('AvailabilityZones.member'),
desired_capacity=self._get_int_param('DesiredCapacity'), desired_capacity=self._get_int_param('DesiredCapacity'),
@ -107,24 +112,24 @@ class AutoScalingResponse(BaseResponse):
def delete_auto_scaling_group(self): def delete_auto_scaling_group(self):
group_name = self._get_param('AutoScalingGroupName') group_name = self._get_param('AutoScalingGroupName')
autoscaling_backend.delete_autoscaling_group(group_name) self.autoscaling_backend.delete_autoscaling_group(group_name)
template = Template(DELETE_AUTOSCALING_GROUP_TEMPLATE) template = Template(DELETE_AUTOSCALING_GROUP_TEMPLATE)
return template.render() return template.render()
def set_desired_capacity(self): def set_desired_capacity(self):
group_name = self._get_param('AutoScalingGroupName') group_name = self._get_param('AutoScalingGroupName')
desired_capacity = self._get_int_param('DesiredCapacity') desired_capacity = self._get_int_param('DesiredCapacity')
autoscaling_backend.set_desired_capacity(group_name, desired_capacity) self.autoscaling_backend.set_desired_capacity(group_name, desired_capacity)
template = Template(SET_DESIRED_CAPACITY_TEMPLATE) template = Template(SET_DESIRED_CAPACITY_TEMPLATE)
return template.render() return template.render()
def describe_auto_scaling_instances(self): def describe_auto_scaling_instances(self):
instances = autoscaling_backend.describe_autoscaling_instances() instances = self.autoscaling_backend.describe_autoscaling_instances()
template = Template(DESCRIBE_AUTOSCALING_INSTANCES_TEMPLATE) template = Template(DESCRIBE_AUTOSCALING_INSTANCES_TEMPLATE)
return template.render(instances=instances) return template.render(instances=instances)
def put_scaling_policy(self): def put_scaling_policy(self):
policy = autoscaling_backend.create_autoscaling_policy( policy = self.autoscaling_backend.create_autoscaling_policy(
name=self._get_param('PolicyName'), name=self._get_param('PolicyName'),
adjustment_type=self._get_param('AdjustmentType'), adjustment_type=self._get_param('AdjustmentType'),
as_name=self._get_param('AutoScalingGroupName'), as_name=self._get_param('AutoScalingGroupName'),
@ -135,19 +140,19 @@ class AutoScalingResponse(BaseResponse):
return template.render(policy=policy) return template.render(policy=policy)
def describe_policies(self): def describe_policies(self):
policies = autoscaling_backend.describe_policies() policies = self.autoscaling_backend.describe_policies()
template = Template(DESCRIBE_SCALING_POLICIES_TEMPLATE) template = Template(DESCRIBE_SCALING_POLICIES_TEMPLATE)
return template.render(policies=policies) return template.render(policies=policies)
def delete_policy(self): def delete_policy(self):
group_name = self._get_param('PolicyName') group_name = self._get_param('PolicyName')
autoscaling_backend.delete_policy(group_name) self.autoscaling_backend.delete_policy(group_name)
template = Template(DELETE_POLICY_TEMPLATE) template = Template(DELETE_POLICY_TEMPLATE)
return template.render() return template.render()
def execute_policy(self): def execute_policy(self):
group_name = self._get_param('PolicyName') group_name = self._get_param('PolicyName')
autoscaling_backend.execute_policy(group_name) self.autoscaling_backend.execute_policy(group_name)
template = Template(EXECUTE_POLICY_TEMPLATE) template = Template(EXECUTE_POLICY_TEMPLATE)
return template.render() return template.render()

View File

@ -9,8 +9,8 @@ from .utils import convert_regex_to_flask_path
class MockAWS(object): class MockAWS(object):
nested_count = 0 nested_count = 0
def __init__(self, backend): def __init__(self, backends):
self.backend = backend self.backends = backends
if self.__class__.nested_count == 0: if self.__class__.nested_count == 0:
HTTPretty.reset() HTTPretty.reset()
@ -26,13 +26,15 @@ class MockAWS(object):
def start(self): def start(self):
self.__class__.nested_count += 1 self.__class__.nested_count += 1
self.backend.reset() for backend in self.backends.values():
backend.reset()
if not HTTPretty.is_enabled(): if not HTTPretty.is_enabled():
HTTPretty.enable() HTTPretty.enable()
for method in HTTPretty.METHODS: for method in HTTPretty.METHODS:
for key, value in self.backend.urls.iteritems(): backend = self.backends.values()[0]
for key, value in backend.urls.iteritems():
HTTPretty.register_uri( HTTPretty.register_uri(
method=method, method=method,
uri=re.compile(key), uri=re.compile(key),
@ -151,6 +153,6 @@ class BaseBackend(object):
def decorator(self, func=None): def decorator(self, func=None):
if func: if func:
return MockAWS(self)(func) return MockAWS({'global': self})(func)
else: else:
return MockAWS(self) return MockAWS({'global': self})

View File

@ -1,5 +1,6 @@
import datetime import datetime
import json import json
import re
from urlparse import parse_qs, urlparse from urlparse import parse_qs, urlparse
@ -9,6 +10,8 @@ from moto.core.utils import camelcase_to_underscores, method_names_from_class
class BaseResponse(object): class BaseResponse(object):
region = 'us-east-1'
def dispatch(self, request, full_url, headers): def dispatch(self, request, full_url, headers):
querystring = {} querystring = {}
@ -38,6 +41,9 @@ class BaseResponse(object):
self.path = urlparse(full_url).path self.path = urlparse(full_url).path
self.querystring = querystring self.querystring = querystring
self.method = request.method self.method = request.method
region = re.search(r'\.(.+?)\.amazonaws\.com', full_url)
if region:
self.region = region.group(1)
self.headers = dict(request.headers) self.headers = dict(request.headers)
self.response_headers = headers self.response_headers = headers

View File

@ -1,2 +1,8 @@
from .models import ec2_backend from .models import ec2_backends, ec2_backend
mock_ec2 = ec2_backend.decorator from ..core.models import MockAWS
def mock_ec2(func=None):
if func:
return MockAWS(ec2_backends)(func)
else:
return MockAWS(ec2_backends)

View File

@ -2,6 +2,7 @@ import copy
import itertools import itertools
from collections import defaultdict from collections import defaultdict
import boto
from boto.ec2.instance import Instance as BotoInstance, Reservation from boto.ec2.instance import Instance as BotoInstance, Reservation
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
@ -1360,4 +1361,8 @@ class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
raise EC2ClientError(code, message) raise EC2ClientError(code, message)
ec2_backend = EC2Backend() ec2_backends = {}
for region in boto.ec2.regions():
ec2_backends[region.name] = EC2Backend()
ec2_backend = ec2_backends['us-east-1']

View File

@ -60,4 +60,7 @@ class EC2Response(
VPNConnections, VPNConnections,
Windows, Windows,
): ):
pass @property
def ec2_backend(self):
from moto.ec2.models import ec2_backends
return ec2_backends[self.region]

View File

@ -2,7 +2,6 @@ from jinja2 import Template
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.core.utils import camelcase_to_underscores from moto.core.utils import camelcase_to_underscores
from moto.ec2.models import ec2_backend
from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations from moto.ec2.utils import instance_ids_from_querystring, filters_from_querystring, filter_reservations
@ -10,9 +9,9 @@ class InstanceResponse(BaseResponse):
def describe_instances(self): def describe_instances(self):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
if instance_ids: if instance_ids:
reservations = ec2_backend.get_reservations_by_instance_ids(instance_ids) reservations = self.ec2_backend.get_reservations_by_instance_ids(instance_ids)
else: else:
reservations = ec2_backend.all_reservations(make_copy=True) reservations = self.ec2_backend.all_reservations(make_copy=True)
filter_dict = filters_from_querystring(self.querystring) filter_dict = filters_from_querystring(self.querystring)
reservations = filter_reservations(reservations, filter_dict) reservations = filter_reservations(reservations, filter_dict)
@ -29,7 +28,7 @@ class InstanceResponse(BaseResponse):
instance_type = self.querystring.get("InstanceType", ["m1.small"])[0] instance_type = self.querystring.get("InstanceType", ["m1.small"])[0]
subnet_id = self.querystring.get("SubnetId", [None])[0] subnet_id = self.querystring.get("SubnetId", [None])[0]
key_name = self.querystring.get("KeyName", [None])[0] key_name = self.querystring.get("KeyName", [None])[0]
new_reservation = ec2_backend.add_instances( new_reservation = self.ec2_backend.add_instances(
image_id, min_count, user_data, security_group_names, image_id, min_count, user_data, security_group_names,
instance_type=instance_type, subnet_id=subnet_id, instance_type=instance_type, subnet_id=subnet_id,
key_name=key_name, security_group_ids=security_group_ids) key_name=key_name, security_group_ids=security_group_ids)
@ -38,25 +37,25 @@ class InstanceResponse(BaseResponse):
def terminate_instances(self): def terminate_instances(self):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instances = ec2_backend.terminate_instances(instance_ids) instances = self.ec2_backend.terminate_instances(instance_ids)
template = Template(EC2_TERMINATE_INSTANCES) template = Template(EC2_TERMINATE_INSTANCES)
return template.render(instances=instances) return template.render(instances=instances)
def reboot_instances(self): def reboot_instances(self):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instances = ec2_backend.reboot_instances(instance_ids) instances = self.ec2_backend.reboot_instances(instance_ids)
template = Template(EC2_REBOOT_INSTANCES) template = Template(EC2_REBOOT_INSTANCES)
return template.render(instances=instances) return template.render(instances=instances)
def stop_instances(self): def stop_instances(self):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instances = ec2_backend.stop_instances(instance_ids) instances = self.ec2_backend.stop_instances(instance_ids)
template = Template(EC2_STOP_INSTANCES) template = Template(EC2_STOP_INSTANCES)
return template.render(instances=instances) return template.render(instances=instances)
def start_instances(self): def start_instances(self):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instances = ec2_backend.start_instances(instance_ids) instances = self.ec2_backend.start_instances(instance_ids)
template = Template(EC2_START_INSTANCES) template = Template(EC2_START_INSTANCES)
return template.render(instances=instances) return template.render(instances=instances)
@ -64,9 +63,9 @@ class InstanceResponse(BaseResponse):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
if instance_ids: if instance_ids:
instances = ec2_backend.get_multi_instances_by_id(instance_ids) instances = self.ec2_backend.get_multi_instances_by_id(instance_ids)
else: else:
instances = ec2_backend.all_instances() instances = self.ec2_backend.all_instances()
template = Template(EC2_INSTANCE_STATUS) template = Template(EC2_INSTANCE_STATUS)
return template.render(instances=instances) return template.render(instances=instances)
@ -78,7 +77,7 @@ class InstanceResponse(BaseResponse):
key = camelcase_to_underscores(attribute) key = camelcase_to_underscores(attribute)
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instance_id = instance_ids[0] instance_id = instance_ids[0]
instance, value = ec2_backend.describe_instance_attribute(instance_id, key) instance, value = self.ec2_backend.describe_instance_attribute(instance_id, key)
template = Template(EC2_DESCRIBE_INSTANCE_ATTRIBUTE) template = Template(EC2_DESCRIBE_INSTANCE_ATTRIBUTE)
return template.render(instance=instance, attribute=attribute, value=value) return template.render(instance=instance, attribute=attribute, value=value)
@ -126,7 +125,7 @@ class InstanceResponse(BaseResponse):
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instance_id = instance_ids[0] instance_id = instance_ids[0]
instance = ec2_backend.get_instance(instance_id) instance = self.ec2_backend.get_instance(instance_id)
block_device_type = instance.block_device_mapping[device_name_value] block_device_type = instance.block_device_mapping[device_name_value]
block_device_type.delete_on_termination = del_on_term_value block_device_type.delete_on_termination = del_on_term_value
@ -151,7 +150,7 @@ class InstanceResponse(BaseResponse):
normalized_attribute = camelcase_to_underscores(attribute_key.split(".")[0]) normalized_attribute = camelcase_to_underscores(attribute_key.split(".")[0])
instance_ids = instance_ids_from_querystring(self.querystring) instance_ids = instance_ids_from_querystring(self.querystring)
instance_id = instance_ids[0] instance_id = instance_ids[0]
ec2_backend.modify_instance_attribute(instance_id, normalized_attribute, value) self.ec2_backend.modify_instance_attribute(instance_id, normalized_attribute, value)
return EC2_MODIFY_INSTANCE_ATTRIBUTE return EC2_MODIFY_INSTANCE_ATTRIBUTE

View File

@ -0,0 +1,132 @@
import boto.ec2
import boto.ec2.autoscale
import sure
from moto import mock_ec2, mock_autoscaling
def add_servers_to_region(ami_id, count, region):
conn = boto.ec2.connect_to_region(region)
for index in range(count):
conn.run_instances(ami_id)
@mock_ec2
def test_add_servers_to_a_single_region():
region = 'ap-northeast-1'
add_servers_to_region('ami-1234abcd', 1, region)
add_servers_to_region('ami-5678efgh', 1, region)
conn = boto.ec2.connect_to_region(region)
reservations = conn.get_all_instances()
len(reservations).should.equal(2)
reservations.sort(key=lambda x: x.instances[0].image_id)
reservations[0].instances[0].image_id.should.equal('ami-1234abcd')
reservations[1].instances[0].image_id.should.equal('ami-5678efgh')
@mock_ec2
def test_add_servers_to_multiple_regions():
region1 = 'us-east-1'
region2 = 'ap-northeast-1'
add_servers_to_region('ami-1234abcd', 1, region1)
add_servers_to_region('ami-5678efgh', 1, region2)
us_conn = boto.ec2.connect_to_region(region1)
ap_conn = boto.ec2.connect_to_region(region2)
us_reservations = us_conn.get_all_instances()
ap_reservations = ap_conn.get_all_instances()
len(us_reservations).should.equal(1)
len(ap_reservations).should.equal(1)
us_reservations[0].instances[0].image_id.should.equal('ami-1234abcd')
ap_reservations[0].instances[0].image_id.should.equal('ami-5678efgh')
@mock_autoscaling
def test_create_autoscaling_group():
us_conn = boto.ec2.autoscale.connect_to_region('us-east-1')
config = boto.ec2.autoscale.LaunchConfiguration(
name='us_tester',
image_id='ami-abcd1234',
instance_type='m1.small',
)
us_conn.create_launch_configuration(config)
group = boto.ec2.autoscale.AutoScalingGroup(
name='us_tester_group',
availability_zones=['us-east-1c'],
default_cooldown=60,
desired_capacity=2,
health_check_period=100,
health_check_type="EC2",
max_size=2,
min_size=2,
launch_config=config,
load_balancers=["us_test_lb"],
placement_group="us_test_placement",
vpc_zone_identifier='subnet-1234abcd',
termination_policies=["OldestInstance", "NewestInstance"],
)
us_conn.create_auto_scaling_group(group)
ap_conn = boto.ec2.autoscale.connect_to_region('ap-northeast-1')
config = boto.ec2.autoscale.LaunchConfiguration(
name='ap_tester',
image_id='ami-efgh5678',
instance_type='m1.small',
)
ap_conn.create_launch_configuration(config)
group = boto.ec2.autoscale.AutoScalingGroup(
name='ap_tester_group',
availability_zones=['ap-northeast-1a'],
default_cooldown=60,
desired_capacity=2,
health_check_period=100,
health_check_type="EC2",
max_size=2,
min_size=2,
launch_config=config,
load_balancers=["ap_test_lb"],
placement_group="ap_test_placement",
vpc_zone_identifier='subnet-5678efgh',
termination_policies=["OldestInstance", "NewestInstance"],
)
ap_conn.create_auto_scaling_group(group)
len(us_conn.get_all_groups()).should.equal(1)
len(ap_conn.get_all_groups()).should.equal(1)
us_group = us_conn.get_all_groups()[0]
us_group.name.should.equal('us_tester_group')
list(us_group.availability_zones).should.equal(['us-east-1c'])
us_group.desired_capacity.should.equal(2)
us_group.max_size.should.equal(2)
us_group.min_size.should.equal(2)
us_group.vpc_zone_identifier.should.equal('subnet-1234abcd')
us_group.launch_config_name.should.equal('us_tester')
us_group.default_cooldown.should.equal(60)
us_group.health_check_period.should.equal(100)
us_group.health_check_type.should.equal("EC2")
list(us_group.load_balancers).should.equal(["us_test_lb"])
us_group.placement_group.should.equal("us_test_placement")
list(us_group.termination_policies).should.equal(["OldestInstance", "NewestInstance"])
ap_group = ap_conn.get_all_groups()[0]
ap_group.name.should.equal('ap_tester_group')
list(ap_group.availability_zones).should.equal(['ap-northeast-1a'])
ap_group.desired_capacity.should.equal(2)
ap_group.max_size.should.equal(2)
ap_group.min_size.should.equal(2)
ap_group.vpc_zone_identifier.should.equal('subnet-5678efgh')
ap_group.launch_config_name.should.equal('ap_tester')
ap_group.default_cooldown.should.equal(60)
ap_group.health_check_period.should.equal(100)
ap_group.health_check_type.should.equal("EC2")
list(ap_group.load_balancers).should.equal(["ap_test_lb"])
ap_group.placement_group.should.equal("ap_test_placement")
list(ap_group.termination_policies).should.equal(["OldestInstance", "NewestInstance"])

View File

@ -12,7 +12,10 @@ def test_ec2_server_get():
backend = server.create_backend_app("ec2") backend = server.create_backend_app("ec2")
test_client = backend.test_client() test_client = backend.test_client()
res = test_client.get('/?Action=RunInstances&ImageId=ami-60a54009') res = test_client.get(
'/?Action=RunInstances&ImageId=ami-60a54009',
headers={"Host": "ec2.us-east-1.amazonaws.com"}
)
groups = re.search("<instanceId>(.*)</instanceId>", res.data) groups = re.search("<instanceId>(.*)</instanceId>", res.data)
instance_id = groups.groups()[0] instance_id = groups.groups()[0]