Skip to content

Commit

Permalink
Merge #913.
Browse files Browse the repository at this point in the history
  • Loading branch information
spulec committed May 11, 2017
1 parent 408a709 commit 0adebee
Show file tree
Hide file tree
Showing 36 changed files with 669 additions and 58 deletions.
5 changes: 3 additions & 2 deletions moto/autoscaling/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import unicode_literals
from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
from moto.ec2 import ec2_backends
from moto.elb import elb_backends
Expand Down Expand Up @@ -284,8 +285,8 @@ def set_desired_capacity(self, new_capacity):
class AutoScalingBackend(BaseBackend):

def __init__(self, ec2_backend, elb_backend):
self.autoscaling_groups = {}
self.launch_configurations = {}
self.autoscaling_groups = OrderedDict()
self.launch_configurations = OrderedDict()
self.policies = {}
self.ec2_backend = ec2_backend
self.elb_backend = elb_backend
Expand Down
40 changes: 35 additions & 5 deletions moto/autoscaling/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,22 @@ def create_launch_configuration(self):

def describe_launch_configurations(self):
names = self._get_multi_param('LaunchConfigurationNames.member')
launch_configurations = self.autoscaling_backend.describe_launch_configurations(
names)
all_launch_configurations = self.autoscaling_backend.describe_launch_configurations(names)
marker = self._get_param('NextToken')
all_names = [lc.name for lc in all_launch_configurations]
if marker:
start = all_names.index(marker) + 1
else:
start = 0
max_records = self._get_param('MaxRecords', 50) # the default is 100, but using 50 to make testing easier
launch_configurations_resp = all_launch_configurations[start:start + max_records]
next_token = None
if len(all_launch_configurations) > start + max_records:
next_token = launch_configurations_resp[-1].name

template = self.response_template(
DESCRIBE_LAUNCH_CONFIGURATIONS_TEMPLATE)
return template.render(launch_configurations=launch_configurations)
return template.render(launch_configurations=launch_configurations_resp, next_token=next_token)

