Merge branch 'master' of https://github.com/silveregg/moto into 0.4.1-threadsafe
* 'master' of https://github.com/silveregg/moto: (22 commits) filtering the items is needed because of defaultdict is not threadsafe and returns an empty dict which results in an exception here add tests for list_endpoints_by_platform_application add mock for list_endpoints_by_platform_application method [S3]Only add multipart part_id to partlist if it is not already in there. Closes #324. Fix etag for reduced min part size. Add test_multipart_duplicate_upload Fix reduced_min_part_size so that tests run Fix authors Add @mikegrima to authors Fixed how parameters are passed in following clarification on GitHub comments. Added in test for the boto IAM method: list_instance_profiles_for_role() Change SecurityGroupBackend.{authorize,revoke}_security_group_ingress() methods to receive group name or id, never both Add support to AWS::EC2::SecurityGroupIngress creation Add @aaltepet to authors. Add publish command. Add support to tag filtering to Security Groups slight change in formatting fix test for ec2 instance type filter Update minimum support boto version. support 'instance_type' filter ...
This commit is contained in:
commit
f5c4ac0b44
19 changed files with 552 additions and 33 deletions
|
|
@ -26,6 +26,7 @@ MODEL_MAP = {
|
|||
"AWS::EC2::Route": ec2_models.Route,
|
||||
"AWS::EC2::RouteTable": ec2_models.RouteTable,
|
||||
"AWS::EC2::SecurityGroup": ec2_models.SecurityGroup,
|
||||
"AWS::EC2::SecurityGroupIngress": ec2_models.SecurityGroupIngress,
|
||||
"AWS::EC2::Subnet": ec2_models.Subnet,
|
||||
"AWS::EC2::SubnetRouteTableAssociation": ec2_models.SubnetRouteTableAssociation,
|
||||
"AWS::EC2::Volume": ec2_models.Volume,
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ class Table(object):
|
|||
results = []
|
||||
last_page = True # Once pagination is implemented, change this
|
||||
|
||||
possible_results = [item for item in list(self.all_items()) if item.hash_key == hash_key]
|
||||
possible_results = [item for item in list(self.all_items()) if isinstance(item, Item) and item.hash_key == hash_key]
|
||||
if range_comparison:
|
||||
for result in possible_results:
|
||||
if result.range_key.compare(range_comparison, range_objs):
|
||||
|
|
|
|||
|
|
@ -92,7 +92,9 @@ from .utils import (
|
|||
filter_reservations,
|
||||
random_network_acl_id,
|
||||
random_network_acl_subnet_association_id,
|
||||
random_vpn_gateway_id)
|
||||
random_vpn_gateway_id,
|
||||
is_tag_filter,
|
||||
)
|
||||
|
||||
|
||||
def validate_resource_ids(resource_ids):
|
||||
|
|
@ -119,6 +121,9 @@ class TaggedEC2Resource(object):
|
|||
tags = self.ec2_backend.describe_tags(filters={'resource-id': [self.id]})
|
||||
return tags
|
||||
|
||||
def add_tag(self, key, value):
|
||||
self.ec2_backend.create_tags([self.id], {key: value})
|
||||
|
||||
def get_filter_value(self, filter_name):
|
||||
tags = self.get_tags()
|
||||
|
||||
|
|
@ -1071,12 +1076,16 @@ class SecurityGroup(TaggedEC2Resource):
|
|||
vpc_id=vpc_id,
|
||||
)
|
||||
|
||||
for tag in properties.get("Tags", []):
|
||||
tag_key = tag["Key"]
|
||||
tag_value = tag["Value"]
|
||||
security_group.add_tag(tag_key, tag_value)
|
||||
|
||||
for ingress_rule in properties.get('SecurityGroupIngress', []):
|
||||
source_group_id = ingress_rule.get('SourceSecurityGroupId')
|
||||
|
||||
ec2_backend.authorize_security_group_ingress(
|
||||
group_name=security_group.name,
|
||||
group_id=security_group.id,
|
||||
group_name_or_id=security_group.id,
|
||||
ip_protocol=ingress_rule['IpProtocol'],
|
||||
from_port=ingress_rule['FromPort'],
|
||||
to_port=ingress_rule['ToPort'],
|
||||
|
|
@ -1113,6 +1122,9 @@ class SecurityGroup(TaggedEC2Resource):
|
|||
for ingress in self.ingress_rules:
|
||||
if getattr(ingress, ingress_attr) in filter_value:
|
||||
return True
|
||||
elif is_tag_filter(key):
|
||||
tag_value = self.get_filter_value(key)
|
||||
return tag_value in filter_value
|
||||
else:
|
||||
attr_name = to_attr(key)
|
||||
return getattr(self, attr_name) in filter_value
|
||||
|
|
@ -1205,9 +1217,15 @@ class SecurityGroupBackend(object):
|
|||
default_group = self.create_security_group("default", "The default security group", vpc_id=vpc_id, force=True)
|
||||
return default_group
|
||||
|
||||
def get_security_group_by_name_or_id(self, group_name_or_id, vpc_id):
|
||||
# try searching by id, fallbacks to name search
|
||||
group = self.get_security_group_from_id(group_name_or_id)
|
||||
if group is None:
|
||||
group = self.get_security_group_from_name(group_name_or_id, vpc_id)
|
||||
return group
|
||||
|
||||
def authorize_security_group_ingress(self,
|
||||
group_name,
|
||||
group_id,
|
||||
group_name_or_id,
|
||||
ip_protocol,
|
||||
from_port,
|
||||
to_port,
|
||||
|
|
@ -1215,12 +1233,7 @@ class SecurityGroupBackend(object):
|
|||
source_group_names=None,
|
||||
source_group_ids=None,
|
||||
vpc_id=None):
|
||||
# to auth a group in a VPC you need the group_id the name isn't enough
|
||||
|
||||
if group_name:
|
||||
group = self.get_security_group_from_name(group_name, vpc_id)
|
||||
elif group_id:
|
||||
group = self.get_security_group_from_id(group_id)
|
||||
group = self.get_security_group_by_name_or_id(group_name_or_id, vpc_id)
|
||||
|
||||
if ip_ranges and not isinstance(ip_ranges, list):
|
||||
ip_ranges = [ip_ranges]
|
||||
|
|
@ -1248,8 +1261,7 @@ class SecurityGroupBackend(object):
|
|||
group.ingress_rules.append(security_rule)
|
||||
|
||||
def revoke_security_group_ingress(self,
|
||||
group_name,
|
||||
group_id,
|
||||
group_name_or_id,
|
||||
ip_protocol,
|
||||
from_port,
|
||||
to_port,
|
||||
|
|
@ -1258,10 +1270,7 @@ class SecurityGroupBackend(object):
|
|||
source_group_ids=None,
|
||||
vpc_id=None):
|
||||
|
||||
if group_name:
|
||||
group = self.get_security_group_from_name(group_name, vpc_id)
|
||||
elif group_id:
|
||||
group = self.get_security_group_from_id(group_id)
|
||||
group = self.get_security_group_by_name_or_id(group_name_or_id, vpc_id)
|
||||
|
||||
source_groups = []
|
||||
for source_group_name in source_group_names:
|
||||
|
|
@ -1282,6 +1291,63 @@ class SecurityGroupBackend(object):
|
|||
raise InvalidPermissionNotFoundError()
|
||||
|
||||
|
||||
class SecurityGroupIngress(object):
|
||||
|
||||
def __init__(self, security_group, properties):
|
||||
self.security_group = security_group
|
||||
self.properties = properties
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
ec2_backend = ec2_backends[region_name]
|
||||
group_name = properties.get('GroupName')
|
||||
group_id = properties.get('GroupId')
|
||||
ip_protocol = properties.get("IpProtocol")
|
||||
cidr_ip = properties.get("CidrIp")
|
||||
from_port = properties.get("FromPort")
|
||||
source_security_group_id = properties.get("SourceSecurityGroupId")
|
||||
source_security_group_name = properties.get("SourceSecurityGroupName")
|
||||
source_security_owner_id = properties.get("SourceSecurityGroupOwnerId") # IGNORED AT THE MOMENT
|
||||
to_port = properties.get("ToPort")
|
||||
|
||||
assert group_id or group_name
|
||||
assert source_security_group_name or cidr_ip or source_security_group_id
|
||||
assert ip_protocol
|
||||
|
||||
if source_security_group_id:
|
||||
source_security_group_ids = [source_security_group_id]
|
||||
else:
|
||||
source_security_group_ids = None
|
||||
if source_security_group_name:
|
||||
source_security_group_names = [source_security_group_name]
|
||||
else:
|
||||
source_security_group_names = None
|
||||
if cidr_ip:
|
||||
ip_ranges = [cidr_ip]
|
||||
else:
|
||||
ip_ranges = []
|
||||
|
||||
|
||||
if group_id:
|
||||
security_group = ec2_backend.describe_security_groups(group_ids=[group_id])[0]
|
||||
else:
|
||||
security_group = ec2_backend.describe_security_groups(groupnames=[group_name])[0]
|
||||
|
||||
ec2_backend.authorize_security_group_ingress(
|
||||
group_name_or_id=security_group.id,
|
||||
ip_protocol=ip_protocol,
|
||||
from_port=from_port,
|
||||
to_port=to_port,
|
||||
ip_ranges=ip_ranges,
|
||||
source_group_ids=source_security_group_ids,
|
||||
source_group_names=source_security_group_names,
|
||||
)
|
||||
|
||||
return cls(security_group, properties)
|
||||
|
||||
|
||||
class VolumeAttachment(object):
|
||||
def __init__(self, volume, instance, device):
|
||||
self.volume = volume
|
||||
|
|
|
|||
|
|
@ -4,14 +4,10 @@ from moto.ec2.utils import filters_from_querystring
|
|||
|
||||
|
||||
def process_rules_from_querystring(querystring):
|
||||
|
||||
name = None
|
||||
group_id = None
|
||||
|
||||
try:
|
||||
name = querystring.get('GroupName')[0]
|
||||
group_name_or_id = querystring.get('GroupName')[0]
|
||||
except:
|
||||
group_id = querystring.get('GroupId')[0]
|
||||
group_name_or_id = querystring.get('GroupId')[0]
|
||||
|
||||
ip_protocol = querystring.get('IpPermissions.1.IpProtocol')[0]
|
||||
from_port = querystring.get('IpPermissions.1.FromPort')[0]
|
||||
|
|
@ -30,7 +26,7 @@ def process_rules_from_querystring(querystring):
|
|||
elif 'IpPermissions.1.Groups' in key:
|
||||
source_groups.append(value[0])
|
||||
|
||||
return (name, group_id, ip_protocol, from_port, to_port, ip_ranges, source_groups, source_group_ids)
|
||||
return (group_name_or_id, ip_protocol, from_port, to_port, ip_ranges, source_groups, source_group_ids)
|
||||
|
||||
|
||||
class SecurityGroups(BaseResponse):
|
||||
|
|
|
|||
|
|
@ -310,7 +310,9 @@ def get_object_value(obj, attr):
|
|||
|
||||
|
||||
def is_tag_filter(filter_name):
|
||||
return filter_name.startswith('tag:')
|
||||
return (filter_name.startswith('tag:') or
|
||||
filter_name.startswith('tag-value') or
|
||||
filter_name.startswith('tag-key'))
|
||||
|
||||
|
||||
def get_obj_tag(obj, filter_name):
|
||||
|
|
@ -318,10 +320,24 @@ def get_obj_tag(obj, filter_name):
|
|||
tags = dict((tag['key'], tag['value']) for tag in obj.get_tags())
|
||||
return tags.get(tag_name)
|
||||
|
||||
def get_obj_tag_names(obj):
|
||||
tags = set((tag['key'] for tag in obj.get_tags()))
|
||||
return tags
|
||||
|
||||
def get_obj_tag_values(obj):
|
||||
tags = set((tag['value'] for tag in obj.get_tags()))
|
||||
return tags
|
||||
|
||||
def tag_filter_matches(obj, filter_name, filter_values):
|
||||
tag_value = get_obj_tag(obj, filter_name)
|
||||
return tag_value in filter_values
|
||||
if filter_name == 'tag-key':
|
||||
tag_names = get_obj_tag_names(obj)
|
||||
return len(set(filter_values).intersection(tag_names)) > 0
|
||||
elif filter_name == 'tag-value':
|
||||
tag_values = get_obj_tag_values(obj)
|
||||
return len(set(filter_values).intersection(tag_values)) > 0
|
||||
else:
|
||||
tag_value = get_obj_tag(obj, filter_name)
|
||||
return tag_value in filter_values
|
||||
|
||||
|
||||
filter_dict_attribute_mapping = {
|
||||
|
|
@ -331,7 +347,8 @@ filter_dict_attribute_mapping = {
|
|||
'source-dest-check': 'source_dest_check',
|
||||
'vpc-id': 'vpc_id',
|
||||
'group-id': 'security_groups',
|
||||
'instance.group-id': 'security_groups'
|
||||
'instance.group-id': 'security_groups',
|
||||
'instance-type': 'instance_type'
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -291,6 +291,16 @@ class IAMBackend(BaseBackend):
|
|||
def get_instance_profiles(self):
|
||||
return self.instance_profiles.values()
|
||||
|
||||
def get_instance_profiles_for_role(self, role_name):
|
||||
found_profiles = []
|
||||
|
||||
for profile in self.get_instance_profiles():
|
||||
if len(profile.roles) > 0:
|
||||
if profile.roles[0].name == role_name:
|
||||
found_profiles.append(profile)
|
||||
|
||||
return found_profiles
|
||||
|
||||
def add_role_to_instance_profile(self, profile_name, role_name):
|
||||
profile = self.get_instance_profile(profile_name)
|
||||
role = self.get_role(role_name)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,13 @@ class IamResponse(BaseResponse):
|
|||
template = self.response_template(LIST_INSTANCE_PROFILES_TEMPLATE)
|
||||
return template.render(instance_profiles=profiles)
|
||||
|
||||
def list_instance_profiles_for_role(self):
|
||||
role_name = self._get_param('RoleName')
|
||||
profiles = iam_backend.get_instance_profiles_for_role(role_name=role_name)
|
||||
|
||||
template = self.response_template(LIST_INSTANCE_PROFILES_FOR_ROLE_TEMPLATE)
|
||||
return template.render(instance_profiles=profiles)
|
||||
|
||||
def upload_server_certificate(self):
|
||||
cert_name = self._get_param('ServerCertificateName')
|
||||
cert_body = self._get_param('CertificateBody')
|
||||
|
|
@ -601,4 +608,36 @@ CREDENTIAL_REPORT = """<GetCredentialReportResponse>
|
|||
<ResponseMetadata>
|
||||
<RequestId>fa788a82-aa8a-11e4-a278-1786c418872b"</RequestId>
|
||||
</ResponseMetadata>
|
||||
</GetCredentialReportResponse>"""
|
||||
</GetCredentialReportResponse>"""
|
||||
|
||||
LIST_INSTANCE_PROFILES_FOR_ROLE_TEMPLATE = """<ListInstanceProfilesForRoleResponse>
|
||||
<ListInstanceProfilesForRoleResult>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<InstanceProfiles>
|
||||
{% for profile in instance_profiles %}
|
||||
<member>
|
||||
<Id>{{ profile.id }}</Id>
|
||||
<Roles>
|
||||
{% for role in profile.roles %}
|
||||
<member>
|
||||
<Path>{{ role.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:role{{ role.path }}S3Access</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_policy_document }}</AssumeRolePolicyDocument>
|
||||
<CreateDate>2012-05-09T15:45:35Z</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Roles>
|
||||
<InstanceProfileName>{{ profile.name }}</InstanceProfileName>
|
||||
<Path>{{ profile.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:instance-profile{{ profile.path }}Webserver</Arn>
|
||||
<CreateDate>2012-05-09T16:27:11Z</CreateDate>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</InstanceProfiles>
|
||||
</ListInstanceProfilesForRoleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>6a8c3992-99f4-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListInstanceProfilesForRoleResponse>"""
|
||||
|
|
@ -152,7 +152,8 @@ class FakeMultipart(object):
|
|||
|
||||
key = FakeKey(part_id, value)
|
||||
self.parts[part_id] = key
|
||||
insort(self.partlist, part_id)
|
||||
if part_id not in self.partlist:
|
||||
insort(self.partlist, part_id)
|
||||
return key
|
||||
|
||||
def list_parts(self):
|
||||
|
|
|
|||
|
|
@ -184,3 +184,25 @@ class SNSResponse(BaseResponse):
|
|||
}
|
||||
}
|
||||
})
|
||||
|
||||
def list_endpoints_by_platform_application(self):
|
||||
return json.dumps({
|
||||
"ListEndpointsByPlatformApplicationResponse": {
|
||||
"ListEndpointsByPlatformApplicationResult": {
|
||||
"Endpoints": [
|
||||
{
|
||||
"Attributes": {
|
||||
"Token": "TOKEN",
|
||||
"Enabled": "true",
|
||||
"CustomUserData": ""
|
||||
},
|
||||
"EndpointArn": "FAKE_ARN_ENDPOINT"
|
||||
}
|
||||
],
|
||||
"NextToken": None
|
||||
},
|
||||
"ResponseMetadata": {
|
||||
"RequestId": "384ac68d-3775-11df-8963-01868b7c937a",
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue