def update_stack(req_params): stack_name = req_params.get('StackName') stack = find_stack(stack_name) if not stack: return not_found_error('Unable to update non-existing stack "%s"' % stack_name) template_preparer.prepare_template_body(req_params) template = template_preparer.parse_template(req_params['TemplateBody']) new_stack = Stack(req_params, template) deployer = template_deployer.TemplateDeployer(stack) try: deployer.update_stack(new_stack) except Exception as e: stack.set_stack_status('UPDATE_FAILED') msg = 'Unable to update stack "%s": %s' % (stack_name, e) LOG.debug('%s %s' % (msg, traceback.format_exc())) return error_response(msg, code=400, code_string='ValidationError') result = {'StackId': stack.stack_id} return result
def update_stack( self, context: RequestContext, request: UpdateStackInput, ) -> UpdateStackOutput: stack_name = request.get("StackName") stack = find_stack(stack_name) if not stack: return not_found_error(f'Unable to update non-existing stack "{stack_name}"') template_preparer.prepare_template_body(request) template = template_preparer.parse_template(request["TemplateBody"]) new_stack = Stack(request, template) deployer = template_deployer.TemplateDeployer(stack) try: deployer.update_stack(new_stack) except Exception as e: stack.set_stack_status("UPDATE_FAILED") msg = f'Unable to update stack "{stack_name}": {e}' LOG.exception("%s", msg) raise ValidationError(msg) from e return UpdateStackOutput(StackId=stack.stack_id)
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 create_change_set( self, context: RequestContext, request: CreateChangeSetInput ) -> CreateChangeSetOutput: req_params = request change_set_type = req_params.get("ChangeSetType", "UPDATE") stack_name = req_params.get("StackName") change_set_name = req_params.get("ChangeSetName") template_body = req_params.get("TemplateBody") # s3 or secretsmanager url template_url = req_params.get("TemplateURL") stack = find_stack(stack_name) # validate and resolve template if template_body and template_url: raise ValidationError( "Specify exactly one of 'TemplateBody' or 'TemplateUrl'" ) # TODO: check proper message if not template_body and not template_url: raise ValidationError( "Specify exactly one of 'TemplateBody' or 'TemplateUrl'" ) # 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: raise ValidationError( f"Stack '{stack_name}' does not exist." ) # stack should exist already elif change_set_type == "CREATE": # create new (empty) stack if stack is not None: raise ValidationError( f"Stack {stack_name} already exists" ) # 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 " f"constraint: Member must satisfy enum value set: [IMPORT, UPDATE, CREATE] " ) raise ValidationError(msg) change_set = StackChangeSet(req_params, template) # TODO: refactor the flow here deployer = template_deployer.TemplateDeployer(change_set) changes = deployer.construct_changes( stack, change_set, change_set_id=change_set.change_set_id, append_to_changeset=True, filter_unchanged_resources=True, ) deployer.apply_parameter_changes( change_set, change_set ) # TODO: bandaid to populate metadata stack.change_sets.append(change_set) if not changes: change_set.metadata["Status"] = "FAILED" change_set.metadata["ExecutionStatus"] = "UNAVAILABLE" change_set.metadata[ "StatusReason" ] = "The submitted information didn't contain changes. Submit different information to create a change set." else: 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 CreateChangeSetOutput(StackId=change_set.stack_id, Id=change_set.change_set_id)