aws / aws-cdk

The AWS Cloud Development Kit is a framework for defining cloud infrastructure in code
https://aws.amazon.com/cdk
Apache License 2.0
11.57k stars 3.88k forks source link

elasticache: Serverless cache attr_endpoint returning aws_cdk.Reference #28246

Open Jmevorach opened 10 months ago

Jmevorach commented 10 months ago

Describe the bug

The attr_endpoint attribute of the CfnServerlessCache should be a string or be resolvable to a string but instead it's aws_cdk.Reference instead.

Expected Behavior

I expected that this script should be able to run:

import aws_cdk as cdk
from aws_cdk import (
    Stack,
    aws_elasticache as elasticache,
    aws_ssm as ssm
)
from constructs import Construct
import os

class test_stack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        self.redis_cluster = elasticache.CfnServerlessCache(
            self,
            "RedisCluster",
            engine="redis",
            serverless_cache_name="ServerlessCache",
        )

        self.redis_endpoint = ssm.StringParameter(
            self,
            "redis-endpoint",
            parameter_name="redis_endpoint",
            string_value=self.redis_cluster.attr_endpoint
        )

if __name__ == "__main__":
    app = cdk.App()
    test_stack(app, "test",
    env=cdk.Environment(
        account=os.getenv('CDK_DEFAULT_ACCOUNT'),
        region=os.getenv('CDK_DEFAULT_REGION')
    ))
    app.synth()

Current Behavior

Instead of running successfully this traceback is returned: Traceback (most recent call last): File "/Users/home/test.py", line 32, in test_stack(app, "test", File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/jsii/_runtime.py", line 118, in call inst = super(JSIIMeta, cast(JSIIMeta, cls)).call(*args, *kwargs) File "/Users/home/test.py", line 22, in init self.redis_endpoint = ssm.StringParameter( File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/jsii/_runtime.py", line 118, in call inst = super(JSIIMeta, cast(JSIIMeta, cls)).call(args, **kwargs) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/aws_cdk/aws_ssm/init.py", line 8397, in init props = StringParameterProps( File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/aws_cdk/aws_ssm/init.py", line 8924, in init check_type(argname="argument string_value", value=string_value, expected_type=type_hints["string_value"]) File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/typeguard/init.py", line 785, in check_type raise TypeError( TypeError: type of argument string_value must be str; got aws_cdk.Reference instead

Reproduction Steps

Running this script below should reproduce the bug:

import aws_cdk as cdk
from aws_cdk import (
    Stack,
    aws_elasticache as elasticache,
    aws_ssm as ssm
)
from constructs import Construct
import os

class test_stack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        self.redis_cluster = elasticache.CfnServerlessCache(
            self,
            "RedisCluster",
            engine="redis",
            serverless_cache_name="ServerlessCache",
        )

        self.redis_endpoint = ssm.StringParameter(
            self,
            "redis-endpoint",
            parameter_name="redis_endpoint",
            string_value=self.redis_cluster.attr_endpoint
        )

if __name__ == "__main__":
    app = cdk.App()
    test_stack(app, "test",
    env=cdk.Environment(
        account=os.getenv('CDK_DEFAULT_ACCOUNT'),
        region=os.getenv('CDK_DEFAULT_REGION')
    ))
    app.synth()

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.113.0

Framework Version

No response

Node.js Version

v18.2.0

OS

MacOS 13.5.2

Language

Python

Language Version

3.10

Other information

No response

Jmevorach commented 10 months ago

If I change string_value=self.redis_cluster.attr_endpoint to string_value=self.redis_cluster.attr_endpoint.to_string() and run cdk deploy the stack fails to deploy with ...

| CREATE_FAILED        | AWS::SSM::Parameter                         | redisendpointA956750C
Properties validation failed for resource redisendpointA956750C with message:
#/Value: expected type: String, found: JSONObject
pahud commented 10 months ago

I haven't deployed this resource before it's pretty new but according to the CFN doc

Endpoint Represents the information required for client programs to connect to a cache node.

And JS SDK https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/ElastiCache.html#createServerlessCache-property

I guess it should be a json object that contains both Address and Port.

Are you able to return its address by self.redis_cluster.attr_endpoint.address or attr_endpoint.get('Address') ?

vktrl commented 10 months ago

Having the same issue with the TS CDK. Haven't found a way to extract the address property from the JSON nor verify that it has it.

Jmevorach commented 10 months ago

Using self.redis_cluster.attr_endpoint.address gives AttributeError: 'Reference' object has no attribute 'address'.

Using self.redis_cluster.attr_endpoint.get('Address') gives AttributeError: 'Reference' object has no attribute 'get'.

CEVO-MatheusJGSantos commented 10 months ago

Same issue here. Oddly if I do print("{}".format(self.resolve(myElasticache.get_att('Endpoint')))) I will get a function that should work, but I can't use the actual object anywhere in my stack: {'Fn::GetAtt': ['serverlessRedisCache', 'Endpoint']}

jayhilden commented 9 months ago

+1. If I try to export the address I get an error using the TS client on 2.113.0

Code

const redisServerlessCluster = new CfnServerlessCache(this, cacheName, {
            ...
        })

this.redisServerlessURI = "rediss://" + redisServerlessCluster.attrEndpoint
new CfnOutput(this,  'redisServerlessURI', {
    exportName: 'redisServerlessURI',
    value: this.redisServerlessURI,
})

CFN Error

Requested attribute Endpoint must be a readonly property in schema for AWS::ElastiCache::ServerlessCache

MatheusAfinovicz commented 9 months ago

Having the same issue here with TS CDK. Did a workaround with the SDK to get the DB using its name, and then accessing the endpoint:

        const client = new ElastiCacheClient({
        region
    });

    const describeServerlessCachesCommand = new DescribeServerlessCachesCommand({
        MaxResults: 20,
        ServerlessCacheName: your-db-name
    });

    const redis = await client.send(describeServerlessCachesCommand);

    if (!redis || !redis.ServerlessCaches) throw new Error('No redis found');

    const endpoint = redis.ServerlessCaches[0].Endpoint;

    if (!endpoint) throw new Error('No redis endpoint found');

    return `${endpoint.Address}:${endpoint.Port}`;
ChadMoran commented 9 months ago

I found a workaround.

const cluster = new elasticache.CfnServerlessCache(this, 'RedisCluster', {
  serverlessCacheName: 'redis',
  engine: 'redis',
});

new cdk.CfnOutput(this, 'RedisEndpoint', {
  value: cluster.getAtt('Endpoint.Address').toString(),
});

You can also access Endpoint.Port as seen from aws elasticache describe-serverless-caches

It looks like the docs are out of date or incorrect. It seems that .attr_Endpoint should return the CfnServerlessCache.Endpoint type.