jsii icon indicating copy to clipboard operation
jsii copied to clipboard

Golang CDK: synth fails when using lazy string due to circular reference

Open tdenniston opened this issue 1 year ago • 3 comments

Describe the bug

This is probably specific to the Go CDK bindings, or it is also possible I am misunderstanding the use of the lazy string mechanism. When I try to use the awscdk.Lazy_String method for e.g. a log group name, synth fails due to a circular reference:

$ CDK_DEBUG=true cdk synth
panic: TypeError: Resolution error: Resolution error: Resolution error: Converting circular structure to JSON
    --> starting at object with constructor 'CfnLogGroup'
    |     property 'node' -> object with constructor 'Node'
    --- property 'host' closes the circle.
Object creation stack:
  at Lazy.string (/tmp/jsii-kernel-u0LhzS/node_modules/aws-cdk-lib/core/lib/lazy.js:1:953)
  at /tmp/jsii-runtime.3237986321/lib/program.js:9876:152
  at Kernel._Kernel_ensureSync (/tmp/jsii-runtime.3237986321/lib/program.js:10480:24)
  at Kernel.sinvoke (/tmp/jsii-runtime.3237986321/lib/program.js:9876:102)
  at KernelHost.processRequest (/tmp/jsii-runtime.3237986321/lib/program.js:11696:36)
  at KernelHost.run (/tmp/jsii-runtime.3237986321/lib/program.js:11656:22)
  at Immediate._onImmediate (/tmp/jsii-runtime.3237986321/lib/program.js:11657:46)
  at process.processImmediate (node:internal/timers:476:21)..

See below for a very simple example that reproduces the issue.

Expected Behavior

Able to use the awscdk.Lazy_String function.

Current Behavior

Synth fails when using the awscdk.Lazy_String function.

Reproduction Steps

Complete example:

package main

import (
	"github.com/aws/aws-cdk-go/awscdk/v2"
	"github.com/aws/aws-cdk-go/awscdk/v2/awslogs"

	"github.com/aws/jsii-runtime-go"
)

type LazyString struct{}

func (l *LazyString) Produce() *string {
	s := "value"
	return &s
}

func main() {
	defer jsii.Close()

	app := awscdk.NewApp(nil)
	stack := awscdk.NewStack(app, jsii.String("stack"), &awscdk.StackProps{})

	lazy := &LazyString{}
	awslogs.NewLogGroup(stack, jsii.String("log-group"), &awslogs.LogGroupProps{
		LogGroupName: awscdk.Lazy_String(lazy, &awscdk.LazyStringValueOptions{}),
	})

	app.Synth(nil)
}

Then just run cdk synth, and it will fail.

Possible Solution

No response

Additional Information/Context

No response

CDK CLI Version

2.137.0 (build bb90b4c)

Framework Version

No response

Node.js Version

18.18.2

OS

Linux

Language

Go

Language Version

go version go1.20.12 linux/amd64

Other information

No response

tdenniston avatar Apr 19 '24 15:04 tdenniston

Reproducible.

  • Similar code below in TypeScript works:

    import * as cdk from 'aws-cdk-lib';
    import * as awslogs from 'aws-cdk-lib/aws-logs';
    import { TypescriptStack } from '../lib/typescript-stack';
    
    const app = new cdk.App();
    
    const stack = new cdk.Stack(app, 'MyStack');
    new awslogs.LogGroup(stack, 'log-group', {
        logGroupName: cdk.Lazy.string({
            produce() {
                return 'value';
            }})
    });
    
  • .NET code:

    using Amazon.CDK;
    using Amazon.CDK.AWS.Logs;
    
    namespace Dotnet
    {
        sealed class Program
        {
            public static void Main(string[] args)
            {
                var app = new App();
                var stack = new Stack(app, "MyStack");
    
                new LogGroup(stack, "log-group", new LogGroupProps()
                {
                    LogGroupName = Lazy.String(new Producer())
                });
    
                app.Synth();
            }
    
            class Producer : IStableStringProducer
            {
                public string Produce()
                {
                    return "value";
                }
            }
        }
    }
    

    fails with below error:

    Unhandled exception. System.ArgumentException: Could not convert argument 'Dotnet.Program+Producer' to Jsii (Parameter 'arguments')
       at Amazon.JSII.Runtime.Deputy.DeputyBase.<>c__DisplayClass20_0.<ConvertArguments>b__0(Parameter parameter, Object frameworkArgument)
       at System.Linq.Enumerable.ZipIterator[TFirst,TSecond,TResult](IEnumerable`1 first, IEnumerable`1 second, Func`3 resultSelector)+MoveNext()
       at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
       at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
       at Amazon.JSII.Runtime.Deputy.DeputyBase.ConvertArguments(Parameter[] parameters, Object[] arguments)
       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.InvokeStaticMethod[T](Type type, Type[] parameterTypes, Object[] arguments, String methodName)
       at Amazon.CDK.Lazy.String(IStableStringProducer producer, ILazyStringValueOptions options)
       at Dotnet.Program.Main(String[] args) in /Users/tempuser/dev/repros/cdk/issue29906_go/dotnet/src/Dotnet/Program.cs:line 14
    
    Subprocess exited with error 134
    
  • There was an issue https://github.com/aws/jsii/issues/1781 for Python which was closed due to staleness.

ashishdhingra avatar Apr 19 '24 22:04 ashishdhingra

I hit this bug with python and found a workaround. Taking the example from the CDK Guide:

class Producer:
    def __init__(self, func):
        self.produce = func
class FixedProducer:
    __jsii_type__ = None

    def __init__(self, func):
        self.func = func

    @jsii.member(jsii_name='produce')
    def produce(self, context):
        return self.func()

Maybe this can be of some use with Go as well?

teemu-laakso avatar May 16 '24 13:05 teemu-laakso