Skip to content

Commit 34c435e

Browse files
authored
Default output type is any, better inference for try(...) expressions (#398)
1 parent f0b2f24 commit 34c435e

File tree

9 files changed

+188
-67
lines changed

9 files changed

+188
-67
lines changed

pkg/modprovider/tfmodules.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,23 @@ func inferExpressionType(expr hcl.Expression) schema.TypeSpec {
208208
case "compact":
209209
// compact function has return type string[]
210210
return arrayType(stringType)
211+
case "try":
212+
// expressions of format: try(<expr1>, <expr2>, ..., <default>)
213+
// we check the last argument to see if it is a string literal or null
214+
// if it is, we return a string type
215+
if len(functionCall.Args) > 0 {
216+
lastArg := functionCall.Args[len(functionCall.Args)-1]
217+
switch lastArg := lastArg.(type) {
218+
case *hclsyntax.LiteralValueExpr:
219+
literalStringOrNull := lastArg.Val.Type().Equals(cty.String) ||
220+
lastArg.Val.Type().Equals(cty.DynamicPseudoType)
221+
if literalStringOrNull {
222+
// try function with a string literal as the last argument
223+
// returns a string type
224+
return stringType
225+
}
226+
}
227+
}
211228
}
212229
}
213230

@@ -232,7 +249,11 @@ func inferExpressionType(expr hcl.Expression) schema.TypeSpec {
232249
return arrayType(stringType)
233250
}
234251

235-
return stringType
252+
// default output type is any
253+
// language SDKs are very strict when it comes to type schematized
254+
// they have to match with the actual type at runtime from terraform
255+
// so we use any as a fallback
256+
return anyType
236257
}
237258

238259
// isVariableReference checks if the given expression is a reference to a variable
@@ -495,25 +516,33 @@ func combineInferredModuleSchema(
495516

496517
// if the output already exists, we need to merge the types
497518
existingOutput := inferredSchema.Outputs[name]
498-
if output.Ref != "" {
499-
existingOutput.Ref = output.Ref
500-
}
519+
501520
if output.Description != "" {
502521
existingOutput.Description = output.Description
503522
}
504523
if output.Secret {
505524
existingOutput.Secret = output.Secret
506525
}
526+
527+
if output.Ref != "" {
528+
existingOutput.Ref = output.Ref
529+
existingOutput.TypeSpec.AdditionalProperties = nil
530+
existingOutput.TypeSpec.Items = nil
531+
existingOutput.TypeSpec.Type = ""
532+
}
507533
if output.TypeSpec.Type != "" {
508534
existingOutput.TypeSpec.Type = output.TypeSpec.Type
535+
existingOutput.TypeSpec.Ref = ""
509536
}
510537
if output.TypeSpec.Items != nil {
511538
existingOutput.TypeSpec.Items = output.TypeSpec.Items
512539
existingOutput.TypeSpec.AdditionalProperties = nil
540+
existingOutput.TypeSpec.Ref = ""
513541
}
514542
if output.TypeSpec.AdditionalProperties != nil {
515543
existingOutput.TypeSpec.AdditionalProperties = output.TypeSpec.AdditionalProperties
516544
existingOutput.TypeSpec.Items = nil
545+
existingOutput.TypeSpec.Ref = ""
517546
}
518547
}
519548

tests/acc_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,21 @@ func Test_LocalModule_RelativePath_OpenTofu(t *testing.T) {
17671767
t.Logf("%s", previewResult.StdErr+previewResult.StdOut)
17681768
}
17691769

1770+
// verify that pulumi package get-schema works which also verifies that the
1771+
// generated schema is valid and can be used to generate an SDK
1772+
func TestPulumiPackageGetSchema(t *testing.T) {
1773+
t.Parallel()
1774+
localProviderBinPath := ensureCompiledProvider(t)
1775+
loadedSchema := pulumiGetSchema(t, localProviderBinPath, []string{
1776+
"terraform-module",
1777+
"terraform-aws-modules/vpc/aws",
1778+
"5.18.1",
1779+
"vpc",
1780+
})
1781+
1782+
assert.NotEmpty(t, loadedSchema, "expected schema to be loaded")
1783+
}
1784+
17701785
// assertTFStateResourceExists checks if a resource exists in the TF state.
17711786
// packageName should be the name of the package used in `pulumi package add`
17721787
// resourceAddress should be the full TF address of the resource, e.g. "module.test-bucket.aws_s3_bucket.this"
@@ -1927,6 +1942,29 @@ func ensureCompiledProvider(t *testing.T) string {
19271942
return binPath
19281943
}
19291944

1945+
func pulumiGetSchema(t *testing.T, localProviderBinPath string, args []string) string {
1946+
t.Helper()
1947+
1948+
localPulumi := filepath.Join(getRoot(t), ".pulumi", "bin", "pulumi")
1949+
1950+
if _, err := os.Stat(localPulumi); os.IsNotExist(err) {
1951+
t.Errorf("This test requires a locally pinned Pulumi CLI; run `make prepare_local_workspace` first")
1952+
}
1953+
1954+
cmd := exec.Command(localPulumi, append([]string{"package", "get-schema"}, args...)...)
1955+
1956+
path := os.Getenv("PATH")
1957+
path = fmt.Sprintf("%s:%s", filepath.Dir(localProviderBinPath), path)
1958+
1959+
cmd.Env = append(os.Environ(), fmt.Sprintf("PATH=%s", path))
1960+
out, err := cmd.CombinedOutput()
1961+
if err != nil {
1962+
t.Fatalf("failed to run pulumi package get-schema: %v\n%s", err, out)
1963+
}
1964+
1965+
return string(out)
1966+
}
1967+
19301968
func pulumiConvert(t *testing.T, localProviderBinPath, sourceDir, targetDir, language string, generateOnly bool) {
19311969
t.Helper()
19321970

tests/testdata/aws-vpc/go-views/sdks/vpc/vpc/module.go

Lines changed: 27 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testdata/aws-vpc/go-views/sdks/vpc/vpc/provider.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)