def test_sns_topic_fifo_without_suffix_fails(
    cfn_client,
    sns_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    """topic name needs .fifo suffix to be valid"""
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    topic_name = f"topic-{short_uid()}"
    template_rendered = jinja2.Template(load_template_raw("sns_topic_fifo_dedup.yaml")).render(
        sns_topic=topic_name
    )

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]
    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        stack = cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0]
        assert stack.get("StackStatus") == "CREATE_FAILED"  # TODO: might be different on AWS, check
    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_execute_change_set(cfn_client, sns_client, is_change_set_finished,
                            cleanup_changesets, cleanup_stacks):
    """check if executing a change set succeeds in creating/modifying the resources in changed"""

    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("sns_topic_simple.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]
    assert change_set_id
    assert stack_id

    try:
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_change_set_finished(change_set_id))
        # check if stack resource was created
        topics = sns_client.list_topics()
        topic_arns = list(map(lambda x: x["TopicArn"], topics["Topics"]))
        assert any(("sns-topic-simple" in t) for t in topic_arns)
    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_create_change_set_create_existing(cfn_client, is_stack_created,
                                           cleanup_changesets, cleanup_stacks):
    """tries to create an already existing stack"""

    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("sns_topic_simple.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]
    assert change_set_id
    assert stack_id
    try:
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        with pytest.raises(Exception) as ex:
            change_set_name2 = f"change-set-{short_uid()}"
            cfn_client.create_change_set(
                StackName=stack_name,
                ChangeSetName=change_set_name2,
                TemplateBody=load_template_raw("sns_topic_simple.yaml"),
                ChangeSetType="CREATE",
            )
        assert ex is not None
    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_cfn_secretsmanager_gen_secret(
    cfn_client,
    secretsmanager_client,
    is_stack_created,
    cleanup_stacks,
):
    stack_name = f"stack-{short_uid()}"
    response = cfn_client.create_stack(
        StackName=stack_name,
        TemplateBody=TEMPLATE_GENERATE_SECRET,
    )
    stack_id = response["StackId"]

    try:
        wait_until(is_stack_created(stack_id))
        secret = secretsmanager_client.describe_secret(SecretId="/dev/db/pass")
        assert "/dev/db/pass" == secret["Name"]
        assert "secret:/dev/db/pass" in secret["ARN"]

        # assert that secret has ben generated and added to the result template JSON
        value = secretsmanager_client.get_secret_value(SecretId="/dev/db/pass")
        secret = value.get("SecretString")
        secret = json.loads(secret)
        assert "password" in secret
        assert len(secret["password"]) == 30
    finally:
        cleanup_stacks([stack_id])
Example #5
0
def test_bucket_autoname(
    cfn_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"STACK-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("s3_bucket_autoname.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        descr_response = cfn_client.describe_stacks(StackName=stack_id)
        output = descr_response["Stacks"][0]["Outputs"][0]

        assert output["OutputKey"] == "BucketNameOutput"
        assert stack_name.lower() in output["OutputValue"]

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #6
0
def test_cfn_secretsmanager_gen_secret(
    cfn_client,
    secretsmanager_client,
    is_stack_created,
    cleanup_stacks,
):
    stack_name = f"stack-{short_uid()}"
    response = cfn_client.create_stack(
        StackName=stack_name,
        TemplateBody=TEMPLATE_GENERATE_SECRET,
    )
    stack_id = response["StackId"]

    try:
        wait_until(is_stack_created(stack_id))
        secret = secretsmanager_client.describe_secret(SecretId="/dev/db/pass")
        assert "/dev/db/pass" == secret["Name"]
        assert "secret:/dev/db/pass" in secret["ARN"]

        # assert that secret has ben generated and added to the result template JSON
        value = secretsmanager_client.get_secret_value(SecretId="/dev/db/pass")
        secret = value.get("SecretString")
        secret = json.loads(secret)
        assert "password" in secret
        assert len(secret["password"]) == 30

        # assert that the Ref properly returns the secret ARN
        result = cfn_client.describe_stacks(StackName=stack_name)["Stacks"][0]
        assert len(result["Outputs"]) == 1
        assert result["Outputs"][0]["OutputKey"] == "SecretARN"
        assert re.match(r".*%s-[a-zA-Z0-9]+" % SECRET_NAME,
                        result["Outputs"][0]["OutputValue"])
    finally:
        cleanup_stacks([stack_id])
def test_resolve_ssm_withversion(
    ssm_client,
    cfn_client,
    is_change_set_created_and_available,
    is_stack_created,
    cleanup_changesets,
    cleanup_stacks,
    create_parameter,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    parameter_key = f"param-key-{short_uid()}"
    parameter_value_v0 = f"param-value-{short_uid()}"
    parameter_value_v1 = f"param-value-{short_uid()}"
    parameter_value_v2 = f"param-value-{short_uid()}"

    create_parameter(Name=parameter_key,
                     Type="String",
                     Value=parameter_value_v0)
    v1 = ssm_client.put_parameter(Name=parameter_key,
                                  Overwrite=True,
                                  Type="String",
                                  Value=parameter_value_v1)
    ssm_client.put_parameter(Name=parameter_key,
                             Overwrite=True,
                             Type="String",
                             Value=parameter_value_v2)

    template_rendered = jinja2.Template(
        load_template_raw("resolve_ssm_withversion.yaml")).render(
            parameter_key=parameter_key, parameter_version=str(v1["Version"]))

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))
        describe_result = cfn_client.describe_stacks(
            StackName=stack_id)["Stacks"][0]
        assert describe_result["StackStatus"] == "CREATE_COMPLETE"

        topic_name = [
            o["OutputValue"] for o in describe_result["Outputs"]
            if o["OutputKey"] == "TopicName"
        ][0]
        assert topic_name == parameter_value_v1

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #8
0
def test_intrinsic_functions(
    cfn_client,
    s3_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
    intrinsic_fn,
    parameter_1,
    parameter_2,
    expected_bucket_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    bucket_name = f"ls-bucket-{short_uid()}"
    template_rendered = jinja2.Template(
        load_template_raw("cfn_intrinsic_functions.yaml")).render(
            bucket_name=bucket_name,
            intrinsic_fn=intrinsic_fn,
        )
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
        Parameters=[
            {
                "ParameterKey": "Param1",
                "ParameterValue": parameter_1
            },
            {
                "ParameterKey": "Param2",
                "ParameterValue": parameter_2
            },
        ],
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        buckets = s3_client.list_buckets()
        bucket_names = [b["Name"] for b in buckets["Buckets"]]

        assert (bucket_name in bucket_names) == expected_bucket_created

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_create_change_set_without_parameters(
        cfn_client, sns_client, cleanup_stacks, cleanup_changesets,
        is_change_set_created_and_available):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("sns_topic_simple.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]
    assert change_set_id
    assert stack_id

    try:
        # make sure the change set wasn't executed (which would create a topic)
        topics = sns_client.list_topics()
        topic_arns = list(map(lambda x: x["TopicArn"], topics["Topics"]))
        assert not any("sns-topic-simple" in arn for arn in topic_arns)
        # stack is initially in REVIEW_IN_PROGRESS state. only after executing the change_set will it change its status
        stack_response = cfn_client.describe_stacks(StackName=stack_id)
        assert stack_response["Stacks"][0][
            "StackStatus"] == "REVIEW_IN_PROGRESS"

        # Change set can now either be already created/available or it is pending/unavailable
        wait_until(is_change_set_created_and_available(change_set_id),
                   2,
                   10,
                   strategy="exponential")
        describe_response = cfn_client.describe_change_set(
            ChangeSetName=change_set_id)

        assert describe_response["ChangeSetName"] == change_set_name
        assert describe_response["ChangeSetId"] == change_set_id
        assert describe_response["StackId"] == stack_id
        assert describe_response["StackName"] == stack_name
        assert describe_response["ExecutionStatus"] == "AVAILABLE"
        assert describe_response["Status"] == "CREATE_COMPLETE"
        changes = describe_response["Changes"]
        assert len(changes) == 1
        assert changes[0]["Type"] == "Resource"
        assert changes[0]["ResourceChange"]["Action"] == "Add"
        assert changes[0]["ResourceChange"][
            "ResourceType"] == "AWS::SNS::Topic"
        assert changes[0]["ResourceChange"]["LogicalResourceId"] == "topic123"
    finally:
        cleanup_stacks([stack_id])
        cleanup_changesets([change_set_id])
def test_multiregion_nested(region_name, statemachine_definition):
    client1 = aws_stack.create_external_boto_client("stepfunctions",
                                                    region_name=region_name)
    # create state machine
    child_machine_name = f"sf-child-{short_uid()}"
    role = aws_stack.role_arn("sfn_role")
    child_machine_result = client1.create_state_machine(
        name=child_machine_name,
        definition=json.dumps(TEST_STATE_MACHINE),
        roleArn=role)
    child_machine_arn = child_machine_result["stateMachineArn"]

    # create parent state machine
    name = f"sf-parent-{short_uid()}"
    role = aws_stack.role_arn("sfn_role")
    result = client1.create_state_machine(
        name=name,
        definition=json.dumps(statemachine_definition).replace(
            "__machine_arn__", child_machine_arn),
        roleArn=role,
    )
    machine_arn = result["stateMachineArn"]
    try:
        # list state machine
        result = client1.list_state_machines()["stateMachines"]
        assert len(result) > 0
        assert len([sm for sm in result if sm["name"] == name]) == 1
        assert len([sm for sm in result
                    if sm["name"] == child_machine_name]) == 1

        # start state machine execution
        result = client1.start_execution(stateMachineArn=machine_arn)

        execution = client1.describe_execution(
            executionArn=result["executionArn"])
        assert execution["stateMachineArn"] == machine_arn
        assert execution["status"] in ["RUNNING", "SUCCEEDED"]

        def assert_success():
            return (client1.describe_execution(
                executionArn=result["executionArn"])["status"] == "SUCCEEDED")

        wait_until(assert_success)

        result = client1.describe_state_machine_for_execution(
            executionArn=result["executionArn"])
        assert result["stateMachineArn"] == machine_arn

    finally:
        client1.delete_state_machine(stateMachineArn=machine_arn)
        client1.delete_state_machine(stateMachineArn=child_machine_arn)
Example #11
0
def test_logstream(
    cfn_client,
    logs_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("logs_group_and_stream.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))
        assert (cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0]
                ["StackStatus"] == "CREATE_COMPLETE")

        descr_response = cfn_client.describe_stacks(StackName=stack_id)
        outputs = {
            o["OutputKey"]: o["OutputValue"]
            for o in descr_response["Stacks"][0]["Outputs"]
        }
        group_name = outputs["LogGroupNameOutput"]
        stream_name = outputs["LogStreamNameOutput"]
        assert group_name
        assert stream_name

        streams = logs_client.describe_log_streams(
            logGroupName=group_name,
            logStreamNamePrefix=stream_name)["logStreams"]
        assert len(streams) == 1
        assert streams[0]["logStreamName"] == stream_name
        assert re.match(
            r"arn:(aws|aws-cn|aws-iso|aws-iso-b|aws-us-gov):logs:.+:.+:log-group:.+:log-stream:.+",
            streams[0]["arn"],
        )
        assert testutil.response_arn_matches_partition(cfn_client,
                                                       streams[0]["arn"])

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #12
0
def test_url_output(
    cfn_client,
    apigateway_client,
    is_change_set_created_and_available,
    is_stack_created,
    cleanup_changesets,
    cleanup_stacks,
    tmp_http_server,
):
    test_port, invocations, proxy = tmp_http_server
    integration_uri = f"http://localhost:{test_port}/{{proxy}}"
    api_name = f"rest-api-{short_uid()}"
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    template_rendered = jinja2.Template(
        load_template_raw("apigateway-url-output.yaml")).render(
            api_name=api_name, integration_uri=integration_uri)
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        describe_response = cfn_client.describe_stacks(StackName=stack_id)
        outputs = describe_response["Stacks"][0]["Outputs"]
        assert len(outputs) == 2
        api_id = [
            o["OutputValue"] for o in outputs
            if o["OutputKey"] == "ApiV1IdOutput"
        ][0]
        api_url = [
            o["OutputValue"] for o in outputs
            if o["OutputKey"] == "ApiV1UrlOutput"
        ][0]
        assert api_id
        assert api_url
        assert api_id in api_url

        assert f"https://{api_id}.execute-api.{constants.LOCALHOST_HOSTNAME}:4566" in api_url

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_bucketpolicy(
    cfn_client,
    s3_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    bucket_name = f"ls-bucket-{short_uid()}"
    template_rendered = jinja2.Template(load_template_raw("s3_bucketpolicy.yaml")).render(
        bucket_name=bucket_name,
        include_policy=True,
    )

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        bucket_policy = s3_client.get_bucket_policy(Bucket=bucket_name)["Policy"]
        assert bucket_policy

        nopolicy_template = jinja2.Template(load_template_raw("s3_bucketpolicy.yaml")).render(
            bucket_name=bucket_name,
            include_policy=False,
        )
        nopolicy_changeset_name = f"change-set-{short_uid()}"
        response = cfn_client.create_change_set(
            StackName=stack_name,
            ChangeSetName=nopolicy_changeset_name,
            TemplateBody=nopolicy_template,
            ChangeSetType="UPDATE",
        )
        change_set_id = response["Id"]
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(
            is_stack_created(stack_id)
        )  # TODO: fix cloudformation update status when using changesets

        with pytest.raises(Exception) as err:
            s3_client.get_bucket_policy(Bucket=bucket_name).get("Policy")

        assert err.value.response["Error"]["Code"] == "NoSuchBucketPolicy"

    finally:
        # pass
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #14
0
def test_eventbus_policy_statement(
    cfn_client,
    events_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    event_bus_name = f"event-bus-{short_uid()}"
    statement_id = f"statement-{short_uid()}"
    template_rendered = jinja2.Template(
        load_template_raw("eventbridge_policy_statement.yaml")).render(
            event_bus_name=event_bus_name, statement_id=statement_id)

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )

    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))
        assert (cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0]
                ["StackStatus"] == "CREATE_COMPLETE")

        describe_response = events_client.describe_event_bus(
            Name=event_bus_name)
        policy = json.loads(describe_response["Policy"])
        assert policy["Version"] == "2012-10-17"
        assert len(policy["Statement"]) == 1
        statement = policy["Statement"][0]
        assert statement["Sid"] == statement_id
        assert statement["Action"] == "events:PutEvents"
        assert statement["Principal"] == "*"
        assert statement["Effect"] == "Allow"
        assert event_bus_name in statement["Resource"]

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #15
0
    def _wait_until_ready(function_name: str, qualifier: str = None):
        def _is_not_pending():
            kwargs = {}
            if qualifier:
                kwargs["Qualifier"] = qualifier
            try:
                result = (lambda_client.get_function(
                    FunctionName=function_name)["Configuration"]["State"] !=
                          "Pending")
                LOG.debug(f"lambda state result: {result=}")
                return result
            except Exception as e:
                LOG.error(e)
                raise

        wait_until(_is_not_pending)
def test_resolve_secretsmanager(
    secretsmanager_client,
    cfn_client,
    is_change_set_created_and_available,
    is_stack_created,
    create_secret,
    create_parameter,
    cleanup_changesets,
    cleanup_stacks,
    template_name,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    parameter_key = f"param-key-{short_uid()}"
    parameter_value = f"param-value-{short_uid()}"

    create_secret(Name=parameter_key, SecretString=parameter_value)

    template_rendered = jinja2.Template(
        load_template_raw(template_name)).render(parameter_key=parameter_key, )

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))
        describe_result = cfn_client.describe_stacks(
            StackName=stack_id)["Stacks"][0]
        assert describe_result["StackStatus"] == "CREATE_COMPLETE"

        topic_name = [
            o["OutputValue"] for o in describe_result["Outputs"]
            if o["OutputKey"] == "TopicName"
        ][0]
        assert topic_name == parameter_value

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #17
0
    def _deploy(
        *,
        is_update: Optional[bool] = False,
        stack_name: Optional[str] = None,
        template: Optional[str] = None,
        template_file_name: Optional[str] = None,
        template_mapping: Optional[Dict[str, any]] = None,
        parameters: Optional[Dict[str, str]] = None,
    ) -> DeployResult:
        if is_update:
            assert stack_name
        else:
            stack_name = f"stack-{short_uid()}"
        change_set_name = f"change-set-{short_uid()}"

        if template_file_name is not None and os.path.exists(
                template_path(template_file_name)):
            template = load_file(template_path(template_file_name))
        template_rendered = render_template(template, **(template_mapping
                                                         or {}))

        response = cfn_client.create_change_set(
            StackName=stack_name,
            ChangeSetName=change_set_name,
            TemplateBody=template_rendered,
            ChangeSetType=("UPDATE" if is_update else "CREATE"),
            Parameters=[{
                "ParameterKey": k,
                "ParameterValue": v,
            } for (k, v) in (parameters or {}).items()],
        )
        change_set_id = response["Id"]
        stack_id = response["StackId"]

        assert wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        assert wait_until(is_change_set_finished(change_set_id))

        outputs = cfn_client.describe_stacks(
            StackName=stack_id)["Stacks"][0].get("Outputs", [])

        mapped_outputs = {o["OutputKey"]: o["OutputValue"] for o in outputs}

        state.append({"stack_id": stack_id, "change_set_id": change_set_id})
        return DeployResult(change_set_id, stack_id, stack_name,
                            change_set_name, mapped_outputs)
Example #18
0
        def _create_function():
            resp = testutil.create_lambda_function(**kwargs)
            lambda_arns.append(resp["CreateFunctionResponse"]["FunctionArn"])

            def _is_not_pending():
                try:
                    result = (lambda_client.get_function(
                        FunctionName=kwargs["func_name"])["Configuration"]
                              ["State"] != "Pending")
                    LOG.debug(f"lambda state result: {result=}")
                    return result
                except Exception as e:
                    LOG.error(e)
                    raise

            wait_until(_is_not_pending)
            return resp
def test_update_lambda_inline_code(cfn_client, lambda_client, is_stack_created,
                                   is_stack_updated, cleanup_stacks):
    stack_name = f"stack-{short_uid()}"
    function_name = f"test-fn-{short_uid()}"

    try:
        template_1 = jinja2.Template(
            load_template_raw("lambda_inline_code.yaml")).render(
                lambda_return_value="hello world",
                arch="x86_64",
                function_name=function_name,
            )
        response = cfn_client.create_stack(StackName=stack_name,
                                           TemplateBody=template_1,
                                           Capabilities=["CAPABILITY_IAM"])
        stack_id = response["StackId"]
        assert stack_id
        wait_until(is_stack_created(stack_id))

        rs = lambda_client.get_function(FunctionName=function_name)
        assert function_name == rs["Configuration"]["FunctionName"]
        assert "x86_64" in rs["Configuration"]["Architectures"]
        result = lambda_client.invoke(FunctionName=function_name)
        result = to_str(result["Payload"].read())
        assert result.strip('" \n') == "hello world"

        template_2 = jinja2.Template(
            load_template_raw("lambda_inline_code.yaml")).render(
                lambda_return_value="hello globe",
                arch="arm64",
                function_name=function_name)
        cfn_client.update_stack(StackName=stack_name,
                                TemplateBody=template_2,
                                Capabilities=["CAPABILITY_IAM"])
        wait_until(is_stack_updated(stack_id))

        rs = lambda_client.get_function(FunctionName=function_name)
        assert function_name == rs["Configuration"]["FunctionName"]
        assert "arm64" in rs["Configuration"]["Architectures"]
        result = lambda_client.invoke(FunctionName=function_name)
        result = to_str(result["Payload"].read())
        assert result.strip('" \n') == "hello globe"
    finally:
        # cleanup
        cleanup_stacks([stack_name])
def test_apigateway_invoke_with_path(cfn_client, deploy_cfn_template, stepfunctions_client):
    deploy_result = deploy_cfn_template(template_file_name="sfn_apigateway_two_integrations.yaml")
    state_machine_arn = deploy_result.outputs["statemachineOutput"]

    execution_arn = stepfunctions_client.start_execution(stateMachineArn=state_machine_arn)[
        "executionArn"
    ]

    def _sfn_finished_running():
        return (
            stepfunctions_client.describe_execution(executionArn=execution_arn)["status"]
            != "RUNNING"
        )

    wait_until(_sfn_finished_running)

    execution_result = stepfunctions_client.describe_execution(executionArn=execution_arn)
    assert execution_result["status"] == "SUCCEEDED"
    assert "hello_with_path from stepfunctions" in execution_result["output"]
def test_list_stack_resources_for_removed_resource(cfn_client,
                                                   is_stack_created,
                                                   is_change_set_finished):
    event_bus_name = f"bus-{short_uid()}"
    template = jinja2.Template(
        load_template_raw("eventbridge_policy.yaml")).render(
            event_bus_name=event_bus_name)

    stack_name = f"stack-{short_uid()}"

    response = cfn_client.create_stack(StackName=stack_name,
                                       TemplateBody=template)
    stack_id = response["StackId"]
    assert stack_id
    wait_until(is_stack_created(stack_id))

    # get list of stack resources
    resources = cfn_client.list_stack_resources(
        StackName=stack_name)["StackResourceSummaries"]
    resources_before = len(resources)
    assert resources_before == 3
    statuses = set([res["ResourceStatus"] for res in resources])
    assert statuses == {"CREATE_COMPLETE", "UPDATE_COMPLETE"}

    # remove one resource from the template, then update stack (via change set)
    template_dict = yaml.load(template)
    template_dict["Resources"].pop("eventPolicy2")
    template2 = yaml.dump(template_dict)

    response = cfn_client.create_change_set(StackName=stack_name,
                                            ChangeSetName="cs1",
                                            TemplateBody=template2)
    change_set_id = response["Id"]
    cfn_client.execute_change_set(ChangeSetName=change_set_id)
    wait_until(is_change_set_finished(change_set_id))

    # get list of stack resources, again - make sure that deleted resource is not contained in result
    resources = cfn_client.list_stack_resources(
        StackName=stack_name)["StackResourceSummaries"]
    assert len(resources) == resources_before - 1
    statuses = set([res["ResourceStatus"] for res in resources])
    assert statuses == {"CREATE_COMPLETE", "UPDATE_COMPLETE"}
Example #22
0
        def _destroy_stack():
            cfn_client.delete_stack(StackName=stack_id)

            def _await_stack_delete():
                return (cfn_client.describe_stacks(StackName=stack_id)
                        ["Stacks"][0]["StackStatus"] == "DELETE_COMPLETE")

            assert wait_until(_await_stack_delete, _max_wait=60)
            time.sleep(
                2
            )  # TODO: fix in localstack. stack should only be in DELETE_COMPLETE state after all resources have been deleted
def test_statemachine_definitionsubstitution(
    cfn_client,
    lambda_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
    stepfunctions_client,
    s3_client,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("stepfunctions_statemachine_substitutions.yaml"),
        ChangeSetType="CREATE",
        Capabilities=["CAPABILITY_IAM"],
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)

        wait_until(is_stack_created(stack_id))

        stack_result = cfn_client.describe_stacks(StackName=stack_id)
        assert stack_result["Stacks"][0]["StackStatus"] == "CREATE_COMPLETE"

        outputs = stack_result["Stacks"][0]["Outputs"]
        assert len(outputs) == 1
        statemachine_arn = outputs[0]["OutputValue"]

        # execute statemachine
        ex_result = stepfunctions_client.start_execution(stateMachineArn=statemachine_arn)

        def _is_executed():
            return (
                stepfunctions_client.describe_execution(executionArn=ex_result["executionArn"])[
                    "status"
                ]
                != "RUNNING"
            )

        wait_until(_is_executed)
        execution_desc = stepfunctions_client.describe_execution(
            executionArn=ex_result["executionArn"]
        )
        assert execution_desc["status"] == "SUCCEEDED"
        # sync execution is currently not supported since botocore adds a "sync-" prefix
        # ex_result = stepfunctions_client.start_sync_execution(stateMachineArn=statemachine_arn)

        assert "hello from statemachine" in execution_desc["output"]

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_apigateway_invoke_localhost_with_path(
    cfn_client, deploy_cfn_template, stepfunctions_client
):
    """tests the same as above but with the "generic" localhost version of invoking the apigateway"""
    deploy_result = deploy_cfn_template(template_file_name="sfn_apigateway_two_integrations.yaml")
    state_machine_arn = deploy_result.outputs["statemachineOutput"]
    api_url = deploy_result.outputs["LsApiEndpointA06D37E8"]

    # instead of changing the template, we're just mapping the endpoint here to the more generic path-based version
    state_def = stepfunctions_client.describe_state_machine(stateMachineArn=state_machine_arn)[
        "definition"
    ]
    parsed = urllib.parse.urlparse(api_url)
    api_id = parsed.hostname.split(".")[0]
    state = json.loads(state_def)
    stage = state["States"]["LsCallApi"]["Parameters"]["Stage"]
    state["States"]["LsCallApi"]["Parameters"]["ApiEndpoint"] = "localhost:4566"
    state["States"]["LsCallApi"]["Parameters"][
        "Stage"
    ] = f"restapis/{api_id}/{stage}/{PATH_USER_REQUEST}"

    stepfunctions_client.update_state_machine(
        stateMachineArn=state_machine_arn, definition=json.dumps(state)
    )

    execution_arn = stepfunctions_client.start_execution(stateMachineArn=state_machine_arn)[
        "executionArn"
    ]

    def _sfn_finished_running():
        return (
            stepfunctions_client.describe_execution(executionArn=execution_arn)["status"]
            != "RUNNING"
        )

    wait_until(_sfn_finished_running)

    execution_result = stepfunctions_client.describe_execution(executionArn=execution_arn)
    assert execution_result["status"] == "SUCCEEDED"
    assert "hello_with_path from stepfunctions" in execution_result["output"]
def test_sns_subscription(
    cfn_client,
    sns_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    topic_name = f"topic-{short_uid()}"
    queue_name = f"topic-{short_uid()}"
    template_rendered = jinja2.Template(load_template_raw("sns_topic_subscription.yaml")).render(
        topic_name=topic_name, queue_name=queue_name
    )
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        outputs = cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0]["Outputs"]
        assert len(outputs) == 1 and outputs[0]["OutputKey"] == "TopicArnOutput"
        topic_arn = outputs[0]["OutputValue"]
        assert topic_arn is not None

        subscriptions = sns_client.list_subscriptions_by_topic(TopicArn=topic_arn)
        assert len(subscriptions["Subscriptions"]) > 0

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
Example #26
0
def test_kms_key_disabled(
    cfn_client,
    sqs_client,
    kms_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"

    template_rendered = load_template_raw("kms_key_disabled.yaml")
    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))
        outputs = cfn_client.describe_stacks(
            StackName=stack_id)["Stacks"][0]["Outputs"]
        assert len(outputs) == 1
        key_id = outputs[0]["OutputValue"]
        assert key_id
        my_key = kms_client.describe_key(KeyId=key_id)
        assert not my_key["KeyMetadata"]["Enabled"]

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_create_change_set_update_without_parameters(
    cfn_client,
    sns_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_change_set_finished,
):
    """after creating a stack via a CREATE change set we send an UPDATE change set changing the SNS topic name"""
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    change_set_name2 = f"change-set-{short_uid()}"

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=load_template_raw("sns_topic_simple.yaml"),
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]
    assert change_set_id
    assert stack_id

    try:
        # Change set can now either be already created/available or it is pending/unavailable
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_change_set_finished(change_set_id))
        template = load_template_raw("sns_topic_simple.yaml")

        update_response = cfn_client.create_change_set(
            StackName=stack_name,
            ChangeSetName=change_set_name2,
            TemplateBody=template.replace("sns-topic-simple",
                                          "sns-topic-simple-2"),
            ChangeSetType="UPDATE",
        )
        wait_until(is_change_set_created_and_available(update_response["Id"]))
        describe_response = cfn_client.describe_change_set(
            ChangeSetName=update_response["Id"])
        changes = describe_response["Changes"]
        assert len(changes) == 1
        assert changes[0]["Type"] == "Resource"
        change = changes[0]["ResourceChange"]
        assert change["Action"] == "Modify"
        assert change["ResourceType"] == "AWS::SNS::Topic"
        assert change["LogicalResourceId"] == "topic123"
        assert "sns-topic-simple" in change["PhysicalResourceId"]
        assert change["Replacement"] == "True"
        assert "Properties" in change["Scope"]
        assert len(change["Details"]) == 1
        assert change["Details"][0]["Target"]["Name"] == "TopicName"
        assert change["Details"][0]["Target"]["RequiresRecreation"] == "Always"
    finally:
        cleanup_changesets(changesets=[change_set_id])
        cleanup_stacks(stacks=[stack_id])
