Esempio n. 1
0
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
Esempio n. 2
0
    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)
Esempio n. 3
0
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}
Esempio n. 4
0
    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)