2014-08-27 11:17:06 -04:00
|
|
|
from __future__ import unicode_literals
|
2014-12-29 22:12:23 -05:00
|
|
|
|
2014-03-27 19:12:53 -04:00
|
|
|
import json
|
2015-02-14 08:43:17 -05:00
|
|
|
from six.moves.urllib.parse import urlparse
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
from moto.core.responses import BaseResponse
|
2014-12-29 22:12:23 -05:00
|
|
|
from moto.s3 import s3_backend
|
2014-11-15 13:34:52 -05:00
|
|
|
from .models import cloudformation_backends
|
2016-02-29 19:50:25 +00:00
|
|
|
from .exceptions import ValidationError
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
|
|
|
|
class CloudFormationResponse(BaseResponse):
|
|
|
|
|
2014-11-15 13:34:52 -05:00
|
|
|
@property
|
|
|
|
def cloudformation_backend(self):
|
|
|
|
return cloudformation_backends[self.region]
|
|
|
|
|
2014-12-29 22:12:23 -05:00
|
|
|
def _get_stack_from_s3_url(self, template_url):
|
2014-12-29 22:17:50 -05:00
|
|
|
template_url_parts = urlparse(template_url)
|
2014-12-29 22:12:23 -05:00
|
|
|
bucket_name = template_url_parts.netloc.split(".")[0]
|
|
|
|
key_name = template_url_parts.path.lstrip("/")
|
|
|
|
|
|
|
|
key = s3_backend.get_key(bucket_name, key_name)
|
2014-12-29 22:17:50 -05:00
|
|
|
return key.value.decode("utf-8")
|
2014-12-29 22:12:23 -05:00
|
|
|
|
2014-03-27 19:12:53 -04:00
|
|
|
def create_stack(self):
|
|
|
|
stack_name = self._get_param('StackName')
|
|
|
|
stack_body = self._get_param('TemplateBody')
|
2014-12-29 22:12:23 -05:00
|
|
|
template_url = self._get_param('TemplateURL')
|
2014-12-31 14:21:47 -05:00
|
|
|
parameters_list = self._get_list_prefix("Parameters.member")
|
2015-08-31 16:48:36 -04:00
|
|
|
tags = dict((item['key'], item['value']) for item in self._get_list_prefix("Tags.member"))
|
2014-12-31 14:21:47 -05:00
|
|
|
|
|
|
|
# Hack dict-comprehension
|
|
|
|
parameters = dict([
|
|
|
|
(parameter['parameter_key'], parameter['parameter_value'])
|
|
|
|
for parameter
|
|
|
|
in parameters_list
|
|
|
|
])
|
2014-12-29 22:12:23 -05:00
|
|
|
if template_url:
|
|
|
|
stack_body = self._get_stack_from_s3_url(template_url)
|
2014-10-29 11:59:41 -04:00
|
|
|
stack_notification_arns = self._get_multi_param('NotificationARNs.member')
|
2014-03-27 19:12:53 -04:00
|
|
|
|
2014-11-15 13:34:52 -05:00
|
|
|
stack = self.cloudformation_backend.create_stack(
|
2014-03-27 19:12:53 -04:00
|
|
|
name=stack_name,
|
|
|
|
template=stack_body,
|
2014-12-31 14:21:47 -05:00
|
|
|
parameters=parameters,
|
2014-11-15 13:34:52 -05:00
|
|
|
region_name=self.region,
|
2015-08-31 16:48:36 -04:00
|
|
|
notification_arns=stack_notification_arns,
|
|
|
|
tags=tags,
|
2014-03-27 19:12:53 -04:00
|
|
|
)
|
2016-02-29 19:50:23 +00:00
|
|
|
if self.request_json:
|
|
|
|
return json.dumps({
|
|
|
|
'CreateStackResponse': {
|
|
|
|
'CreateStackResult': {
|
|
|
|
'StackId': stack.stack_id,
|
|
|
|
}
|
2014-03-27 19:12:53 -04:00
|
|
|
}
|
2016-02-29 19:50:23 +00:00
|
|
|
})
|
|
|
|
else:
|
|
|
|
template = self.response_template(CREATE_STACK_RESPONSE_TEMPLATE)
|
|
|
|
return template.render(stack=stack)
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
def describe_stacks(self):
|
2014-10-22 23:58:42 -04:00
|
|
|
stack_name_or_id = None
|
|
|
|
if self._get_param('StackName'):
|
|
|
|
stack_name_or_id = self.querystring.get('StackName')[0]
|
2014-11-15 13:34:52 -05:00
|
|
|
stacks = self.cloudformation_backend.describe_stacks(stack_name_or_id)
|
2014-03-27 19:12:53 -04:00
|
|
|
|
2014-12-12 12:46:07 -08:00
|
|
|
template = self.response_template(DESCRIBE_STACKS_TEMPLATE)
|
2014-03-27 19:12:53 -04:00
|
|
|
return template.render(stacks=stacks)
|
|
|
|
|
2016-02-29 19:50:25 +00:00
|
|
|
def describe_stack_resource(self):
|
|
|
|
stack_name = self._get_param('StackName')
|
|
|
|
stack = self.cloudformation_backend.get_stack(stack_name)
|
|
|
|
logical_resource_id = self._get_param('LogicalResourceId')
|
|
|
|
|
|
|
|
for stack_resource in stack.stack_resources:
|
|
|
|
if stack_resource.logical_resource_id == logical_resource_id:
|
|
|
|
resource = stack_resource
|
|
|
|
break
|
|
|
|
else:
|
2016-04-28 09:21:54 -04:00
|
|
|
raise ValidationError(logical_resource_id)
|
|
|
|
|
2016-02-29 19:50:25 +00:00
|
|
|
template = self.response_template(DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE)
|
|
|
|
return template.render(stack=stack, resource=resource)
|
|
|
|
|
2014-03-27 19:12:53 -04:00
|
|
|
def describe_stack_resources(self):
|
|
|
|
stack_name = self._get_param('StackName')
|
2014-11-15 13:34:52 -05:00
|
|
|
stack = self.cloudformation_backend.get_stack(stack_name)
|
2014-03-27 19:12:53 -04:00
|
|
|
|
2016-02-29 19:50:25 +00:00
|
|
|
template = self.response_template(DESCRIBE_STACK_RESOURCES_RESPONSE)
|
2014-03-27 19:12:53 -04:00
|
|
|
return template.render(stack=stack)
|
|
|
|
|
|
|
|
def list_stacks(self):
|
2014-11-15 13:34:52 -05:00
|
|
|
stacks = self.cloudformation_backend.list_stacks()
|
2014-12-12 12:46:07 -08:00
|
|
|
template = self.response_template(LIST_STACKS_RESPONSE)
|
2014-03-27 19:12:53 -04:00
|
|
|
return template.render(stacks=stacks)
|
|
|
|
|
2015-07-13 11:05:36 -04:00
|
|
|
def list_stack_resources(self):
|
|
|
|
stack_name_or_id = self._get_param('StackName')
|
|
|
|
resources = self.cloudformation_backend.list_stack_resources(stack_name_or_id)
|
|
|
|
|
|
|
|
template = self.response_template(LIST_STACKS_RESOURCES_RESPONSE)
|
|
|
|
return template.render(resources=resources)
|
|
|
|
|
2014-03-27 19:12:53 -04:00
|
|
|
def get_template(self):
|
|
|
|
name_or_stack_id = self.querystring.get('StackName')[0]
|
2014-11-15 13:34:52 -05:00
|
|
|
stack = self.cloudformation_backend.get_stack(name_or_stack_id)
|
2016-04-28 09:21:54 -04:00
|
|
|
|
2016-02-29 19:50:23 +00:00
|
|
|
if self.request_json:
|
|
|
|
return json.dumps({
|
|
|
|
"GetTemplateResponse": {
|
|
|
|
"GetTemplateResult": {
|
|
|
|
"TemplateBody": stack.template,
|
|
|
|
"ResponseMetadata": {
|
|
|
|
"RequestId": "2d06e36c-ac1d-11e0-a958-f9382b6eb86bEXAMPLE"
|
|
|
|
}
|
|
|
|
}
|
2015-10-05 15:21:30 -04:00
|
|
|
}
|
2016-02-29 19:50:23 +00:00
|
|
|
})
|
|
|
|
else:
|
|
|
|
template = self.response_template(GET_TEMPLATE_RESPONSE_TEMPLATE)
|
|
|
|
return template.render(stack=stack)
|
2014-03-27 19:12:53 -04:00
|
|
|
|
2015-07-13 13:56:46 -04:00
|
|
|
def update_stack(self):
|
|
|
|
stack_name = self._get_param('StackName')
|
2016-04-28 09:21:54 -04:00
|
|
|
if self._get_param('UsePreviousTemplate') == "true":
|
|
|
|
stack_body = self.cloudformation_backend.get_stack(stack_name).template
|
|
|
|
else:
|
|
|
|
stack_body = self._get_param('TemplateBody')
|
2015-07-13 13:56:46 -04:00
|
|
|
|
|
|
|
stack = self.cloudformation_backend.update_stack(
|
|
|
|
name=stack_name,
|
|
|
|
template=stack_body,
|
|
|
|
)
|
2016-04-18 14:37:12 -04:00
|
|
|
if self.request_json:
|
2016-04-28 09:21:54 -04:00
|
|
|
stack_body = {
|
2016-04-18 14:37:12 -04:00
|
|
|
'UpdateStackResponse': {
|
|
|
|
'UpdateStackResult': {
|
|
|
|
'StackId': stack.name,
|
|
|
|
}
|
2015-07-13 13:56:46 -04:00
|
|
|
}
|
2016-04-28 09:21:54 -04:00
|
|
|
}
|
|
|
|
return json.dumps(stack_body)
|
2016-04-18 14:37:12 -04:00
|
|
|
else:
|
|
|
|
template = self.response_template(UPDATE_STACK_RESPONSE_TEMPLATE)
|
|
|
|
return template.render(stack=stack)
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
def delete_stack(self):
|
|
|
|
name_or_stack_id = self.querystring.get('StackName')[0]
|
|
|
|
|
2014-11-15 13:34:52 -05:00
|
|
|
self.cloudformation_backend.delete_stack(name_or_stack_id)
|
2016-02-29 19:50:23 +00:00
|
|
|
if self.request_json:
|
|
|
|
return json.dumps({
|
|
|
|
'DeleteStackResponse': {
|
|
|
|
'DeleteStackResult': {},
|
|
|
|
}
|
|
|
|
})
|
|
|
|
else:
|
|
|
|
template = self.response_template(DELETE_STACK_RESPONSE_TEMPLATE)
|
|
|
|
return template.render()
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
|
2016-02-29 19:50:23 +00:00
|
|
|
CREATE_STACK_RESPONSE_TEMPLATE = """<CreateStackResponse>
|
|
|
|
<CreateStackResult>
|
|
|
|
<StackId>{{ stack.stack_id }}</StackId>
|
|
|
|
</CreateStackResult>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</CreateStackResponse>
|
|
|
|
"""
|
|
|
|
|
2016-04-18 14:37:12 -04:00
|
|
|
UPDATE_STACK_RESPONSE_TEMPLATE = """<UpdateStackResponse>
|
|
|
|
<UpdateStackResult>
|
|
|
|
<StackId>{{ stack.stack_id }}</StackId>
|
|
|
|
</UpdateStackResult>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>b9b5b068-3a41-11e5-94eb-example</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</UpdateStackResponse>
|
|
|
|
"""
|
2016-02-29 19:50:23 +00:00
|
|
|
|
|
|
|
DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResponse>
|
|
|
|
<DescribeStacksResult>
|
|
|
|
<Stacks>
|
|
|
|
{% for stack in stacks %}
|
|
|
|
<member>
|
|
|
|
<StackName>{{ stack.name }}</StackName>
|
|
|
|
<StackId>{{ stack.stack_id }}</StackId>
|
|
|
|
<CreationTime>2010-07-27T22:28:28Z</CreationTime>
|
|
|
|
<StackStatus>{{ stack.status }}</StackStatus>
|
|
|
|
{% if stack.notification_arns %}
|
|
|
|
<NotificationARNs>
|
|
|
|
{% for notification_arn in stack.notification_arns %}
|
|
|
|
<member>{{ notification_arn }}</member>
|
|
|
|
{% endfor %}
|
|
|
|
</NotificationARNs>
|
|
|
|
{% else %}
|
|
|
|
<NotificationARNs/>
|
|
|
|
{% endif %}
|
|
|
|
<DisableRollback>false</DisableRollback>
|
|
|
|
<Outputs>
|
|
|
|
{% for output in stack.stack_outputs %}
|
|
|
|
<member>
|
|
|
|
<OutputKey>{{ output.key }}</OutputKey>
|
|
|
|
<OutputValue>{{ output.value }}</OutputValue>
|
|
|
|
</member>
|
2014-10-29 11:59:41 -04:00
|
|
|
{% endfor %}
|
2016-02-29 19:50:23 +00:00
|
|
|
</Outputs>
|
|
|
|
<Parameters>
|
|
|
|
{% for param_name, param_value in stack.stack_parameters.items() %}
|
2015-08-31 16:48:36 -04:00
|
|
|
<member>
|
2016-02-29 19:50:23 +00:00
|
|
|
<ParameterKey>{{ param_name }}</ParameterKey>
|
|
|
|
<ParameterValue>{{ param_value }}</ParameterValue>
|
2015-08-31 16:48:36 -04:00
|
|
|
</member>
|
|
|
|
{% endfor %}
|
2016-02-29 19:50:23 +00:00
|
|
|
</Parameters>
|
|
|
|
<Tags>
|
|
|
|
{% for tag_key, tag_value in stack.tags.items() %}
|
|
|
|
<member>
|
|
|
|
<Key>{{ tag_key }}</Key>
|
|
|
|
<Value>{{ tag_value }}</Value>
|
|
|
|
</member>
|
|
|
|
{% endfor %}
|
|
|
|
</Tags>
|
|
|
|
</member>
|
|
|
|
{% endfor %}
|
|
|
|
</Stacks>
|
|
|
|
</DescribeStacksResult>
|
|
|
|
</DescribeStacksResponse>"""
|
|
|
|
|
|
|
|
|
2016-02-29 19:50:25 +00:00
|
|
|
DESCRIBE_STACK_RESOURCE_RESPONSE_TEMPLATE = """<DescribeStackResourceResponse>
|
|
|
|
<DescribeStackResourceResult>
|
|
|
|
<StackResourceDetail>
|
|
|
|
<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>{{ stack.status }}</ResourceStatus>
|
|
|
|
</StackResourceDetail>
|
|
|
|
</DescribeStackResourceResult>
|
|
|
|
</DescribeStackResourceResponse>"""
|
|
|
|
|
|
|
|
|
2016-03-31 13:33:13 -07:00
|
|
|
DESCRIBE_STACK_RESOURCES_RESPONSE = """<DescribeStackResourcesResponse>
|
|
|
|
<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>{{ stack.status }}</ResourceStatus>
|
|
|
|
</member>
|
|
|
|
{% endfor %}
|
|
|
|
</StackResources>
|
|
|
|
</DescribeStackResourcesResult>
|
|
|
|
</DescribeStackResourcesResponse>"""
|
2014-03-27 19:12:53 -04:00
|
|
|
|
|
|
|
|
|
|
|
LIST_STACKS_RESPONSE = """<ListStacksResponse>
|
|
|
|
<ListStacksResult>
|
|
|
|
<StackSummaries>
|
|
|
|
{% for stack in stacks %}
|
|
|
|
<member>
|
2015-08-02 16:27:08 +02:00
|
|
|
<StackId>{{ stack.stack_id }}</StackId>
|
2014-10-22 23:58:42 -04:00
|
|
|
<StackStatus>{{ stack.status }}</StackStatus>
|
2014-03-27 19:12:53 -04:00
|
|
|
<StackName>{{ stack.name }}</StackName>
|
|
|
|
<CreationTime>2011-05-23T15:47:44Z</CreationTime>
|
|
|
|
<TemplateDescription>{{ stack.description }}</TemplateDescription>
|
|
|
|
</member>
|
|
|
|
{% endfor %}
|
|
|
|
</StackSummaries>
|
|
|
|
</ListStacksResult>
|
|
|
|
</ListStacksResponse>"""
|
|
|
|
|
|
|
|
|
2015-07-13 11:05:36 -04:00
|
|
|
LIST_STACKS_RESOURCES_RESPONSE = """<ListStackResourcesResponse>
|
|
|
|
<ListStackResourcesResult>
|
|
|
|
<StackResourceSummaries>
|
|
|
|
{% for resource in resources %}
|
|
|
|
<member>
|
|
|
|
<ResourceStatus>CREATE_COMPLETE</ResourceStatus>
|
|
|
|
<LogicalResourceId>{{ resource.logical_resource_id }}</LogicalResourceId>
|
|
|
|
<LastUpdatedTimestamp>2011-06-21T20:15:58Z</LastUpdatedTimestamp>
|
|
|
|
<PhysicalResourceId>{{ resource.physical_resource_id }}</PhysicalResourceId>
|
|
|
|
<ResourceType>{{ resource.type }}</ResourceType>
|
|
|
|
</member>
|
|
|
|
{% endfor %}
|
|
|
|
</StackResourceSummaries>
|
|
|
|
</ListStackResourcesResult>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>2d06e36c-ac1d-11e0-a958-f9382b6eb86b</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</ListStackResourcesResponse>"""
|
2016-02-29 19:50:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
GET_TEMPLATE_RESPONSE_TEMPLATE = """<GetTemplateResponse>
|
|
|
|
<GetTemplateResult>
|
|
|
|
<TemplateBody>{{ stack.template }}
|
|
|
|
</TemplateBody>
|
|
|
|
</GetTemplateResult>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</GetTemplateResponse>"""
|
|
|
|
|
|
|
|
|
2016-04-28 09:21:54 -04:00
|
|
|
UPDATE_STACK_RESPONSE_TEMPLATE = """<UpdateStackResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
|
|
|
<UpdateStackResult>
|
|
|
|
<StackId>{{ stack.stack_id }}</StackId>
|
|
|
|
</UpdateStackResult>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>b9b4b068-3a41-11e5-94eb-example</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</UpdateStackResponse>
|
|
|
|
"""
|
|
|
|
|
2016-02-29 19:50:23 +00:00
|
|
|
DELETE_STACK_RESPONSE_TEMPLATE = """<DeleteStackResponse>
|
|
|
|
<ResponseMetadata>
|
|
|
|
<RequestId>5ccc7dcd-744c-11e5-be70-example</RequestId>
|
|
|
|
</ResponseMetadata>
|
|
|
|
</DeleteStackResponse>
|
|
|
|
"""
|