class StackActionHandler(object): def __init__(self, config): self.logger = get_logger(root=True) self.config = config self.cfn = CloudFormation(region=self.config.region) self.parameter_resolver = ParameterResolver(region=self.config.region) self.cli_parameters = config.cli_params def create_or_update_stacks(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format(", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) raw_template = FileLoader.get_file_from_url(stack_config.template_url, stack_config.working_dir) template = CloudFormationTemplateTransformer.transform_template(raw_template) parameters = self.parameter_resolver.resolve_parameter_values(stack_config.parameters, stack_name) merged_parameters = self.parameter_resolver.update_parameters_with_cli_parameters( parameters=parameters, cli_parameters=self.cli_parameters, stack_name=stack_name) stack = CloudFormationStack(template=template, parameters=merged_parameters, tags=self.config.tags, name=stack_name, region=self.config.region, timeout=stack_config.timeout) if stack_name in existing_stacks: self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.update_stack(stack) else: self.cfn.create_stack(stack) CustomResourceHandler.process_post_resources(stack) def delete_stacks(self): existing_stacks = self.cfn.get_stack_names() stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(stacks) stack_processing_order.reverse() self.logger.info("Will delete stacks in the following order: {0}".format(", ".join(stack_processing_order))) for stack_name in stack_processing_order: if stack_name in existing_stacks: stack = CloudFormationStack(None, None, stack_name, None, None) self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.delete_stack(stack) else: self.logger.info("Stack {0} is already deleted".format(stack_name))
def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_rollback_complete_state(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "ROLLBACK_COMPLETE" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_rollback_complete_state(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "ROLLBACK_COMPLETE" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, cloudformation_mock): cloudformation_mock.return_value.describe_stacks.side_effect = BotoServerError('400', 'Bad Request') stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnSphereBotoError): cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
def test_validate_stack_is_ready_for_action_raises_exception_on_create_in_progress(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "CREATE_IN_PROGRESS" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_exception_on_create_in_progress(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "CREATE_IN_PROGRESS" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_exception_on_unknown_stack_state(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "FOO" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_exception_on_unknown_stack_state(self, get_stack_mock): stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "FOO" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error( self, cloudformation_mock): cloudformation_mock.return_value.describe_stacks.side_effect = BotoServerError( '400', 'Bad Request') stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnSphereBotoError): cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with( 'my-stack')
def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, get_stack_mock): get_stack_mock.side_effect = CfnSphereBotoError(None) stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "UPDATE_COMPLETE" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnSphereBotoError): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_good_state(self, cloudformation_mock): describe_stack_mock = Mock() describe_stack_mock.stack_status = "UPDATE_COMPLETE" describe_stack_mock.stack_name = "my-stack" cloudformation_mock.return_value.describe_stacks.return_value = [describe_stack_mock] stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
def test_validate_stack_is_ready_for_action_raises_proper_exception_on_boto_error(self, get_stack_mock): get_stack_mock.side_effect = CfnSphereBotoError(None) stack_mock = Mock() stack_mock.stack_name = "my-stack" stack_mock.stack_status = "UPDATE_COMPLETE" get_stack_mock.return_value = stack_mock stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnSphereBotoError): cfn.validate_stack_is_ready_for_action(stack)
def test_validate_stack_is_ready_for_action_raises_exception_on_bad_stack_state(self, cloudformation_mock): describe_stack_mock = Mock() describe_stack_mock.stack_status = "UPDATE_IN_PROGRESS" describe_stack_mock.stack_name = "my-stack" cloudformation_mock.return_value.describe_stacks.return_value = [describe_stack_mock] stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with('my-stack')
def test_validate_stack_is_ready_for_action_passes_if_stack_is_in_good_state( self, cloudformation_mock): describe_stack_mock = Mock() describe_stack_mock.stack_status = "UPDATE_COMPLETE" describe_stack_mock.stack_name = "my-stack" cloudformation_mock.return_value.describe_stacks.return_value = [ describe_stack_mock ] stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with( 'my-stack')
def test_validate_stack_is_ready_for_action_raises_exception_on_bad_stack_state( self, cloudformation_mock): describe_stack_mock = Mock() describe_stack_mock.stack_status = "UPDATE_IN_PROGRESS" describe_stack_mock.stack_name = "my-stack" cloudformation_mock.return_value.describe_stacks.return_value = [ describe_stack_mock ] stack = CloudFormationStack('', [], 'my-stack', 'my-region') cfn = CloudFormation() with self.assertRaises(CfnStackActionFailedException): cfn.validate_stack_is_ready_for_action(stack) cloudformation_mock.return_value.describe_stacks.assert_called_once_with( 'my-stack')
class StackActionHandler(object): def __init__(self, config): self.logger = get_logger(root=True) self.config = config self.region = config.region self.cfn = CloudFormation(region=self.region) self.parameter_resolver = ParameterResolver(region=self.region) def create_or_update_stacks(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order( desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) raw_template = FileLoader.get_file_from_url( stack_config.template_url, stack_config.working_dir) template = CloudFormationTemplateTransformer.transform_template( raw_template) parameters = self.parameter_resolver.resolve_parameter_values( stack_config.parameters, stack_name) stack = CloudFormationStack(template=template, parameters=parameters, tags=(stack_config.tags), name=stack_name, region=self.region, timeout=stack_config.timeout) if stack_name in existing_stacks: self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.update_stack(stack) else: self.cfn.create_stack(stack) CustomResourceHandler.process_post_resources(stack) def delete_stacks(self): existing_stacks = self.cfn.get_stack_names() stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(stacks) stack_processing_order.reverse() self.logger.info( "Will delete stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: if stack_name in existing_stacks: stack = CloudFormationStack(None, None, stack_name, None, None) self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.delete_stack(stack) else: self.logger.info( "Stack {0} is already deleted".format(stack_name))
class StackActionHandler(object): def __init__(self, config): self.logger = get_logger(root=True) self.config = config self.cfn = CloudFormation(region=self.config.region) self.parameter_resolver = ParameterResolver(region=self.config.region) self.cli_parameters = config.cli_params def create_or_update_stacks(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format(", ".join(stack_processing_order)) ) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_config.stack_policy_url: self.logger.info("Using stack policy from {0}".format(stack_config.stack_policy_url)) stack_policy = FileLoader.get_yaml_or_json_file(stack_config.stack_policy_url, stack_config.working_dir) else: stack_policy = None template = TemplateHandler.get_template(stack_config.template_url, stack_config.working_dir) parameters = self.parameter_resolver.resolve_parameter_values(stack_name, stack_config, self.cli_parameters) stack = CloudFormationStack( template=template, parameters=parameters, tags=stack_config.tags, name=stack_name, region=self.config.region, timeout=stack_config.timeout, service_role=stack_config.service_role, stack_policy=stack_policy, failure_action=stack_config.failure_action, ) if stack_name in existing_stacks: self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.update_stack(stack) else: self.cfn.create_stack(stack) CustomResourceHandler.process_post_resources(stack) def delete_stacks(self): existing_stacks = self.cfn.get_stack_names() stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(stacks) stack_processing_order.reverse() self.logger.info("Will delete stacks in the following order: {0}".format(", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_name in existing_stacks: stack = CloudFormationStack(None, None, stack_name, None, None, service_role=stack_config.service_role) self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.delete_stack(stack) else: self.logger.info("Stack {0} is already deleted".format(stack_name))
class StackActionHandler(object): def __init__(self, config): self.logger = get_logger(root=True) self.config = config self.cfn = CloudFormation(region=self.config.region) self.parameter_resolver = ParameterResolver(region=self.config.region) self.cli_parameters = config.cli_params def create_or_update_stacks(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order( desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_config.stack_policy_url: self.logger.info("Using stack policy from {0}".format( stack_config.stack_policy_url)) stack_policy = FileLoader.get_yaml_or_json_file( stack_config.stack_policy_url, stack_config.working_dir) else: stack_policy = None template = TemplateHandler.get_template(stack_config.template_url, stack_config.working_dir) parameters = self.parameter_resolver.resolve_parameter_values( stack_name, stack_config, self.cli_parameters) stack = CloudFormationStack( template=template, parameters=parameters, tags=stack_config.tags, name=stack_name, region=self.config.region, timeout=stack_config.timeout, service_role=stack_config.service_role, stack_policy=stack_policy, failure_action=stack_config.failure_action) if stack_name in existing_stacks: self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.update_stack(stack) else: self.cfn.create_stack(stack) def delete_stacks(self): existing_stacks = self.cfn.get_stack_names() stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(stacks) stack_processing_order.reverse() self.logger.info( "Will delete stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_name in existing_stacks: stack = CloudFormationStack( None, None, stack_name, None, None, service_role=stack_config.service_role) self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.delete_stack(stack) else: self.logger.info( "Stack {0} is already deleted".format(stack_name))
class StackActionHandler(object): def __init__(self, config, dry_run=False): self.logger = get_logger(root=True) self.config = config self.cfn = CloudFormation(region=self.config.region, dry_run=dry_run) self.parameter_resolver = ParameterResolver(self.cfn, region=self.config.region) self.cli_parameters = config.cli_params def execute_change_set(self): print('executing change set') print(self.config.change_set) change_set = self.cfn.get_change_set(self.config.change_set) if change_set is None: raise CfnStackActionFailedException( "Could not execute change set {0}: Does not exist or is in an invalid state." .format(self.config.change_set)) stack_name = self.cfn.get_stack_name_by_arn(change_set['StackId']) stack = self.cfn.get_stack(stack_name) if not self.cfn.change_set_is_executable(change_set): raise CfnStackActionFailedException( "Could not execute change set {0}: Does not exist or is in an invalid state." .format(self.config.change_set)) else: self.cfn.execute_change_set(stack, self.config.change_set) def create_change_set(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order( desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_config.stack_policy_url: self.logger.info("Using stack policy from {0}".format( stack_config.stack_policy_url)) stack_policy = FileLoader.get_yaml_or_json_file( stack_config.stack_policy_url, stack_config.working_dir) else: stack_policy = None template = TemplateHandler.get_template( stack_config.template_url, stack_config.working_dir, self.config.region, stack_config.package_bucket) parameters = self.parameter_resolver.resolve_parameter_values( stack_name, stack_config, self.cli_parameters) stack = CloudFormationStack( template=template, parameters=parameters, tags=stack_config.tags, name=stack_name, region=self.config.region, timeout=stack_config.timeout, service_role=stack_config.service_role, stack_policy=stack_policy, failure_action=stack_config.failure_action) if stack_name in existing_stacks: self.cfn.create_change_set(stack, 'UPDATE') else: self.cfn.create_change_set(stack, 'CREATE') def create_or_update_stacks(self): existing_stacks = self.cfn.get_stack_names() desired_stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order( desired_stacks) if len(stack_processing_order) > 1: self.logger.info( "Will process stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_config.stack_policy_url: self.logger.info("Using stack policy from {0}".format( stack_config.stack_policy_url)) stack_policy = FileLoader.get_yaml_or_json_file( stack_config.stack_policy_url, stack_config.working_dir) else: stack_policy = None template = TemplateHandler.get_template( stack_config.template_url, stack_config.working_dir, self.config.region, stack_config.package_bucket) parameters = self.parameter_resolver.resolve_parameter_values( stack_name, stack_config, self.cli_parameters) stack = CloudFormationStack( template=template, parameters=parameters, tags=stack_config.tags, name=stack_name, region=self.config.region, timeout=stack_config.timeout, service_role=stack_config.service_role, stack_policy=stack_policy, failure_action=stack_config.failure_action) if stack_name in existing_stacks: self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.update_stack(stack) else: self.cfn.create_stack(stack) def delete_stacks(self): existing_stacks = self.cfn.get_stack_names() stacks = self.config.stacks stack_processing_order = DependencyResolver().get_stack_order(stacks) stack_processing_order.reverse() self.logger.info( "Will delete stacks in the following order: {0}".format( ", ".join(stack_processing_order))) for stack_name in stack_processing_order: stack_config = self.config.stacks.get(stack_name) if stack_name in existing_stacks: stack = CloudFormationStack( None, None, stack_name, None, None, service_role=stack_config.service_role) self.cfn.validate_stack_is_ready_for_action(stack) self.cfn.delete_stack(stack) else: self.logger.info( "Stack {0} is already deleted".format(stack_name))