def delete_launch_configuration(self):
launch_configurations_name = self.querystring.get(
Expand Down Expand Up @@ -78,9 +89,22 @@ def create_auto_scaling_group(self):

def describe_auto_scaling_groups(self):
names = self._get_multi_param("AutoScalingGroupNames.member")
groups = self.autoscaling_backend.describe_autoscaling_groups(names)
token = self._get_param("NextToken")
all_groups = self.autoscaling_backend.describe_autoscaling_groups(names)
all_names = [group.name for group in all_groups]
if token:
start = all_names.index(token) + 1
else:
start = 0
max_records = self._get_param("MaxRecords", 50)
if max_records > 100:
raise ValueError
groups = all_groups[start:start + max_records]
next_token = None
if max_records and len(all_groups) > start + max_records:
next_token = groups[-1].name
template = self.response_template(DESCRIBE_AUTOSCALING_GROUPS_TEMPLATE)
return template.render(groups=groups)
return template.render(groups=groups, next_token=next_token)

def update_auto_scaling_group(self):
self.autoscaling_backend.update_autoscaling_group(
Expand Down Expand Up @@ -239,6 +263,9 @@ def execute_policy(self):
</member>
{% endfor %}
</LaunchConfigurations>
{% if next_token %}
<NextToken>{{ next_token }}</NextToken>
{% endif %}
</DescribeLaunchConfigurationsResult>
<ResponseMetadata>
<RequestId>d05a22f8-b690-11e2-bf8e-2113fEXAMPLE</RequestId>
Expand Down Expand Up @@ -331,6 +358,9 @@ def execute_policy(self):
</member>
{% endfor %}
</AutoScalingGroups>
{% if next_token %}
<NextToken>{{ next_token }}</NextToken>
{% endif %}
</DescribeAutoScalingGroupsResult>
<ResponseMetadata>
<RequestId>0f02a07d-b677-11e2-9eb0-dd50EXAMPLE</RequestId>
Expand Down
5 changes: 3 additions & 2 deletions moto/cloudformation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import uuid

import boto.cloudformation
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel

from .parsing import ResourceMap, OutputMap
Expand Down Expand Up @@ -121,7 +122,7 @@ def __init__(self, stack_id, stack_name, logical_resource_id, physical_resource_
class CloudFormationBackend(BaseBackend):

def __init__(self):
self.stacks = {}
self.stacks = OrderedDict()
self.deleted_stacks = {}

def create_stack(self, name, template, parameters, region_name, notification_arns=None, tags=None, role_arn=None):
Expand Down Expand Up @@ -152,7 +153,7 @@ def describe_stacks(self, name_or_stack_id):
return [stack]
raise ValidationError(name_or_stack_id)
else:
return stacks
return list(stacks)

def list_stacks(self):
return self.stacks.values()
Expand Down
17 changes: 15 additions & 2 deletions moto/cloudformation/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,20 @@ def describe_stacks(self):
stack_name_or_id = None
if self._get_param('StackName'):
stack_name_or_id = self.querystring.get('StackName')[0]
token = self._get_param('NextToken')
stacks = self.cloudformation_backend.describe_stacks(stack_name_or_id)

stack_ids = [stack.stack_id for stack in stacks]
if token:
start = stack_ids.index(token) + 1
else:
start = 0
max_results = 50 # using this to mske testing of paginated stacks more convenient than default 1 MB
stacks_resp = stacks[start:start + max_results]
next_token = None
if len(stacks) > (start + max_results):
next_token = stacks_resp[-1].stack_id
template = self.response_template(DESCRIBE_STACKS_TEMPLATE)
return template.render(stacks=stacks)
return template.render(stacks=stacks_resp, next_token=next_token)

def describe_stack_resource(self):
stack_name = self._get_param('StackName')
Expand Down Expand Up @@ -270,6 +280,9 @@ def delete_stack(self):
</member>
{% endfor %}
</Stacks>
{% if next_token %}
<NextToken>{{ next_token }}</NextToken>
{% endif %}
</DescribeStacksResult>
</DescribeStacksResponse>"""

Expand Down
3 changes: 2 additions & 1 deletion moto/datapipeline/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import datetime
import boto.datapipeline
from moto.compat import OrderedDict
from moto.core import BaseBackend, BaseModel
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys

Expand Down Expand Up @@ -111,7 +112,7 @@ def create_from_cloudformation_json(cls, resource_name, cloudformation_json, reg
class DataPipelineBackend(BaseBackend):

def __init__(self):
self.pipelines = {}
self.pipelines = OrderedDict()

def create_pipeline(self, name, unique_id, **kwargs):
pipeline = Pipeline(name, unique_id, **kwargs)
Expand Down
21 changes: 17 additions & 4 deletions moto/datapipeline/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,25 @@ def create_pipeline(self):
})

def list_pipelines(self):
pipelines = self.datapipeline_backend.list_pipelines()
pipelines = list(self.datapipeline_backend.list_pipelines())
pipeline_ids = [pipeline.pipeline_id for pipeline in pipelines]
max_pipelines = 50
marker = self.parameters.get('marker')
if marker:
start = pipeline_ids.index(marker) + 1
else:
start = 0
pipelines_resp = pipelines[start:start + max_pipelines]
has_more_results = False
marker = None
if start + max_pipelines < len(pipeline_ids) - 1:
has_more_results = True
marker = pipelines_resp[-1].pipeline_id
return json.dumps({
"hasMoreResults": False,
"marker": None,
"hasMoreResults": has_more_results,
"marker": marker,
"pipelineIdList": [
pipeline.to_meta_json() for pipeline in pipelines
pipeline.to_meta_json() for pipeline in pipelines_resp
]
})

Expand Down
20 changes: 19 additions & 1 deletion moto/dynamodb2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ def __init__(self, table_name, schema=None, attr=None, throughput=None, indexes=
self.global_indexes = global_indexes if global_indexes else []
self.created_at = datetime.datetime.utcnow()
self.items = defaultdict(dict)
self.table_arn = self._generate_arn(table_name)
self.tags = []

def _generate_arn(self, name):
return 'arn:aws:dynamodb:us-east-1:123456789011:table/' + name

def describe(self, base_key='TableDescription'):
results = {
Expand All @@ -209,11 +214,12 @@ def describe(self, base_key='TableDescription'):
'TableSizeBytes': 0,
'TableName': self.name,
'TableStatus': 'ACTIVE',
'TableArn': self.table_arn,
'KeySchema': self.schema,
'ItemCount': len(self),
'CreationDateTime': unix_time(self.created_at),
'GlobalSecondaryIndexes': [index for index in self.global_indexes],
'LocalSecondaryIndexes': [index for index in self.indexes]
'LocalSecondaryIndexes': [index for index in self.indexes],
}
}
return results
Expand Down Expand Up @@ -505,6 +511,18 @@ def create_table(self, name, **params):
def delete_table(self, name):
return self.tables.pop(name, None)

def tag_resource(self, table_arn, tags):
for table in self.tables:
if self.tables[table].table_arn == table_arn:
self.tables[table].tags.extend(tags)

def list_tags_of_resource(self, table_arn):
required_table = None
for table in self.tables:
if self.tables[table].table_arn == table_arn:
required_table = self.tables[table]
return required_table.tags

def update_table_throughput(self, name, throughput):
table = self.tables[name]
table.throughput = throughput
Expand Down
31 changes: 30 additions & 1 deletion moto/dynamodb2/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def call_action(self):

def list_tables(self):
body = self.body
limit = body.get('Limit')
limit = body.get('Limit', 100)
if body.get("ExclusiveStartTableName"):
last = body.get("ExclusiveStartTableName")
start = list(dynamodb_backend2.tables.keys()).index(last) + 1
Expand Down Expand Up @@ -124,6 +124,35 @@ def delete_table(self):
er = 'com.amazonaws.dynamodb.v20111205#ResourceNotFoundException'
return self.error(er)

def tag_resource(self):
tags = self.body['Tags']
table_arn = self.body['ResourceArn']
dynamodb_backend2.tag_resource(table_arn, tags)
return json.dumps({})

def list_tags_of_resource(self):
try:
table_arn = self.body['ResourceArn']
all_tags = dynamodb_backend2.list_tags_of_resource(table_arn)
all_tag_keys = [tag['Key'] for tag in all_tags]
marker = self.body.get('NextToken')
if marker:
start = all_tag_keys.index(marker) + 1
else:
start = 0
max_items = 10 # there is no default, but using 10 to make testing easier
tags_resp = all_tags[start:start + max_items]
next_marker = None
if len(all_tags) > start + max_items:
next_marker = tags_resp[-1]['Key']
if next_marker:
return json.dumps({'Tags': tags_resp,
'NextToken': next_marker})
return json.dumps({'Tags': tags_resp})
except AttributeError:
er = 'com.amazonaws.dynamodb.v20111205#ResourceNotFoundException'
return self.error(er)

def update_table(self):
name = self.body['TableName']
if 'GlobalSecondaryIndexUpdates' in self.body:
Expand Down
22 changes: 18 additions & 4 deletions moto/ec2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
from boto.ec2.launchspecification import LaunchSpecification

from moto.compat import OrderedDict
from moto.core import BaseBackend
from moto.core.models import Model, BaseModel
from moto.core.utils import iso_8601_datetime_with_milliseconds, camelcase_to_underscores
Expand Down Expand Up @@ -618,7 +619,7 @@ def get_cfn_attribute(self, attribute_name):
class InstanceBackend(object):

def __init__(self):
self.reservations = {}
self.reservations = OrderedDict()
super(InstanceBackend, self).__init__()

def get_instance(self, instance_id):
Expand Down Expand Up @@ -1049,12 +1050,22 @@ def copy_image(self, source_image_id, source_region, name=None, description=None
self.amis[ami_id] = ami
return ami

def describe_images(self, ami_ids=(), filters=None):
def describe_images(self, ami_ids=(), filters=None, exec_users=None):
images = []
if exec_users:
for ami_id in self.amis:
found = False
for user_id in exec_users:
if user_id in self.amis[ami_id].launch_permission_users:
found = True
if found:
images.append(self.amis[ami_id])
if images == []:
return images
if filters:
images = self.amis.values()
images = images or self.amis.values()
return generic_filter(filters, images)
else:
images = []
for ami_id in ami_ids:
if ami_id in self.amis:
images.append(self.amis[ami_id])
Expand Down Expand Up @@ -1766,6 +1777,9 @@ def get_filter_value(self, filter_name):
if filter_name == 'encrypted':
return str(self.encrypted).lower()

if filter_name == 'status':
return self.status

filter_value = super(Snapshot, self).get_filter_value(filter_name)

if filter_value is None:
Expand Down
5 changes: 3 additions & 2 deletions moto/ec2/responses/amis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
from moto.core.responses import BaseResponse
from moto.ec2.utils import instance_ids_from_querystring, image_ids_from_querystring, \
filters_from_querystring, sequence_from_querystring
filters_from_querystring, sequence_from_querystring, executable_users_from_querystring


class AmisResponse(BaseResponse):
Expand Down Expand Up @@ -43,8 +43,9 @@ def deregister_image(self):
def describe_images(self):
ami_ids = image_ids_from_querystring(self.querystring)
filters = filters_from_querystring(self.querystring)
exec_users = executable_users_from_querystring(self.querystring)
images = self.ec2_backend.describe_images(
ami_ids=ami_ids, filters=filters)
ami_ids=ami_ids, filters=filters, exec_users=exec_users)
template = self.response_template(DESCRIBE_IMAGES_RESPONSE)
return template.render(images=images)

Expand Down
16 changes: 15 additions & 1 deletion moto/ec2/responses/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,26 @@ class InstanceResponse(BaseResponse):
def describe_instances(self):
filter_dict = filters_from_querystring(self.querystring)
instance_ids = instance_ids_from_querystring(self.querystring)
token = self._get_param("NextToken")
if instance_ids:
reservations = self.ec2_backend.get_reservations_by_instance_ids(
instance_ids, filters=filter_dict)
else:
reservations = self.ec2_backend.all_reservations(
make_copy=True, filters=filter_dict)

reservation_ids = [reservation.id for reservation in reservations]
if token:
start = reservation_ids.index(token) + 1
else:
start = 0
max_results = int(self._get_param('MaxResults', 100))
reservations_resp = reservations[start:start + max_results]
next_token = None
if max_results and len(reservations) > (start + max_results):
next_token = reservations_resp[-1].id
template = self.response_template(EC2_DESCRIBE_INSTANCES)
return template.render(reservations=reservations)
return template.render(reservations=reservations_resp, next_token=next_token)

def run_instances(self):
min_count = int(self.querystring.get('MinCount', ['1'])[0])
Expand Down Expand Up @@ -492,6 +503,9 @@ def _security_grp_instance_attribute_handler(self):
</item>
{% endfor %}
</reservationSet>
{% if next_token %}
<nextToken>{{ next_token }}</nextToken>
{% endif %}
</DescribeInstancesResponse>"""

EC2_TERMINATE_INSTANCES = """
Expand Down

0 comments on commit 0adebee

Please sign in to comment.