Allow actual use of cloudformation input parameters.
This commit is contained in:
parent
56007660d8
commit
1e4df18c42
7 changed files with 114 additions and 22 deletions
|
|
@ -1,5 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
from boto.exception import BotoServerError
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from jinja2 import Template
|
||||
|
||||
|
||||
|
|
@ -8,17 +8,31 @@ class UnformattedGetAttTemplateException(Exception):
|
|||
status_code = 400
|
||||
|
||||
|
||||
class ValidationError(BotoServerError):
|
||||
class ValidationError(BadRequest):
|
||||
def __init__(self, name_or_id):
|
||||
template = Template(STACK_DOES_NOT_EXIST_RESPONSE)
|
||||
super(ValidationError, self).__init__(status=400, reason='Bad Request',
|
||||
body=template.render(name_or_id=name_or_id))
|
||||
template = Template(ERROR_RESPONSE)
|
||||
super(ValidationError, self).__init__()
|
||||
self.description = template.render(
|
||||
code="ValidationError",
|
||||
messgae="Stack:{0} does not exist".format(name_or_id),
|
||||
)
|
||||
|
||||
STACK_DOES_NOT_EXIST_RESPONSE = """<ErrorResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
|
||||
class MissingParameterError(BadRequest):
|
||||
def __init__(self, parameter_name):
|
||||
template = Template(ERROR_RESPONSE)
|
||||
super(MissingParameterError, self).__init__()
|
||||
self.description = template.render(
|
||||
code="Missing Parameter",
|
||||
messgae="Missing parameter {0}".format(parameter_name),
|
||||
)
|
||||
|
||||
|
||||
ERROR_RESPONSE = """<ErrorResponse xmlns="http://cloudformation.amazonaws.com/doc/2010-05-15/">
|
||||
<Error>
|
||||
<Type>Sender</Type>
|
||||
<Code>ValidationError</Code>
|
||||
<Message>Stack:{{ name_or_id }} does not exist</Message>
|
||||
<Code>{{ code }}</Code>
|
||||
<Message>{{ message }}</Message>
|
||||
</Error>
|
||||
<RequestId>cf4c737e-5ae2-11e4-a7c9-ad44eEXAMPLE</RequestId>
|
||||
</ErrorResponse>
|
||||
|
|
|
|||
|
|
@ -10,23 +10,28 @@ from .exceptions import ValidationError
|
|||
|
||||
|
||||
class FakeStack(object):
|
||||
def __init__(self, stack_id, name, template, region_name, notification_arns=None):
|
||||
def __init__(self, stack_id, name, template, parameters, region_name, notification_arns=None):
|
||||
self.stack_id = stack_id
|
||||
self.name = name
|
||||
self.template = template
|
||||
self.parameters = parameters
|
||||
self.region_name = region_name
|
||||
self.notification_arns = notification_arns if notification_arns else []
|
||||
self.template = template
|
||||
self.status = 'CREATE_COMPLETE'
|
||||
|
||||
template_dict = json.loads(self.template)
|
||||
self.description = template_dict.get('Description')
|
||||
|
||||
self.resource_map = ResourceMap(stack_id, name, region_name, template_dict)
|
||||
self.resource_map = ResourceMap(stack_id, name, parameters, region_name, template_dict)
|
||||
self.resource_map.create()
|
||||
|
||||
self.output_map = OutputMap(self.resource_map, template_dict)
|
||||
self.output_map.create()
|
||||
|
||||
@property
|
||||
def stack_parameters(self):
|
||||
return self.resource_map.resolved_parameters
|
||||
|
||||
@property
|
||||
def stack_resources(self):
|
||||
return self.resource_map.values()
|
||||
|
|
@ -42,12 +47,13 @@ class CloudFormationBackend(BaseBackend):
|
|||
self.stacks = {}
|
||||
self.deleted_stacks = {}
|
||||
|
||||
def create_stack(self, name, template, region_name, notification_arns=None):
|
||||
def create_stack(self, name, template, parameters, region_name, notification_arns=None):
|
||||
stack_id = generate_stack_id(name)
|
||||
new_stack = FakeStack(
|
||||
stack_id=stack_id,
|
||||
name=name,
|
||||
template=template,
|
||||
parameters=parameters,
|
||||
region_name=region_name,
|
||||
notification_arns=notification_arns,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from moto.elb import models as elb_models
|
|||
from moto.iam import models as iam_models
|
||||
from moto.sqs import models as sqs_models
|
||||
from .utils import random_suffix
|
||||
from .exceptions import UnformattedGetAttTemplateException
|
||||
from .exceptions import MissingParameterError, UnformattedGetAttTemplateException
|
||||
from boto.cloudformation.stack import Output
|
||||
from boto.exception import BotoServerError
|
||||
|
||||
|
|
@ -164,10 +164,12 @@ class ResourceMap(collections.Mapping):
|
|||
each resources is passed this lazy map that it can grab dependencies from.
|
||||
"""
|
||||
|
||||
def __init__(self, stack_id, stack_name, region_name, template):
|
||||
def __init__(self, stack_id, stack_name, parameters, region_name, template):
|
||||
self._template = template
|
||||
self._resource_json_map = template['Resources']
|
||||
self._region_name = region_name
|
||||
self.input_parameters = parameters
|
||||
self.resolved_parameters = {}
|
||||
|
||||
# Create the default resources
|
||||
self._parsed_resources = {
|
||||
|
|
@ -199,10 +201,22 @@ class ResourceMap(collections.Mapping):
|
|||
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] = ""
|
||||
parameter_slots = self._template.get('Parameters', {})
|
||||
for parameter_name, parameter in parameter_slots.items():
|
||||
# Set the default values.
|
||||
self.resolved_parameters[parameter_name] = parameter.get('Default')
|
||||
|
||||
# Set any input parameters that were passed
|
||||
for key, value in self.input_parameters.items():
|
||||
if key in self.resolved_parameters:
|
||||
self.resolved_parameters[key] = value
|
||||
|
||||
# Check if there are any non-default params that were not passed input params
|
||||
for key, value in self.resolved_parameters.items():
|
||||
if value is None:
|
||||
raise MissingParameterError(key)
|
||||
|
||||
self._parsed_resources.update(self.resolved_parameters)
|
||||
|
||||
def create(self):
|
||||
self.load_parameters()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,14 @@ class CloudFormationResponse(BaseResponse):
|
|||
stack_name = self._get_param('StackName')
|
||||
stack_body = self._get_param('TemplateBody')
|
||||
template_url = self._get_param('TemplateURL')
|
||||
parameters_list = self._get_list_prefix("Parameters.member")
|
||||
|
||||
# Hack dict-comprehension
|
||||
parameters = dict([
|
||||
(parameter['parameter_key'], parameter['parameter_value'])
|
||||
for parameter
|
||||
in parameters_list
|
||||
])
|
||||
if template_url:
|
||||
stack_body = self._get_stack_from_s3_url(template_url)
|
||||
stack_notification_arns = self._get_multi_param('NotificationARNs.member')
|
||||
|
|
@ -33,6 +41,7 @@ class CloudFormationResponse(BaseResponse):
|
|||
stack = self.cloudformation_backend.create_stack(
|
||||
name=stack_name,
|
||||
template=stack_body,
|
||||
parameters=parameters,
|
||||
region_name=self.region,
|
||||
notification_arns=stack_notification_arns
|
||||
)
|
||||
|
|
@ -126,6 +135,14 @@ DESCRIBE_STACKS_TEMPLATE = """<DescribeStacksResult>
|
|||
</member>
|
||||
{% endfor %}
|
||||
</Outputs>
|
||||
<Parameters>
|
||||
{% for param_name, param_value in stack.stack_parameters.items() %}
|
||||
<member>
|
||||
<ParameterKey>{{ param_name }}</ParameterKey>
|
||||
<ParameterValue>{{ param_value }}</ParameterValue>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Parameters>
|
||||
</member>
|
||||
{% endfor %}
|
||||
</Stacks>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue