def stack_parameters(self, defaults=True) -> List[Dict[str, Any]]: result = {} # add default template parameter values if defaults: for key, value in self.template_parameters.items(): param_value = value.get("Default") result[key] = { "ParameterKey": key, "ParameterValue": param_value, } # TODO: extract dynamic parameter resolving # TODO: support different types and refactor logic to use metadata (here not yet populated properly) param_type = value.get("Type", "") if not is_none_or_empty(param_type): if param_type == "AWS::SSM::Parameter::Value<String>": ssm_client = aws_stack.connect_to_service("ssm") resolved_value = ssm_client.get_parameter(Name=param_value)["Parameter"][ "Value" ] result[key]["ResolvedValue"] = resolved_value elif param_type.startswith("AWS::"): LOG.info( f"Parameter Type '{param_type}' is currently not supported. Coming soon, stay tuned!" ) else: # lets assume we support the normal CFn parameters pass # add stack parameters result.update({p["ParameterKey"]: p for p in self.metadata["Parameters"]}) # add parameters of change sets for change_set in self.change_sets: result.update({p["ParameterKey"]: p for p in change_set.metadata["Parameters"]}) result = list(result.values()) return result
def create_change_set(req_params: Dict[str, Any]): change_set_type: ChangeSetTypes = req_params.get("ChangeSetType", "UPDATE") stack_name: Optional[str] = req_params.get("StackName") change_set_name: Optional[str] = req_params.get("ChangeSetName") template_body: Optional[str] = req_params.get("TemplateBody") # s3 or secretsmanager url template_url: Optional[str] = req_params.get( "TemplateUrl") or req_params.get("TemplateURL") if is_none_or_empty(change_set_name): return error_response("ChangeSetName required", 400, "ValidationError") # TODO: check proper message if is_none_or_empty(stack_name): return error_response("StackName required", 400, "ValidationError") # TODO: check proper message stack: Optional[Stack] = find_stack(stack_name) # validate and resolve template if template_body and template_url: return error_response( "Specify exactly one of 'TemplateBody' or 'TemplateUrl'", 400, "ValidationError") # TODO: check proper message if not template_body and not template_url: return error_response( "Specify exactly one of 'TemplateBody' or 'TemplateUrl'", 400, "ValidationError") # TODO: check proper message prepare_template_body( req_params) # TODO: function has too many unclear responsibilities template = template_preparer.parse_template(req_params["TemplateBody"]) del req_params["TemplateBody"] # TODO: stop mutating req_params template["StackName"] = stack_name template[ "ChangeSetName"] = change_set_name # TODO: validate with AWS what this is actually doing? if change_set_type == "UPDATE": # add changeset to existing stack if stack is None: return error_response( f"Stack '{stack_name}' does not exist.", 400, "ValidationError") # stack should exist already elif change_set_type == "CREATE": # create new (empty) stack if stack is not None: return error_response( f"Stack {stack_name} already exists", 400, "ValidationError" ) # stack should not exist yet (TODO: check proper message) state = CloudFormationRegion.get() empty_stack_template = dict(template) empty_stack_template["Resources"] = {} req_params_copy = clone_stack_params(req_params) stack = Stack(req_params_copy, empty_stack_template) state.stacks[stack.stack_id] = stack stack.set_stack_status("REVIEW_IN_PROGRESS") elif change_set_type == "IMPORT": raise NotImplementedError() # TODO: implement importing resources else: msg = f"1 validation error detected: Value '{change_set_type}' at 'changeSetType' failed to satisfy constraint: Member must satisfy enum value set: [IMPORT, UPDATE, CREATE]" return error_response(msg, code=400, code_string="ValidationError") change_set = StackChangeSet(req_params, template) # TODO: refactor the flow here deployer = template_deployer.TemplateDeployer(change_set) deployer.construct_changes( stack, change_set, change_set_id=change_set.change_set_id, append_to_changeset=True, ) # TODO: ignores return value (?) deployer.apply_parameter_changes( change_set, change_set) # TODO: bandaid to populate metadata stack.change_sets.append(change_set) change_set.metadata[ "Status"] = "CREATE_COMPLETE" # technically for some time this should first be CREATE_PENDING change_set.metadata[ "ExecutionStatus"] = "AVAILABLE" # technically for some time this should first be UNAVAILABLE return {"StackId": change_set.stack_id, "Id": change_set.change_set_id}
def test_is_none_or_empty_lists(obj, result): assert is_none_or_empty(obj) == result
def test_is_none_or_empty_strings(obj, result): assert is_none_or_empty(obj) == result