def handle_fn_get_azs(self, intrinsic_value, ignore_errors): """ { "Fn::GetAZs" : "" } { "Fn::GetAZs" : { "Ref" : "AWS::Region" } } { "Fn::GetAZs" : "us-east-1" } This intrinsic function will get the availability zones specified for the specified region. This is usually used with {"Ref": "AWS::Region"}. If it is an empty string, it will get the default region. This intrinsic function will resolve all the objects within the function's value and check their type. Parameter ---------- intrinsic_value: list, dict This is the value of the object inside the Fn::GetAZs intrinsic function property Return ------- A string with the resolved attributes """ intrinsic_value = self.intrinsic_property_resolver( intrinsic_value, ignore_errors, parent_function=IntrinsicResolver.FN_GET_AZS) verify_intrinsic_type_str(intrinsic_value, IntrinsicResolver.FN_GET_AZS) if not intrinsic_value: intrinsic_value = self._symbol_resolver.handle_pseudo_region() if intrinsic_value not in self._symbol_resolver.REGIONS: raise InvalidIntrinsicException( "Invalid region string passed in to {}".format( IntrinsicResolver.FN_GET_AZS)) return self._symbol_resolver.REGIONS.get(intrinsic_value)
def handle_fn_transform(self, intrinsic_value, ignore_errors): """ { "Fn::Transform" : { "Name" : macro name, "Parameters" : {key : value, ... } } } This intrinsic function will transform the data with the body provided This intrinsic function will resolve all the objects within the function's value and check their type. Parameter ---------- intrinsic_value: list, dict This is the value of the object inside the Fn::Transform intrinsic function property Return ------- A string with the resolved attributes """ macro_name = intrinsic_value.get("Name") name = self.intrinsic_property_resolver( macro_name, ignore_errors, parent_function=IntrinsicResolver.FN_TRANSFORM ) if name not in IntrinsicResolver.SUPPORTED_MACRO_TRANSFORMATIONS: raise InvalidIntrinsicException( "The type {} is not currently supported in {}".format(name, IntrinsicResolver.FN_TRANSFORM) ) parameters = intrinsic_value.get("Parameters") verify_intrinsic_type_dict( parameters, IntrinsicResolver.FN_TRANSFORM, message=" Fn::Transform requires parameters section" ) location = self.intrinsic_property_resolver(parameters.get("Location"), ignore_errors) location_data = get_template_data(location) return location_data
def resolve_attribute(self, cloud_formation_property, ignore_errors=False): """ This will parse through every entry in a CloudFormation root key and resolve them based on the symbol_resolver. Customers can optionally ignore resource errors and default to whatever the resource provides. Parameters ----------- cloud_formation_property: dict A high Level dictionary containg either the Mappings, Resources, Outputs, or Parameters Dictionary ignore_errors: bool An option to ignore errors that are InvalidIntrinsicException and InvalidSymbolException Return ------- A resolved template with all references possible simplified """ processed_dict = OrderedDict() for key, val in cloud_formation_property.items(): processed_key = self._symbol_resolver.get_translation(key) or key try: processed_resource = self.intrinsic_property_resolver( val, ignore_errors, parent_function=processed_key) processed_dict[processed_key] = processed_resource except (InvalidIntrinsicException, InvalidSymbolException) as e: resource_type = val.get("Type", "") if ignore_errors: LOG.error("Unable to process properties of %s.%s", key, resource_type) processed_dict[key] = val else: raise InvalidIntrinsicException( "Exception with property of {}.{}".format( key, resource_type) + ": " + str(e.args)) from e return processed_dict
def verify_non_null(argument, property_type="", message="", position_in_list=""): if argument is None: raise InvalidIntrinsicException( message or "The {} argument to {} is missing from the intrinsic function". format(position_in_list, property_type))
def intrinsic_property_resolver(self, intrinsic, parent_function="template"): """ This resolves the intrinsic of the format { intrinsic: dict } by calling the function with the relevant intrinsic function resolver. This also supports returning a string, list, boolean, int since they may be intermediate steps in the recursion process. No transformations are done on these. By default this will just return the item if non of the types match. This is because of the function resolve_all_attributes which will recreate the resources by processing every aspect of resource. This code resolves in a top down depth first fashion in order to create a functional style recursion that doesn't mutate any of the properties. Parameters ---------- intrinsic: dict, str, list, bool, int This is an intrinsic property or an intermediate step parent_function: str In case there is a missing property, this is used to figure out where the property resolved is missing. Return --------- The simplified version of the intrinsic function. This could be a list,str,dict depending on the format required """ if intrinsic is None: raise InvalidIntrinsicException("Missing Intrinsic property in {}".format(parent_function)) if isinstance(intrinsic, list): return [self.intrinsic_property_resolver(item) for item in intrinsic] if not isinstance(intrinsic, dict) or intrinsic == {}: return intrinsic # `intrinsic` is a dict at this point. keys = list(intrinsic.keys()) key = keys[0] if key in self.intrinsic_key_function_map: intrinsic_value = intrinsic.get(key) return self.intrinsic_key_function_map.get(key)(intrinsic_value) elif key in self.conditional_key_function_map: intrinsic_value = intrinsic.get(key) return self.conditional_key_function_map.get(key)(intrinsic_value) # In this case, it is a dictionary that doesn't directly contain an intrinsic resolver, we must recursively # resolve each of it's sub properties. sanitized_dict = {} for key, val in intrinsic.items(): sanitized_key = self.intrinsic_property_resolver(key, parent_function=parent_function) sanitized_val = self.intrinsic_property_resolver(val, parent_function=parent_function) verify_intrinsic_type_str(sanitized_key, message="The keys of the dictionary {} in {} must all resolve to a string".format( sanitized_key, parent_function )) sanitized_dict[sanitized_key] = sanitized_val return sanitized_dict
def verify_intrinsic_type(argument, property_type="", message="", position_in_list="", primitive_type=string_types): verify_non_null(argument, property_type, message, position_in_list) if not isinstance(argument, primitive_type): raise InvalidIntrinsicException( message or "The {} argument to {} must resolve to a {} type".format( position_in_list, property_type, primitive_type))
def handle_fn_import_value(self, intrinsic_value, ignore_errors): """ { "Fn::ImportValue" : sharedValueToImport } This intrinsic function requires handling multiple stacks, which is not currently supported by SAM-CLI. Thus, it will thrown an exception. Return ------- An InvalidIntrinsicException """ raise InvalidIntrinsicException("Fn::ImportValue is currently not supported by IntrinsicResolver")
def verify_intrinsic_type_int(argument, property_type="", message="", position_in_list=""): # Special case since bool is a subclass of int in python if isinstance(argument, bool): raise InvalidIntrinsicException( message or "The {} argument to {} must resolve to a {} type".format( position_in_list, property_type, int)) verify_intrinsic_type(argument, property_type, message, position_in_list, primitive_type=int)
def verify_in_bounds(objects, index, property_type=""): if index < 0 or index >= len(objects): raise InvalidIntrinsicException( "The index of {} resolved properties must be within the range". format(property_type))
def verify_number_arguments(arguments, property_type="", num=0): if not len(arguments) == num: raise InvalidIntrinsicException( "The arguments to {} must have {} arguments instead of {} arguments" .format(property_type, num, len(arguments)))