def test_create_stack_with_ssm_parameters(cfn_client, ssm_client, sns_client,
                                          cleanup_stacks, is_stack_created):
    stack_name = f"stack-{short_uid()}"
    parameter_name = f"ls-param-{short_uid()}"
    parameter_value = f"ls-param-value-{short_uid()}"
    parameter_logical_id = "parameter123"
    ssm_client.put_parameter(Name=parameter_name,
                             Value=parameter_value,
                             Type="String")
    template = load_template_raw("dynamicparameter_ssm_string.yaml")
    template_rendered = jinja2.Template(template).render(
        parameter_name=parameter_name)
    response = cfn_client.create_stack(
        StackName=stack_name,
        TemplateBody=template_rendered,
    )
    stack_id = response["StackId"]
    assert stack_id

    try:
        wait_until(is_stack_created(stack_id))

        created_stack = cfn_client.describe_stacks(
            StackName=stack_name)["Stacks"][0]
        assert created_stack is not None
        assert created_stack["Parameters"][0][
            "ParameterKey"] == parameter_logical_id
        assert created_stack["Parameters"][0][
            "ParameterValue"] == parameter_name
        assert created_stack["Parameters"][0][
            "ResolvedValue"] == parameter_value

        topics = sns_client.list_topics()
        topic_arns = [t["TopicArn"] for t in topics["Topics"]]
        assert any(parameter_value in t for t in topic_arns)
    finally:
        cleanup_stacks([stack_id])
