Skip to content

Commit

Permalink
Merge branch 'master' into ah/harkylton/fix-update-changesets
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-harvey-z3q committed Aug 3, 2024
2 parents 79570a5 + deef08e commit e44eed7
Show file tree
Hide file tree
Showing 20 changed files with 242 additions and 322 deletions.
18 changes: 0 additions & 18 deletions .github/workflows/comment-integration-tests.yaml

This file was deleted.

4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ repos:
hooks:
- id: yamllint
- repo: https://github.com/awslabs/cfn-python-lint
rev: v1.2.5.a11
rev: v1.8.2
hooks:
- id: cfn-python-lint
args:
Expand Down Expand Up @@ -47,7 +47,7 @@ repos:
language_version: python3.10
args: ['--check']
- repo: https://github.com/sirosen/check-jsonschema
rev: 0.28.5
rev: 0.29.1
hooks:
- id: check-github-workflows
- id: check-github-actions
3 changes: 1 addition & 2 deletions integration-tests/features/create-change-set.feature
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ Feature: Create change set
Given stack "1/A" does not exist
and the template for stack "1/A" is "valid_template.json"
When the user creates change set "A" for stack "1/A"
Then a "ClientError" is raised
and the user is told "stack does not exist"
Then stack "1/A" has change set "A" in "CREATE_COMPLETE" state

Scenario: create new change set with updated template and ignore dependencies
Given stack "1/A" exists in "CREATE_COMPLETE" state
Expand Down
3 changes: 1 addition & 2 deletions integration-tests/features/describe-change-set.feature
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ Feature: Describe change sets
Given stack "1/A" exists in "CREATE_COMPLETE" state
and stack "1/A" has no change sets
When the user describes change set "A" for stack "1/A"
Then a "ClientError" is raised
and the user is told "change set does not exist"
Then the user is told "Failed describing Change Set"

Scenario: describe a change set that exists with ignore dependencies
Given stack "1/A" exists in "CREATE_COMPLETE" state
Expand Down
23 changes: 0 additions & 23 deletions integration-tests/features/describe-stack-group-resources.feature

This file was deleted.

25 changes: 0 additions & 25 deletions integration-tests/features/describe-stack-group.feature

This file was deleted.

11 changes: 0 additions & 11 deletions integration-tests/features/describe-stack-resources.feature

This file was deleted.

3 changes: 1 addition & 2 deletions integration-tests/features/execute-change-set.feature
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ Feature: Execute change set
Given stack "1/A" exists in "CREATE_COMPLETE" state
And stack "1/A" does not have change set "A"
When the user executes change set "A" for stack "1/A"
Then a "ClientError" is raised
And the user is told "change set does not exist"
Then the user is told "change set does not exist"

Scenario: execute a change set that exists with ignore dependencies
Given stack "1/A" exists in "CREATE_COMPLETE" state
Expand Down
5 changes: 3 additions & 2 deletions integration-tests/steps/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ def step_impl(context, message):
msg = context.error.response["Error"]["Message"]
assert msg.endswith("does not exist")
elif message == "change set does not exist":
msg = context.error.response["Error"]["Message"]
assert msg.endswith("does not exist")
assert context.log_capture.find_event("does not exist")
elif message == "the template is valid":
for stack, status in context.response.items():
assert status["ResponseMetadata"]["HTTPStatusCode"] == 200
elif message == "the template is malformed":
msg = context.error.response["Error"]["Message"]
assert msg.startswith("Template format error")
elif message == "Failed describing Change Set":
assert context.log_capture.find_event(message)
else:
raise Exception("Step has incorrect message")

Expand Down
122 changes: 0 additions & 122 deletions integration-tests/steps/stack_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,68 +97,6 @@ def step_impl(context, stack_group_name):
sceptre_plan.delete()


@when('the user describes stack_group "{stack_group_name}"')
def step_impl(context, stack_group_name):
sceptre_context = SceptreContext(
command_path=stack_group_name, project_path=context.sceptre_dir
)

sceptre_plan = SceptrePlan(sceptre_context)
responses = sceptre_plan.describe()

stack_names = get_full_stack_names(context, stack_group_name)
cfn_stacks = {}

for response in responses.values():
if response is None:
continue
for stack in response["Stacks"]:
cfn_stacks[stack["StackName"]] = stack["StackStatus"]

context.response = [
{short_name: cfn_stacks[full_name]}
for short_name, full_name in stack_names.items()
if cfn_stacks.get(full_name)
]


@when('the user describes stack_group "{stack_group_name}" with ignore dependencies')
def step_impl(context, stack_group_name):
sceptre_context = SceptreContext(
command_path=stack_group_name,
project_path=context.sceptre_dir,
ignore_dependencies=True,
)

sceptre_plan = SceptrePlan(sceptre_context)
responses = sceptre_plan.describe()

stack_names = get_full_stack_names(context, stack_group_name)
cfn_stacks = {}

for response in responses.values():
if response is None:
continue
for stack in response["Stacks"]:
cfn_stacks[stack["StackName"]] = stack["StackStatus"]

context.response = [
{short_name: cfn_stacks[full_name]}
for short_name, full_name in stack_names.items()
if cfn_stacks.get(full_name)
]


@when('the user describes resources in stack_group "{stack_group_name}"')
def step_impl(context, stack_group_name):
sceptre_context = SceptreContext(
command_path=stack_group_name, project_path=context.sceptre_dir
)

sceptre_plan = SceptrePlan(sceptre_context)
context.response = sceptre_plan.describe_resources().values()


