Exemplo n.º 1
0
    def create_stack(self, context: RequestContext, request: CreateStackInput) -> CreateStackOutput:
        state = CloudFormationRegion.get()
        template_deployer.prepare_template_body(request)  # TODO: avoid mutating request directly
        template = template_preparer.parse_template(request["TemplateBody"])
        stack_name = template["StackName"] = request.get("StackName")
        stack = Stack(request, template)

        # find existing stack with same name, and remove it if this stack is in DELETED state
        existing = ([s for s in state.stacks.values() if s.stack_name == stack_name] or [None])[0]
        if existing:
            if "DELETE" not in existing.status:
                raise ValidationError(
                    f'Stack named "{stack_name}" already exists with status "{existing.status}"'
                )
            state.stacks.pop(existing.stack_id)

        state.stacks[stack.stack_id] = stack
        LOG.debug(
            'Creating stack "%s" with %s resources ...',
            stack.stack_name,
            len(stack.template_resources),
        )
        deployer = template_deployer.TemplateDeployer(stack)
        try:
            # TODO: create separate step to first resolve parameters
            deployer.deploy_stack()
        except Exception as e:
            stack.set_stack_status("CREATE_FAILED")
            msg = 'Unable to create stack "%s": %s' % (stack.stack_name, e)
            LOG.exception("%s")
            raise ValidationError(msg) from e

        return CreateStackOutput(StackId=stack.stack_id)
Exemplo n.º 2
0
def create_change_set(req_params):
    stack_name = req_params.get('StackName')
    template_deployer.prepare_template_body(req_params)
    template = template_preparer.parse_template(req_params.pop('TemplateBody'))
    template['StackName'] = stack_name
    template['ChangeSetName'] = req_params.get('ChangeSetName')
    stack = existing = find_stack(stack_name)
    if not existing:
        # automatically create (empty) stack if none exists yet
        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('CREATE_COMPLETE')
    change_set = StackChangeSet(req_params, template)
    deployer = template_deployer.TemplateDeployer(stack)
    deployer.construct_changes(stack,
                               change_set,
                               change_set_id=change_set.change_set_id,
                               append_to_changeset=True)
    stack.change_sets.append(change_set)
    change_set.metadata['Status'] = 'CREATE_COMPLETE'
    change_set.metadata['ExecutionStatus'] = 'AVAILABLE'
    change_set.metadata['StatusReason'] = 'Changeset created'
    return {'StackId': change_set.stack_id, 'Id': change_set.change_set_id}
Exemplo n.º 3
0
    def execute_change_set(
        self,
        context: RequestContext,
        change_set_name: ChangeSetNameOrId,
        stack_name: StackNameOrId = None,
        client_request_token: ClientRequestToken = None,
        disable_rollback: DisableRollback = None,
    ) -> ExecuteChangeSetOutput:
        change_set = find_change_set(change_set_name, stack_name=stack_name)
        if not change_set:
            return not_found_error(
                f'Unable to find change set "{change_set_name}" for stack "{stack_name}"'
            )
        if change_set.metadata.get("ExecutionStatus") != ExecutionStatus.AVAILABLE:
            LOG.debug("Change set %s not in execution status 'AVAILABLE'", change_set_name)
            raise InvalidChangeSetStatusException(
                f"ChangeSet [{change_set.metadata['ChangeSetId']}] cannot be executed in its current status of [{change_set.metadata.get('Status')}]"
            )
        stack_name = change_set.stack.stack_name
        LOG.debug(
            'Executing change set "%s" for stack "%s" with %s resources ...',
            change_set_name,
            stack_name,
            len(change_set.template_resources),
        )
        deployer = template_deployer.TemplateDeployer(change_set.stack)
        try:
            deployer.apply_change_set(change_set)
            change_set.stack.metadata["ChangeSetId"] = change_set.change_set_id
        except NoStackUpdates:
            # TODO: parity-check if this exception should be re-raised or swallowed
            raise ValidationError("No updates to be performed for stack change set")

        return ExecuteChangeSetOutput()
