Skip to content

Commit

Permalink
Check for compound default info values in provider schemas (#1458)
Browse files Browse the repository at this point in the history
Fixes #1325.  This PR adds an early check for the type of `tfbridge.DefaultInfo.Value` to reject unsupported values such as slices and maps. Only simple values such as strings and integers are currently supported. The PR also adds a line in the documentation about this limitation.
  • Loading branch information
VenelinMartinov authored Oct 23, 2023
1 parent 4069819 commit e0f1893
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 0 deletions.
66 changes: 66 additions & 0 deletions internal/testprovider/schema_default_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2016-2023, 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"
)

// Subset of pulumi-random provider.
func ProviderDefaultInfo() *schema.Provider {
resourceRuleset := func() *schema.Resource {
return &schema.Resource{
Description: "Deploy a ruleset",
Schema: resourceDefaultInfoSchema(),
}
}

return &schema.Provider{
Schema: map[string]*schema.Schema{},
ResourcesMap: map[string]*schema.Resource{
"default_ruleset": resourceRuleset(),
},
}
}

func resourceDefaultInfoSchema() map[string]*schema.Schema {
return map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "Name of the ruleset.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "Brief summary of the ruleset and its intended use.",
},
"rules": {
Type: schema.TypeList,
Optional: true,
Description: "List of rules to apply to the ruleset.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Computed: true,
Description: "Unique rule identifier.",
},
},
},
},
}
}
2 changes: 2 additions & 0 deletions pkg/tfbridge/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ type DefaultInfo struct {
ComputeDefault func(ctx context.Context, opts ComputeDefaultOptions) (interface{}, error)

// Value injects a raw literal value as the default.
// Note that only simple types such as string, int and boolean are currently supported here.
// Structs, slices and maps are not yet supported.
Value interface{}
// EnvVars to use for defaults. If none of these variables have values at runtime, the value of `Value` (if any)
// will be used as the default.
Expand Down
43 changes: 43 additions & 0 deletions pkg/tfgen/generate_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"

Expand Down Expand Up @@ -553,6 +554,48 @@ func (g *schemaGenerator) genProperty(prop *variable) pschema.PropertySpec {
if i, ok := defaultValue.(int); ok {
defaultValue = float64(i)
}
rt := reflect.TypeOf(defaultValue)
if rt != nil {
switch kind := rt.Kind(); kind {
case
reflect.Bool,
reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64,
reflect.Uint,
reflect.Uint8,
reflect.Uint16,
reflect.Uint32,
reflect.Uint64,
reflect.Float32,
reflect.Float64,
reflect.String:
// These are fine.
case
reflect.Uintptr,
reflect.Complex64,
reflect.Complex128,
reflect.Array,
reflect.Chan,
reflect.Func,
reflect.Interface,
reflect.Map,
reflect.Pointer,
reflect.Slice,
reflect.Struct,
reflect.UnsafePointer:
fallthrough
default:
contract.Failf(
"Property %v has a DefaultInfo Value %v of kind %v which is not currently supported.",
prop.name,
prop.info.Default.Value,
kind.String(),
)
}
}

if len(defaults.EnvVars) != 0 {
defaultInfo = &pschema.DefaultSpec{
Expand Down
21 changes: 21 additions & 0 deletions pkg/tfgen/generate_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,24 @@ func TestPropagateLanguageOptions(t *testing.T) {
assert.Equal(t, "gradle", actual["buildFiles"])
})
}

func TestDefaultInfoFails(t *testing.T) {
provider := testprovider.ProviderDefaultInfo()
meta, err := metadata.New(nil)
require.NoError(t, err)
provider.MetadataInfo = &tfbridge.MetadataInfo{
Path: "non-nil",
Data: meta,
}
require.NoError(t, err)
defer func() {
r := recover()
assert.Contains(
t,
r,
"Property id has a DefaultInfo Value [default_id] of kind slice which is not currently supported.",
)
}()
// Should panic
_, _ = GenerateSchema(provider, diag.DefaultSink(io.Discard, io.Discard, diag.FormatOptions{Color: colors.Never}))
}
76 changes: 76 additions & 0 deletions pkg/tfgen/internal/testprovider/defaultinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// 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 (
"unicode"

"github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfbridge"
shimv2 "github.com/pulumi/pulumi-terraform-bridge/v3/pkg/tfshim/sdk-v2"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"

testproviderdata "github.com/pulumi/pulumi-terraform-bridge/v3/internal/testprovider"
)

func ProviderDefaultInfo() tfbridge.ProviderInfo {

member := func(mod string, mem string) tokens.ModuleMember {
return tokens.ModuleMember("cloudflare" + ":" + mod + ":" + mem)
}

typ := func(mod string, typ string) tokens.Type {
return tokens.Type(member(mod, typ))
}

resource := func(mod string, res string) tokens.Type {
fn := string(unicode.ToLower(rune(res[0]))) + res[1:]
return typ(mod+"/"+fn, res)
}

return tfbridge.ProviderInfo{
P: shimv2.NewProvider(testproviderdata.ProviderDefaultInfo()),
Name: "default-info",
Description: "",
Keywords: []string{"pulumi", "random"},
License: "Apache-2.0",
Homepage: "https://pulumi.io",
Repository: "",
Config: map[string]*tfbridge.SchemaInfo{
"project": {
Default: &tfbridge.DefaultInfo{
Value: []string{"default_project"},
},
},
},
Resources: map[string]*tfbridge.ResourceInfo{
"default_ruleset": {
Tok: resource("index", "Ruleset"),
Fields: map[string]*tfbridge.SchemaInfo{
"rules": {
Elem: &tfbridge.SchemaInfo{
Fields: map[string]*tfbridge.SchemaInfo{
"id": {
Default: &tfbridge.DefaultInfo{
Value: []string{"default_id"},
},
},
},
},
},
},
},
},
}
}

0 comments on commit e0f1893

Please sign in to comment.