diff --git a/stacker/blueprints/base.py b/stacker/blueprints/base.py index c746b3a5b..58463aac5 100644 --- a/stacker/blueprints/base.py +++ b/stacker/blueprints/base.py @@ -40,6 +40,20 @@ } +def normalize_defined_variables(variables): + """Looks for any variable definitions that are a "str", and assumes that + they're the name of a CloudFormation parameter type, in which case the type + is converted to a CFNType. + + Returns: + dict: normalized variable definitions + """ + for k, v in variables.iteritems(): + if isinstance(v["type"], str): + v["type"] = CFNType.from_name(v["type"]) + return variables + + class CFNParameter(object): def __init__(self, name, value): @@ -339,7 +353,8 @@ def get_parameter_definitions(self): """ output = {} - for var_name, attrs in self.defined_variables().iteritems(): + defined_variables = self._normalized_defined_variables() + for var_name, attrs in defined_variables.iteritems(): var_type = attrs.get("type") if isinstance(var_type, CFNType): cfn_attrs = copy.deepcopy(attrs) @@ -391,6 +406,9 @@ def defined_variables(self): """ return copy.deepcopy(getattr(self, "VARIABLES", {})) + def _normalized_defined_variables(self): + return normalize_defined_variables(self.defined_variables()) + def get_variables(self): """Return a dictionary of variables available to the template. @@ -435,7 +453,7 @@ def resolve_variables(self, provided_variables): """ self.resolved_variables = {} - defined_variables = self.defined_variables() + defined_variables = self._normalized_defined_variables() variable_dict = dict((var.name, var) for var in provided_variables) for var_name, var_def in defined_variables.iteritems(): value = resolve_variable( diff --git a/stacker/blueprints/variables/types.py b/stacker/blueprints/variables/types.py index dd7ff34a8..d7cf65094 100644 --- a/stacker/blueprints/variables/types.py +++ b/stacker/blueprints/variables/types.py @@ -120,6 +120,10 @@ def __init__(self, parameter_type): """ self.parameter_type = parameter_type + @staticmethod + def from_name(name): + return CFNType(name) + CFNString = CFNType("String") CFNNumber = CFNType("Number") diff --git a/stacker/tests/blueprints/test_base.py b/stacker/tests/blueprints/test_base.py index 83639e209..5d4f339c0 100644 --- a/stacker/tests/blueprints/test_base.py +++ b/stacker/tests/blueprints/test_base.py @@ -66,7 +66,8 @@ class TestBlueprint(Blueprint): "Param2": {"type": CFNNumber}, "Param3": {"type": CFNCommaDelimitedList}, "Param4": {"default": "foo", "type": str}, - "Param5": {"default": 5, "type": int} + "Param5": {"default": 5, "type": int}, + "Param6": {"type": "AWS::EC2::Image::Id"} } def create_template(self): @@ -86,6 +87,9 @@ def create_template(self): }, "Param3": { "Type": "CommaDelimitedList" + }, + "Param6": { + "Type": "AWS::EC2::Image::Id" } }, "Resources": {} diff --git a/stacker/tests/fixtures/mock_blueprints.py b/stacker/tests/fixtures/mock_blueprints.py index 16aea7167..a0c772de1 100644 --- a/stacker/tests/fixtures/mock_blueprints.py +++ b/stacker/tests/fixtures/mock_blueprints.py @@ -28,12 +28,12 @@ class FunctionalTests(Blueprint): VARIABLES = { "StackerNamespace": { - "type": CFNString, + "type": "String", "description": "The stacker namespace that the tests will use. " "Access to cloudformation will be restricted to " "only allow access to stacks with this prefix."}, "StackerBucket": { - "type": CFNString, + "type": "String", "description": "The name of the bucket that the tests will use " "for uploading templates."} } @@ -172,38 +172,38 @@ class VPC(Blueprint): "default": 2, }, "PrivateSubnets": { - "type": CFNCommaDelimitedList, + "type": "CommaDelimitedList", "description": "Comma separated list of subnets to use for " "non-public hosts. NOTE: Must have as many subnets " "as AZCount"}, "PublicSubnets": { - "type": CFNCommaDelimitedList, + "type": "CommaDelimitedList", "description": "Comma separated list of subnets to use for " "public hosts. NOTE: Must have as many subnets " "as AZCount"}, "InstanceType": { - "type": CFNString, + "type": "String", "description": "NAT EC2 instance type.", "default": "m3.medium"}, "BaseDomain": { - "type": CFNString, + "type": "String", "default": "", "description": "Base domain for the stack."}, "InternalDomain": { - "type": CFNString, + "type": "String", "default": "", "description": "Internal domain name, if you have one."}, "CidrBlock": { - "type": CFNString, + "type": "String", "description": "Base CIDR block for subnets.", "default": "10.128.0.0/16"}, "ImageName": { - "type": CFNString, + "type": "String", "description": "The image name to use from the AMIMap (usually " "found in the config file.)", "default": "NAT"}, "UseNatGateway": { - "type": CFNString, + "type": "String", "allowed_values": ["true", "false"], "description": "If set to true, will configure a NAT Gateway" "instead of NAT instances.", @@ -217,7 +217,7 @@ def create_template(self): class DiffTester(Blueprint): VARIABLES = { "InstanceType": { - "type": CFNString, + "type": "String", "description": "NAT EC2 instance type.", "default": "m3.medium"}, "WaitConditionCount": {