Fix merge conflicts. Add basic cloudformation support. Closes #111.
This commit is contained in:
parent
069c48b43a
commit
ef876dd27e
28 changed files with 2473 additions and 11 deletions
|
|
@ -2,11 +2,13 @@ import logging
|
|||
logging.getLogger('boto').setLevel(logging.CRITICAL)
|
||||
|
||||
from .autoscaling import mock_autoscaling
|
||||
from .cloudformation import mock_cloudformation
|
||||
from .dynamodb import mock_dynamodb
|
||||
from .dynamodb2 import mock_dynamodb2
|
||||
from .ec2 import mock_ec2
|
||||
from .elb import mock_elb
|
||||
from .emr import mock_emr
|
||||
from .iam import mock_iam
|
||||
from .s3 import mock_s3
|
||||
from .s3bucket_path import mock_s3bucket_path
|
||||
from .ses import mock_ses
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class FakeLaunchConfiguration(object):
|
|||
self.name = name
|
||||
self.image_id = image_id
|
||||
self.key_name = key_name
|
||||
self.security_groups = security_groups
|
||||
self.security_groups = security_groups if security_groups else []
|
||||
self.user_data = user_data
|
||||
self.instance_type = instance_type
|
||||
self.instance_monitoring = instance_monitoring
|
||||
|
|
@ -42,6 +42,31 @@ class FakeLaunchConfiguration(object):
|
|||
self.ebs_optimized = ebs_optimized
|
||||
self.associate_public_ip_address = associate_public_ip_address
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
instance_profile_name = properties.get("IamInstanceProfile")
|
||||
|
||||
config = autoscaling_backend.create_launch_configuration(
|
||||
name=resource_name,
|
||||
image_id=properties.get("ImageId"),
|
||||
key_name=properties.get("KeyName"),
|
||||
security_groups=properties.get("SecurityGroups"),
|
||||
user_data=properties.get("UserData"),
|
||||
instance_type=properties.get("InstanceType"),
|
||||
instance_monitoring=properties.get("InstanceMonitoring"),
|
||||
instance_profile_name=instance_profile_name,
|
||||
spot_price=properties.get("SpotPrice"),
|
||||
ebs_optimized=properties.get("EbsOptimized"),
|
||||
associate_public_ip_address=properties.get("AssociatePublicIpAddress"),
|
||||
)
|
||||
return config
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def instance_monitoring_enabled(self):
|
||||
if self.instance_monitoring:
|
||||
|
|
@ -73,6 +98,34 @@ class FakeAutoScalingGroup(object):
|
|||
self.instances = []
|
||||
self.set_desired_capacity(desired_capacity)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
launch_config_name = properties.get("LaunchConfigurationName")
|
||||
load_balancer_names = properties.get("LoadBalancerNames", [])
|
||||
|
||||
group = autoscaling_backend.create_autoscaling_group(
|
||||
name=resource_name,
|
||||
availability_zones=properties.get("AvailabilityZones", []),
|
||||
desired_capacity=properties.get("DesiredCapacity"),
|
||||
max_size=properties.get("MaxSize"),
|
||||
min_size=properties.get("MinSize"),
|
||||
launch_config_name=launch_config_name,
|
||||
vpc_zone_identifier=properties.get("VPCZoneIdentifier"),
|
||||
default_cooldown=properties.get("Cooldown"),
|
||||
health_check_period=properties.get("HealthCheckGracePeriod"),
|
||||
health_check_type=properties.get("HealthCheckType"),
|
||||
load_balancers=load_balancer_names,
|
||||
placement_group=None,
|
||||
termination_policies=properties.get("TerminationPolicies", []),
|
||||
)
|
||||
return group
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
def update(self, availability_zones, desired_capacity, max_size, min_size,
|
||||
launch_config_name, vpc_zone_identifier, default_cooldown,
|
||||
health_check_period, health_check_type, load_balancers,
|
||||
|
|
@ -164,6 +217,15 @@ class AutoScalingBackend(BaseBackend):
|
|||
default_cooldown, health_check_period,
|
||||
health_check_type, load_balancers,
|
||||
placement_group, termination_policies):
|
||||
|
||||
def make_int(value):
|
||||
return int(value) if value is not None else value
|
||||
|
||||
max_size = make_int(max_size)
|
||||
min_size = make_int(min_size)
|
||||
default_cooldown = make_int(default_cooldown)
|
||||
health_check_period = make_int(health_check_period)
|
||||
|
||||
group = FakeAutoScalingGroup(
|
||||
name=name,
|
||||
availability_zones=availability_zones,
|
||||
|
|
|
|||
2
moto/cloudformation/__init__.py
Normal file
2
moto/cloudformation/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from .models import cloudformation_backend
|
||||
mock_cloudformation = cloudformation_backend.decorator
|
||||
70
moto/cloudformation/models.py
Normal file
70
moto/cloudformation/models.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import json
|
||||
|
||||
from moto.core import BaseBackend
|
||||
|
||||
from .parsing import ResourceMap
|
||||
from .utils import generate_stack_id
|
||||
|
||||
|
||||
class FakeStack(object):
|
||||
def __init__(self, stack_id, name, template):
|
||||
self.stack_id = stack_id
|
||||
self.name = name
|
||||
self.template = template
|
||||
|
||||
template_dict = json.loads(self.template)
|
||||
self.description = template_dict.get('Description')
|
||||
|
||||
self.resource_map = ResourceMap(stack_id, name, template_dict)
|
||||
self.resource_map.create()
|
||||
|
||||
@property
|
||||
def stack_resources(self):
|
||||
return self.resource_map.values()
|
||||
|
||||
|
||||
class CloudFormationBackend(BaseBackend):
|
||||
|
||||
def __init__(self):
|
||||
self.stacks = {}
|
||||
|
||||
def create_stack(self, name, template):
|
||||
stack_id = generate_stack_id(name)
|
||||
new_stack = FakeStack(stack_id=stack_id, name=name, template=template)
|
||||
self.stacks[stack_id] = new_stack
|
||||
return new_stack
|
||||
|
||||
def describe_stacks(self, names):
|
||||
stacks = self.stacks.values()
|
||||
if names:
|
||||
return [stack for stack in stacks if stack.name in names]
|
||||
else:
|
||||
return stacks
|
||||
|
||||
def list_stacks(self):
|
||||
return self.stacks.values()
|
||||
|
||||
def get_stack(self, name_or_stack_id):
|
||||
if name_or_stack_id in self.stacks:
|
||||
# Lookup by stack id
|
||||
return self.stacks.get(name_or_stack_id)
|
||||
else:
|
||||
# Lookup by stack name
|
||||
return [stack for stack in self.stacks.values() if stack.name == name_or_stack_id][0]
|
||||
|
||||
# def update_stack(self, name, template):
|
||||
# stack = self.get_stack(name)
|
||||
# stack.template = template
|
||||
# return stack
|
||||
|
||||
def delete_stack(self, name_or_stack_id):
|
||||
if name_or_stack_id in self.stacks:
|
||||
# Delete by stack id
|
||||
return self.stacks.pop(name_or_stack_id, None)
|
||||
else:
|
||||
# Delete by stack name
|
||||
stack_to_delete = [stack for stack in self.stacks.values() if stack.name == name_or_stack_id][0]
|
||||
self.delete_stack(stack_to_delete.stack_id)
|
||||
|
||||
|
||||
cloudformation_backend = CloudFormationBackend()
|
||||
140
moto/cloudformation/parsing.py
Normal file
140
moto/cloudformation/parsing.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import collections
|
||||
import logging
|
||||
|
||||
from moto.autoscaling import models as autoscaling_models
|
||||
from moto.ec2 import models as ec2_models
|
||||
from moto.elb import models as elb_models
|
||||
from moto.iam import models as iam_models
|
||||
from moto.sqs import models as sqs_models
|
||||
|
||||
MODEL_MAP = {
|
||||
"AWS::AutoScaling::AutoScalingGroup": autoscaling_models.FakeAutoScalingGroup,
|
||||
"AWS::AutoScaling::LaunchConfiguration": autoscaling_models.FakeLaunchConfiguration,
|
||||
"AWS::EC2::EIP": ec2_models.ElasticAddress,
|
||||
"AWS::EC2::Instance": ec2_models.Instance,
|
||||
"AWS::EC2::InternetGateway": ec2_models.InternetGateway,
|
||||
"AWS::EC2::Route": ec2_models.Route,
|
||||
"AWS::EC2::RouteTable": ec2_models.RouteTable,
|
||||
"AWS::EC2::SecurityGroup": ec2_models.SecurityGroup,
|
||||
"AWS::EC2::Subnet": ec2_models.Subnet,
|
||||
"AWS::EC2::SubnetRouteTableAssociation": ec2_models.SubnetRouteTableAssociation,
|
||||
"AWS::EC2::Volume": ec2_models.Volume,
|
||||
"AWS::EC2::VolumeAttachment": ec2_models.VolumeAttachment,
|
||||
"AWS::EC2::VPC": ec2_models.VPC,
|
||||
"AWS::EC2::VPCGatewayAttachment": ec2_models.VPCGatewayAttachment,
|
||||
"AWS::ElasticLoadBalancing::LoadBalancer": elb_models.FakeLoadBalancer,
|
||||
"AWS::IAM::InstanceProfile": iam_models.InstanceProfile,
|
||||
"AWS::IAM::Role": iam_models.Role,
|
||||
"AWS::SQS::Queue": sqs_models.Queue,
|
||||
}
|
||||
|
||||
# Just ignore these models types for now
|
||||
NULL_MODELS = [
|
||||
"AWS::CloudFormation::WaitCondition",
|
||||
"AWS::CloudFormation::WaitConditionHandle",
|
||||
]
|
||||
|
||||
logger = logging.getLogger("moto")
|
||||
|
||||
|
||||
def clean_json(resource_json, resources_map):
|
||||
"""
|
||||
Cleanup the a resource dict. For now, this just means replacing any Ref node
|
||||
with the corresponding physical_resource_id.
|
||||
|
||||
Eventually, this is where we would add things like function parsing (fn::)
|
||||
"""
|
||||
if isinstance(resource_json, dict):
|
||||
if 'Ref' in resource_json:
|
||||
# Parse resource reference
|
||||
resource = resources_map[resource_json['Ref']]
|
||||
if hasattr(resource, 'physical_resource_id'):
|
||||
return resource.physical_resource_id
|
||||
else:
|
||||
return resource
|
||||
|
||||
cleaned_json = {}
|
||||
for key, value in resource_json.iteritems():
|
||||
cleaned_json[key] = clean_json(value, resources_map)
|
||||
return cleaned_json
|
||||
elif isinstance(resource_json, list):
|
||||
return [clean_json(val, resources_map) for val in resource_json]
|
||||
else:
|
||||
return resource_json
|
||||
|
||||
|
||||
def resource_class_from_type(resource_type):
|
||||
if resource_type in NULL_MODELS:
|
||||
return None
|
||||
if resource_type not in MODEL_MAP:
|
||||
logger.warning("No Moto CloudFormation support for %s", resource_type)
|
||||
return None
|
||||
return MODEL_MAP.get(resource_type)
|
||||
|
||||
|
||||
def parse_resource(resource_name, resource_json, resources_map):
|
||||
resource_type = resource_json['Type']
|
||||
resource_class = resource_class_from_type(resource_type)
|
||||
if not resource_class:
|
||||
return None
|
||||
|
||||
resource_json = clean_json(resource_json, resources_map)
|
||||
resource = resource_class.create_from_cloudformation_json(resource_name, resource_json)
|
||||
resource.type = resource_type
|
||||
resource.logical_resource_id = resource_name
|
||||
return resource
|
||||
|
||||
|
||||
class ResourceMap(collections.Mapping):
|
||||
"""
|
||||
This is a lazy loading map for resources. This allows us to create resources
|
||||
without needing to create a full dependency tree. Upon creation, each
|
||||
each resources is passed this lazy map that it can grab dependencies from.
|
||||
"""
|
||||
|
||||
def __init__(self, stack_id, stack_name, template):
|
||||
self._template = template
|
||||
self._resource_json_map = template['Resources']
|
||||
|
||||
# Create the default resources
|
||||
self._parsed_resources = {
|
||||
"AWS::AccountId": "123456789012",
|
||||
"AWS::Region": "us-east-1",
|
||||
"AWS::StackId": stack_id,
|
||||
"AWS::StackName": stack_name,
|
||||
}
|
||||
|
||||
def __getitem__(self, key):
|
||||
resource_name = key
|
||||
|
||||
if resource_name in self._parsed_resources:
|
||||
return self._parsed_resources[resource_name]
|
||||
else:
|
||||
resource_json = self._resource_json_map.get(resource_name)
|
||||
new_resource = parse_resource(resource_name, resource_json, self)
|
||||
self._parsed_resources[resource_name] = new_resource
|
||||
return new_resource
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.resource_names)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._resource_json_map)
|
||||
|
||||
@property
|
||||
def resource_names(self):
|
||||
return self._resource_json_map.keys()
|
||||
|
||||
def load_parameters(self):
|
||||
parameters = self._template.get('Parameters', {})
|
||||
for parameter_name, parameter in parameters.items():
|
||||
# Just initialize parameters to empty string for now.
|
||||
self._parsed_resources[parameter_name] = ""
|
||||
|
||||
def create(self):
|
||||
self.load_parameters()
|
||||
|
||||
# Since this is a lazy map, to create every object we just need to
|
||||
# iterate through self.
|
||||
for resource_name in self.resource_names:
|
||||
self[resource_name]
|
||||
128
moto/cloudformation/responses.py
Normal file
128
moto/cloudformation/responses.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import json
|
||||
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import cloudformation_backend
|
||||
|
||||
|
||||
class CloudFormationResponse(BaseResponse):
|
||||
|
||||
def create_stack(self):
|
||||
stack_name = self._get_param('StackName')
|
||||
stack_body = self._get_param('TemplateBody')
|
||||
|
||||
stack = cloudformation_backend.create_stack(
|
||||
name=stack_name,
|
||||
template=stack_body,
|
||||
)
|
||||
stack_body = {
|
||||
'CreateStackResponse': {
|
||||
'CreateStackResult': {
|
||||
'StackId': stack.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
return json.dumps(stack_body)
|
||||
|
||||
def describe_stacks(self):
|
||||
names = [value[0] for key, value in self.querystring.items() if "StackName" in key]
|
||||
stacks = cloudformation_backend.describe_stacks(names)
|
||||
|
||||
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)
|
||||
|
||||
template = Template(LIST_STACKS_RESOURCES_RESPONSE)
|
||||
return template.render(stack=stack)
|
||||
|
||||
def list_stacks(self):
|
||||
stacks = 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)
|
||||
return stack.template
|
||||
|
||||
# def update_stack(self):
|
||||
# stack_name = self._get_param('StackName')
|
||||
# stack_body = self._get_param('TemplateBody')
|
||||
|
||||
# stack = cloudformation_backend.update_stack(
|
||||
# name=stack_name,
|
||||
# template=stack_body,
|
||||
# )
|
||||
# stack_body = {
|
||||
# 'UpdateStackResponse': {
|
||||
# 'UpdateStackResult': {
|
||||
# 'StackId': stack.name,
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# return json.dumps(stack_body)
|
||||
|
||||
def delete_stack(self):
|
||||
name_or_stack_id = self.querystring.get('StackName')[0]
|
||||
|
||||
cloudformation_backend.delete_stack(name_or_stack_id)
|
||||
return json.dumps({
|
||||
'DeleteStackResponse': {
|
||||
'DeleteStackResult': {},
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResult>
|
||||
<Stacks>
|
||||
{% for stack in stacks %}
|
||||
<member>
|
||||
<StackName>{{ stack.name }}</StackName>
|
||||
<StackId>{{ stack.stack_id }}</StackId>
|
||||
<CreationTime>2010-07-27T22:28:28Z</CreationTime>
|
||||
<StackStatus>CREATE_COMPLETE</StackStatus>
|
||||
<DisableRollback>false</DisableRollback>
|
||||
<Outputs></Outputs>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Stacks>
|
||||
</DescribeStacksResult>"""
|
||||
|
||||
|
||||
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
||||
<ListStacksResult>
|
||||
<StackSummaries>
|
||||
{% for stack in stacks %}
|
||||
<member>
|
||||
<StackId>{{ stack.id }}</StackId>
|
||||
<StackStatus>CREATE_IN_PROGRESS</StackStatus>
|
||||
<StackName>{{ stack.name }}</StackName>
|
||||
<CreationTime>2011-05-23T15:47:44Z</CreationTime>
|
||||
<TemplateDescription>{{ stack.description }}</TemplateDescription>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</StackSummaries>
|
||||
</ListStacksResult>
|
||||
</ListStacksResponse>"""
|
||||
|
||||
|
||||
LIST_STACKS_RESOURCES_RESPONSE = """<DescribeStackResourcesResult>
|
||||
<StackResources>
|
||||
{% for resource in stack.stack_resources %}
|
||||
<member>
|
||||
<StackId>{{ stack.stack_id }}</StackId>
|
||||
<StackName>{{ stack.name }}</StackName>
|
||||
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
||||
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
||||
<ResourceType>{{ resource.type }}</ResourceType>
|
||||
<Timestamp>2010-07-27T22:27:28Z</Timestamp>
|
||||
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</StackResources>
|
||||
</DescribeStackResourcesResult>"""
|
||||
9
moto/cloudformation/urls.py
Normal file
9
moto/cloudformation/urls.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from .responses import CloudFormationResponse
|
||||
|
||||
url_bases = [
|
||||
"https?://cloudformation.(.+).amazonaws.com",
|
||||
]
|
||||
|
||||
url_paths = {
|
||||
'{0}/$': CloudFormationResponse().dispatch,
|
||||
}
|
||||
6
moto/cloudformation/utils.py
Normal file
6
moto/cloudformation/utils.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import uuid
|
||||
|
||||
|
||||
def generate_stack_id(stack_name):
|
||||
random_id = uuid.uuid4()
|
||||
return "arn:aws:cloudformation:us-east-1:123456789:stack/{0}/{1}".format(stack_name, random_id)
|
||||
|
|
@ -59,6 +59,9 @@ class BaseResponse(object):
|
|||
return status, headers, body
|
||||
raise NotImplementedError("The {0} action has not been implemented".format(action))
|
||||
|
||||
def _get_param(self, param_name):
|
||||
return self.querystring.get(param_name, [None])[0]
|
||||
|
||||
|
||||
def metadata_response(request, full_url, headers):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -8,18 +8,20 @@ from moto.core import BaseBackend
|
|||
from .exceptions import InvalidIdError
|
||||
from .utils import (
|
||||
random_ami_id,
|
||||
random_eip_allocation_id,
|
||||
random_eip_association_id,
|
||||
random_gateway_id,
|
||||
random_instance_id,
|
||||
random_ip,
|
||||
random_key_pair,
|
||||
random_reservation_id,
|
||||
random_route_table_id,
|
||||
random_security_group_id,
|
||||
random_snapshot_id,
|
||||
random_spot_request_id,
|
||||
random_subnet_id,
|
||||
random_volume_id,
|
||||
random_vpc_id,
|
||||
random_eip_association_id,
|
||||
random_eip_allocation_id,
|
||||
random_ip,
|
||||
random_key_pair,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -38,6 +40,25 @@ class Instance(BotoInstance):
|
|||
self.user_data = user_data
|
||||
self.security_groups = security_groups
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
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]
|
||||
|
||||
reservation = ec2_backend.add_instances(
|
||||
image_id=properties['ImageId'],
|
||||
user_data=properties.get('UserData'),
|
||||
count=1,
|
||||
security_group_names=group_names,
|
||||
)
|
||||
return reservation.instances[0]
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
self._state.name = "running"
|
||||
self._state.code = 16
|
||||
|
|
@ -138,6 +159,12 @@ class InstanceBackend(object):
|
|||
instances.append(instance)
|
||||
return instances
|
||||
|
||||
def get_instance_by_id(self, instance_id):
|
||||
for reservation in self.all_reservations():
|
||||
for instance in reservation.instances:
|
||||
if instance.id == instance_id:
|
||||
return instance
|
||||
|
||||
def get_reservations_by_instance_ids(self, instance_ids):
|
||||
""" Go through all of the reservations and filter to only return those
|
||||
associated with the given instance_ids.
|
||||
|
|
@ -343,6 +370,37 @@ class SecurityGroup(object):
|
|||
self.egress_rules = []
|
||||
self.vpc_id = vpc_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
vpc_id = properties.get('VpcId')
|
||||
security_group = ec2_backend.create_security_group(
|
||||
name=resource_name,
|
||||
description=properties.get('GroupDescription'),
|
||||
vpc_id=vpc_id,
|
||||
)
|
||||
|
||||
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,
|
||||
ip_protocol=ingress_rule['IpProtocol'],
|
||||
from_port=ingress_rule['FromPort'],
|
||||
to_port=ingress_rule['ToPort'],
|
||||
ip_ranges=ingress_rule.get('CidrIp'),
|
||||
source_group_ids=[source_group_id],
|
||||
vpc_id=vpc_id,
|
||||
)
|
||||
|
||||
return security_group
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class SecurityGroupBackend(object):
|
||||
|
||||
|
|
@ -401,7 +459,7 @@ class SecurityGroupBackend(object):
|
|||
ip_protocol,
|
||||
from_port,
|
||||
to_port,
|
||||
ip_ranges=None,
|
||||
ip_ranges,
|
||||
source_group_names=None,
|
||||
source_group_ids=None,
|
||||
vpc_id=None):
|
||||
|
|
@ -412,6 +470,12 @@ class SecurityGroupBackend(object):
|
|||
elif group_id:
|
||||
group = self.get_security_group_from_id(group_id)
|
||||
|
||||
if ip_ranges and not isinstance(ip_ranges, list):
|
||||
ip_ranges = [ip_ranges]
|
||||
|
||||
source_group_names = source_group_names if source_group_names else []
|
||||
source_group_ids = source_group_ids if source_group_ids else []
|
||||
|
||||
source_groups = []
|
||||
for source_group_name in source_group_names:
|
||||
source_group = self.get_security_group_from_name(source_group_name, vpc_id)
|
||||
|
|
@ -433,7 +497,7 @@ class SecurityGroupBackend(object):
|
|||
ip_protocol,
|
||||
from_port,
|
||||
to_port,
|
||||
ip_ranges=None,
|
||||
ip_ranges,
|
||||
source_group_names=None,
|
||||
source_group_ids=None,
|
||||
vpc_id=None):
|
||||
|
|
@ -467,6 +531,20 @@ class VolumeAttachment(object):
|
|||
self.instance = instance
|
||||
self.device = device
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
instance_id = properties['InstanceId']
|
||||
volume_id = properties['VolumeId']
|
||||
|
||||
attachment = ec2_backend.attach_volume(
|
||||
volume_id=volume_id,
|
||||
instance_id=instance_id,
|
||||
device_path=properties['Device'],
|
||||
)
|
||||
return attachment
|
||||
|
||||
|
||||
class Volume(object):
|
||||
def __init__(self, volume_id, size, zone):
|
||||
|
|
@ -475,6 +553,20 @@ class Volume(object):
|
|||
self.zone = zone
|
||||
self.attachment = None
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
volume = ec2_backend.create_volume(
|
||||
size=properties.get('Size'),
|
||||
zone_name=properties.get('AvailabilityZone'),
|
||||
)
|
||||
return volume
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.attachment:
|
||||
|
|
@ -554,6 +646,19 @@ class VPC(object):
|
|||
self.id = vpc_id
|
||||
self.cidr_block = cidr_block
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
vpc = ec2_backend.create_vpc(
|
||||
cidr_block=properties['CidrBlock'],
|
||||
)
|
||||
return vpc
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class VPCBackend(object):
|
||||
def __init__(self):
|
||||
|
|
@ -582,6 +687,21 @@ class Subnet(object):
|
|||
self.vpc = vpc
|
||||
self.cidr_block = cidr_block
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
vpc_id = properties['VpcId']
|
||||
subnet = ec2_backend.create_subnet(
|
||||
vpc_id=vpc_id,
|
||||
cidr_block=properties['CidrBlock']
|
||||
)
|
||||
return subnet
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class SubnetBackend(object):
|
||||
def __init__(self):
|
||||
|
|
@ -602,6 +722,154 @@ class SubnetBackend(object):
|
|||
return self.subnets.pop(subnet_id, None)
|
||||
|
||||
|
||||
class SubnetRouteTableAssociation(object):
|
||||
def __init__(self, route_table_id, subnet_id):
|
||||
self.route_table_id = route_table_id
|
||||
self.subnet_id = subnet_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
route_table_id = properties['RouteTableId']
|
||||
subnet_id = properties['SubnetId']
|
||||
|
||||
subnet_association = ec2_backend.create_subnet_association(
|
||||
route_table_id=route_table_id,
|
||||
subnet_id=subnet_id,
|
||||
)
|
||||
return subnet_association
|
||||
|
||||
|
||||
class SubnetRouteTableAssociationBackend(object):
|
||||
def __init__(self):
|
||||
self.subnet_associations = {}
|
||||
super(SubnetRouteTableAssociationBackend, self).__init__()
|
||||
|
||||
def create_subnet_association(self, route_table_id, subnet_id):
|
||||
subnet_association = SubnetRouteTableAssociation(route_table_id, subnet_id)
|
||||
self.subnet_associations["{0}:{1}".format(route_table_id, subnet_id)] = subnet_association
|
||||
return subnet_association
|
||||
|
||||
|
||||
class RouteTable(object):
|
||||
def __init__(self, route_table_id, vpc_id):
|
||||
self.id = route_table_id
|
||||
self.vpc_id = vpc_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
vpc_id = properties['VpcId']
|
||||
route_table = ec2_backend.create_route_table(
|
||||
vpc_id=vpc_id,
|
||||
)
|
||||
return route_table
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class RouteTableBackend(object):
|
||||
def __init__(self):
|
||||
self.route_tables = {}
|
||||
super(RouteTableBackend, self).__init__()
|
||||
|
||||
def create_route_table(self, vpc_id):
|
||||
route_table_id = random_route_table_id()
|
||||
route_table = RouteTable(route_table_id, vpc_id)
|
||||
self.route_tables[route_table_id] = route_table
|
||||
return route_table
|
||||
|
||||
|
||||
class Route(object):
|
||||
def __init__(self, route_table_id, destination_cidr_block, gateway_id):
|
||||
self.route_table_id = route_table_id
|
||||
self.destination_cidr_block = destination_cidr_block
|
||||
self.gateway_id = gateway_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
gateway_id = properties['GatewayId']
|
||||
route_table_id = properties['RouteTableId']
|
||||
route_table = ec2_backend.create_route(
|
||||
route_table_id=route_table_id,
|
||||
destination_cidr_block=properties['DestinationCidrBlock'],
|
||||
gateway_id=gateway_id,
|
||||
)
|
||||
return route_table
|
||||
|
||||
|
||||
class RouteBackend(object):
|
||||
def __init__(self):
|
||||
self.routes = {}
|
||||
super(RouteBackend, self).__init__()
|
||||
|
||||
def create_route(self, route_table_id, destination_cidr_block, gateway_id):
|
||||
route = Route(route_table_id, destination_cidr_block, gateway_id)
|
||||
self.routes[destination_cidr_block] = route
|
||||
return route
|
||||
|
||||
|
||||
class InternetGateway(object):
|
||||
def __init__(self, gateway_id):
|
||||
self.id = gateway_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
return ec2_backend.create_gateway()
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class InternetGatewayBackend(object):
|
||||
def __init__(self):
|
||||
self.gateways = {}
|
||||
super(InternetGatewayBackend, self).__init__()
|
||||
|
||||
def create_gateway(self):
|
||||
gateway_id = random_gateway_id()
|
||||
gateway = InternetGateway(gateway_id)
|
||||
self.gateways[gateway_id] = gateway
|
||||
return gateway
|
||||
|
||||
|
||||
class VPCGatewayAttachment(object):
|
||||
def __init__(self, gateway_id, vpc_id):
|
||||
self.gateway_id = gateway_id
|
||||
self.vpc_id = vpc_id
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
return ec2_backend.create_vpc_gateway_attachment(
|
||||
gateway_id=properties['InternetGatewayId'],
|
||||
vpc_id=properties['VpcId'],
|
||||
)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class VPCGatewayAttachmentBackend(object):
|
||||
def __init__(self):
|
||||
self.gateway_attachments = {}
|
||||
super(VPCGatewayAttachmentBackend, self).__init__()
|
||||
|
||||
def create_vpc_gateway_attachment(self, vpc_id, gateway_id):
|
||||
attachment = VPCGatewayAttachment(vpc_id, gateway_id)
|
||||
self.gateway_attachments[gateway_id] = attachment
|
||||
return attachment
|
||||
|
||||
|
||||
class SpotInstanceRequest(object):
|
||||
def __init__(self, spot_request_id, price, image_id, type, valid_from,
|
||||
valid_until, launch_group, availability_zone_group, key_name,
|
||||
|
|
@ -678,6 +946,25 @@ class ElasticAddress(object):
|
|||
self.instance = None
|
||||
self.association_id = None
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
eip = ec2_backend.allocate_address(
|
||||
domain=properties['Domain']
|
||||
)
|
||||
|
||||
instance_id = properties.get('InstanceId')
|
||||
if instance_id:
|
||||
instance = ec2_backend.get_instance_by_id(instance_id)
|
||||
ec2_backend.associate_address(instance, eip.public_ip)
|
||||
|
||||
return eip
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.allocation_id
|
||||
|
||||
|
||||
class ElasticAddressBackend(object):
|
||||
|
||||
|
|
@ -755,8 +1042,10 @@ class ElasticAddressBackend(object):
|
|||
|
||||
class EC2Backend(BaseBackend, InstanceBackend, TagBackend, AmiBackend,
|
||||
RegionsAndZonesBackend, SecurityGroupBackend, EBSBackend,
|
||||
VPCBackend, SubnetBackend, SpotRequestBackend, ElasticAddressBackend,
|
||||
KeyPairBackend):
|
||||
VPCBackend, SubnetBackend, SubnetRouteTableAssociationBackend,
|
||||
RouteTableBackend, RouteBackend, InternetGatewayBackend,
|
||||
VPCGatewayAttachmentBackend, SpotRequestBackend,
|
||||
ElasticAddressBackend, KeyPairBackend):
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,9 @@ DESCRIBE_ADDRESS_RESPONSE = """<DescribeAddressesResponse xmlns="http://ec2.amaz
|
|||
{% else %}
|
||||
<instanceId/>
|
||||
{% endif %}
|
||||
{% if address.allocation_id %}
|
||||
<allocationId>{{ address.allocation_id }}</allocationId>
|
||||
{% endif %}
|
||||
{% if address.association_id %}
|
||||
<associationId>{{ address.association_id }}</associationId>
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ def random_eip_association_id():
|
|||
return random_id(prefix='eipassoc')
|
||||
|
||||
|
||||
def random_gateway_id():
|
||||
return random_id(prefix='igw')
|
||||
|
||||
|
||||
def random_route_table_id():
|
||||
return random_id(prefix='rtb')
|
||||
|
||||
|
||||
def random_eip_allocation_id():
|
||||
return random_id(prefix='eipalloc')
|
||||
|
||||
|
|
@ -123,8 +131,6 @@ def keypair_names_from_querystring(querystring_dict):
|
|||
keypair_names.append(value[0])
|
||||
return keypair_names
|
||||
|
||||
|
||||
|
||||
filter_dict_attribute_mapping = {
|
||||
'instance-state-name': 'state'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,25 @@ class FakeLoadBalancer(object):
|
|||
)
|
||||
self.listeners.append(listener)
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
new_elb = elb_backend.create_load_balancer(
|
||||
name=properties.get('LoadBalancerName', resource_name),
|
||||
zones=properties.get('AvailabilityZones'),
|
||||
ports=[],
|
||||
)
|
||||
|
||||
instance_ids = cloudformation_json.get('Instances', [])
|
||||
for instance_id in instance_ids:
|
||||
elb_backend.register_instances(new_elb.name, [instance_id])
|
||||
return new_elb
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class ELBBackend(BaseBackend):
|
||||
|
||||
|
|
|
|||
2
moto/iam/__init__.py
Normal file
2
moto/iam/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from .models import iam_backend
|
||||
mock_iam = iam_backend.decorator
|
||||
99
moto/iam/models.py
Normal file
99
moto/iam/models.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
from moto.core import BaseBackend
|
||||
|
||||
from .utils import random_resource_id
|
||||
|
||||
|
||||
class Role(object):
|
||||
|
||||
def __init__(self, role_id, name, assume_role_policy_document, path, policies):
|
||||
self.id = role_id
|
||||
self.name = name
|
||||
self.assume_role_policy_document = assume_role_policy_document
|
||||
self.path = path
|
||||
self.policies = policies
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
return iam_backend.create_role(
|
||||
role_name=resource_name,
|
||||
assume_role_policy_document=properties['AssumeRolePolicyDocument'],
|
||||
path=properties['Path'],
|
||||
policies=properties.get('Policies', []),
|
||||
)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
|
||||
class InstanceProfile(object):
|
||||
def __init__(self, instance_profile_id, name, path, roles):
|
||||
self.id = instance_profile_id
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.roles = roles if roles else []
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
role_ids = properties['Roles']
|
||||
return iam_backend.create_instance_profile(
|
||||
name=resource_name,
|
||||
path=properties['Path'],
|
||||
role_ids=role_ids,
|
||||
)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class IAMBackend(BaseBackend):
|
||||
|
||||
def __init__(self):
|
||||
self.instance_profiles = {}
|
||||
self.roles = {}
|
||||
super(IAMBackend, self).__init__()
|
||||
|
||||
def create_role(self, role_name, assume_role_policy_document, path, policies):
|
||||
role_id = random_resource_id()
|
||||
role = Role(role_id, role_name, assume_role_policy_document, path, policies)
|
||||
self.roles[role_id] = role
|
||||
return role
|
||||
|
||||
def get_role_by_id(self, role_id):
|
||||
return self.roles.get(role_id)
|
||||
|
||||
def get_role(self, role_name):
|
||||
for role in self.get_roles():
|
||||
if role.name == role_name:
|
||||
return role
|
||||
|
||||
def get_roles(self):
|
||||
return self.roles.values()
|
||||
|
||||
def create_instance_profile(self, name, path, role_ids):
|
||||
instance_profile_id = random_resource_id()
|
||||
|
||||
roles = [iam_backend.get_role_by_id(role_id) for role_id in role_ids]
|
||||
instance_profile = InstanceProfile(instance_profile_id, name, path, roles)
|
||||
self.instance_profiles[instance_profile_id] = instance_profile
|
||||
return instance_profile
|
||||
|
||||
def get_instance_profile(self, profile_name):
|
||||
for profile in self.get_instance_profiles():
|
||||
if profile.name == profile_name:
|
||||
return profile
|
||||
|
||||
def get_instance_profiles(self):
|
||||
return self.instance_profiles.values()
|
||||
|
||||
def add_role_to_instance_profile(self, profile_name, role_name):
|
||||
profile = self.get_instance_profile(profile_name)
|
||||
role = self.get_role(role_name)
|
||||
profile.roles.append(role)
|
||||
|
||||
iam_backend = IAMBackend()
|
||||
184
moto/iam/responses.py
Normal file
184
moto/iam/responses.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
from jinja2 import Template
|
||||
|
||||
from moto.core.responses import BaseResponse
|
||||
from .models import iam_backend
|
||||
|
||||
|
||||
class IamResponse(BaseResponse):
|
||||
|
||||
def _get_param(self, param_name):
|
||||
return self.querystring.get(param_name, [None])[0]
|
||||
|
||||
def create_role(self):
|
||||
role_name = self._get_param('RoleName')
|
||||
path = self._get_param('Path')
|
||||
assume_role_policy_document = self._get_param('AssumeRolePolicyDocument')
|
||||
|
||||
role = iam_backend.create_role(role_name, assume_role_policy_document, path, policies=[])
|
||||
template = Template(CREATE_ROLE_TEMPLATE)
|
||||
return template.render(role=role)
|
||||
|
||||
def get_role(self):
|
||||
role_name = self._get_param('RoleName')
|
||||
role = iam_backend.get_role(role_name)
|
||||
|
||||
template = Template(GET_ROLE_TEMPLATE)
|
||||
return template.render(role=role)
|
||||
|
||||
def create_instance_profile(self):
|
||||
profile_name = self._get_param('InstanceProfileName')
|
||||
path = self._get_param('Path')
|
||||
|
||||
profile = iam_backend.create_instance_profile(profile_name, path, role_ids=[])
|
||||
template = Template(CREATE_INSTANCE_PROFILE_TEMPLATE)
|
||||
return template.render(profile=profile)
|
||||
|
||||
def get_instance_profile(self):
|
||||
profile_name = self._get_param('InstanceProfileName')
|
||||
profile = iam_backend.get_instance_profile(profile_name)
|
||||
|
||||
template = Template(GET_INSTANCE_PROFILE_TEMPLATE)
|
||||
return template.render(profile=profile)
|
||||
|
||||
def add_role_to_instance_profile(self):
|
||||
profile_name = self._get_param('InstanceProfileName')
|
||||
role_name = self._get_param('RoleName')
|
||||
|
||||
iam_backend.add_role_to_instance_profile(profile_name, role_name)
|
||||
template = Template(ADD_ROLE_TO_INSTANCE_PROFILE_TEMPLATE)
|
||||
return template.render()
|
||||
|
||||
def list_roles(self):
|
||||
roles = iam_backend.get_roles()
|
||||
|
||||
template = Template(LIST_ROLES_TEMPLATE)
|
||||
return template.render(roles=roles)
|
||||
|
||||
def list_instance_profiles(self):
|
||||
profiles = iam_backend.get_instance_profiles()
|
||||
|
||||
template = Template(LIST_INSTANCE_PROFILES_TEMPLATE)
|
||||
return template.render(instance_profiles=profiles)
|
||||
|
||||
CREATE_INSTANCE_PROFILE_TEMPLATE = """<CreateInstanceProfileResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<CreateInstanceProfileResult>
|
||||
<InstanceProfile>
|
||||
<InstanceProfileId>{{ profile.id }}</InstanceProfileId>
|
||||
<Roles/>
|
||||
<InstanceProfileName>{{ profile.name }}</InstanceProfileName>
|
||||
<Path>{{ profile.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Webserver</Arn>
|
||||
<CreateDate>2012-05-09T16:11:10.222Z</CreateDate>
|
||||
</InstanceProfile>
|
||||
</CreateInstanceProfileResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>974142ee-99f1-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateInstanceProfileResponse>"""
|
||||
|
||||
GET_INSTANCE_PROFILE_TEMPLATE = """<GetInstanceProfileResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<GetInstanceProfileResult>
|
||||
<InstanceProfile>
|
||||
<InstanceProfileId>{{ profile.id }}</InstanceProfileId>
|
||||
<Roles>
|
||||
{% for role in profile.roles %}
|
||||
<member>
|
||||
<Path>{{ role.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:role/application_abc/component_xyz/S3Access</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_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/application_abc/component_xyz/Webserver</Arn>
|
||||
<CreateDate>2012-05-09T16:11:10Z</CreateDate>
|
||||
</InstanceProfile>
|
||||
</GetInstanceProfileResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>37289fda-99f2-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</GetInstanceProfileResponse>"""
|
||||
|
||||
CREATE_ROLE_TEMPLATE = """<CreateRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<CreateRoleResult>
|
||||
<Role>
|
||||
<Path>{{ role.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:role/application_abc/component_xyz/S3Access</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||
<CreateDate>2012-05-08T23:34:01.495Z</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
</Role>
|
||||
</CreateRoleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>4a93ceee-9966-11e1-b624-b1aEXAMPLE7c</RequestId>
|
||||
</ResponseMetadata>
|
||||
</CreateRoleResponse>"""
|
||||
|
||||
GET_ROLE_TEMPLATE = """<GetRoleResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<GetRoleResult>
|
||||
<Role>
|
||||
<Path>{{ role.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:role/application_abc/component_xyz/S3Access</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||
<CreateDate>2012-05-08T23:34:01Z</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
</Role>
|
||||
</GetRoleResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>df37e965-9967-11e1-a4c3-270EXAMPLE04</RequestId>
|
||||
</ResponseMetadata>
|
||||
</GetRoleResponse>"""
|
||||
|
||||
ADD_ROLE_TO_INSTANCE_PROFILE_TEMPLATE = """<AddRoleToInstanceProfileResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<ResponseMetadata>
|
||||
<RequestId>12657608-99f2-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</AddRoleToInstanceProfileResponse>"""
|
||||
|
||||
LIST_ROLES_TEMPLATE = """<ListRolesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<ListRolesResult>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<Roles>
|
||||
{% for role in roles %}
|
||||
<member>
|
||||
<Path>{{ role.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:role/application_abc/component_xyz/S3Access</Arn>
|
||||
<RoleName>{{ role.name }}</RoleName>
|
||||
<AssumeRolePolicyDocument>{{ role.assume_role_policy_document }}</AssumeRolePolicyDocument>
|
||||
<CreateDate>2012-05-09T15:45:35Z</CreateDate>
|
||||
<RoleId>{{ role.id }}</RoleId>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Roles>
|
||||
</ListRolesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>20f7279f-99ee-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListRolesResponse>"""
|
||||
|
||||
LIST_INSTANCE_PROFILES_TEMPLATE = """<ListInstanceProfilesResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||
<ListInstanceProfilesResult>
|
||||
<IsTruncated>false</IsTruncated>
|
||||
<InstanceProfiles>
|
||||
{% for instance in instance_profiles %}
|
||||
<member>
|
||||
<Id>{{ instance.id }}</Id>
|
||||
<Roles/>
|
||||
<InstanceProfileName>{{ instance.name }}</InstanceProfileName>
|
||||
<Path>{{ instance.path }}</Path>
|
||||
<Arn>arn:aws:iam::123456789012:instance-profile/application_abc/component_xyz/Database</Arn>
|
||||
<CreateDate>2012-05-09T16:27:03Z</CreateDate>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</InstanceProfiles>
|
||||
</ListInstanceProfilesResult>
|
||||
<ResponseMetadata>
|
||||
<RequestId>fd74fa8d-99f3-11e1-a4c3-27EXAMPLE804</RequestId>
|
||||
</ResponseMetadata>
|
||||
</ListInstanceProfilesResponse>"""
|
||||
9
moto/iam/urls.py
Normal file
9
moto/iam/urls.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
from .responses import IamResponse
|
||||
|
||||
url_bases = [
|
||||
"https?://iam.amazonaws.com",
|
||||
]
|
||||
|
||||
url_paths = {
|
||||
'{0}/$': IamResponse().dispatch,
|
||||
}
|
||||
9
moto/iam/utils.py
Normal file
9
moto/iam/utils.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import random
|
||||
import string
|
||||
|
||||
|
||||
def random_resource_id():
|
||||
size = 20
|
||||
chars = range(10) + list(string.lowercase)
|
||||
|
||||
return ''.join(unicode(random.choice(chars)) for x in range(size))
|
||||
|
|
@ -51,6 +51,19 @@ class Queue(object):
|
|||
self.queue_arn = 'arn:aws:sqs:sqs.us-east-1:123456789012:%s' % self.name
|
||||
self.receive_message_wait_time_seconds = 0
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(cls, resource_name, cloudformation_json):
|
||||
properties = cloudformation_json['Properties']
|
||||
|
||||
return sqs_backend.create_queue(
|
||||
name=properties['QueueName'],
|
||||
visibility_timeout=properties.get('VisibilityTimeout'),
|
||||
)
|
||||
|
||||
@property
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def attributes(self):
|
||||
result = {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue