-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from mhahn/destroy-action
Add destroy action
- Loading branch information
Showing
12 changed files
with
333 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import logging | ||
|
||
from .base import BaseAction | ||
from ..exceptions import StackDoesNotExist | ||
from ..plan import ( | ||
COMPLETE, | ||
SKIPPED, | ||
SUBMITTED, | ||
Plan, | ||
) | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class Action(BaseAction): | ||
"""Responsible for destroying CloudFormation stacks. | ||
Generates a destruction plan based on stack dependencies. Stack | ||
dependencies are reversed from the build action. For example, if a Stack B | ||
requires Stack A during build, during destroy Stack A requires Stack B be | ||
destroyed first. | ||
The plan defaults to printing an outline of what will be destroyed. If | ||
forced to execute, each stack will get destroyed in order. | ||
""" | ||
|
||
def _get_dependencies(self, stacks_dict): | ||
dependencies = {} | ||
for stack_name, stack in stacks_dict.iteritems(): | ||
required_stacks = stack.requires | ||
if not required_stacks: | ||
if stack_name not in dependencies: | ||
dependencies[stack_name] = required_stacks | ||
continue | ||
|
||
for requirement in required_stacks: | ||
dependencies.setdefault(requirement, set()).add(stack_name) | ||
return dependencies | ||
|
||
def _generate_plan(self): | ||
plan = Plan(description='Destroy stacks') | ||
stacks_dict = self.context.get_stacks_dict() | ||
dependencies = self._get_dependencies(stacks_dict) | ||
for stack_name in self.get_stack_execution_order(dependencies): | ||
plan.add( | ||
stacks_dict[stack_name], | ||
run_func=self._destroy_stack, | ||
requires=dependencies.get(stack_name), | ||
) | ||
return plan | ||
|
||
def _destroy_stack(self, stack, **kwargs): | ||
try: | ||
provider_stack = self.provider.get_stack(stack.fqn) | ||
except StackDoesNotExist: | ||
logger.debug("Stack %s does not exist.", stack.fqn) | ||
# Once the stack has been destroyed, it doesn't exist. If the | ||
# status of the step was SUBMITTED, we know we just deleted it, | ||
# otherwise it should be skipped | ||
if kwargs.get('status', None) is SUBMITTED: | ||
return COMPLETE | ||
else: | ||
return SKIPPED | ||
|
||
logger.debug( | ||
"Stack %s provider status: %s", | ||
self.provider.get_stack_name(provider_stack), | ||
self.provider.get_stack_status(provider_stack), | ||
) | ||
if self.provider.is_stack_destroyed(provider_stack): | ||
return COMPLETE | ||
elif self.provider.is_stack_in_progress(provider_stack): | ||
return SUBMITTED | ||
else: | ||
self.provider.destroy_stack(provider_stack) | ||
return SUBMITTED | ||
|
||
def run(self, force, *args, **kwargs): | ||
plan = self._generate_plan() | ||
if force: | ||
# need to generate a new plan to log since the outline sets the | ||
# steps to COMPLETE in order to log them | ||
debug_plan = self._generate_plan() | ||
debug_plan.outline(logging.DEBUG) | ||
plan.execute() | ||
else: | ||
plan.outline(message='To execute this plan, run with "-f, --force" flag.') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
"""Destroys CloudFormation stacks based on the given config. | ||
Stacker will determine the order in which stacks should be destroyed based on | ||
any manual requirements they specify or output values they rely on from other | ||
stacks. | ||
""" | ||
from .base import StackerCommand | ||
from ...actions import destroy | ||
|
||
|
||
class Destroy(StackerCommand): | ||
|
||
name = "destroy" | ||
description = __doc__ | ||
|
||
def add_arguments(self, parser): | ||
super(Destroy, self).add_arguments(parser) | ||
parser.add_argument('-f', '--force', action='store_true', | ||
help="Whehter or not you want to go through " | ||
" with destroying the stacks") | ||
|
||
def run(self, options, **kwargs): | ||
super(Destroy, self).run(options, **kwargs) | ||
action = destroy.Action(options.context, provider=options.provider) | ||
stack_names = [' - %s' % (s.fqn,) for s in options.context.get_stacks()] | ||
message = '\nAre you sure you want to destroy the following stacks:\n%s\n\n(yes/no): ' % ( | ||
'\n'.join(stack_names), | ||
) | ||
force = False | ||
if options.force: | ||
confirm = raw_input(message) | ||
force = confirm.lower() == 'yes' | ||
if not force: | ||
self.logger.info('Confirmation failed, printing ouline...') | ||
action.execute(force=force) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.