diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5ecddce9..f34b730e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,3 +1,6 @@ ### Improvements +- Support (logical) name for resources and config variables + [#546](https://github.com/pulumi/pulumi-yaml/pull/546) + ### Bug Fixes diff --git a/README.md b/README.md index 0b0eee54..c9a1d6bb 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ variables: arguments: filters: - name: name - values: ["amzn-ami-hvm-*-x86_64-ebs"] + values: ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] owners: ["137112412989"] mostRecent: true return: id diff --git a/examples/webserver-json/Main.json b/examples/webserver-json/Main.json index ef07a887..afcf8d4f 100644 --- a/examples/webserver-json/Main.json +++ b/examples/webserver-json/Main.json @@ -31,7 +31,7 @@ "function": "aws:getAmi", "arguments": { "filters": [ - { "name": "name", "values": ["amzn-ami-hvm-*-x86_64-ebs"] } + { "name": "name", "values": ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] } ], "owners": ["137112412989"], "mostRecent": true diff --git a/examples/webserver/Pulumi.yaml b/examples/webserver/Pulumi.yaml index 6bcff8c3..74157489 100755 --- a/examples/webserver/Pulumi.yaml +++ b/examples/webserver/Pulumi.yaml @@ -12,7 +12,7 @@ variables: arguments: filters: - name: name - values: ["amzn-ami-hvm-*-x86_64-ebs"] + values: ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] owners: ["137112412989"] mostRecent: true return: id diff --git a/pkg/pulumiyaml/ast/template.go b/pkg/pulumiyaml/ast/template.go index 2f0d23f1..20f98884 100644 --- a/pkg/pulumiyaml/ast/template.go +++ b/pkg/pulumiyaml/ast/template.go @@ -272,6 +272,7 @@ type ConfigParamDecl struct { declNode Type *StringExpr + Name *StringExpr Secret *BooleanExpr Default Expr Value Expr @@ -281,19 +282,20 @@ func (d *ConfigParamDecl) recordSyntax() *syntax.Node { return &d.syntax } -func ConfigParamSyntax(node *syntax.ObjectNode, typ *StringExpr, +func ConfigParamSyntax(node *syntax.ObjectNode, typ *StringExpr, name *StringExpr, secret *BooleanExpr, defaultValue Expr) *ConfigParamDecl { return &ConfigParamDecl{ declNode: decl(node), Type: typ, + Name: name, Secret: secret, Default: defaultValue, } } -func ConfigParam(typ *StringExpr, defaultValue Expr, secret *BooleanExpr) *ConfigParamDecl { - return ConfigParamSyntax(nil, typ, secret, defaultValue) +func ConfigParam(typ *StringExpr, name *StringExpr, defaultValue Expr, secret *BooleanExpr) *ConfigParamDecl { + return ConfigParamSyntax(nil, typ, name, secret, defaultValue) } type ResourceOptionsDecl struct { @@ -411,6 +413,7 @@ type ResourceDecl struct { declNode Type *StringExpr + Name *StringExpr DefaultProvider *BooleanExpr Properties PropertyMapDecl Options ResourceOptionsDecl @@ -423,14 +426,15 @@ func (d *ResourceDecl) recordSyntax() *syntax.Node { // The names of exported fields. func (*ResourceDecl) Fields() []string { - return []string{"type", "defaultprovider", "properties", "options", "get"} + return []string{"type", "name", "defaultprovider", "properties", "options", "get"} } -func ResourceSyntax(node *syntax.ObjectNode, typ *StringExpr, defaultProvider *BooleanExpr, +func ResourceSyntax(node *syntax.ObjectNode, typ *StringExpr, name *StringExpr, defaultProvider *BooleanExpr, properties PropertyMapDecl, options ResourceOptionsDecl, get GetResourceDecl) *ResourceDecl { return &ResourceDecl{ declNode: decl(node), Type: typ, + Name: name, DefaultProvider: defaultProvider, Properties: properties, Options: options, @@ -438,8 +442,14 @@ func ResourceSyntax(node *syntax.ObjectNode, typ *StringExpr, defaultProvider *B } } -func Resource(typ *StringExpr, defaultProvider *BooleanExpr, properties PropertyMapDecl, options ResourceOptionsDecl, get GetResourceDecl) *ResourceDecl { - return ResourceSyntax(nil, typ, defaultProvider, properties, options, get) +func Resource( + typ *StringExpr, + name *StringExpr, + defaultProvider *BooleanExpr, + properties PropertyMapDecl, + options ResourceOptionsDecl, + get GetResourceDecl) *ResourceDecl { + return ResourceSyntax(nil, typ, name, defaultProvider, properties, options, get) } type CustomTimeoutsDecl struct { diff --git a/pkg/pulumiyaml/codegen/gen_program.go b/pkg/pulumiyaml/codegen/gen_program.go index 2e6cd9ef..a2e84284 100644 --- a/pkg/pulumiyaml/codegen/gen_program.go +++ b/pkg/pulumiyaml/codegen/gen_program.go @@ -32,16 +32,6 @@ import ( func GenerateProgram(program *pcl.Program) (map[string][]byte, hcl.Diagnostics, error) { g := generator{} - g.logicalNames = map[string]string{} - for _, n := range program.Nodes { - switch n := n.(type) { - case *pcl.Resource: - g.logicalNames[n.Name()] = n.LogicalName() - case *pcl.OutputVariable: - g.logicalNames[n.Name()] = n.LogicalName() - } - } - for _, n := range program.Nodes { g.genNode(n) } @@ -96,7 +86,6 @@ func GenerateProject(directory string, project workspace.Project, program *pcl.P type generator struct { diags hcl.Diagnostics - logicalNames map[string]string // These values can be assembled into a template config []syn.ObjectPropertyDef resources []syn.ObjectPropertyDef @@ -239,6 +228,11 @@ func (g *generator) genResource(n *pcl.Resource) { entries := []syn.ObjectPropertyDef{ g.TypeProperty(collapseToken(n.Token)), } + + if n.Name() != n.LogicalName() { + entries = append(entries, syn.ObjectProperty(syn.String("name"), syn.String(n.LogicalName()))) + } + if len(properties) > 0 { entries = append(entries, syn.ObjectProperty(syn.String("properties"), syn.Object(properties...))) } @@ -248,7 +242,7 @@ func (g *generator) genResource(n *pcl.Resource) { r := syn.Object(entries...) g.resources = append(g.resources, syn.ObjectProperty( - syn.StringSyntax(trivia(n.Definition), n.LogicalName()), r)) + syn.StringSyntax(trivia(n.Definition), n.Name()), r)) } func (g *generator) genOutputVariable(n *pcl.OutputVariable) { @@ -561,9 +555,6 @@ func (g *generator) expr(e model.Expression) syn.Node { case *model.ScopeTraversalExpression: rootName := e.RootName - if logicalName, found := g.logicalNames[rootName]; found { - rootName = logicalName - } traversal := g.Traversal(e.Traversal).WithRoot(rootName, e.Tokens.Root.Range().Ptr()) s := fmt.Sprintf("${%s}", traversal) return syn.String(s) @@ -640,6 +631,10 @@ func (g *generator) genConfigVariable(n *pcl.ConfigVariable) { entries := []syn.ObjectPropertyDef{ g.TypeProperty(n.Type().String()), } + + if n.Name() != n.LogicalName() { + entries = append(entries, syn.ObjectProperty(syn.String("name"), syn.String(n.LogicalName()))) + } if n.DefaultValue != nil { prop := syn.ObjectProperty(syn.String("default"), g.expr(n.DefaultValue)) entries = append(entries, prop) diff --git a/pkg/pulumiyaml/codegen/gen_program_test.go b/pkg/pulumiyaml/codegen/gen_program_test.go index 8be7a2f7..21a867b2 100644 --- a/pkg/pulumiyaml/codegen/gen_program_test.go +++ b/pkg/pulumiyaml/codegen/gen_program_test.go @@ -172,8 +172,6 @@ func TestGenerateProgram(t *testing.T) { // But the actual error is that it is using a Splat operator. case "components": // https://github.com/pulumi/pulumi-yaml/issues/476 - case "logical-name": - // https://github.com/pulumi/pulumi-yaml/issues/477 case "unknown-resource": // https://github.com/pulumi/pulumi-yaml/issues/478 case "optional-complex-config": @@ -201,6 +199,7 @@ func TestGenerateProgram(t *testing.T) { case "regress-node-12507": // https://github.com/pulumi/pulumi-yaml/issues/494 case "config-variables": + case "logical-name": // Needs config set in order to compile/run. tt.SkipCompile = codegen.NewStringSet("yaml") l = append(l, tt) @@ -218,6 +217,7 @@ func TestGenerateProgram(t *testing.T) { assert.NoError(t, err) assert.Falsef(t, diags.HasErrors(), "%s", diags.Error()) err = pulumi.RunErr(func(ctx *pulumi.Context) error { + return pulumiyaml.RunTemplate(ctx, templateDecl, nil, nil, testPackageLoader{t}) }, pulumi.WithMocks("test", "gen", &testMonitor{}), func(ri *pulumi.RunInfo) { ri.DryRun = true }) assert.NoError(t, err) diff --git a/pkg/pulumiyaml/run.go b/pkg/pulumiyaml/run.go index 31f4c350..37eaa84c 100644 --- a/pkg/pulumiyaml/run.go +++ b/pkg/pulumiyaml/run.go @@ -877,6 +877,9 @@ func (e *programEvaluator) registerConfig(intm configNode) (interface{}, bool) { case configNodeYaml: k, intmKey = intm.Key.Value, intm.Key c := intm.Value + if c.Name != nil && c.Name.Value != "" { + k = c.Name.Value + } // If we implement global type checking, the type of configuration variables // can be inferred and this requirement relaxed. if c.Type == nil && c.Default == nil { @@ -1216,6 +1219,11 @@ func (e *programEvaluator) registerResource(kvp resourceNode) (lateboundResource // Create either a latebound custom resource or latebound provider resource depending on // whether the type token indicates a special provider type. + resourceName := k + if v.Name != nil && v.Name.Value != "" { + resourceName = v.Name.Value + } + var state lateboundResource var res pulumi.Resource var resourceSchema *schema.Resource @@ -1224,12 +1232,12 @@ func (e *programEvaluator) registerResource(kvp resourceNode) (lateboundResource } isProvider := false if strings.HasPrefix(v.Type.Value, "pulumi:providers:") { - r := lateboundProviderResourceState{name: k, resourceSchema: resourceSchema} + r := lateboundProviderResourceState{name: resourceName, resourceSchema: resourceSchema} state = &r res = &r isProvider = true } else { - r := lateboundCustomResourceState{name: k, resourceSchema: resourceSchema} + r := lateboundCustomResourceState{name: resourceName, resourceSchema: resourceSchema} state = &r res = &r } @@ -1296,7 +1304,7 @@ func (e *programEvaluator) registerResource(kvp resourceNode) (lateboundResource // Now register the resulting resource with the engine. if isComponent { - err = e.pulumiCtx.RegisterRemoteComponentResource(string(typ), k, untypedArgs(props), res, opts...) + err = e.pulumiCtx.RegisterRemoteComponentResource(string(typ), resourceName, untypedArgs(props), res, opts...) } else if isRead { s, ok := e.evaluateExpr(v.Get.Id) if !ok { @@ -1311,9 +1319,15 @@ func (e *programEvaluator) registerResource(kvp resourceNode) (lateboundResource e.errorf(v.Get.Id, "get.id must be a prompt string, instead got type %T", s) return nil, false } - err = e.pulumiCtx.ReadResource(string(typ), k, pulumi.ID(id), untypedArgs(props), res.(pulumi.CustomResource), opts...) + err = e.pulumiCtx.ReadResource( + string(typ), + resourceName, + pulumi.ID(id), + untypedArgs(props), + res.(pulumi.CustomResource), + opts...) } else { - err = e.pulumiCtx.RegisterResource(string(typ), k, untypedArgs(props), res, opts...) + err = e.pulumiCtx.RegisterResource(string(typ), resourceName, untypedArgs(props), res, opts...) } if err != nil { e.error(kvp.Key, err.Error()) diff --git a/pkg/pulumiyaml/run_plugin_version_test.go b/pkg/pulumiyaml/run_plugin_version_test.go index 454e687f..2b260b64 100644 --- a/pkg/pulumiyaml/run_plugin_version_test.go +++ b/pkg/pulumiyaml/run_plugin_version_test.go @@ -127,7 +127,7 @@ resources: arguments: filters: - name: name - values: ["amzn-ami-hvm-*-x86_64-ebs"] + values: ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] owners: ["137112412989"] mostRecent: true Return: id diff --git a/pkg/pulumiyaml/run_test.go b/pkg/pulumiyaml/run_test.go index fca97e68..5faba7ae 100644 --- a/pkg/pulumiyaml/run_test.go +++ b/pkg/pulumiyaml/run_test.go @@ -559,6 +559,37 @@ configuration: assert.False(t, found, "We should not get any errors: '%s'", diags) } +func TestConfigNames(t *testing.T) { //nolint:paralleltest + const text = `name: test-yaml +runtime: yaml +configuration: + foo: + type: String + name: logicalFoo + bar: + type: String +` + + tmpl := yamlTemplate(t, text) + fooValue := "value from logicalName" + barValue := "value from config" + setConfig(t, + resource.PropertyMap{ + projectConfigKey("logicalFoo"): resource.NewStringProperty(fooValue), + projectConfigKey("bar"): resource.NewStringProperty(barValue), + }) + testRan := false + err := testTemplateDiags(t, tmpl, func(e *programEvaluator) { + assert.Equal(t, fooValue, e.config["foo"]) + assert.Equal(t, barValue, e.config["bar"]) + + testRan = true + }) + assert.True(t, testRan, "Our tests didn't run") + diags, found := HasDiagnostics(err) + assert.False(t, found, "We should not get any errors: '%s'", diags) +} + func TestConflictingConfigSecrets(t *testing.T) { //nolint:paralleltest const text = `name: test-yaml runtime: yaml @@ -1944,6 +1975,48 @@ resources: assert.NoError(t, err) } +func TestResourceWithLogicalName(t *testing.T) { + t.Parallel() + + text := ` +name: test-logical-name +runtime: yaml +resources: + sourceName: + type: test:resource:UsingLogicalName + name: actual-registered-name + + sourceNameOnly: + type: test:resource:WithoutLogicalName +` + tmpl := yamlTemplate(t, strings.TrimSpace(text)) + mocks := &testMonitor{ + NewResourceF: func(args pulumi.MockResourceArgs) (string, resource.PropertyMap, error) { + t.Logf("args: %+v", args) + if args.TypeToken == "test:resource:UsingLogicalName" { + registeredName := "actual-registered-name" + assert.Equal(t, registeredName, args.Name) + assert.Equal(t, registeredName, args.RegisterRPC.GetName()) + } else if args.TypeToken == "test:resource:WithoutLogicalName" { + assert.Equal(t, "sourceNameOnly", args.Name) + assert.Equal(t, "sourceNameOnly", args.RegisterRPC.GetName()) + } else { + t.Fatalf("unexpected type token: %s", args.TypeToken) + } + + return args.Name, args.Inputs, nil + }, + } + err := pulumi.RunErr(func(ctx *pulumi.Context) error { + runner := newRunner(tmpl, newMockPackageMap()) + err := runner.Evaluate(ctx) + assert.Len(t, err, 0) + assert.Equal(t, err.Error(), "no diagnostics") + return nil + }, pulumi.WithMocks("project", "stack", mocks)) + assert.NoError(t, err) +} + func TestGetConfNodesFromMap(t *testing.T) { t.Parallel() tests := []struct { diff --git a/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/aws-webserver.pp b/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/aws-webserver.pp index fdebbf85..70f0418a 100644 --- a/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/aws-webserver.pp +++ b/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/aws-webserver.pp @@ -12,7 +12,7 @@ ami = invoke("aws:index:getAmi", { filters = [{ name = "name" - values = ["amzn-ami-hvm-*-x86_64-ebs"] + values = ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] }] owners = ["137112412989"] // Amazon mostRecent = true diff --git a/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/yaml/aws-webserver.yaml b/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/yaml/aws-webserver.yaml index 7ca2b43d..84300dff 100644 --- a/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/yaml/aws-webserver.yaml +++ b/pkg/pulumiyaml/testing/test/testdata/aws-webserver-pp/yaml/aws-webserver.yaml @@ -32,7 +32,7 @@ variables: filters: - name: name values: - - amzn-ami-hvm-*-x86_64-ebs + - amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs owners: - '137112412989' mostRecent: true diff --git a/pkg/pulumiyaml/testing/test/testdata/logical-name-pp/yaml/logical-name.yaml b/pkg/pulumiyaml/testing/test/testdata/logical-name-pp/yaml/logical-name.yaml index eafa383f..1fb65034 100644 --- a/pkg/pulumiyaml/testing/test/testdata/logical-name-pp/yaml/logical-name.yaml +++ b/pkg/pulumiyaml/testing/test/testdata/logical-name-pp/yaml/logical-name.yaml @@ -1,5 +1,12 @@ +configuration: + configLexicalName: + type: string + name: "cC-Charlie_charlie.\U0001F603⁉️" resources: - "aA-Alpha_alpha.\U0001F92F⁉️": + resourceLexicalName: type: random:RandomPet + name: "aA-Alpha_alpha.\U0001F92F⁉️" + properties: + prefix: ${configLexicalName} outputs: - "bB-Beta_beta.\U0001F49C⁉": "${[\"aA-Alpha_alpha.\U0001F92F⁉️\"].id}" + "bB-Beta_beta.\U0001F49C⁉": ${resourceLexicalName.id} diff --git a/pkg/tests/transpiled_examples/webserver-json-pp/webserver-json.pp b/pkg/tests/transpiled_examples/webserver-json-pp/webserver-json.pp index f7f3259f..2a19803c 100644 --- a/pkg/tests/transpiled_examples/webserver-json-pp/webserver-json.pp +++ b/pkg/tests/transpiled_examples/webserver-json-pp/webserver-json.pp @@ -23,7 +23,7 @@ ami = invoke("aws:index/getAmi:getAmi", { filters = [{ name = "name", - values = ["amzn-ami-hvm-*-x86_64-ebs"] + values = ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] }], owners = ["137112412989"], mostRecent = true diff --git a/pkg/tests/transpiled_examples/webserver-pp/webserver.pp b/pkg/tests/transpiled_examples/webserver-pp/webserver.pp index f4282ce2..23e9d0b9 100644 --- a/pkg/tests/transpiled_examples/webserver-pp/webserver.pp +++ b/pkg/tests/transpiled_examples/webserver-pp/webserver.pp @@ -6,7 +6,7 @@ ec2Ami = invoke("aws:index/getAmi:getAmi", { filters = [{ name = "name", - values = ["amzn-ami-hvm-*-x86_64-ebs"] + values = ["amzn2-ami-hvm-2.0.20231218.0-x86_64-ebs"] }], owners = ["137112412989"], mostRecent = true