diff --git a/sceptre/config/reader.py b/sceptre/config/reader.py index fc7421772..0245bc9a4 100644 --- a/sceptre/config/reader.py +++ b/sceptre/config/reader.py @@ -489,6 +489,14 @@ def _render(self, directory_path, basename, stack_group_config): f"{Path(directory_path, basename).as_posix()} - {err}" ) from err + # Reset the template cache to avoid leakage between StackGroups (#937) + template_vars = {"var": self.templating_vars["var"]} + if "stack_group_config" in self.templating_vars: + template_vars["stack_group_config"] = self.templating_vars[ + "stack_group_config" + ] + self.templating_vars = template_vars + self.templating_vars.update(stack_group_config) try: diff --git a/tests/test_config_reader.py b/tests/test_config_reader.py index 1bd9f7eaf..ddd403f8c 100644 --- a/tests/test_config_reader.py +++ b/tests/test_config_reader.py @@ -621,6 +621,65 @@ def test_render__existing_config_file__returns_dict(self): assert result == {"key": "value"} + def test_render__existing_config_file__no_leak_between_multiple_stack_group_configs( + self, + ): + """A test for bug #937""" + with self.runner.isolated_filesystem(): + project_path = os.path.abspath("./example") + + self.context.project_path = project_path + + config_dir = os.path.join(project_path, "config") + + stack_group_config_1 = {"j2_environment": {}, "param1": "value1"} + stack_group_config_2 = {"j2_environment": {}, "param2": "value2"} + + directory_path_1 = os.path.join(config_dir, "dir1") + directory_path_2 = os.path.join(config_dir, "dir2") + os.makedirs(directory_path_1) + os.makedirs(directory_path_2) + + config_reader = ConfigReader(self.context) + + # First config file + basename_1 = "file1.yaml" + test_config_path_1 = os.path.join(directory_path_1, basename_1) + test_config_content_1 = "var: initial_value\nparam1: value1" + with open(test_config_path_1, "w") as file: + file.write(test_config_content_1) + + # Second config file + basename_2 = "file2.yaml" + test_config_path_2 = os.path.join(directory_path_2, basename_2) + test_config_content_2 = "var: initial_value\nparam2: value2" + with open(test_config_path_2, "w") as file: + file.write(test_config_content_2) + + config_reader.full_config_path = project_path + config_reader.templating_vars = {"var": "initial_value"} + + # Run _render for the first stack group + result_1 = config_reader._render( + "config/dir1", basename_1, stack_group_config_1 + ) + expected_result_1 = {"var": "initial_value", "param1": "value1"} + assert expected_result_1 == result_1 + + # Run _render for the second stack group + result_2 = config_reader._render( + "config/dir2", basename_2, stack_group_config_2 + ) + expected_result_2 = {"var": "initial_value", "param2": "value2"} + assert expected_result_2 == result_2 + + # Ensure the templating_vars is not leaking + assert { + "var": "initial_value", + "j2_environment": {}, + "param2": "value2", + } == config_reader.templating_vars + def test_render__invalid_jinja_template__raises_and_creates_debug_file(self): with self.runner.isolated_filesystem(): project_path = os.path.abspath("./example")