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

[Resolve #1169] Add detect-stack-drift command #1170

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
e16eabf
Bump the minor version by 1
mrowlingfox May 18, 2021
bdb794b
Merge branch 'master' of github.com:Sceptre/sceptre
mrowlingfox May 21, 2021
e8fcbf3
Merge branch 'master' of github.com:Sceptre/sceptre
alex-harvey-z3q Sep 24, 2021
80e66fe
Merge branch 'master' of github.com:Sceptre/sceptre
alex-harvey-z3q Oct 14, 2021
421b2e3
[Resolve #1169] Add detect-stack-drift command
alex-harvey-z3q Nov 25, 2021
7a9c2ce
Add missing output_format line
alex-harvey-z3q Nov 28, 2021
c942021
Use type annotations
alex-harvey-z3q Nov 28, 2021
db2e536
_wait_for_drift => _wait_for_drift_status
alex-harvey-z3q Nov 28, 2021
233a481
Use f string
alex-harvey-z3q Nov 28, 2021
530430a
Use type annotations
alex-harvey-z3q Nov 28, 2021
4294e86
_print_drift_status => _log_drift_status
alex-harvey-z3q Nov 28, 2021
f846856
Use f strings
alex-harvey-z3q Nov 28, 2021
5f5206c
Handle DETECTION_FAILED
alex-harvey-z3q Nov 28, 2021
2aa9871
Add a test for timeout
alex-harvey-z3q Nov 28, 2021
10406a8
Refactor to create command group drift
alex-harvey-z3q Dec 18, 2021
83e4371
Gracefully handle stacks that do not exist
alex-harvey-z3q Dec 18, 2021
7b104da
Refactor to return status from drift_show
alex-harvey-z3q Dec 19, 2021
f36cbf7
Correction to return value in docs
alex-harvey-z3q Dec 19, 2021
3d33bbf
Add else for unreachable clause
alex-harvey-z3q Dec 19, 2021
18a2e96
Fix a failing test
alex-harvey-z3q Dec 19, 2021
98487dc
Fix up tests
alex-harvey-z3q Dec 19, 2021
3eaaf07
Bugfix and add tests for drift detect
alex-harvey-z3q Dec 19, 2021
0388cab
Add missing test
alex-harvey-z3q Dec 19, 2021
4542633
Update sceptre/cli/drift.py
alex-harvey-z3q Dec 20, 2021
18a1280
Update sceptre/cli/drift.py
alex-harvey-z3q Dec 20, 2021
ef2cf4b
Use a TimeoutError exception
alex-harvey-z3q Dec 20, 2021
c163e73
Merge branch 'alexharvey/issue-1169-detect-stack-drift' of github.com…
alex-harvey-z3q Dec 20, 2021
6fe47b6
Fix tests
alex-harvey-z3q Dec 20, 2021
dba1882
Refactor _wait_for_drift_status to return full response
alex-harvey-z3q Dec 20, 2021
83190e7
Correction to return type hint
alex-harvey-z3q Dec 20, 2021
7473b3b
Refactor for drift_show to call drift_detect
alex-harvey-z3q Dec 20, 2021
0f704a1
Clean up
alex-harvey-z3q Dec 21, 2021
2b2802a
Correction in CLI methods
alex-harvey-z3q Dec 21, 2021
7d8973b
Remove Timestamp and ResponseMetadata
alex-harvey-z3q Dec 21, 2021
c695ceb
Clean up
alex-harvey-z3q Dec 21, 2021
d474273
Revert "Remove Timestamp and ResponseMetadata"
alex-harvey-z3q Dec 22, 2021
be56dff
Second implementation hiding ResponseMetadata and Timestamp
alex-harvey-z3q Dec 22, 2021
73088f9
Refactor with better docs
alex-harvey-z3q Dec 22, 2021
20b75ac
Add failing drift-detect.feature
alex-harvey-z3q Dec 23, 2021
63896fa
Correction to resolve message about stack_name receiving too many values
alex-harvey-z3q Dec 24, 2021
2abff3c
Finishes off integration tests
alex-harvey-z3q Dec 26, 2021
5ed2cc2
Correction to wording
alex-harvey-z3q Dec 26, 2021
887a3cc
Finish integration tests
alex-harvey-z3q Dec 26, 2021
0a9aaf2
Add missing tests
alex-harvey-z3q Dec 26, 2021
4dc286f
Change sleep interval as requested
alex-harvey-z3q Dec 26, 2021
1b1b896
Satisfy linter
alex-harvey-z3q Dec 26, 2021
e53c907
Merge branch 'master' of github.com:Sceptre/sceptre
alex-harvey-z3q Dec 26, 2021
f55df66
Refactor all changes as requested
alex-harvey-z3q Dec 29, 2021
0454388
Satisfy linter
alex-harvey-z3q Dec 29, 2021
4936bac
Edit doc strings
alex-harvey-z3q Dec 29, 2021
f5ef4a2
Update integration-tests/steps/drift.py
alex-harvey-z3q Dec 29, 2021
9856c5a
Further updates
alex-harvey-z3q Dec 29, 2021
fe266ce
Restore 9/B.yaml
alex-harvey-z3q Dec 29, 2021
0b409b2
Delete unused code
alex-harvey-z3q Dec 29, 2021
aa72749
Add some missing type hints
alex-harvey-z3q Dec 30, 2021
d6f6421
Correction
alex-harvey-z3q Dec 30, 2021
2f27c51
Debugging
alex-harvey-z3q Dec 31, 2021
1b4e68a
Correction
alex-harvey-z3q Dec 31, 2021
c09cff0
Double retries
alex-harvey-z3q Dec 31, 2021
04921a5
Revert redundant tests
alex-harvey-z3q Dec 31, 2021
fe28b0a
Add debugging
alex-harvey-z3q Jan 4, 2022
ab751b9
Add a fix to write function in helpers.py
alex-harvey-z3q Jan 4, 2022
d429dd2
Make debug output for drift to info
alex-harvey-z3q Jan 4, 2022
8997ed2
Make default output yaml
alex-harvey-z3q Jan 4, 2022
e528217
Satisfy linter
alex-harvey-z3q Jan 4, 2022
c08513e
Update sceptre/cli/drift.py
alex-harvey-z3q Jan 5, 2022
cd80f90
More fixup
alex-harvey-z3q Jan 5, 2022
f21ed1a
Further tweaks to write
alex-harvey-z3q Jan 5, 2022
62152d6
Fix logger and timeout as requested
alex-harvey-z3q Jan 5, 2022
6090ad1
Add deserialize_json_properties
alex-harvey-z3q Jan 7, 2022
44a4452
Refactor integration test to use an SNS topic
alex-harvey-z3q Jan 10, 2022
c3f49f1
Revert "Add debugging"
alex-harvey-z3q Jan 10, 2022
c717d8c
Revert "Debugging"
alex-harvey-z3q Jan 10, 2022
de60bfb
Merge branch 'master' of github.com:Sceptre/sceptre
alex-harvey-z3q Jan 10, 2022
4d2eeda
Merge branch 'master' into alexharvey/issue-1169-detect-stack-drift
alex-harvey-z3q Jan 10, 2022
b676af1
Grammatical corrections
alex-harvey-z3q Jan 27, 2022
6d6e37d
Remove stray debugging line
alex-harvey-z3q Jan 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions integration-tests/steps/stacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,16 @@ def wait_for_final_state(context, stack_name):
delay = 2
max_retries = 300
attempts = 0
rollback_printed = False
while attempts < max_retries:
retry_boto_call(stack.load)
if not stack.stack_status.endswith("IN_PROGRESS"):
return
if stack.stack_status == "ROLLBACK_IN_PROGRESS" and not rollback_printed:
client = boto3.client("cloudformation")
response = client.describe_stack_events(StackName=stack_name)
print(response)
rollback_printed = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the thinking behind this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jfalkenstein Those are just troubleshooting lines for me to figure out why the build keeps failing in the CI pipeline where I can't reproduce it on my laptop.

attempts += 1
print("Stack in status", stack.stack_status)
time.sleep(delay)
Expand Down
8 changes: 6 additions & 2 deletions sceptre/cli/drift.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,16 @@ def drift_detect(ctx: Context, path: str):
plan = SceptrePlan(context)
responses = plan.drift_detect()

output_format = "yaml" if context.output_format == "text" else context.output_format

exit_status = 0
for stack, response in responses.items():
status = response["DetectionStatus"]
if status in BAD_STATUSES:
exit_status += 1
for key in ["Timestamp", "ResponseMetadata"]:
response.pop(key, None)
write({stack.external_name: response}, context.output_format)
write({stack.external_name: response}, output_format)

exit(exit_status)

Expand Down Expand Up @@ -91,11 +93,13 @@ def drift_show(ctx, path):
plan = SceptrePlan(context)
responses = plan.drift_show()

output_format = "yaml" if context.output_format == "text" else context.output_format

exit_status = 0
for stack, (status, response) in responses.items():
if status in BAD_STATUSES:
exit_status += 1
response.pop("ResponseMetadata", None)
write({stack.external_name: response}, context.output_format)
write({stack.external_name: response}, output_format)

exit(exit_status)
2 changes: 2 additions & 0 deletions sceptre/cli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ def _generate_yaml(stream):
[yaml.load(item, Loader=CfnYamlLoader) for item in items],
default_flow_style=False, explicit_start=True
)
elif isinstance(stream, dict):
return yaml.dump(stream, default_flow_style=False)
else:
try:
return yaml.safe_loads(stream)
Expand Down
10 changes: 5 additions & 5 deletions sceptre/plan/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,7 +1069,7 @@ def _wait_for_drift_status(self, detection_id: str) -> dict:
if elapsed >= timeout:
raise TimeoutError(f"Timed out after {elapsed} seconds")

self.logger.debug(f"{self.stack.name} - Waiting for drift detection")
self.logger.info(f"{self.stack.name} - Waiting for drift detection")
response = self._describe_stack_drift_detection_status(detection_id)
detection_status = response["DetectionStatus"]

Expand All @@ -1095,15 +1095,15 @@ def _log_drift_status(self, response: dict) -> None:

for key in keys:
if key in response:
self.logger.debug(
self.logger.info(
f"{self.stack.name} - {key} - {response[key]}"
)

def _detect_stack_drift(self) -> dict:
"""
Run detect_stack_drift.
"""
self.logger.debug(f"{self.stack.name} - Detecting Stack Drift")
self.logger.info(f"{self.stack.name} - Detecting Stack Drift")

return self.connection_manager.call(
service="cloudformation",
Expand All @@ -1117,7 +1117,7 @@ def _describe_stack_drift_detection_status(self, detection_id: str) -> dict:
"""
Run describe_stack_drift_detection_status.
"""
self.logger.debug(f"{self.stack.name} - Describing Stack Drift Detection Status")
self.logger.info(f"{self.stack.name} - Describing Stack Drift Detection Status")

return self.connection_manager.call(
service="cloudformation",
Expand All @@ -1131,7 +1131,7 @@ def _describe_stack_resource_drifts(self) -> dict:
"""
Detects stack resource_drifts for a running stack.
"""
self.logger.debug(f"{self.stack.name} - Describing Stack Resource Drifts")
self.logger.info(f"{self.stack.name} - Describing Stack Resource Drifts")

return self.connection_manager.call(
service="cloudformation",
Expand Down
21 changes: 11 additions & 10 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,8 +876,8 @@ def test_setup_logging_without_debug(self):
"output_format,no_colour,expected_output", [
("json", True, '{\n "stack": "CREATE_COMPLETE"\n}'),
("json", False, '{\n "stack": "\x1b[32mCREATE_COMPLETE\x1b[0m\"\n}'),
("yaml", True, {'stack': 'CREATE_COMPLETE'}),
("yaml", False, '{\'stack\': \'\x1b[32mCREATE_COMPLETE\x1b[0m\'}')
("yaml", True, 'stack: CREATE_COMPLETE\n'),
("yaml", False, 'stack: \x1b[32mCREATE_COMPLETE\x1b[0m\n')
]
)
def test_write_formats(
Expand Down Expand Up @@ -1010,13 +1010,14 @@ def test_drift_detect(self):
cli, ["drift", "detect", "dev/vpc.yaml"]
)
assert result.exit_code == 0
assert result.output == " ".join([
"{'fake_stack': {'StackId': 'fake-stack-id',",
"'StackDriftDetectionId': '3fb76910-f660-11eb-80ac-0246f7a6da62',",
"'StackDriftStatus': 'IN_SYNC',",
"'DetectionStatus': 'DETECTION_COMPLETE',",
"'DriftedStackResourceCount': 0}}\n"
])
assert result.output == (
'fake_stack:\n'
' DetectionStatus: DETECTION_COMPLETE\n'
' DriftedStackResourceCount: 0\n'
' StackDriftDetectionId: 3fb76910-f660-11eb-80ac-0246f7a6da62\n'
' StackDriftStatus: IN_SYNC\n'
' StackId: fake-stack-id\n\n'
)

def test_drift_show(self):
self.mock_stack_actions.drift_show.return_value = (
Expand All @@ -1026,4 +1027,4 @@ def test_drift_show(self):
cli, ["drift", "show", "dev/vpc.yaml"]
)
assert result.exit_code == 0
assert result.output == "{'fake_stack': {'some': 'json'}}\n"
assert result.output == "fake_stack:\n some: json\n\n"