Repository: remind101/stacker
Pull request #83 (2015-08-22 00:58:29)

Allow all parameter properties
Not really sure what kind of tests would be useful here, since the
function is so basic.

Also not entirely sure I like the 'magic' of converting from snake_case
to CamelCase attributes, but that's how it was handled before.


Patch:
diff --git a/conf/empire/empire.yaml b/conf/empire/empire.yaml
index c6e3df4..b958381 100644
--- a/conf/empire/empire.yaml
+++ b/conf/empire/empire.yaml
@@ -71,15 +71,12 @@ stacks:
   - name: vpc
     class_path: stacker.blueprints.vpc.VPC
     parameters:
-      # AZCount is the # of AvailabilityZones to attempt to build in. You
-      # should build in as many as you can afford in order to provide for
-      # better fault tolerance. Note: Since this is all done in a VPC, you
-      # need to find out how many AZs can handle VPC subnets for the
-      # region you are building in. As of this writing, here are the max
-      # allowed AZCount's for each zone:
-      #     us-east-1: 4, us-west-1: 2, us-west-2: 3, eu-west-1: 3
-      # Note: The minimum allowed AZCount is 2.
-      AZCount: ${azcount}
+      # Only build in 4 AZs, can be overridden with -p on the command line
+      # Note: If you want more than 4 AZs you should add more subnets below
+      #       Also you need at least 2 AZs in order to use the DB because
+      #       of the fact that the DB blueprint uses MultiAZ
+      # Note: You will create a NAT instance PER Availability zone.
+      AZCount: 4
       # Enough subnets for 4 AZs
       PublicSubnets: 10.128.0.0/24,10.128.1.0/24,10.128.2.0/24,10.128.3.0/24
       PrivateSubnets: 10.128.8.0/22,10.128.12.0/22,10.128.16.0/22,10.128.20.0/22
diff --git a/conf/empire/example.env b/conf/empire/example.env
index 806a204..86e3961 100644
--- a/conf/empire/example.env
+++ b/conf/empire/example.env
@@ -1,13 +1,3 @@
-# azcount is the # of AvailabilityZones to attempt to build in. You
-# should build in as many as you can afford in order to provide for
-# better fault tolerance. Note: Since this is all done in a VPC, you
-# need to find out how many AZs can handle VPC subnets for the
-# region you are building in. As of this writing, here are the max
-# allowed azcount's for each zone:
-#     us-east-1: 4, us-west-1: 2, us-west-2: 3, eu-west-1: 3
-# Note: The minimum allowed azcount is 2.
-azcount: 2
-
 # An external domain where empire.<domain> CNAME will be created
 external_domain:
 ssh_key_name:
diff --git a/stacker/blueprints/base.py b/stacker/blueprints/base.py
index 1b7cddf..40a1d56 100644
--- a/stacker/blueprints/base.py
+++ b/stacker/blueprints/base.py
@@ -41,18 +41,42 @@ def get_local_parameters(parameter_def, parameters):
                 value = attrs['default']
             except KeyError:
                 raise MissingLocalParameterException(param)
-
-        _type = attrs.get('type')
-        if _type:
-            try:
-                value = _type(value)
-            except ValueError:
-                raise ValueError("Local parameter %s must be %s.", param,
-                                 _type)
         local[param] = value
 
     return local
 
+PARAMETER_PROPERTIES = {
+    'default': 'Default',
+    'description': 'Description',
+    'no_echo': 'NoEcho',
+    'allowed_values': 'AllowedValues',
+    'allowed_pattern': 'AllowedPattern',
+    'max_length': 'MaxLength',
+    'min_length': 'MinLength',
+    'max_value': 'MaxValue',
+    'min_value': 'MinValue',
+    'constaint_description': 'ConstraintDescription'
+}
+
+
+def build_parameter(name, properties):
+    """Builds a troposphere Parameter with the given properties.
+
+    Args:
+        name (string): The name of the parameter.
+        properties (dict): Contains the properties that will be applied to the
+            parameter. See:
+            http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
+
+    Returns:
+        :class:`troposphere.Parameter`: The created parameter object.
+    """
+    p = Parameter(name, Type=properties.get('type'))
+    for name, attr in PARAMETER_PROPERTIES.items():
+        if name in properties:
+            setattr(p, attr, properties[name])
+    return p
+
 
 class Blueprint(object):
     """Base implementation for dealing with a troposphere template.
@@ -103,11 +127,7 @@ class Blueprint(object):
             return
 
         for param, attrs in parameters.items():
-            p = Parameter(param,
-                          Type=attrs.get('type'),
-                          Description=attrs.get('description', ''))
-            if 'default' in attrs:
-                p.Default = attrs['default']
+            p = build_parameter(param, attrs)
             t.add_parameter(p)
 
     def import_mappings(self):
diff --git a/stacker/blueprints/vpc.py b/stacker/blueprints/vpc.py
index ece6cf8..4f3b311 100644
--- a/stacker/blueprints/vpc.py
+++ b/stacker/blueprints/vpc.py
@@ -22,14 +22,13 @@ NAT_SG = "NATSG"
 
 
 class VPC(Blueprint):
-    LOCAL_PARAMETERS = {
-        "AZCount":  {
-            "type": int,
-            "default": 2,
-        }
-    }
-
     PARAMETERS = {
+        "AZCount": {
+            "type": "Number",
+            "default": "2",
+            "description": "The number of AZs to build the VPC in. NOTE: "
+                           "this is used by stacker, not by cloudformation "
+                           "directly."},
         "PrivateSubnets": {
             "type": "CommaDelimitedList",
             "description": "Comma separated list of subnets to use for "
@@ -172,7 +171,7 @@ class VPC(Blueprint):
         subnets = {'public': [], 'private': []}
         net_types = subnets.keys()
         zones = []
-        for i in range(self.local_parameters["AZCount"]):
+        for i in range(int(self.context.parameters["AZCount"])):
             az = Select(i, GetAZs(""))
             zones.append(az)
             name_suffix = i


Test patch:
diff --git a/stacker/tests/blueprints/test_base.py b/stacker/tests/blueprints/test_base.py
index be4ea15..446474a 100644
--- a/stacker/tests/blueprints/test_base.py
+++ b/stacker/tests/blueprints/test_base.py
@@ -1,6 +1,6 @@
 import unittest
 
-from stacker.blueprints.base import get_local_parameters
+from stacker.blueprints.base import get_local_parameters, build_parameter
 from stacker.exceptions import MissingLocalParameterException
 
 
@@ -27,3 +27,10 @@ class TestLocalParameters(unittest.TestCase):
 
         local = get_local_parameters(parameter_def, parameters)
         self.assertEquals(parameters, local)
+
+
+class TestBuildParameter(unittest.TestCase):
+    def test_base_parameter(self):
+        p = build_parameter("BasicParam", {'type': 'String'})
+        p.validate()
+        self.assertEquals(p.Type, 'String')