diff --git a/internal/testprovider/schema_required_input_with_default.go b/internal/testprovider/schema_required_input_with_default.go new file mode 100644 index 000000000..0788dacfb --- /dev/null +++ b/internal/testprovider/schema_required_input_with_default.go @@ -0,0 +1,55 @@ +// Copyright 2016-2022, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testprovider + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ProviderRequiredInputWithDefaultFunc() *schema.Provider { + resourceRequiredInputWithDefaultFunc := func() *schema.Resource { + return &schema.Resource{ + Schema: resourceRequiredInputWithDefaultSchema(), + } + } + + return &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ResourcesMap: map[string]*schema.Resource{ + "testprovider_res": resourceRequiredInputWithDefaultFunc(), + }, + } +} + +func resourceRequiredInputWithDefaultSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + DefaultFunc: func() (interface{}, error) { + return "default", nil + }, + }, + "other_name": { + Type: schema.TypeString, + Required: true, + Default: "default", + }, + "req": { + Type: schema.TypeString, + Required: true, + }, + } +} diff --git a/pkg/pf/internal/pfutils/attr.go b/pkg/pf/internal/pfutils/attr.go index 0b6899350..4952c5306 100644 --- a/pkg/pf/internal/pfutils/attr.go +++ b/pkg/pf/internal/pfutils/attr.go @@ -31,13 +31,14 @@ import ( // attrLike exposes these methods. // // GetAttributes method is special since it returns a NestedAttributes interface that is also internal and cannot be -// linked to. Instead, NestedAttriutes information is recorded in a dedicated new field. +// linked to. Instead, NestedAttributes information is recorded in a dedicated new field. type Attr interface { AttrLike IsNested() bool Nested() map[string]Attr NestingMode() NestingMode HasNestedObject() bool + HasDefault() bool } type AttrLike interface { @@ -66,9 +67,11 @@ func FromResourceAttribute(x rschema.Attribute) Attr { func FromAttrLike(attrLike AttrLike) Attr { nested, nestingMode := extractNestedAttributes(attrLike) + hasDefault := hasDefault(attrLike) return &attrAdapter{ nested: nested, nestingMode: nestingMode, + hasDefault: hasDefault, AttrLike: attrLike, } } @@ -76,6 +79,7 @@ func FromAttrLike(attrLike AttrLike) Attr { type attrAdapter struct { nested map[string]Attr nestingMode NestingMode + hasDefault bool AttrLike } @@ -102,6 +106,10 @@ func (a *attrAdapter) NestingMode() NestingMode { return a.nestingMode } +func (a *attrAdapter) HasDefault() bool { + return a.hasDefault +} + type NestingMode uint8 const ( diff --git a/pkg/pf/internal/pfutils/default.go b/pkg/pf/internal/pfutils/default.go new file mode 100644 index 000000000..82111b902 --- /dev/null +++ b/pkg/pf/internal/pfutils/default.go @@ -0,0 +1,99 @@ +package pfutils + +import ( + "github.com/hashicorp/terraform-plugin-framework/resource/schema/defaults" +) + +// These interfaces are re-implemented here from "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" +// as we can not link to them directly. + +type attributeLikeWithBoolDefaultValue interface { + AttrLike + BoolDefaultValue() defaults.Bool +} + +type attributeLikeWithFloat32DefaultValue interface { + AttrLike + Float32DefaultValue() defaults.Float32 +} + +type attributeLikeWithFloat64DefaultValue interface { + AttrLike + Float64DefaultValue() defaults.Float64 +} + +type attributeLikeWithInt32DefaultValue interface { + AttrLike + Int32DefaultValue() defaults.Int32 +} + +type attributeLikeWithInt64DefaultValue interface { + AttrLike + Int64DefaultValue() defaults.Int64 +} + +type attributeLikeWithListDefaultValue interface { + AttrLike + ListDefaultValue() defaults.List +} + +type attributeLikeWithMapDefaultValue interface { + AttrLike + MapDefaultValue() defaults.Map +} + +type attributeLikeWithNumberDefaultValue interface { + AttrLike + NumberDefaultValue() defaults.Number +} + +type attributeLikeWithObjectDefaultValue interface { + AttrLike + ObjectDefaultValue() defaults.Object +} + +type attributeLikeWithSetDefaultValue interface { + AttrLike + SetDefaultValue() defaults.Set +} + +type attributeLikeWithStringDefaultValue interface { + AttrLike + StringDefaultValue() defaults.String +} + +type attributeLikeWithDynamicDefaultValue interface { + AttrLike + DynamicDefaultValue() defaults.Dynamic +} + +func hasDefault(attr AttrLike) bool { + switch a := attr.(type) { + case attributeLikeWithBoolDefaultValue: + return a.BoolDefaultValue() != nil + case attributeLikeWithFloat32DefaultValue: + return a.Float32DefaultValue() != nil + case attributeLikeWithFloat64DefaultValue: + return a.Float64DefaultValue() != nil + case attributeLikeWithInt32DefaultValue: + return a.Int32DefaultValue() != nil + case attributeLikeWithInt64DefaultValue: + return a.Int64DefaultValue() != nil + case attributeLikeWithListDefaultValue: + return a.ListDefaultValue() != nil + case attributeLikeWithMapDefaultValue: + return a.MapDefaultValue() != nil + case attributeLikeWithNumberDefaultValue: + return a.NumberDefaultValue() != nil + case attributeLikeWithObjectDefaultValue: + return a.ObjectDefaultValue() != nil + case attributeLikeWithSetDefaultValue: + return a.SetDefaultValue() != nil + case attributeLikeWithStringDefaultValue: + return a.StringDefaultValue() != nil + case attributeLikeWithDynamicDefaultValue: + return a.DynamicDefaultValue() != nil + default: + return false + } +} diff --git a/pkg/pf/internal/schemashim/attr_schema.go b/pkg/pf/internal/schemashim/attr_schema.go index 250af55ea..19088bfab 100644 --- a/pkg/pf/internal/schemashim/attr_schema.go +++ b/pkg/pf/internal/schemashim/attr_schema.go @@ -32,6 +32,8 @@ var _ shim.Schema = (*attrSchema)(nil) var _ shim.SchemaWithWriteOnly = (*attrSchema)(nil) +var _ shim.SchemaWithHasDefault = (*attrSchema)(nil) + func (s *attrSchema) Type() shim.ValueType { ty := s.attr.GetType() vt, err := convertType(ty) @@ -63,6 +65,10 @@ func (*attrSchema) DefaultValue() (interface{}, error) { return nil, bridge.ErrSchemaDefaultValue } +func (s *attrSchema) HasDefault() bool { + return s.attr.HasDefault() +} + func (s *attrSchema) Description() string { if desc := s.attr.GetMarkdownDescription(); desc != "" { return desc diff --git a/pkg/pf/tfgen/testdata/TestPFNestedFullyComputed.golden b/pkg/pf/tfgen/testdata/TestPFNestedFullyComputed.golden new file mode 100644 index 000000000..ebb250d51 --- /dev/null +++ b/pkg/pf/tfgen/testdata/TestPFNestedFullyComputed.golden @@ -0,0 +1,112 @@ +{ + "name": "testprovider", + "attribution": "This Pulumi package is based on the [`testprovider` Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "types": { + "testprovider:index/ResB1:ResB1": { + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + } + }, + "type": "object" + }, + "testprovider:index/ResB1Output:ResB1Output": { + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + } + }, + "type": "object", + "required": [ + "a1" + ], + "language": { + "nodejs": { + "requiredInputs": [] + } + } + } + }, + "provider": { + "description": "The provider type for the testprovider package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n", + "methods": { + "terraformConfig": "pulumi:providers:testprovider/terraformConfig" + } + }, + "resources": { + "testprovider:index:Res": { + "properties": { + "b1": { + "$ref": "#/types/testprovider:index/ResB1Output:ResB1OutputOutput" + } + }, + "inputProperties": { + "b1": { + "$ref": "#/types/testprovider:index/ResB1:ResB1" + } + }, + "stateInputs": { + "description": "Input properties used for looking up and filtering Res resources.\n", + "properties": { + "b1": { + "$ref": "#/types/testprovider:index/ResB1:ResB1" + } + }, + "type": "object" + } + } + }, + "functions": { + "pulumi:providers:testprovider/terraformConfig": { + "description": "This function returns a Terraform config object with terraform-namecased keys,to be used with the Terraform Module Provider.", + "inputs": { + "properties": { + "__self__": { + "type": "ref", + "$ref": "#/resources/pulumi:providers:testprovider" + } + }, + "type": "pulumi:providers:testprovider/terraformConfig", + "required": [ + "__self__" + ] + }, + "outputs": { + "properties": { + "result": { + "additionalProperties": { + "$ref": "pulumi.json#/Any" + }, + "type": "object" + } + }, + "required": [ + "result" + ], + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/pkg/pf/tfgen/testdata/TestPFRequiredInputWithDefault.golden b/pkg/pf/tfgen/testdata/TestPFRequiredInputWithDefault.golden new file mode 100644 index 000000000..5e7cd58f5 --- /dev/null +++ b/pkg/pf/tfgen/testdata/TestPFRequiredInputWithDefault.golden @@ -0,0 +1,96 @@ +{ + "name": "testprovider", + "attribution": "This Pulumi package is based on the [`testprovider` Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "provider": { + "description": "The provider type for the testprovider package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n", + "methods": { + "terraformConfig": "pulumi:providers:testprovider/terraformConfig" + } + }, + "resources": { + "testprovider:index:Res": { + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + } + }, + "required": [ + "a1", + "a2" + ], + "inputProperties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + } + }, + "requiredInputs": [ + "a2" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering Res resources.\n", + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "functions": { + "pulumi:providers:testprovider/terraformConfig": { + "description": "This function returns a Terraform config object with terraform-namecased keys,to be used with the Terraform Module Provider.", + "inputs": { + "properties": { + "__self__": { + "type": "ref", + "$ref": "#/resources/pulumi:providers:testprovider" + } + }, + "type": "pulumi:providers:testprovider/terraformConfig", + "required": [ + "__self__" + ] + }, + "outputs": { + "properties": { + "result": { + "additionalProperties": { + "$ref": "pulumi.json#/Any" + }, + "type": "object" + } + }, + "required": [ + "result" + ], + "type": "object" + } + } + } +} \ No newline at end of file diff --git a/pkg/pf/tfgen/tfgen_test.go b/pkg/pf/tfgen/tfgen_test.go index dd10f6c11..55bc9215e 100644 --- a/pkg/pf/tfgen/tfgen_test.go +++ b/pkg/pf/tfgen/tfgen_test.go @@ -27,6 +27,7 @@ import ( pschema "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" rschema "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hexops/autogold/v2" @@ -521,3 +522,90 @@ func TestWriteOnlyOmit(t *testing.T) { require.NoError(t, json.Indent(&b, res.ProviderMetadata.PackageSchema, "", " ")) autogold.ExpectFile(t, autogold.Raw(b.String())) } + +func TestPFRequiredInputWithDefault(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skipf("Skipping on windows - tests cases need to be made robust to newline handling") + } + + schema := rschema.Schema{ + Attributes: map[string]rschema.Attribute{ + "id": rschema.StringAttribute{Computed: true}, + "a1": rschema.StringAttribute{ + Required: true, + Default: stringdefault.StaticString("default"), + }, + "a2": rschema.StringAttribute{ + Required: true, + }, + }, + } + + info := &tfbridge.ResourceInfo{ + Tok: "testprovider:index:Res", + Docs: &tfbridge.DocInfo{Markdown: []byte{' '}}, + } + + res, err := GenerateSchema(context.Background(), GenerateSchemaOptions{ + ProviderInfo: tfbridge.ProviderInfo{ + Name: "testprovider", + UpstreamRepoPath: ".", // no invalid mappings warnings + P: pftfbridge.ShimProvider(&schemaTestProvider{ + resources: map[string]rschema.Schema{ + "res": schema, + }, + }), + Resources: map[string]*tfbridge.ResourceInfo{ + "test_res": info, + }, + }, + }) + require.NoError(t, err) + var b bytes.Buffer + require.NoError(t, json.Indent(&b, res.ProviderMetadata.PackageSchema, "", " ")) + autogold.ExpectFile(t, autogold.Raw(b.String())) +} + +func TestPFNestedFullyComputed(t *testing.T) { + t.Parallel() + + schema := rschema.Schema{ + Attributes: map[string]rschema.Attribute{ + "id": rschema.StringAttribute{Computed: true}, + }, + Blocks: map[string]rschema.Block{ + "b1": rschema.SingleNestedBlock{ + Attributes: map[string]rschema.Attribute{ + "a1": rschema.StringAttribute{Computed: true}, + "a2": rschema.StringAttribute{Optional: true}, + }, + }, + }, + } + + info := &tfbridge.ResourceInfo{ + Tok: "testprovider:index:Res", + Docs: &tfbridge.DocInfo{Markdown: []byte{' '}}, + } + + res, err := GenerateSchema(context.Background(), GenerateSchemaOptions{ + ProviderInfo: tfbridge.ProviderInfo{ + Name: "testprovider", + UpstreamRepoPath: ".", // no invalid mappings warnings + P: pftfbridge.ShimProvider(&schemaTestProvider{ + resources: map[string]rschema.Schema{ + "res": schema, + }, + }), + Resources: map[string]*tfbridge.ResourceInfo{ + "test_res": info, + }, + }, + }) + require.NoError(t, err) + var b bytes.Buffer + require.NoError(t, json.Indent(&b, res.ProviderMetadata.PackageSchema, "", " ")) + autogold.ExpectFile(t, autogold.Raw(b.String())) +} diff --git a/pkg/tests/schema_test.go b/pkg/tests/schema_test.go new file mode 100644 index 000000000..cb20ee63a --- /dev/null +++ b/pkg/tests/schema_test.go @@ -0,0 +1,94 @@ +package tests + +import ( + "bytes" + "encoding/json" + "io" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hexops/autogold/v2" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/internal/tests/pulcheck" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfgen" + pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag" + "github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNestedFullyComputed(t *testing.T) { + t.Parallel() + p := &schema.Provider{ + Schema: map[string]*schema.Schema{}, + ResourcesMap: map[string]*schema.Resource{ + "testprovider_res": { + Schema: map[string]*schema.Schema{ + "list_block": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "a1": { + Type: schema.TypeString, + Computed: true, + }, + "a2": { + Type: schema.TypeString, + Optional: true, + }, + "a3": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "object_block": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "b1": { + Type: schema.TypeString, + Computed: true, + }, + "b2": { + Type: schema.TypeString, + Optional: true, + }, + "b3": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + }, + }, + }, + } + + info := pulcheck.BridgedProvider(t, "testprovider", p) + + schema, err := tfgen.GenerateSchema(info, diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{ + Color: colors.Never, + })) + require.NoError(t, err) + + marshal := func(s pschema.PackageSpec, w io.Writer) error { + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + return enc.Encode(s) + } + + toString := func(s pschema.PackageSpec) string { + buf := bytes.Buffer{} + err := marshal(s, &buf) + assert.NoError(t, err) + return buf.String() + } + + autogold.ExpectFile(t, autogold.Raw(toString(schema))) +} diff --git a/pkg/tests/testdata/TestNestedFullyComputed.golden b/pkg/tests/testdata/TestNestedFullyComputed.golden new file mode 100644 index 000000000..00f0d7281 --- /dev/null +++ b/pkg/tests/testdata/TestNestedFullyComputed.golden @@ -0,0 +1,79 @@ +{ + "name": "testprovider", + "version": "0.0.1", + "attribution": "This Pulumi package is based on the [`testprovider` Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider).", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "types": { + "testprovider:index/ResListBlock:ResListBlock": { + "properties": { + "a2": { + "type": "string" + } + }, + "type": "object", + "language": { + "nodejs": { + "requiredOutputs": [ + "a1" + ] + } + } + } + }, + "provider": { + "description": "The provider type for the testprovider package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n" + }, + "resources": { + "testprovider:index/res:Res": { + "properties": { + "listBlocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResListBlock:ResListBlock" + } + } + }, + "required": [ + "listBlocks" + ], + "inputProperties": { + "listBlocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResListBlock:ResListBlock" + } + } + }, + "requiredInputs": [ + "listBlocks" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering Res resources.\n", + "properties": { + "listBlocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResListBlock:ResListBlock" + } + } + }, + "type": "object" + } + } + } +} diff --git a/pkg/tfgen/generate.go b/pkg/tfgen/generate.go index 5dedee44a..c609b1e87 100644 --- a/pkg/tfgen/generate.go +++ b/pkg/tfgen/generate.go @@ -1798,6 +1798,10 @@ func (g *Generator) propertyVariable(parentPath paths.TypePath, key string, varInfo = info[key] } + if !input(shimSchema, varInfo) && !out { + return nil, nil + } + // If a variable is marked as omitted, omit it. // // Because the recursive traversal into the fields used by this type are diff --git a/pkg/tfgen/generate_schema.go b/pkg/tfgen/generate_schema.go index 627550008..a10dbc94c 100644 --- a/pkg/tfgen/generate_schema.go +++ b/pkg/tfgen/generate_schema.go @@ -44,6 +44,7 @@ import ( "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfgen/internal/paths" + shim "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim" "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/x/muxer" "github.com/pulumi/pulumi-terraform-bridge/v3/unstable/metadata" ) @@ -135,6 +136,7 @@ func (nt *schemaNestedTypes) declareType(typePath paths.TypePath, declarer decla typeName = typ.nestedType.Name().String() } + typ.name = typeName required := codegen.StringSet{} @@ -865,7 +867,12 @@ func (g *schemaGenerator) genResourceType(mod tokens.Module, res *resourceType) } spec.InputProperties[prop.name] = g.genProperty(prop) - if !prop.optional() { + hasDefault := false + if s, ok := prop.schema.(shim.SchemaWithHasDefault); ok { + hasDefault = s.HasDefault() + } + + if !prop.optional() && !hasDefault { spec.RequiredInputs = append(spec.RequiredInputs, prop.name) } } diff --git a/pkg/tfgen/generate_schema_test.go b/pkg/tfgen/generate_schema_test.go index 9ced81ae7..ab108de17 100644 --- a/pkg/tfgen/generate_schema_test.go +++ b/pkg/tfgen/generate_schema_test.go @@ -438,6 +438,17 @@ func TestNestedDescriptions(t *testing.T) { bridgetesting.AssertEqualsJSONFile(t, "test_data/nested-descriptions-schema.json", schema) } +func TestRequiredInputWithDefault(t *testing.T) { + t.Parallel() + provider := testprovider.ProviderRequiredInputWithDefaultFunc() + schema, err := GenerateSchema(provider, diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{ + Color: colors.Never, + })) + require.NoError(t, err) + require.Empty(t, schema.Resources["testprovider:index:Res"].RequiredInputs) + bridgetesting.AssertEqualsJSONFile(t, "test_data/required-input-with-default-schema.json", schema) +} + func TestAppendExample_InsertMiddle(t *testing.T) { t.Parallel() descTmpl := `Description text diff --git a/pkg/tfgen/internal/testprovider/optionalinputwithdefault.go b/pkg/tfgen/internal/testprovider/optionalinputwithdefault.go new file mode 100644 index 000000000..87c51163e --- /dev/null +++ b/pkg/tfgen/internal/testprovider/optionalinputwithdefault.go @@ -0,0 +1,38 @@ +// Copyright 2016-2022, Pulumi Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package testprovider + +import ( + testproviderdata "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testprovider" + "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge" + shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2" +) + +func ProviderRequiredInputWithDefaultFunc() tfbridge.ProviderInfo { + return tfbridge.ProviderInfo{ + P: shimv2.NewProvider(testproviderdata.ProviderRequiredInputWithDefaultFunc()), + Name: "testprovider", + Description: "A Pulumi package to safely use testprovider in Pulumi programs.", + Keywords: []string{"pulumi", "testprovider"}, + License: "Apache-2.0", + Homepage: "https://pulumi.io", + Repository: "https://github.com/pulumi/pulumi-testprovider", + Resources: map[string]*tfbridge.ResourceInfo{ + "testprovider_res": { + Tok: tfbridge.MakeResource("testprovider", "index", "Res"), + }, + }, + } +} diff --git a/pkg/tfgen/test_data/nested-fully-computed-schema.json b/pkg/tfgen/test_data/nested-fully-computed-schema.json new file mode 100644 index 000000000..48005371f --- /dev/null +++ b/pkg/tfgen/test_data/nested-fully-computed-schema.json @@ -0,0 +1,197 @@ +{ + "name": "testprovider", + "keywords": [ + "pulumi", + "testprovider" + ], + "homepage": "https://pulumi.io", + "license": "Apache-2.0", + "attribution": "This Pulumi package is based on the [`testprovider` Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider).", + "repository": "https://github.com/pulumi/pulumi-testprovider", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-testprovider` repo](https://github.com/pulumi/pulumi-testprovider/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-testprovider` repo](https://github.com/pulumi/pulumi-testprovider/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "types": { + "testprovider:index/ResBlock:ResBlock": { + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + }, + "a3": { + "type": "string" + } + }, + "type": "object", + "required": [ + "a3" + ], + "language": { + "nodejs": { + "requiredOutputs": [] + } + } + }, + "testprovider:index/ResBlockOutput:ResBlockOutput": { + "properties": { + "a1": { + "type": "string" + }, + "a2": { + "type": "string" + }, + "a3": { + "type": "string" + } + }, + "type": "object", + "required": [ + "a1", + "a3" + ], + "language": { + "nodejs": { + "requiredInputs": [] + } + } + }, + "testprovider:index/ResOtherBlock:ResOtherBlock": { + "properties": { + "b1": { + "type": "string" + }, + "b2": { + "type": "string" + } + }, + "type": "object", + "required": [ + "b2" + ], + "language": { + "nodejs": { + "requiredOutputs": [] + } + } + }, + "testprovider:index/ResOtherBlockOutput:ResOtherBlockOutput": { + "properties": { + "b1": { + "type": "string" + }, + "b2": { + "type": "string" + } + }, + "type": "object", + "required": [ + "b1", + "b2" + ], + "language": { + "nodejs": { + "requiredInputs": [] + } + } + } + }, + "provider": { + "description": "The provider type for the testprovider package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n", + "methods": { + "terraformConfig": "pulumi:providers:testprovider/terraformConfig" + } + }, + "resources": { + "testprovider:index/res:Res": { + "properties": { + "blocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResBlockOutput:ResBlockOutputOutput" + } + }, + "otherBlock": { + "$ref": "#/types/testprovider:index/ResOtherBlockOutput:ResOtherBlockOutputOutput" + } + }, + "required": [ + "blocks" + ], + "inputProperties": { + "blocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResBlock:ResBlock" + } + }, + "otherBlock": { + "$ref": "#/types/testprovider:index/ResOtherBlock:ResOtherBlock" + } + }, + "requiredInputs": [ + "blocks" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering Res resources.\n", + "properties": { + "blocks": { + "type": "array", + "items": { + "$ref": "#/types/testprovider:index/ResBlock:ResBlock" + } + }, + "otherBlock": { + "$ref": "#/types/testprovider:index/ResOtherBlock:ResOtherBlock" + } + }, + "type": "object" + } + } + }, + "functions": { + "pulumi:providers:testprovider/terraformConfig": { + "description": "This function returns a Terraform config object with terraform-namecased keys,to be used with the Terraform Module Provider.", + "inputs": { + "properties": { + "__self__": { + "type": "ref", + "$ref": "#/resources/pulumi:providers:testprovider" + } + }, + "type": "pulumi:providers:testprovider/terraformConfig", + "required": [ + "__self__" + ] + }, + "outputs": { + "properties": { + "result": { + "additionalProperties": { + "$ref": "pulumi.json#/Any" + }, + "type": "object" + } + }, + "required": [ + "result" + ], + "type": "object" + } + } + } +} diff --git a/pkg/tfgen/test_data/required-input-with-default-schema.json b/pkg/tfgen/test_data/required-input-with-default-schema.json new file mode 100644 index 000000000..e28ac3d32 --- /dev/null +++ b/pkg/tfgen/test_data/required-input-with-default-schema.json @@ -0,0 +1,115 @@ +{ + "name": "testprovider", + "description": "A Pulumi package to safely use testprovider in Pulumi programs.", + "keywords": [ + "pulumi", + "testprovider" + ], + "homepage": "https://pulumi.io", + "license": "Apache-2.0", + "attribution": "This Pulumi package is based on the [`testprovider` Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider).", + "repository": "https://github.com/pulumi/pulumi-testprovider", + "meta": { + "moduleFormat": "(.*)(?:/[^/]*)" + }, + "language": { + "nodejs": { + "packageDescription": "A Pulumi package to safely use testprovider in Pulumi programs.", + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-testprovider` repo](https://github.com/pulumi/pulumi-testprovider/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "disableUnionOutputTypes": true + }, + "python": { + "readme": "\u003e This provider is a derived work of the [Terraform Provider](https://github.com/terraform-providers/terraform-provider-testprovider)\n\u003e distributed under [MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/). If you encounter a bug or missing feature,\n\u003e first check the [`pulumi-testprovider` repo](https://github.com/pulumi/pulumi-testprovider/issues); however, if that doesn't turn up anything,\n\u003e please consult the source [`terraform-provider-testprovider` repo](https://github.com/terraform-providers/terraform-provider-testprovider/issues).", + "compatibility": "tfbridge20", + "pyproject": {} + } + }, + "config": {}, + "provider": { + "description": "The provider type for the testprovider package. By default, resources use package-wide configuration\nsettings, however an explicit `Provider` instance may be created and passed during resource\nconstruction to achieve fine-grained programmatic control over provider settings. See the\n[documentation](https://www.pulumi.com/docs/reference/programming-model/#providers) for more information.\n", + "methods": { + "terraformConfig": "pulumi:providers:testprovider/terraformConfig" + } + }, + "resources": { + "testprovider:index/res:Res": { + "properties": { + "name": { + "type": "string" + }, + "otherName": { + "type": "string" + }, + "req": { + "type": "string" + } + }, + "required": [ + "name", + "otherName", + "req" + ], + "inputProperties": { + "name": { + "type": "string" + }, + "otherName": { + "type": "string" + }, + "req": { + "type": "string" + } + }, + "requiredInputs": [ + "req" + ], + "stateInputs": { + "description": "Input properties used for looking up and filtering Res resources.\n", + "properties": { + "name": { + "type": "string" + }, + "otherName": { + "type": "string" + }, + "req": { + "type": "string" + } + }, + "type": "object" + } + } + }, + "functions": { + "pulumi:providers:testprovider/terraformConfig": { + "description": "This function returns a Terraform config object with terraform-namecased keys,to be used with the Terraform Module Provider.", + "inputs": { + "properties": { + "__self__": { + "type": "ref", + "$ref": "#/resources/pulumi:providers:testprovider" + } + }, + "type": "pulumi:providers:testprovider/terraformConfig", + "required": [ + "__self__" + ] + }, + "outputs": { + "properties": { + "result": { + "additionalProperties": { + "$ref": "pulumi.json#/Any" + }, + "type": "object" + } + }, + "required": [ + "result" + ], + "type": "object" + } + } + } +} diff --git a/pkg/tfshim/sdk-v2/schema.go b/pkg/tfshim/sdk-v2/schema.go index 2b59f0275..d29d3042b 100644 --- a/pkg/tfshim/sdk-v2/schema.go +++ b/pkg/tfshim/sdk-v2/schema.go @@ -13,6 +13,7 @@ var ( _ = shim.SchemaMap(v2SchemaMap{}) _ = shim.SchemaWithWriteOnly(v2Schema{}) _ = shim.SchemaWithSetElementHash(v2Schema{}) + _ = shim.SchemaWithHasDefault(v2Schema{}) ) // UnknownVariableValue is the sentinal defined in github.com/hashicorp/terraform/configs/hcl2shim, @@ -70,6 +71,11 @@ func (s v2Schema) DefaultValue() (interface{}, error) { return withPatchedDefaults(s.tf).DefaultValue() } +func (s v2Schema) HasDefault() bool { + sch := withPatchedDefaults(s.tf) + return sch.Default != nil || sch.DefaultFunc != nil +} + func (s v2Schema) Description() string { return s.tf.Description } diff --git a/pkg/tfshim/shim.go b/pkg/tfshim/shim.go index 86ddde283..8791216f5 100644 --- a/pkg/tfshim/shim.go +++ b/pkg/tfshim/shim.go @@ -222,6 +222,11 @@ type SchemaWithSetElementHash interface { SetElementHash(v interface{}) (int, error) } +type SchemaWithHasDefault interface { + Schema + HasDefault() bool +} + type SchemaMap interface { Len() int Get(key string) Schema