@when(
'the user describes resources in stack_group "{stack_group_name}" with ignore dependencies'
)
Expand Down Expand Up @@ -196,21 +134,6 @@ def step_impl(context, stack_group_name):
check_stack_status(context, full_stack_names, None)


@then('all stacks in stack_group "{stack_group_name}" are described as "{status}"')
def step_impl(context, stack_group_name, status):
stacks_names = get_stack_names(context, stack_group_name)
expected_response = [{stack_name: status} for stack_name in stacks_names]
for response in context.response:
assert response in expected_response


@then("no resources are described")
def step_impl(context):
for stack_resources in context.response:
stack_name = next(iter(stack_resources))
assert stack_resources == {stack_name: []}


@then('stack "{stack_name}" is described as "{status}"')
def step_impl(context, stack_name, status):
response = next(
Expand All @@ -221,51 +144,6 @@ def step_impl(context, stack_name, status):
assert response[stack_name] == status


@then('only all resources in stack_group "{stack_group_name}" are described')
def step_impl(context, stack_group_name):
stacks_names = get_full_stack_names(context, stack_group_name)
expected_resources = {}
sceptre_response = []
for stack_resources in context.response:
for resource in stack_resources.values():
sceptre_response.append(resource[0]["PhysicalResourceId"])

for short_name, full_name in stacks_names.items():
time.sleep(1)
response = retry_boto_call(
context.client.describe_stack_resources, StackName=full_name
)
expected_resources[short_name] = response["StackResources"]

for short_name, resources in expected_resources.items():
for resource in resources:
sceptre_response.remove(resource["PhysicalResourceId"])

assert sceptre_response == []


@then('only resources in stack "{stack_name}" are described')
def step_impl(context, stack_name):
expected_resources = {}
sceptre_response = []
for stack_resources in context.response:
for resource in stack_resources.values():
if resource:
sceptre_response.append(resource[0].get("PhysicalResourceId"))

response = retry_boto_call(
context.client.describe_stack_resources,
StackName=get_cloudformation_stack_name(context, stack_name),
)
expected_resources[stack_name] = response["StackResources"]

for short_name, resources in expected_resources.items():
for resource in resources:
sceptre_response.remove(resource["PhysicalResourceId"])

assert sceptre_response == []


@then('that stack "{first_stack}" was created before "{second_stack}"')
def step_impl(context, first_stack, second_stack):
stacks = [
Expand Down
39 changes: 0 additions & 39 deletions integration-tests/steps/stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,30 +310,6 @@ def step_impl(context, stack_name):
launch_stack(context, stack_name, False, True)


@when('the user describes the resources of stack "{stack_name}"')
def step_impl(context, stack_name):
sceptre_context = SceptreContext(
command_path=stack_name + ".yaml", project_path=context.sceptre_dir
)

sceptre_plan = SceptrePlan(sceptre_context)
context.output = list(sceptre_plan.describe_resources().values())


@when(
'the user describes the resources of stack "{stack_name}" with ignore dependencies'
)
def step_impl(context, stack_name):
sceptre_context = SceptreContext(
command_path=stack_name + ".yaml",
project_path=context.sceptre_dir,
ignore_dependencies=True,
)

sceptre_plan = SceptrePlan(sceptre_context)
context.output = list(sceptre_plan.describe_resources().values())


@when('the user diffs stack "{stack_name}" with "{diff_type}"')
def step_impl(context, stack_name, diff_type):
sceptre_context = SceptreContext(
Expand Down Expand Up @@ -381,21 +357,6 @@ def step_impl(context, stack_name):
assert status is None


@then('the resources of stack "{stack_name}" are described')
def step_impl(context, stack_name):
full_name = get_cloudformation_stack_name(context, stack_name)
response = retry_boto_call(
context.client.describe_stack_resources, StackName=full_name
)
properties = {"LogicalResourceId", "PhysicalResourceId"}
formatted_response = [
{k: v for k, v in item.items() if k in properties}
for item in response["StackResources"]
]

assert [{stack_name: formatted_response}] == context.output


@then(
'stack "{stack_name}" does not exist and stack "{dependant_stack_name}" exists in "{desired_state}"'
)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ classifiers = [

[tool.poetry.plugins."sceptre.hooks"]
"asg_scheduled_actions" = "sceptre.hooks.asg_scaling_processes:ASGScalingProcesses"
"asg_scaling_processes" = "sceptre.hooks.asg_scaling_processes:ASGScalingProcesses"
"cmd" = "sceptre.hooks.cmd:Cmd"

[tool.poetry.plugins."sceptre.resolvers"]
Expand Down
3 changes: 3 additions & 0 deletions sceptre/cli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,9 @@ def simplify_change_set_description(response):
:returns: A more concise description of the change set.
:rtype: dict
"""
if not response:
return {"ChangeSetName": "ChangeSetNotFound"}

desired_response_items = [
"ChangeSetName",
"CreationTime",
Expand Down
10 changes: 7 additions & 3 deletions sceptre/diffing/stack_differ.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from sceptre.plan.actions import StackActions
from sceptre.stack import Stack

from botocore.exceptions import ClientError

DiffType = TypeVar("DiffType")

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -192,10 +194,12 @@ def _extract_parameters_from_generated_stack(self, stack: Stack) -> dict:
def _create_deployed_stack_config(
self, stack_actions: StackActions
) -> Optional[StackConfiguration]:
description = stack_actions.describe()
if description is None:
try:
description = stack_actions.describe()
except ClientError as err:
# This means the stack has not been deployed yet
return None
if err.response["Error"]["Message"].endswith("does not exist"):
return None

stacks = description["Stacks"]
for stack in stacks:
Expand Down
Loading

0 comments on commit e44eed7

Please sign in to comment.