def test_lambda_autogenerated_name(
    cfn_client,
    lambda_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    lambda_functional_id = f"MyFn{short_uid()}"
    template_rendered = jinja2.Template(
        load_template_raw("cfn_lambda_noname.yaml")).render(
            lambda_functional_id=lambda_functional_id)

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        outputs = cfn_client.describe_stacks(
            StackName=stack_id)["Stacks"][0]["Outputs"]
        assert len(outputs) == 1
        assert lambda_functional_id in outputs[0]["OutputValue"]

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])
def test_sns_topic_fifo_with_deduplication(
    cfn_client,
    sns_client,
    cleanup_stacks,
    cleanup_changesets,
    is_change_set_created_and_available,
    is_stack_created,
):
    stack_name = f"stack-{short_uid()}"
    change_set_name = f"change-set-{short_uid()}"
    topic_name = f"topic-{short_uid()}.fifo"
    template_rendered = jinja2.Template(load_template_raw("sns_topic_fifo_dedup.yaml")).render(
        sns_topic=topic_name
    )

    response = cfn_client.create_change_set(
        StackName=stack_name,
        ChangeSetName=change_set_name,
        TemplateBody=template_rendered,
        ChangeSetType="CREATE",
    )
    change_set_id = response["Id"]
    stack_id = response["StackId"]

    try:
        wait_until(is_change_set_created_and_available(change_set_id))
        cfn_client.execute_change_set(ChangeSetName=change_set_id)
        wait_until(is_stack_created(stack_id))

        topics = sns_client.list_topics()["Topics"]
        topic_arns = [t["TopicArn"] for t in topics]

        assert len([t for t in topic_arns if topic_name in t]) == 1

    finally:
        cleanup_changesets([change_set_id])
        cleanup_stacks([stack_id])