From ed39c54d41f45def532f9ffbb672b2f2c24a5062 Mon Sep 17 00:00:00 2001 From: Michael Barrett Date: Thu, 6 Aug 2015 16:26:36 -0700 Subject: [PATCH] Local parameters for python This allows the definition of local parameters that are meant to be used by python, not by cloudformation. The parameters are still passed into the blueprint the same way (from the command line, or the config) but they can be used without having to share them with the template. --- stacker/blueprints/base.py | 52 ++++++++++++++++++++++++++- stacker/exceptions.py | 11 +++++- stacker/tests/blueprints/__init__.py | 0 stacker/tests/blueprints/test_base.py | 29 +++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 stacker/tests/blueprints/__init__.py create mode 100644 stacker/tests/blueprints/test_base.py diff --git a/stacker/blueprints/base.py b/stacker/blueprints/base.py index 3636d6302..7a02ac3b7 100644 --- a/stacker/blueprints/base.py +++ b/stacker/blueprints/base.py @@ -3,9 +3,49 @@ from troposphere import Parameter, Template +from ..exceptions import MissingLocalParameterException + logger = logging.getLogger(__name__) +def get_local_parameters(parameter_def, parameters): + """Gets local parameters from parameter list. + + Given a local parameter definition, and a list of parameters, extract the + local parameters, or use a default if provided. If the parameter isn't + present, and there is no default, then throw an exception. + + Args: + parameter_def (dict): A dictionary of expected/allowed parameters + and their defaults. If a parameter is in the list, but does not + have a default, it is considered required. + parameters (dict): A dictionary of parameters to pull local parameters + from. + + Returns: + dict: A dictionary of local parameters. + + Raises: + MissingLocalParameterException: If a parameter is defined in + parameter_def, does not have a default, and does not exist in + parameters. + + """ + local = {} + + for param, attrs in parameter_def.items(): + try: + value = parameters[param] + except KeyError: + try: + value = attrs['default'] + except KeyError: + raise MissingLocalParameterException(param) + local[param] = value + + return local + + class Blueprint(object): """Base implementation for dealing with a troposphere template. @@ -23,6 +63,7 @@ def __init__(self, name, context, mappings=None): self.mappings = mappings self.context = context self.outputs = {} + self.local_parameters = self.get_local_parameters() self.reset_template() @property @@ -38,12 +79,21 @@ def required_parameters(self): required.append((k, v)) return required + def get_local_parameters(self): + local_parameters = getattr(self, 'LOCAL_PARAMETERS', {}) + return get_local_parameters(local_parameters, self.context.parameters) + def setup_parameters(self): t = self.template - parameters = getattr(self, 'PARAMETERS') + # First look for CF_PARAMETERS, then fall back to regular PARAMETERS + # for backwards compatibility. + parameters = getattr(self, 'CF_PARAMETERS', + getattr(self, 'PARAMETERS', {})) + if not parameters: logger.debug("No parameters defined.") return + for param, attrs in parameters.items(): p = Parameter(param, Type=attrs.get('type'), diff --git a/stacker/exceptions.py b/stacker/exceptions.py index 1f83997a9..0f314c3ac 100644 --- a/stacker/exceptions.py +++ b/stacker/exceptions.py @@ -11,13 +11,22 @@ class MissingParameterException(Exception): def __init__(self, parameters, *args, **kwargs): self.parameters = parameters - message = 'Missing required parameters: %s' % ( + message = 'Missing required cloudformation parameters: %s' % ( ', '.join(parameters), ) super(MissingParameterException, self).__init__(message, *args, **kwargs) +class MissingLocalParameterException(Exception): + + def __init__(self, parameter, *args, **kwargs): + self.parameter = parameter + message = 'Missing required local parameter: %s' % parameter + super(MissingLocalParameterException, self).__init__(message, *args, + **kwargs) + + class ParameterDoesNotExist(Exception): def __init__(self, parameter, *args, **kwargs): diff --git a/stacker/tests/blueprints/__init__.py b/stacker/tests/blueprints/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/stacker/tests/blueprints/test_base.py b/stacker/tests/blueprints/test_base.py new file mode 100644 index 000000000..be4ea1568 --- /dev/null +++ b/stacker/tests/blueprints/test_base.py @@ -0,0 +1,29 @@ +import unittest + +from stacker.blueprints.base import get_local_parameters +from stacker.exceptions import MissingLocalParameterException + + +class TestLocalParameters(unittest.TestCase): + def test_default_parameter(self): + parameter_def = {'Param1': {'default': 0}} + parameters = {} + + local = get_local_parameters(parameter_def, parameters) + self.assertEquals(local['Param1'], 0) + + def test_missing_required(self): + parameter_def = {'Param1': {'default': 0}, 'Param2': {}} + parameters = {} + + with self.assertRaises(MissingLocalParameterException) as cm: + get_local_parameters(parameter_def, parameters) + + self.assertEquals('Param2', cm.exception.parameter) + + def test_supplied_parameter(self): + parameter_def = {'Param1': {'default': 0}, 'Param2': {}} + parameters = {'Param1': 1, 'Param2': 2} + + local = get_local_parameters(parameter_def, parameters) + self.assertEquals(parameters, local)