Skip to content

Commit

Permalink
Support (logical) name for resources and config variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Zaid-Ajaj committed Jan 18, 2024
1 parent 106f37c commit b7f833c
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 39 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### Improvements

- Support (logical) name for resources and config variables
[#546](https://github.com/pulumi/pulumi-yaml/pull/546)

### Bug Fixes
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/webserver-json/Main.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion examples/webserver/Pulumi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 17 additions & 7 deletions pkg/pulumiyaml/ast/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ type ConfigParamDecl struct {
declNode

Type *StringExpr
Name *StringExpr
Secret *BooleanExpr
Default Expr
Value Expr
Expand All @@ -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 {
Expand Down Expand Up @@ -411,6 +413,7 @@ type ResourceDecl struct {
declNode

Type *StringExpr
Name *StringExpr
DefaultProvider *BooleanExpr
Properties PropertyMapDecl
Options ResourceOptionsDecl
Expand All @@ -423,23 +426,30 @@ 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,
Get: get,
}
}

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 {
Expand Down
25 changes: 10 additions & 15 deletions pkg/pulumiyaml/codegen/gen_program.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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...)))
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions pkg/pulumiyaml/codegen/gen_program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
24 changes: 19 additions & 5 deletions pkg/pulumiyaml/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion pkg/pulumiyaml/run_plugin_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions pkg/pulumiyaml/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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}
Loading

0 comments on commit b7f833c

Please sign in to comment.