From 93cb49db05fb41b7d75c34d15deebd24de0cab9f Mon Sep 17 00:00:00 2001 From: Cole Bosmann Date: Tue, 9 Jan 2018 10:51:29 -0800 Subject: [PATCH] [Resolve #288] Fix how user variables are accessed in config templating (#290) Now variables supplied using the --var and --var-file command options are available in the jinja templating as `{{ var.* }}`. --- sceptre/cli/__init__.py | 19 ++++++++++--------- sceptre/cli/helpers.py | 6 ++++-- tests/test_cli.py | 39 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/sceptre/cli/__init__.py b/sceptre/cli/__init__.py index 10deb6843..770480ddf 100644 --- a/sceptre/cli/__init__.py +++ b/sceptre/cli/__init__.py @@ -56,37 +56,38 @@ def cli( # Enable deprecation warnings warnings.simplefilter("always", DeprecationWarning) ctx.obj = { - "options": {}, + "user_variables": {}, "output_format": output, "no_colour": no_colour, "sceptre_dir": directory if directory else os.getcwd() } - user_variables = {} if var_file: for fh in var_file: parsed = yaml.safe_load(fh.read()) - # intersection - overloaded_keys = set(user_variables.keys()) & set(parsed.keys()) + ctx.obj["user_variables"].update(parsed) + + # the rest of this block is for debug purposes only + existing_keys = set(ctx.obj["user_variables"].keys()) + new_keys = set(parsed.keys()) + overloaded_keys = existing_keys & new_keys # intersection if overloaded_keys: logger.debug( "Duplicate variables encountered: {0}. " "Using values from: {1}." .format(", ".join(overloaded_keys), fh.name) ) - user_variables.update(parsed) + if var: # --var options overwrite --var-file options for variable in var: variable_key, variable_value = variable.split("=") - if variable_key in user_variables: + if variable_key in ctx.obj["user_variables"]: logger.debug( "Duplicate variable encountered: {0}. " "Using value from --var option." .format(variable_key) ) - user_variables.update({variable_key: variable_value}) - if user_variables: - ctx.obj["options"]["user_variables"] = user_variables + ctx.obj["user_variables"].update({variable_key: variable_value}) cli.add_command(init_group) diff --git a/sceptre/cli/helpers.py b/sceptre/cli/helpers.py index 984dfe213..9629597a2 100644 --- a/sceptre/cli/helpers.py +++ b/sceptre/cli/helpers.py @@ -91,7 +91,7 @@ def write(var, output_format="str", no_colour=True): def get_stack_or_env(ctx, path): """ - Parses the path to generate relevant Envrionment and Stack object. + Parses the path to generate relevant Environment and Stack object. :param ctx: Cli context. :type ctx: click.Context @@ -101,7 +101,9 @@ def get_stack_or_env(ctx, path): stack = None env = None - config_reader = ConfigReader(ctx.obj["sceptre_dir"], ctx.obj["options"]) + config_reader = ConfigReader( + ctx.obj["sceptre_dir"], ctx.obj["user_variables"] + ) if os.path.splitext(path)[1]: stack = config_reader.construct_stack(path) diff --git a/tests/test_cli.py b/tests/test_cli.py index 75dfee0ba..f83e212f7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -17,6 +17,7 @@ from sceptre.stack_status import StackStatus, StackChangeSetStatus from sceptre.cli.helpers import setup_logging, write, ColouredFormatter from sceptre.cli.helpers import CustomJsonEncoder, catch_exceptions +from sceptre.cli.helpers import get_stack_or_env from botocore.exceptions import ClientError from sceptre.exceptions import SceptreException @@ -103,7 +104,7 @@ def test_user_variables(self, command, files, output): @cli.command() @click.pass_context def noop(ctx): - click.echo(yaml.safe_dump(ctx.obj["options"]["user_variables"])) + click.echo(yaml.safe_dump(ctx.obj["user_variables"])) self.patcher_getcwd.stop() with self.runner.isolated_filesystem(): @@ -732,3 +733,39 @@ def test_CustomJsonEncoder_with_non_json_serialisable_object(self): encoder = CustomJsonEncoder() response = encoder.encode(datetime.datetime(2016, 5, 3)) assert response == '"2016-05-03 00:00:00"' + + def test_get_stack_or_env_with_stack(self): + ctx = MagicMock(obj={ + "sceptre_dir": sentinel.sceptre_dir, + "user_variables": sentinel.user_variables + }) + stack, env = get_stack_or_env(ctx, "stack.yaml") + self.mock_ConfigReader.assert_called_once_with( + sentinel.sceptre_dir, sentinel.user_variables + ) + assert isinstance(stack, Stack) + assert env is None + + def test_get_stack_or_env_with_nested_stack(self): + ctx = MagicMock(obj={ + "sceptre_dir": sentinel.sceptre_dir, + "user_variables": sentinel.user_variables + }) + stack, env = get_stack_or_env(ctx, "environment/dir/stack.yaml") + self.mock_ConfigReader.assert_called_once_with( + sentinel.sceptre_dir, sentinel.user_variables + ) + assert isinstance(stack, Stack) + assert env is None + + def test_get_stack_or_env_with_env(self): + ctx = MagicMock(obj={ + "sceptre_dir": sentinel.sceptre_dir, + "user_variables": sentinel.user_variables + }) + stack, env = get_stack_or_env(ctx, "environment/dir") + self.mock_ConfigReader.assert_called_once_with( + sentinel.sceptre_dir, sentinel.user_variables + ) + assert isinstance(env, Environment) + assert stack is None