From 31770c33008a8c2d7e3be9048007184df3a70a58 Mon Sep 17 00:00:00 2001 From: Evan Boyle Date: Wed, 24 Jun 2020 11:07:26 -0700 Subject: [PATCH] go program gen: prompt array conversion, unused range vars, id handling (#4884) --- CHANGELOG.md | 3 + pkg/codegen/go/gen_program.go | 35 ++++++++++-- pkg/codegen/go/gen_program_expressions.go | 19 ++++++- pkg/codegen/go/gen_program_test.go | 1 + pkg/codegen/go/gen_program_utils.go | 56 +++++++++++++++++++ .../internal/test/testdata/aws-eks.pp.go | 4 +- .../internal/test/testdata/aws-fargate.pp.go | 11 +++- 7 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 pkg/codegen/go/gen_program_utils.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 10f7aa652..bf878fdea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ CHANGELOG ## HEAD (Unreleased) +- Go program gen: prompt array conversion, unused range vars, id handling + [#4884](https://github.com/pulumi/pulumi/pull/4884) + - Go program gen handling for prompt optional primitives [#4875](https://github.com/pulumi/pulumi/pull/4875) diff --git a/pkg/codegen/go/gen_program.go b/pkg/codegen/go/gen_program.go index d5d8ad7cd..8192b7a97 100644 --- a/pkg/codegen/go/gen_program.go +++ b/pkg/codegen/go/gen_program.go @@ -29,6 +29,7 @@ type generator struct { splatSpiller *splatSpiller optionalSpiller *optionalSpiller scopeTraversalRoots codegen.StringSet + arrayHelpers map[string]*promptToInputArrayHelper } func GenerateProgram(program *hcl2.Program) (map[string][]byte, hcl.Diagnostics, error) { @@ -50,6 +51,7 @@ func GenerateProgram(program *hcl2.Program) (map[string][]byte, hcl.Diagnostics, splatSpiller: &splatSpiller{}, optionalSpiller: &optionalSpiller{}, scopeTraversalRoots: codegen.NewStringSet(), + arrayHelpers: make(map[string]*promptToInputArrayHelper), } g.Formatter = format.NewFormatter(g) @@ -212,6 +214,14 @@ func (g *generator) genPostamble(w io.Writer, nodes []hcl2.Node) { g.Fprint(w, "return nil\n") g.Fprintf(w, "})\n") g.Fprintf(w, "}\n") + + g.genHelpers(w) +} + +func (g *generator) genHelpers(w io.Writer) { + for _, v := range g.arrayHelpers { + v.generateHelperMethod(w) + } } func (g *generator) genNode(w io.Writer, n hcl2.Node) { @@ -243,7 +253,7 @@ func (g *generator) genResource(w io.Writer, r *hcl2.Resource) { g.genTemps(w, temps) } - instantiate := func(varName, resourceName string) { + instantiate := func(varName, resourceName string, w io.Writer) { if g.scopeTraversalRoots.Has(varName) || strings.HasPrefix(varName, "_") { g.Fgenf(w, "%s, err := %s.New%s(ctx, %s, ", varName, mod, typ, resourceName) } else { @@ -273,13 +283,24 @@ func (g *generator) genResource(w io.Writer, r *hcl2.Resource) { g.Fgenf(w, "var %s []*%s.%s\n", resName, mod, typ) - g.Fgenf(w, "for key0, val0 := range %.v {\n", rangeExpr) - instantiate("_res", fmt.Sprintf(`fmt.Sprintf("%s-%%v", key0)`, resName)) + // ahead of range statement declaration generate the resource instantiation + // to detect and removed unused k,v variables + var buf bytes.Buffer + instantiate("_res", fmt.Sprintf(`fmt.Sprintf("%s-%%v", key0)`, resName), &buf) + instantiation := buf.String() + isValUsed := strings.Contains(instantiation, "val0") + valVar := "_" + if isValUsed { + valVar = "val0" + } + + g.Fgenf(w, "for key0, %s := range %.v {\n", valVar, rangeExpr) + g.Fgen(w, instantiation) g.Fgenf(w, "%s = append(%s, _res)\n", resName, resName) g.Fgenf(w, "}\n") } else { - instantiate(resName, fmt.Sprintf("\"%s\"", resName)) + instantiate(resName, fmt.Sprintf("%q", resName), w) } } @@ -357,7 +378,11 @@ func (g *generator) genTempsMultiReturn(w io.Writer, temps []interface{}, zeroVa g.Fgenf(w, "}\n") case *splatTemp: argTyp := g.argumentTypeName(t.Value.Each, t.Value.Each.Type(), false) - g.Fgenf(w, "var %s []%s\n", t.Name, argTyp) + if strings.Contains(argTyp, ".") { + g.Fgenf(w, "var %s %sArray\n", t.Name, argTyp) + } else { + g.Fgenf(w, "var %s []%s\n", t.Name, argTyp) + } g.Fgenf(w, "for _, val0 := range %.v {\n", t.Value.Source) g.Fgenf(w, "%s = append(%s, %.v)\n", t.Name, t.Name, t.Value.Each) g.Fgenf(w, "}\n") diff --git a/pkg/codegen/go/gen_program_expressions.go b/pkg/codegen/go/gen_program_expressions.go index 333b38358..a3a61c9b4 100644 --- a/pkg/codegen/go/gen_program_expressions.go +++ b/pkg/codegen/go/gen_program_expressions.go @@ -436,7 +436,24 @@ func (g *generator) genScopeTraversalExpression(w io.Writer, expr *model.ScopeTr // TODO if it's an array type, we need a lowering step to turn []string -> pulumi.StringArray if isInput { - g.Fgenf(w, "%s(", g.argumentTypeName(expr, expr.Type(), isInput)) + argType := g.argumentTypeName(expr, expr.Type(), isInput) + if strings.HasSuffix(argType, "Array") { + // use a helper to transform prompt arrays into inputty arrays + var helper *promptToInputArrayHelper + if h, ok := g.arrayHelpers[argType]; ok { + helper = h + } else { + // helpers are emitted at the end in the postamble step + helper = &promptToInputArrayHelper{ + destType: argType, + } + g.arrayHelpers[argType] = helper + } + g.Fgenf(w, "%s(", helper.getFnName()) + } else { + g.Fgenf(w, "%s(", g.argumentTypeName(expr, expr.Type(), isInput)) + } + } // TODO: this isn't exhaustively correct as "range" could be a legit var name diff --git a/pkg/codegen/go/gen_program_test.go b/pkg/codegen/go/gen_program_test.go index c4a6c9e72..f3e1ecd50 100644 --- a/pkg/codegen/go/gen_program_test.go +++ b/pkg/codegen/go/gen_program_test.go @@ -119,6 +119,7 @@ func newTestGenerator(t *testing.T, testFile string) *generator { splatSpiller: &splatSpiller{}, optionalSpiller: &optionalSpiller{}, scopeTraversalRoots: codegen.NewStringSet(), + arrayHelpers: make(map[string]*promptToInputArrayHelper), } g.Formatter = format.NewFormatter(g) return g diff --git a/pkg/codegen/go/gen_program_utils.go b/pkg/codegen/go/gen_program_utils.go new file mode 100644 index 000000000..43f3ada5f --- /dev/null +++ b/pkg/codegen/go/gen_program_utils.go @@ -0,0 +1,56 @@ +package gen + +import ( + "fmt" + "io" + "strings" + + "github.com/pulumi/pulumi/sdk/v2/go/common/util/contract" +) + +type promptToInputArrayHelper struct { + destType string +} + +var primitives = map[string]string{ + "String": "string", + "Bool": "bool", + "Int": "int", + "Int64": "int64", + "Float64": "float64", +} + +func (p *promptToInputArrayHelper) generateHelperMethod(w io.Writer) { + promptType := p.getPromptItemType() + inputType := p.getInputItemType() + fnName := p.getFnName() + fmt.Fprintf(w, "func %s(arr []%s) %s {\n", fnName, promptType, p.destType) + fmt.Fprintf(w, "var pulumiArr %s\n", p.destType) + fmt.Fprintf(w, "for _, v := range arr {\n") + fmt.Fprintf(w, "pulumiArr = append(pulumiArr, %s(v))\n", inputType) + fmt.Fprintf(w, "}\n") + fmt.Fprintf(w, "return pulumiArr\n") + fmt.Fprintf(w, "}\n") +} + +func (p *promptToInputArrayHelper) getFnName() string { + parts := strings.Split(p.destType, ".") + contract.Assertf(len(parts) == 2, "promptToInputArrayHelper destType expected to have two parts.") + return fmt.Sprintf("to%s%s", Title(parts[0]), Title(parts[1])) +} + +func (p *promptToInputArrayHelper) getPromptItemType() string { + inputType := p.getInputItemType() + parts := strings.Split(inputType, ".") + contract.Assertf(len(parts) == 2, "promptToInputArrayHelper destType expected to have two parts.") + typ := parts[1] + if t, ok := primitives[typ]; ok { + return t + } + + return typ +} + +func (p *promptToInputArrayHelper) getInputItemType() string { + return strings.TrimSuffix(p.destType, "Array") +} diff --git a/pkg/codegen/internal/test/testdata/aws-eks.pp.go b/pkg/codegen/internal/test/testdata/aws-eks.pp.go index 559a33e81..d3a7f659e 100644 --- a/pkg/codegen/internal/test/testdata/aws-eks.pp.go +++ b/pkg/codegen/internal/test/testdata/aws-eks.pp.go @@ -71,7 +71,7 @@ func main() { vpcSubnet = append(vpcSubnet, _res) } var rta []*ec2.RouteTableAssociation - for key0, val0 := range zones.Names { + for key0, _ := range zones.Names { _res, err := ec2.NewRouteTableAssociation(ctx, fmt.Sprintf("rta-%v", key0), &ec2.RouteTableAssociationArgs{ RouteTableId: eksRouteTable.ID(), SubnetId: vpcSubnet[key0].ID(), @@ -81,7 +81,7 @@ func main() { } rta = append(rta, _res) } - var splat0 []pulumi.String + var splat0 pulumi.StringArray for _, val0 := range vpcSubnet { splat0 = append(splat0, val0.ID()) } diff --git a/pkg/codegen/internal/test/testdata/aws-fargate.pp.go b/pkg/codegen/internal/test/testdata/aws-fargate.pp.go index 122b08004..404b88651 100644 --- a/pkg/codegen/internal/test/testdata/aws-fargate.pp.go +++ b/pkg/codegen/internal/test/testdata/aws-fargate.pp.go @@ -86,7 +86,7 @@ func main() { return err } webLoadBalancer, err := elasticloadbalancingv2.NewLoadBalancer(ctx, "webLoadBalancer", &elasticloadbalancingv2.LoadBalancerArgs{ - Subnets: pulumi.StringArray(subnets.Ids), + Subnets: toPulumiStringArray(subnets.Ids), SecurityGroups: pulumi.StringArray{ webSecurityGroup.ID(), }, @@ -154,7 +154,7 @@ func main() { TaskDefinition: appTask.Arn, NetworkConfiguration: &ecs.ServiceNetworkConfigurationArgs{ AssignPublicIp: pulumi.Bool(true), - Subnets: pulumi.StringArray(subnets.Ids), + Subnets: toPulumiStringArray(subnets.Ids), SecurityGroups: pulumi.StringArray{ webSecurityGroup.ID(), }, @@ -174,3 +174,10 @@ func main() { return nil }) } +func toPulumiStringArray(arr []string) pulumi.StringArray { + var pulumiArr pulumi.StringArray + for _, v := range arr { + pulumiArr = append(pulumiArr, pulumi.String(v)) + } + return pulumiArr +}