Exemplo n.º 4
0
def create_change_set(req_params):
    stack_name = req_params.get("StackName")
    template_deployer.prepare_template_body(req_params)
    template = template_preparer.parse_template(req_params.pop("TemplateBody"))
    template["StackName"] = stack_name
    template["ChangeSetName"] = req_params.get("ChangeSetName")
    stack = existing = find_stack(stack_name)
    if not existing:
        # automatically create (empty) stack if none exists yet
        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("CREATE_COMPLETE")
    change_set = StackChangeSet(req_params, template)
    deployer = template_deployer.TemplateDeployer(stack)
    deployer.construct_changes(
        stack,
        change_set,
        change_set_id=change_set.change_set_id,
        append_to_changeset=True,
    )
    stack.change_sets.append(change_set)
    change_set.metadata["Status"] = "CREATE_COMPLETE"
    change_set.metadata["ExecutionStatus"] = "AVAILABLE"
    change_set.metadata["StatusReason"] = "Changeset created"
    return {"StackId": change_set.stack_id, "Id": change_set.change_set_id}
Exemplo n.º 5
0
def create_stack(req_params):
    state = CloudFormationRegion.get()
    template_deployer.prepare_template_body(req_params)
    template = template_preparer.parse_template(req_params['TemplateBody'])
    stack_name = template['StackName'] = req_params.get('StackName')
    stack = Stack(req_params, template)

    # find existing stack with same name, and remove it if this stack is in DELETED state
    existing = ([s for s in state.stacks.values() if s.stack_name == stack_name] or [None])[0]
    if existing:
        if 'DELETE' not in existing.status:
            return error_response('Stack named "%s" already exists with status "%s"' % (
                stack_name, existing.status), code=400, code_string='ValidationError')
        state.stacks.pop(existing.stack_id)

    state.stacks[stack.stack_id] = stack
    LOG.debug('Creating stack "%s" with %s resources ...' % (stack.stack_name, len(stack.template_resources)))
    deployer = template_deployer.TemplateDeployer(stack)
    try:
        deployer.deploy_stack()
    except Exception as e:
        stack.set_stack_status('CREATE_FAILED')
        msg = 'Unable to create stack "%s": %s' % (stack.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 create_stack(req_params):
    state = CloudFormationRegion.get()
    template_deployer.prepare_template_body(req_params)  # TODO: avoid mutating req_params directly
    template = template_preparer.parse_template(req_params["TemplateBody"])
    stack_name = template["StackName"] = req_params.get("StackName")
    stack = Stack(req_params, template)

    # find existing stack with same name, and remove it if this stack is in DELETED state
    existing = ([s for s in state.stacks.values() if s.stack_name == stack_name] or [None])[0]
    if existing:
        if "DELETE" not in existing.status:
            return error_response(
                'Stack named "%s" already exists with status "%s"' % (stack_name, existing.status),
                code=400,
                code_string="ValidationError",
            )
        state.stacks.pop(existing.stack_id)

    state.stacks[stack.stack_id] = stack
    LOG.debug(
        'Creating stack "%s" with %s resources ...'
        % (stack.stack_name, len(stack.template_resources))
    )
    deployer = template_deployer.TemplateDeployer(stack)
    try:
        # TODO: create separate step to first resolve parameters
        deployer.deploy_stack()
    except Exception as e:
        stack.set_stack_status("CREATE_FAILED")
        msg = 'Unable to create stack "%s": %s' % (stack.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
Exemplo n.º 7
0
def delete_stack(req_params):
    state = RegionState.get()
    stack_name = req_params.get('StackName')
    stack = find_stack(stack_name)
    deployer = template_deployer.TemplateDeployer(stack)
    deployer.delete_stack()
    state.stacks.pop(stack.stack_id)
    return {}
Exemplo n.º 8
0
def delete_stack_set(req_params):
    state = CloudFormationRegion.get()
    set_name = req_params.get('StackSetName')
    stack_set = [sset for sset in state.stack_sets.values() if sset.stack_set_name == set_name]
    if not stack_set:
        return not_found_error('Stack set named "%s" does not exist' % set_name)
    for instance in stack_set[0].stack_instances:
        deployer = template_deployer.TemplateDeployer(instance.stack)
        deployer.delete_stack()
    return {}
Exemplo n.º 9
0
def execute_change_set(req_params):
    stack_name = req_params.get('StackName')
    cs_name = req_params.get('ChangeSetName')
    change_set = find_change_set(cs_name, stack_name=stack_name)
    if not change_set:
        return error_response('Unable to find change set "%s" for stack "%s"' % (cs_name, stack_name))
    deployer = template_deployer.TemplateDeployer(change_set.stack)
    deployer.apply_change_set(change_set)
    change_set.stack.metadata['ChangeSetId'] = change_set.change_set_id
    return {}
Exemplo n.º 10
0
 def delete_stack(
     self,
     context: RequestContext,
     stack_name: StackName,
     retain_resources: RetainResources = None,
     role_arn: RoleARN = None,
     client_request_token: ClientRequestToken = None,
 ) -> None:
     stack = find_stack(stack_name)
     deployer = template_deployer.TemplateDeployer(stack)
     deployer.delete_stack()
Exemplo n.º 11
0
def execute_change_set(req_params):
    stack_name = req_params.get('StackName')
    cs_name = req_params.get('ChangeSetName')
    change_set = find_change_set(cs_name, stack_name=stack_name)
    if not change_set:
        return not_found_error('Unable to find change set "%s" for stack "%s"' % (cs_name, stack_name))
    LOG.debug('Executing change set "%s" for stack "%s" with %s resources ...' % (
        cs_name, stack_name, len(change_set.template_resources)))
    deployer = template_deployer.TemplateDeployer(change_set.stack)
    deployer.apply_change_set(change_set)
    change_set.stack.metadata['ChangeSetId'] = change_set.change_set_id
    return {}
Exemplo n.º 12
0
    def delete_stack_set(
        self, context: RequestContext, stack_set_name: StackSetName, call_as: CallAs = None
    ) -> DeleteStackSetOutput:
        state = CloudFormationRegion.get()
        stack_set = [
            sset for sset in state.stack_sets.values() if sset.stack_set_name == stack_set_name
        ]

        if not stack_set:
            return not_found_error(f'Stack set named "{stack_set_name}" does not exist')

        for instance in stack_set[0].stack_instances:
            deployer = template_deployer.TemplateDeployer(instance.stack)
            deployer.delete_stack()
        return DeleteStackSetOutput()
Exemplo n.º 13
0
def create_stack(req_params):
    state = RegionState.get()
    cloudformation_listener.prepare_template_body(req_params)
    template = template_deployer.parse_template(req_params['TemplateBody'])
    template['StackName'] = req_params.get('StackName')
    stack = Stack(req_params, template)
    state.stacks[stack.stack_id] = stack
    deployer = template_deployer.TemplateDeployer(stack)
    try:
        deployer.deploy_stack()
    except Exception as e:
        stack.set_stack_status('CREATE_FAILED')
        msg = 'Unable to create stack "%s": %s' % (stack.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
Exemplo n.º 14
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
Exemplo n.º 15
0
def create_stack(req_params):
    state = CloudFormationRegion.get()
    template_deployer.prepare_template_body(req_params)
    template = template_preparer.parse_template(req_params['TemplateBody'])
    template['StackName'] = req_params.get('StackName')
    # print("##################333", req_params)
    stack = Stack(req_params, template)
    state.stacks[stack.stack_id] = stack
    LOG.debug('Creating stack "%s" with %s resources ...' % (stack.stack_name, len(stack.template_resources)))
    deployer = template_deployer.TemplateDeployer(stack)
    try:
        deployer.deploy_stack()
    except Exception as e:
        stack.set_stack_status('CREATE_FAILED')
        msg = 'Unable to create stack "%s": %s' % (stack.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
Exemplo n.º 16
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)
Exemplo n.º 17
0
def delete_stack(req_params):
    stack_name = req_params.get("StackName")
    stack = find_stack(stack_name)
    deployer = template_deployer.TemplateDeployer(stack)
    deployer.delete_stack()
    return {}
Exemplo n.º 18
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}
Exemplo n.º 19
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)