def test_delete_stacks_uses_the_correct_order(self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = ['a', 'c'] cfn_mock.return_value.get_stack_names.return_value = ['a', 'c'] stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args): if args[2] == 'a': return stack_a if args[2] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(Mock()) handler.delete_stacks() expected_calls = [call(stack_c), call(stack_a)] six.assertCountEqual(self, expected_calls, cfn_mock.return_value.delete_stack.mock_calls)
def test_create_or_update_tests_exits_gracefully_if_preexisting_stack_disappears(self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock, config_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = ['a', 'c'] cfn_mock.return_value.get_stack_names.return_value = ['a', 'd'] config_mock.return_value.tags.return_value = {} stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args, **kwargs): if kwargs['name'] == 'a': return stack_a if kwargs['name'] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(config_mock) handler.create_or_update_stacks() # stack a needs update cfn_mock.return_value.validate_stack_is_ready_for_action.assert_called_once_with(stack_a) cfn_mock.return_value.update_stack.assert_called_once_with(stack_a) # stack c doesn't exist, must be created cfn_mock.return_value.create_stack.assert_called_once_with(stack_c)
def test_delete_stacks(self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = ['a', 'c'] cfn_mock.return_value.get_stack_names.return_value = ['a', 'd'] stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args): if args[2] == 'a': return stack_a if args[2] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(Mock()) handler.delete_stacks() cfn_mock.return_value.delete_stack.assert_called_once_with(stack_a)
def test_delete_stacks(self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = [ 'a', 'c' ] cfn_mock.return_value.get_stack_names.return_value = ['a', 'd'] stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args, **kwargs): if args[2] == 'a': return stack_a if args[2] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(Mock()) handler.delete_stacks() cfn_mock.return_value.delete_stack.assert_called_once_with(stack_a)
def test_delete_stacks_uses_the_correct_order( self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = [ 'a', 'c' ] cfn_mock.return_value.get_stack_names.return_value = ['a', 'c'] stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args, **kwargs): if args[2] == 'a': return stack_a if args[2] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(Mock()) handler.delete_stacks() expected_calls = [call(stack_c), call(stack_a)] six.assertCountEqual(self, expected_calls, cfn_mock.return_value.delete_stack.mock_calls)
def sync_stacks_with_parameters_overwrite(self, cli_params): config = Config(config_file=os.path.join(self.test_resources_dir, "stacks.yml"), cli_params=cli_params) stack_handler = StackActionHandler(config) self.logger.info("Syncing stacks") stack_handler.create_or_update_stacks()
def test_create_or_update_tests_exits_gracefully_if_preexisting_stack_disappears( self, custom_resource_mock, stack_mock, template_loader_mock, dependency_resolver_mock, parameter_resolver_mock, cfn_mock): dependency_resolver_mock.return_value.get_stack_order.return_value = [ 'a', 'c' ] cfn_mock.return_value.get_stack_names.return_value = ['a', 'd'] stack_a = CloudFormationStack('', [], 'a', '') stack_c = CloudFormationStack('', [], 'c', '') def stack_side_effect(*args, **kwargs): if kwargs['name'] == 'a': return stack_a if kwargs['name'] == 'c': return stack_c return None stack_mock.side_effect = stack_side_effect handler = StackActionHandler(Mock()) handler.create_or_update_stacks() # stack a needs update cfn_mock.return_value.validate_stack_is_ready_for_action.assert_called_once_with( stack_a) cfn_mock.return_value.update_stack.assert_called_once_with(stack_a) # stack c doesn't exist, must be created cfn_mock.return_value.create_stack.assert_called_once_with(stack_c)
def get_stack_action_handler(domain, verification_token=None, dkim_tokens=None): verification_token = verification_token or "" dkim_tokens = dkim_tokens or ["", "", ""] return StackActionHandler(config=Config( config_dict={ 'region': REGION, 'stacks': { get_dns_stack_name(domain): { 'template-url': recordset_template.name, 'parameters': { 'dnsBaseName': domain + ".", 'dkimOne': dkim_tokens[0], 'dkimTwo': dkim_tokens[1], 'dkimThree': dkim_tokens[2], 'verifyTxt': verification_token } }, get_bucket_stack_name(domain): { 'template-url': ses_template.name, } } }))
def sync(config, parameter, suffix, debug, confirm, yes): confirm = confirm or yes if debug: LOGGER.setLevel(logging.DEBUG) boto3.set_stream_logger(name='boto3', level=logging.DEBUG) boto3.set_stream_logger(name='botocore', level=logging.DEBUG) else: LOGGER.setLevel(logging.INFO) if not confirm: check_update_available() click.confirm( 'This action will modify AWS infrastructure in account: {0}\nAre you sure?' .format(get_first_account_alias_or_account_id()), abort=True) try: config = Config(config_file=config, cli_params=parameter, stack_name_suffix=suffix) StackActionHandler(config).create_or_update_stacks() except CfnSphereException as e: LOGGER.error(e) if debug: LOGGER.exception(e) sys.exit(1) except Exception as e: LOGGER.error("Failed with unexpected error") LOGGER.exception(e) LOGGER.info( "Please report at https://github.com/cfn-sphere/cfn-sphere/issues!" ) sys.exit(1)
def delete(config, suffix, debug, confirm, yes): confirm = confirm or yes if debug: LOGGER.setLevel(logging.DEBUG) else: LOGGER.setLevel(logging.INFO) if not confirm: check_update_available() click.confirm( 'This action will delete all stacks in {0} from account: {1}\nAre you sure?' .format(config, get_first_account_alias_or_account_id()), abort=True) try: config = Config(config, stack_name_suffix=suffix) StackActionHandler(config).delete_stacks() except CfnSphereException as e: LOGGER.error(e) if debug: LOGGER.exception(e) sys.exit(1) except Exception as e: LOGGER.error("Failed with unexpected error") LOGGER.exception(e) LOGGER.info( "Please report at https://github.com/cfn-sphere/cfn-sphere/issues!" ) sys.exit(1)
def setUpClass(cls): test_resources_dir = get_resources_dir() cls.cfn_conn = cloudformation.connect_to_region("eu-west-1") cls.config = Config(config_file=os.path.join(test_resources_dir, "stacks.yml")) cls.stack_handler = StackActionHandler(cls.config) LOGGER.info("Syncing stacks") cls.stack_handler.create_or_update_stacks()
def execute_change_set(change_set, debug, confirm, yes, region): confirm = confirm or yes if debug: LOGGER.setLevel(logging.DEBUG) boto3.set_stream_logger(name='boto3', level=logging.DEBUG) boto3.set_stream_logger(name='botocore', level=logging.DEBUG) else: LOGGER.setLevel(logging.INFO) if not confirm: check_update_available() click.confirm( 'This action will modify AWS infrastructure in account: {0}\nAre you sure?' .format(get_first_account_alias_or_account_id()), abort=True) try: matched = re.match(r'arn:aws:cloudformation:([^:]+):.*', change_set) if matched: LOGGER.info('ARN detected, setting region to {}'.format( matched.group(1))) region = matched.group(1) config_dict = {'change_set': change_set, 'region': str(region)} config = Config(config_dict=config_dict) StackActionHandler(config).execute_change_set() except CfnSphereException as e: LOGGER.error(e) if debug: LOGGER.exception(e) sys.exit(1) except Exception as e: LOGGER.error("Failed with unexpected error") LOGGER.exception(e) LOGGER.info( "Please report at https://github.com/KCOM-Enterprise/cfn-square/issues!" ) sys.exit(1)
def sync_stacks(self): stack_handler = StackActionHandler(self.config) self.logger.info("Syncing stacks") stack_handler.create_or_update_stacks()
def delete_stacks(self): StackActionHandler(self.config).delete_stacks() self.verify_stacks_are_gone()