Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Resolves #1388] Handle failures gracefully in stack outputs #1391

Merged
merged 15 commits into from
Feb 8, 2024
36 changes: 30 additions & 6 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 @@ -170,10 +180,24 @@ def resolve(self):

stack_argument = arguments[0]
alex-harvey-z3q marked this conversation as resolved.
Show resolved Hide resolved
if len(arguments) > 1:
alex-harvey-z3q marked this conversation as resolved.
Show resolved Hide resolved
extra_args = arguments[1].split("::", 2)
profile, region, sceptre_role = extra_args + (3 - len(extra_args)) * [None]
try:
extra_args = arguments[1].split("::", 2)
profile, region, sceptre_role = extra_args + (3 - len(extra_args)) * [
None
]
alex-harvey-z3q marked this conversation as resolved.
Show resolved Hide resolved
except ValueError as err:
message = (
"!stack_output_external second arg should be "
"in the format 'profile::region::sceptre_role'"
)
raise SceptreException(message) from err

try:
dependency_stack_name, output_key = stack_argument.split("::")
except ValueError as err:
message = "!stack_output_external arg should match STACK_NAME::OUTPUT_KEY"
raise SceptreException(message) from err
alex-harvey-z3q marked this conversation as resolved.
Show resolved Hide resolved

dependency_stack_name, output_key = stack_argument.split("::")
return self._get_output_value(
dependency_stack_name,
output_key,
Expand Down
24 changes: 24 additions & 0 deletions tests/test_resolvers/test_stack_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

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

from botocore.exceptions import ClientError

from sceptre.connection_manager import ConnectionManager
Expand Down Expand Up @@ -50,6 +52,16 @@ def test_resolver(self, mock_get_output_value):
sceptre_role="dependency_sceptre_role",
)

@patch("sceptre.resolvers.stack_output.StackOutput._get_output_value")
def test_resolver__badly_formatted(self, mock_get_output_value):
stack = MagicMock(spec=Stack)
stack.name = "my/stack"

stack_output_resolver = StackOutput("not_a_valid_stack_output", stack)

with pytest.raises(SceptreException, match="STACK_NAME::OUTPUT_KEY"):
alex-harvey-z3q marked this conversation as resolved.
Show resolved Hide resolved
stack_output_resolver.setup()

@patch("sceptre.resolvers.stack_output.StackOutput._get_output_value")
def test_resolver_with_existing_dependencies(self, mock_get_output_value):
stack = MagicMock(spec=Stack)
Expand Down Expand Up @@ -171,6 +183,18 @@ def test_resolve(self, mock_get_output_value):
)
assert stack.dependencies == []

@patch("sceptre.resolvers.stack_output.StackOutput._get_output_value")
def test_resolve__badly_formatted(self, mock_get_output_value):
stack = MagicMock(spec=Stack)
stack.name = "my/stack"

stack_output_external_resolver = StackOutputExternal(
"not_a_valid_stack_output", stack
)

with pytest.raises(SceptreException, match="STACK_NAME::OUTPUT_KEY"):
stack_output_external_resolver.resolve()

@patch("sceptre.resolvers.stack_output.StackOutputExternal._get_output_value")
def test_resolve_with_args(self, mock_get_output_value):
stack = MagicMock(spec=Stack)
Expand Down