Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Amazon.CDK.AWS.RDS.DatabaseCluster: cyclical reference when using Credentials passed from another stack #30738

Closed
NovaLogicDev opened this issue Jul 2, 2024 · 4 comments
Assignees
Labels
@aws-cdk/aws-rds Related to Amazon Relational Database bug This issue is a bug. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.

Comments

@NovaLogicDev
Copy link

Describe the bug

When running CDK synth on my project:

Unhandled exception. System.Exception: Error: 'ReproBugStack' depends on 'DBStack' (ReproBugStack -> DBStack/AuroraDatabaseCluster/Resource.Ref). Adding this dependency (DBStack -> ReproBugStack/DatabaseClusterSecret/Resource.Ref) would create a cyclic reference.

Expected Behavior

No cyclical reference error, the database stack references the resource created by the ReproBugStack and there is no circular referencing.

Current Behavior

$cdk synth

Unhandled exception. System.Exception: Error: 'ReproBugStack' depends on 'DBStack' (ReproBugStack -> DBStack/AuroraDatabaseCluster/Resource.Ref). Adding this dependency (DBStack -> ReproBugStack/DatabaseClusterSecret/Resource.Ref) would create a cyclic reference.
   at Amazon.JSII.Runtime.Services.Client.TryDeserialize[TResponse](String responseJson)
   at Amazon.JSII.Runtime.Services.Client.ReceiveResponse[TResponse]()
   at Amazon.JSII.Runtime.Services.Client.Send[TRequest,TResponse](TRequest requestObject)
   at Amazon.JSII.Runtime.Services.Client.Invoke(InvokeRequest request)
   at Amazon.JSII.Runtime.Services.Client.Invoke(ObjectReference objectReference, String method, Object[] arguments)
   at Amazon.JSII.Runtime.Deputy.DeputyBase.<>c__DisplayClass17_0`1.<InvokeInstanceMethod>b__1(IClient client, Object[] args)
   at Amazon.JSII.Runtime.Deputy.DeputyBase.<InvokeMethodCore>g__GetResult|18_0[T](<>c__DisplayClass18_0`1&)
   at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeMethodCore[T](JsiiMethodAttribute methodAttribute, Object[] arguments, Func`3 beginFunc, Func`3 invokeFunc)
   at Amazon.JSII.Runtime.Deputy.DeputyBase.InvokeInstanceMethod[T](Type[] parameterTypes, Object[] arguments, String methodName)
   at Amazon.CDK.Stage.Synth(IStageSynthesisOptions options)
   at ReproBug.Program.Main(String[] args) in [REDACTEDFORPRIVACY]/repro-bug/src/ReproBug/Program.cs:line 13

### Reproduction Steps

