Skip to content

Commit

Permalink
[Resolves #1388] Handle failures gracefully in stack outputs (#1391)
Browse files Browse the repository at this point in the history
This adds exception handling in the case of failures to parse the given arguments to `!stack_output` and `!stack_output_external`.
  • Loading branch information
alex-harvey-z3q authored Feb 8, 2024
1 parent 27dd70b commit a9c83b0
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 16 deletions.
60 changes: 46 additions & 14 deletions sceptre/resolvers/stack_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@

from botocore.exceptions import ClientError

from sceptre.exceptions import DependencyStackMissingOutputError, StackDoesNotExistError
from sceptre.exceptions import (
DependencyStackMissingOutputError,
StackDoesNotExistError,
SceptreException,
)

from sceptre.helpers import normalise_path, sceptreise_path
from sceptre.resolvers import Resolver

Expand Down Expand Up @@ -108,7 +113,13 @@ def setup(self):
"""
Adds dependency to a Stack.
"""
dep_stack_name, self.output_key = self.argument.split("::")
try:
dep_stack_name, self.output_key = self.argument.split("::")
except ValueError as err:
raise SceptreException(
"!stack_output arg should match STACK_NAME::OUTPUT_KEY"
) from err

self.dependency_stack_name = sceptreise_path(normalise_path(dep_stack_name))
self.stack.dependencies.append(self.dependency_stack_name)

Expand All @@ -120,7 +131,6 @@ def resolve(self):
:rtype: str
"""
self.logger.debug("Resolving Stack output: {0}".format(self.argument))

friendly_stack_name = self.dependency_stack_name.replace(TEMPLATE_EXTENSION, "")

stack = next(
Expand Down Expand Up @@ -163,21 +173,43 @@ def resolve(self):
"""
self.logger.debug("Resolving external Stack output: {0}".format(self.argument))

profile = None
region = None
sceptre_role = None
arguments = shlex.split(self.argument)

if not arguments:
message = "!stack_output_external requires at least one argument"
raise SceptreException(message)

stack_argument = arguments[0]
stack_args = iter(stack_argument.split("::"))

try:
dependency_stack_name = next(stack_args)
output_key = next(stack_args)

except StopIteration as err:
message = "!stack_output_external arg should match STACK_NAME::OUTPUT_KEY"
raise SceptreException(message) from err

profile = region = sceptre_role = None

if len(arguments) > 1:
extra_args = arguments[1].split("::", 2)
profile, region, sceptre_role = extra_args + (3 - len(extra_args)) * [None]
extra_args = iter(arguments[1].split("::"))

profile = next(extra_args, None)
region = next(extra_args, None)
sceptre_role = next(extra_args, None)

try:
next(extra_args)
message = (
"!stack_output_external second arg should be "
"in the format 'PROFILE[::REGION[::SCEPTRE_ROLE]]'"
)
raise SceptreException(message)

except StopIteration:
pass

dependency_stack_name, output_key = stack_argument.split("::")
return self._get_output_value(
dependency_stack_name,
output_key,
profile or None,
region or None,
sceptre_role or None,
dependency_stack_name, output_key, profile, region, sceptre_role
)
11 changes: 9 additions & 2 deletions tests/test_resolvers/test_stack_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from sceptre.exceptions import DependencyStackMissingOutputError
from sceptre.exceptions import StackDoesNotExistError
from sceptre.exceptions import SceptreException

from botocore.exceptions import ClientError

Expand Down Expand Up @@ -58,7 +59,10 @@ def test_resolver__badly_formatted(self, mock_get_output_value):

stack_output_resolver = StackOutput("not_a_valid_stack_output", stack)

with pytest.raises(ValueError, match="not enough values to unpack"):
with pytest.raises(
SceptreException,
match="!stack_output arg should match STACK_NAME::OUTPUT_KEY",
):
stack_output_resolver.setup()

@patch("sceptre.resolvers.stack_output.StackOutput._get_output_value")
Expand Down Expand Up @@ -191,7 +195,10 @@ def test_resolve__badly_formatted(self, mock_get_output_value):
"not_a_valid_stack_output", stack
)

with pytest.raises(ValueError, match="not enough values to unpack"):
with pytest.raises(
SceptreException,
match="!stack_output_external arg should match STACK_NAME::OUTPUT_KEY",
):
stack_output_external_resolver.resolve()

@patch("sceptre.resolvers.stack_output.StackOutputExternal._get_output_value")
Expand Down

0 comments on commit a9c83b0

Please sign in to comment.