Ejemplo n.º 1
0
    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
Ejemplo n.º 2
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}
Ejemplo n.º 3
0
def test_is_none_or_empty_lists(obj, result):
    assert is_none_or_empty(obj) == result
Ejemplo n.º 4
0
def test_is_none_or_empty_strings(obj, result):
    assert is_none_or_empty(obj) == result