`Program.cs`
```C#
using Amazon.CDK;

namespace ReproBug
{
    sealed class Program
    {
        public static void Main(string[] args)
        {
            var app = new App();
            var stack1 = new ReproBugStack(app, "ReproBugStack", new StackProps());
            var db = new DBStack(app, "DBStack", new StackProps(), stack1.clusterCredentials);
            // db.AddDependency(stack1); // Error whether commented or not
            app.Synth();
        }
    }
}

ReproBugStack.cs

using Amazon.CDK;
using Amazon.CDK.AWS.RDS;
using Amazon.CDK.AWS.SecretsManager;
using Constructs;

namespace ReproBug
{
    public class ReproBugStack : Stack
    {
        private string auroraUsername;
        private Secret AuroraSecret;
        public Credentials clusterCredentials;
        internal ReproBugStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
        {
            this.auroraUsername = "clusteradmin";
            this.AuroraSecret = new Secret(
                this,
                "DatabaseClusterSecret",
                new SecretProps
                {
                    Description = "Database cluster secret for Edfi",
                    SecretName = "EdfiDatabaseClusterSecret",
                    GenerateSecretString = new SecretStringGenerator
                    {
                        ExcludeCharacters = "\"@/\\ '",
                        GenerateStringKey = "password",
                        PasswordLength = 128,
                        SecretStringTemplate="{\"username\": \""+this.auroraUsername+"\"}"
                    }
                }
            );
            this.clusterCredentials = Credentials.FromSecret(this.AuroraSecret, this.auroraUsername);
        }
    }
}

DatabaseStack.cs

using Amazon.CDK;
using Amazon.CDK.AWS.EC2;
using Amazon.CDK.AWS.RDS;
using Constructs;

namespace ReproBug
{
    public class DBStack : Stack
    {
        internal DBStack(Construct scope, string id, IStackProps props,
            Credentials creds) : base(scope, id, props)
        {
            var vpc = new Vpc(this, "DatabaseVPC");

            var auroraEngine = DatabaseClusterEngine.AuroraPostgres(
                new AuroraPostgresClusterEngineProps
                {
                    Version = AuroraPostgresEngineVersion.VER_16_2
                }
            );

            var database = new DatabaseCluster(this, "AuroraDatabaseCluster",
                new DatabaseClusterProps
                {
                    Engine = auroraEngine,
                    Vpc = vpc,
                    Writer = ClusterInstance.ServerlessV2("Instance1",
                        new ServerlessV2ClusterInstanceProps
                        {
                            PubliclyAccessible = false
                        }
                    ),
                    Credentials = creds
                }
            );
        }
    }
}

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.144.0 (build 5fb15bc)

Framework Version

No response

Node.js Version

v20.14.0

OS

Darwin, 14.5

Language

.NET

Language Version

net6.0

Other information

No response

@NovaLogicDev NovaLogicDev added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jul 2, 2024
@github-actions github-actions bot added the @aws-cdk/aws-rds Related to Amazon Relational Database label Jul 2, 2024
@ashishdhingra ashishdhingra self-assigned this Jul 2, 2024
@ashishdhingra ashishdhingra added needs-reproduction This issue needs reproduction. and removed needs-triage This issue or PR still needs to be triaged. labels Jul 2, 2024
@ashishdhingra
Copy link
Contributor

ashishdhingra commented Jul 3, 2024

@NovaLogicDev Good morning. The issue is reproducible using the below TypeScript CDK code as well:

import * as cdk from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { AuroraPostgresEngineVersion, ClusterInstance, Credentials, DatabaseCluster, DatabaseClusterEngine } from 'aws-cdk-lib/aws-rds';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { Construct } from 'constructs';

export class ReproBugStack extends cdk.Stack {
  private auroraUsername: string;
  private auroraSecret: Secret;
  public clusterCredentials : Credentials;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    this.auroraUsername = "clusteradmin";
    this.auroraSecret = new Secret(this, 'DatabaseClusterSecret', {
      description: 'Database cluster secret for Edfi',
      secretName: 'EdfiDatabaseClusterSecret',
      generateSecretString: {
        excludeCharacters: "\"@/\\ '",
        generateStringKey: 'password',
        passwordLength: 128,
        secretStringTemplate: '{"username": \"'+ this.auroraUsername+ '\"}'
      }
    });

    this.clusterCredentials = Credentials.fromSecret(this.auroraSecret, this.auroraUsername);
  }
}

export interface DBStackProps {
  credentials: Credentials;
}
export class DBStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: DBStackProps) {
    super(scope, id);
  
    const vpc = new Vpc(this, 'DatabaseVPC');

    const auroraEngine = DatabaseClusterEngine.auroraPostgres({
      version: AuroraPostgresEngineVersion.VER_16_2
    });

    const database = new DatabaseCluster(this, 'AuroraDatabaseCluster', {
      engine: auroraEngine,
      vpc: vpc,
      writer: ClusterInstance.serverlessV2('Instance1', {
        publiclyAccessible: false
      }),
      credentials: props.credentials
    });
  }
}

const app = new cdk.App();
const stack1 = new ReproBugStack(app, 'ReproBugStack', {});
new DBStack(app, 'DBStack', { credentials: stack1.clusterCredentials });

Running cdk synth gives below error:

Error: 'ReproBugStack' depends on 'DBStack' (ReproBugStack -> DBStack/AuroraDatabaseCluster/Resource.Ref). Adding this dependency (DBStack -> ReproBugStack/DatabaseClusterSecret/Resource.Ref) would create a cyclic reference.
    at DBStack._addAssemblyDependency (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/stack.js:1:11639)
    at operateOnDependency (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/deps.js:1:1831)
    at addDependency (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/deps.js:1:489)
    at DBStack.addDependency (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/stack.js:1:8701)
    at resolveValue (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/private/refs.js:1:3825)
    at resolveReferences (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/private/refs.js:1:1414)
    at prepareApp (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/private/prepare-app.js:1:802)
    at synthesize (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/private/synthesis.js:1:1530)
    at App.synth (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/stage.js:1:2263)
    at process.<anonymous> (/Users/REDACTED/dev/repros/cdk/issue30738/typescript/node_modules/aws-cdk-lib/core/lib/app.js:1:1745)

Subprocess exited with error 1

The reason you are getting this error is that DBStack is depending on the resource Credentials which are yet to be provisioned via auroraSecret in ReproStack.

Since you have the secretName, the way to get around this issue is to get secret by name in DBStack and add dependency on ReproStack.

Modified code below:

import * as cdk from 'aws-cdk-lib';
import { Vpc } from 'aws-cdk-lib/aws-ec2';
import { AuroraPostgresEngineVersion, ClusterInstance, Credentials, DatabaseCluster, DatabaseClusterEngine } from 'aws-cdk-lib/aws-rds';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { Construct } from 'constructs';

export class ReproBugStack extends cdk.Stack {
  private auroraUsername: string;
  private auroraSecret: Secret;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    this.auroraUsername = "clusteradmin";
    this.auroraSecret = new Secret(this, 'DatabaseClusterSecret', {
      description: 'Database cluster secret for Edfi',
      secretName: 'EdfiDatabaseClusterSecret',
      generateSecretString: {
        excludeCharacters: "\"@/\\ '",
        generateStringKey: 'password',
        passwordLength: 128,
        secretStringTemplate: '{"username": \"'+ this.auroraUsername+ '\"}'
      }
    });
  }
}

export interface DBStackProps {
  credentials: Credentials;
}
export class DBStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);
  
    const vpc = new Vpc(this, 'DatabaseVPC');

    const auroraEngine = DatabaseClusterEngine.auroraPostgres({
      version: AuroraPostgresEngineVersion.VER_16_2
    });

    const auroraUsername = "clusteradmin";
    const auroraSecret = Secret.fromSecretNameV2(this, 'DatabaseClusterSecret', 'EdfiDatabaseClusterSecret');
    const creds = Credentials.fromSecret(auroraSecret, auroraUsername);

    const database = new DatabaseCluster(this, 'AuroraDatabaseCluster', {
      engine: auroraEngine,
      vpc: vpc,
      writer: ClusterInstance.serverlessV2('Instance1', {
        publiclyAccessible: false
      }),
      credentials: creds
    });
  }
}

const app = new cdk.App();
const stack1 = new ReproBugStack(app, 'ReproBugStack', {});
new DBStack(app, 'DBStack').addDependency(stack1);

Please let me know if it works for you.

Thanks,
Ashish

@ashishdhingra ashishdhingra added response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days. and removed needs-reproduction This issue needs reproduction. labels Jul 3, 2024
@NovaLogicDev
Copy link
Author

Yup, that does work, thanks

Copy link

github-actions bot commented Jul 3, 2024

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

@aws-cdk-automation
Copy link
Collaborator

Comments on closed issues and PRs are hard for our team to see. If you need help, please open a new issue that references this one.

@aws aws locked as resolved and limited conversation to collaborators Jul 25, 2024
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
@aws-cdk/aws-rds Related to Amazon Relational Database bug This issue is a bug. response-requested Waiting on additional info and feedback. Will move to "closing-soon" in 7 days.
Projects
None yet
Development

No branches or pull requests

3 participants