def test_get_yaml_or_json_file_parses_yaml_on_yml_suffix( self, get_file_mock, yaml_mock): get_file_return_value = Mock() get_file_mock.return_value = get_file_return_value FileLoader.get_yaml_or_json_file('foo.yml', 'baa') yaml_mock.load.assert_called_once_with(get_file_return_value)
def test_get_yaml_or_json_file_parses_json_on_json_suffix( self, get_file_mock, json_mock): get_file_return_value = Mock() get_file_mock.return_value = get_file_return_value FileLoader.get_yaml_or_json_file('foo.json', 'baa') json_mock.loads.assert_called_once_with(get_file_return_value, encoding="utf-8")
def test_get_yaml_or_json_file_parses_yaml_on_yaml_suffix( self, get_file_mock, yaml_mock): get_file_return_value = Mock() get_file_mock.return_value = get_file_return_value FileLoader.get_yaml_or_json_file('foo.yaml', 'baa') if hasattr(yaml_mock, 'FullLoader'): loader = yaml_mock.FullLoader else: loader = yaml_mock.Loader yaml_mock.load.assert_called_once_with(get_file_return_value, Loader=loader)
def __init__(self, config_file=None, config_dict=None, cli_params=None, stack_name_suffix=None): self.logger = get_logger() if isinstance(config_dict, dict): self.stack_config_base_dir = None elif config_file: self.stack_config_base_dir = os.path.dirname( os.path.realpath(config_file)) config_dict = FileLoader.get_yaml_or_json_file( config_file, working_dir=os.getcwd()) else: raise InvalidConfigException( "You need to pass either config_file (path to a file) or config_dict (python dict) property" ) self.cli_params = self._parse_cli_parameters(cli_params) self.region = config_dict.get("region") self.stack_name_suffix = stack_name_suffix self.default_service_role = config_dict.get("service-role") self.default_stack_policy_url = config_dict.get("stack-policy-url") self.default_timeout = config_dict.get("timeout", 600) self.default_tags = config_dict.get("tags", {}) self.default_failure_action = config_dict.get("on_failure", "ROLLBACK") self.default_disable_rollback = config_dict.get( "disable_rollback", False) self._validate(config_dict) stacks = self._parse_stack_configs(config_dict) self.stacks = self._apply_stack_name_suffix(stacks, stack_name_suffix)
def test_get_yaml_or_json_file_accepts_yaml_template_with_getatt_constructor_tag( self, get_file_mock): get_file_mock.return_value = "myKey: !GetAtt myResource.attributeName" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual( {"myKey": { "Fn::GetAtt": ["myResource", "attributeName"] }}, result)
def test_get_yaml_or_json_file_accepts_yaml_with_nested_constructor_tags( self, get_file_mock): get_file_mock.return_value = "myKey: !Join [ b, [ !ref a, !ref b] ]" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual( {"myKey": { "Fn::Join": ["b", [{ "Ref": "a" }, { "Ref": "b" }]] }}, result)
def handle_file_value(value, working_dir): components = value.split('|', 3) if len(components) == 3: url = components[2] return FileLoader.get_file(url, working_dir) elif len(components) == 4: url = components[2] pattern = components[3] file_content = FileLoader.get_yaml_or_json_file(url, working_dir) try: return jmespath.search(pattern, file_content) except JMESPathError as e: raise CfnSphereException(e) else: raise CfnSphereException("Invalid format for |File| macro, it must be |File|<path>[|<pattern>]")
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) template = FileLoader.get_cloudformation_template(stack_config.template_url,stack_config.working_dir) transformed_template = CloudFormationTemplateTransformer.transform_template(template) 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 parameters = self.parameter_resolver.resolve_parameter_values(stack_name, stack_config) merged_parameters = self.parameter_resolver.update_parameters_with_cli_parameters( parameters=parameters, cli_parameters=self.cli_parameters, stack_name=stack_name) self.logger.debug("Parameters after merging with cli options: {0}".format(merged_parameters)) stack = CloudFormationStack(template=transformed_template, parameters=merged_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) 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 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 handle_file_value(value, working_dir): components = value.split('|', 3) if len(components) == 3: url = components[2] return FileLoader.get_file(url, working_dir) elif len(components) == 4: url = components[2] pattern = components[3] file_content = FileLoader.get_yaml_or_json_file(url, working_dir) try: return jmespath.search(pattern, file_content) except JMESPathError as e: raise CfnSphereException(e) else: raise CfnSphereException( "Invalid format for |File| macro, it must be |File|<path>[|<pattern>]" )
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 test_get_yaml_or_json_file_accepts_yaml_with_cfn_join_constructor_tag(self, get_file_mock): get_file_mock.return_value = "myKey: !Join [ b, [ a, c] ]" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Fn::Join": ["b", ["a", "c"]]}}, result)
def test_get_yaml_or_json_file_accepts_yaml_template_with_ref_constructor_tag(self, get_file_mock): get_file_mock.return_value = "myKey: !Ref myResource" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Ref": "myResource"}}, result)
def test_get_yaml_or_json_file_accepts_yaml_template_with_getatt_constructor_tag(self, get_file_mock): get_file_mock.return_value = "myKey: !GetAtt myResource.attributeName" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Fn::GetAtt": ["myResource", "attributeName"]}}, result)
def test_get_yaml_or_json_file_accepts_yaml_with_cfn_join_constructor_tag( self, get_file_mock): get_file_mock.return_value = "myKey: !Join [ b, [ a, c] ]" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Fn::Join": ["b", ["a", "c"]]}}, result)
def test_get_yaml_or_json_file_parses_yaml_on_yml_suffix(self, get_file_mock, yaml_mock): get_file_return_value = Mock() get_file_mock.return_value = get_file_return_value FileLoader.get_yaml_or_json_file('foo.yml', 'baa') yaml_mock.load.assert_called_once_with(get_file_return_value)
def test_get_yaml_or_json_file_accepts_yaml_template_with_ref_constructor_tag( self, get_file_mock): get_file_mock.return_value = "myKey: !Ref myResource" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Ref": "myResource"}}, result)
def test_get_yaml_or_json_file_raises_exception_on_json_error( self, _, json_mock): json_mock.loads.side_effect = ValueError() with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.json', 'baa')
def test_get_yaml_or_json_file_raises_exception_on_yaml_error( self, _, yaml_mock): yaml_mock.load.side_effect = ScannerError() with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.yml', 'baa')
def test_get_yaml_or_json_file_raises_exception_invalid_file_extension( self, _): with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.foo', 'baa')
def test_get_yaml_or_json_file_accepts_yaml_with_nested_constructor_tags(self, get_file_mock): get_file_mock.return_value = "myKey: !Join [ b, [ !ref a, !ref b] ]" result = FileLoader.get_yaml_or_json_file("my-template.yaml", None) self.assertEqual({"myKey": {"Fn::Join": ["b", [{"Ref": "a"}, {"Ref": "b"}]]}}, result)
def test_get_yaml_or_json_file_raises_exception_invalid_file_extension(self, _): with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.foo', 'baa')
def test_get_yaml_or_json_file_raises_exception_on_json_error(self, _, json_mock): json_mock.loads.side_effect = ValueError() with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.json', 'baa')
def test_get_yaml_or_json_file_raises_exception_on_yaml_error(self, _, yaml_mock): yaml_mock.load.side_effect = ScannerError() with self.assertRaises(CfnSphereException): FileLoader.get_yaml_or_json_file('foo.yml', 'baa')