From f6c6d4878522e0ad2dc7a99b86b57a30f3db9df2 Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 11:11:18 -0700 Subject: [PATCH 1/9] Add syntactic sugar for `fn::open` --- ast/environment_test.go | 32 ++++++++++++++++++++++++++++++-- ast/expr.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/ast/environment_test.go b/ast/environment_test.go index 09a27e18..5c8083fb 100644 --- a/ast/environment_test.go +++ b/ast/environment_test.go @@ -13,7 +13,10 @@ import ( "github.com/pulumi/esc/syntax/encoding" ) -const example = ` +func TestExample(t *testing.T) { + t.Parallel() + + const example = ` imports: - green-channel - us-west-2 @@ -30,9 +33,34 @@ config: environment: prod ` -func TestExample(t *testing.T) { + syntax, diags := encoding.DecodeYAML("", yaml.NewDecoder(strings.NewReader(example)), nil) + require.Len(t, diags, 0) + + environment, diags := ParseEnvironment([]byte(example), syntax) + assert.Len(t, diags, 0) + + assert.Nil(t, environment.Description) +} + +func TestExample2(t *testing.T) { t.Parallel() + const example = ` +imports: + - green-channel + - us-west-2 +config: + aws: + fn::aws-oidc: + inputs: + sessionName: site-prod-session + roleArn: some-role-arn + pulumi: + aws:defaultTags: + tags: + environment: prod +` + syntax, diags := encoding.DecodeYAML("", yaml.NewDecoder(strings.NewReader(example)), nil) require.Len(t, diags, 0) diff --git a/ast/expr.go b/ast/expr.go index 40b99a92..58588f81 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -5,12 +5,15 @@ package ast import ( "encoding/json" "fmt" + "regexp" "strings" "github.com/hashicorp/hcl/v2" "github.com/pulumi/esc/syntax" ) +var fnOpenRegex = regexp.MustCompile("fn::[a-zA-Z]+-[a-zA-Z]+") + // Expr represents a Pulumi YAML expression. Expressions may be literals, interpolated strings, symbols, or builtin // functions. type Expr interface { @@ -531,6 +534,34 @@ func tryParseFunction(node *syntax.ObjectNode) (Expr, syntax.Diagnostics, bool) case "fn::toString": parse = parseToString default: + if match, _ := regexp.MatchString(fnOpenRegex.String(), kvp.Key.Value()); match { + // transform the node into fn::open format + providerName := strings.TrimPrefix(kvp.Key.Value(), "fn::") + // case 1: inputs are provided + if _, ok := kvp.Value.(*syntax.ObjectNode); ok { + kvp.Value = syntax.Object( + syntax.ObjectPropertyDef{ + Key: syntax.StringSyntax(kvp.Syntax, "inputs"), + Value: kvp.Value, + }, + syntax.ObjectPropertyDef{ + Key: syntax.StringSyntax(kvp.Syntax, "provider"), + Value: syntax.String(providerName), + }, + ) + } else { + // case 2: inputs are not provided + kvp.Value = syntax.Object( + syntax.ObjectPropertyDef{ + Key: syntax.StringSyntax(kvp.Syntax, "inputs"), + Value: syntax.String(providerName), + }, + ) + } + parse = parseOpen + break + } + if strings.HasPrefix(strings.ToLower(kvp.Key.Value()), "fn::") { diags = append(diags, syntax.Warning(kvp.Key.Syntax().Range(), "'fn::' is a reserved prefix", From dd3ed12f81a811b08b565af631d0e484324e28c5 Mon Sep 17 00:00:00 2001 From: Alex Qiu Date: Wed, 27 Sep 2023 11:44:27 -0700 Subject: [PATCH 2/9] change regex to ^fn::open::[a-zA-Z-]+$ Co-authored-by: Levi Blackstone --- ast/expr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/expr.go b/ast/expr.go index 58588f81..3af59df3 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -12,7 +12,7 @@ import ( "github.com/pulumi/esc/syntax" ) -var fnOpenRegex = regexp.MustCompile("fn::[a-zA-Z]+-[a-zA-Z]+") +var fnOpenRegex = regexp.MustCompile(`^fn::open::[a-zA-Z-]+$`) // Expr represents a Pulumi YAML expression. Expressions may be literals, interpolated strings, symbols, or builtin // functions. From a1b09037256b9458ca2eb6b2e0dfcff63824a3a9 Mon Sep 17 00:00:00 2001 From: Alex Qiu Date: Wed, 27 Sep 2023 12:09:45 -0700 Subject: [PATCH 3/9] better provider name check/parse Co-authored-by: Pat Gavlin --- ast/expr.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ast/expr.go b/ast/expr.go index 3af59df3..c64d4bb4 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -5,7 +5,6 @@ package ast import ( "encoding/json" "fmt" - "regexp" "strings" "github.com/hashicorp/hcl/v2" @@ -534,7 +533,8 @@ func tryParseFunction(node *syntax.ObjectNode) (Expr, syntax.Diagnostics, bool) case "fn::toString": parse = parseToString default: - if match, _ := regexp.MatchString(fnOpenRegex.String(), kvp.Key.Value()); match { + + if providerName, isOpen := strings.CutPrefix(kvp.Key.Value(), "fn::open::"); isOpen { // transform the node into fn::open format providerName := strings.TrimPrefix(kvp.Key.Value(), "fn::") // case 1: inputs are provided From 5c18aed4e4960e9c37ef9e0eefa8c89df2a336dd Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 12:18:23 -0700 Subject: [PATCH 4/9] add new parseShortOpen function --- ast/expr.go | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/ast/expr.go b/ast/expr.go index c64d4bb4..dc923c10 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -533,32 +533,8 @@ func tryParseFunction(node *syntax.ObjectNode) (Expr, syntax.Diagnostics, bool) case "fn::toString": parse = parseToString default: - - if providerName, isOpen := strings.CutPrefix(kvp.Key.Value(), "fn::open::"); isOpen { - // transform the node into fn::open format - providerName := strings.TrimPrefix(kvp.Key.Value(), "fn::") - // case 1: inputs are provided - if _, ok := kvp.Value.(*syntax.ObjectNode); ok { - kvp.Value = syntax.Object( - syntax.ObjectPropertyDef{ - Key: syntax.StringSyntax(kvp.Syntax, "inputs"), - Value: kvp.Value, - }, - syntax.ObjectPropertyDef{ - Key: syntax.StringSyntax(kvp.Syntax, "provider"), - Value: syntax.String(providerName), - }, - ) - } else { - // case 2: inputs are not provided - kvp.Value = syntax.Object( - syntax.ObjectPropertyDef{ - Key: syntax.StringSyntax(kvp.Syntax, "inputs"), - Value: syntax.String(providerName), - }, - ) - } - parse = parseOpen + if strings.HasPrefix(kvp.Key.Value(), "fn::open::") { + parse = parseShortOpen break } @@ -635,6 +611,16 @@ func parseOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, synt return OpenSyntax(node, name, obj, provider, inputs), diags } +func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { + provider := strings.TrimPrefix(node.Key.Value(), "fn::open::") + inputs, ok := args.(*ObjectExpr) + if !ok { + return nil, syntax.Diagnostics{ExprError(args, fmt.Sprintf("the argument to fn::open::%s must be an object", provider), "")} + } + + return OpenSyntax(node, name, inputs, StringSyntaxValue(name, provider), inputs), nil +} + func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { list, ok := args.(*ListExpr) if !ok || len(list.Elements) != 2 { From c87803c90c49e1f934e1832da5d511c2786cde2e Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 12:20:46 -0700 Subject: [PATCH 5/9] update test, remove unused regex --- ast/environment_test.go | 2 +- ast/expr.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ast/environment_test.go b/ast/environment_test.go index 5c8083fb..92906159 100644 --- a/ast/environment_test.go +++ b/ast/environment_test.go @@ -51,7 +51,7 @@ imports: - us-west-2 config: aws: - fn::aws-oidc: + fn::open::aws-oidc: inputs: sessionName: site-prod-session roleArn: some-role-arn diff --git a/ast/expr.go b/ast/expr.go index dc923c10..a2ca5652 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -11,8 +11,6 @@ import ( "github.com/pulumi/esc/syntax" ) -var fnOpenRegex = regexp.MustCompile(`^fn::open::[a-zA-Z-]+$`) - // Expr represents a Pulumi YAML expression. Expressions may be literals, interpolated strings, symbols, or builtin // functions. type Expr interface { From a228d361bd739207cb47f8f2e14704d9ea4a518a Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 12:35:31 -0700 Subject: [PATCH 6/9] lint --- ast/expr.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ast/expr.go b/ast/expr.go index a2ca5652..e11a4525 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -610,13 +610,14 @@ func parseOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, synt } func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { - provider := strings.TrimPrefix(node.Key.Value(), "fn::open::") + kvp := node.Index(0) + provider := strings.TrimPrefix(kvp.Key.Value(), "fn::open::") inputs, ok := args.(*ObjectExpr) if !ok { return nil, syntax.Diagnostics{ExprError(args, fmt.Sprintf("the argument to fn::open::%s must be an object", provider), "")} } - return OpenSyntax(node, name, inputs, StringSyntaxValue(name, provider), inputs), nil + return OpenSyntax(node, name, inputs, String(provider), inputs), nil } func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { From 27a9c6c15e077d2067debc9b2edce949ed5bb978 Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 14:10:23 -0700 Subject: [PATCH 7/9] use StringSyntaxValue to retain pos info --- ast/expr.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ast/expr.go b/ast/expr.go index e11a4525..ad846e02 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -617,7 +617,7 @@ func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, return nil, syntax.Diagnostics{ExprError(args, fmt.Sprintf("the argument to fn::open::%s must be an object", provider), "")} } - return OpenSyntax(node, name, inputs, String(provider), inputs), nil + return OpenSyntax(node, name, inputs, StringSyntaxValue(name.Syntax(), provider), inputs), nil } func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { From eb1746660de7d33d2ae5635cb729deac3194f22e Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 14:27:22 -0700 Subject: [PATCH 8/9] cast to stringnode --- ast/expr.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ast/expr.go b/ast/expr.go index ad846e02..3cca8505 100644 --- a/ast/expr.go +++ b/ast/expr.go @@ -616,8 +616,9 @@ func parseShortOpen(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, if !ok { return nil, syntax.Diagnostics{ExprError(args, fmt.Sprintf("the argument to fn::open::%s must be an object", provider), "")} } + p := name.Syntax().(*syntax.StringNode) - return OpenSyntax(node, name, inputs, StringSyntaxValue(name.Syntax(), provider), inputs), nil + return OpenSyntax(node, name, inputs, StringSyntaxValue(p, provider), inputs), nil } func parseJoin(node *syntax.ObjectNode, name *StringExpr, args Expr) (Expr, syntax.Diagnostics) { From 621958e6b4e673224d242331d39c86c101f1f28a Mon Sep 17 00:00:00 2001 From: aq17 Date: Wed, 27 Sep 2023 16:13:01 -0700 Subject: [PATCH 9/9] fix/simplify test --- ast/environment_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ast/environment_test.go b/ast/environment_test.go index 92906159..9eff5e13 100644 --- a/ast/environment_test.go +++ b/ast/environment_test.go @@ -52,9 +52,8 @@ imports: config: aws: fn::open::aws-oidc: - inputs: - sessionName: site-prod-session - roleArn: some-role-arn + sessionName: site-prod-session + roleArn: some-role-arn pulumi: aws:defaultTags: tags: