Insert default values into provider config
Add any top-level default attributes from the provider schema into Null config values.
This commit is contained in:
parent
ac5f08c5d8
commit
e077c9ce95
|
@ -6,6 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
ctyconvert "github.com/zclconf/go-cty/cty/convert"
|
||||||
"github.com/zclconf/go-cty/cty/msgpack"
|
"github.com/zclconf/go-cty/cty/msgpack"
|
||||||
context "golang.org/x/net/context"
|
context "golang.org/x/net/context"
|
||||||
|
|
||||||
|
@ -88,13 +89,67 @@ func (s *GRPCProviderServer) PrepareProviderConfig(_ context.Context, req *proto
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lookup any required, top-level attributes that are Null, and see if we
|
||||||
|
// have a Default value available.
|
||||||
|
configVal, _ = cty.Transform(configVal, func(path cty.Path, val cty.Value) (cty.Value, error) {
|
||||||
|
// we're only looking for top-level attributes
|
||||||
|
if len(path) != 1 {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing to do if we already have a value
|
||||||
|
if !val.IsNull() {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the Schema definition for this attribute
|
||||||
|
getAttr, ok := path[0].(cty.GetAttrStep)
|
||||||
|
// these should all exist, but just ignore anything strange
|
||||||
|
if !ok {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attrSchema := s.provider.Schema[getAttr.Name]
|
||||||
|
// continue to ignore anything that doesn't match
|
||||||
|
if attrSchema == nil {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// find a default value if it exists
|
||||||
|
def, err := attrSchema.DefaultValue()
|
||||||
|
if err != nil {
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// no default
|
||||||
|
if def == nil {
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a cty.Value and make sure it's the correct type
|
||||||
|
tmpVal := hcl2shim.HCL2ValueFromConfigValue(def)
|
||||||
|
val, err = ctyconvert.Convert(tmpVal, val.Type())
|
||||||
|
return val, err
|
||||||
|
})
|
||||||
|
|
||||||
|
configVal, err = block.CoerceValue(configVal)
|
||||||
|
if err != nil {
|
||||||
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
config := terraform.NewResourceConfigShimmed(configVal, block)
|
config := terraform.NewResourceConfigShimmed(configVal, block)
|
||||||
|
|
||||||
warns, errs := s.provider.Validate(config)
|
warns, errs := s.provider.Validate(config)
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, convert.WarnsAndErrsToProto(warns, errs))
|
||||||
|
|
||||||
// TODO: set defaults
|
preparedConfigMP, err := msgpack.Marshal(configVal, block.ImpliedType())
|
||||||
resp.PreparedConfig = req.Config
|
if err != nil {
|
||||||
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.PreparedConfig = &proto.DynamicValue{Msgpack: preparedConfigMP}
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package plugin
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
@ -425,3 +426,155 @@ func TestApplyResourceChange(t *testing.T) {
|
||||||
t.Fatalf("incorrect final state: %#v\n", newStateVal)
|
t.Fatalf("incorrect final state: %#v\n", newStateVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrepareProviderConfig(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
Name string
|
||||||
|
Schema map[string]*schema.Schema
|
||||||
|
ConfigVal cty.Value
|
||||||
|
ExpectError string
|
||||||
|
ExpectConfig cty.Value
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "test prepare",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("bar"),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("bar"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test default",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
Default: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("default"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test defaultfunc",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Optional: true,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return "defaultfunc", nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("defaultfunc"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test default required",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
DefaultFunc: func() (interface{}, error) {
|
||||||
|
return "defaultfunc", nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("defaultfunc"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test incorrect type",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.NumberIntVal(3),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("3"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "test incorrect default type",
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"foo": &schema.Schema{
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Required: true,
|
||||||
|
Default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ConfigVal: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.NullVal(cty.String),
|
||||||
|
}),
|
||||||
|
ExpectConfig: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"foo": cty.StringVal("true"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
server := &GRPCProviderServer{
|
||||||
|
provider: &schema.Provider{
|
||||||
|
Schema: tc.Schema,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
block := schema.InternalMap(tc.Schema).CoreConfigSchema()
|
||||||
|
|
||||||
|
rawConfig, err := msgpack.Marshal(tc.ConfigVal, block.ImpliedType())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testReq := &proto.PrepareProviderConfig_Request{
|
||||||
|
Config: &proto.DynamicValue{
|
||||||
|
Msgpack: rawConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := server.PrepareProviderConfig(nil, testReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.ExpectError == "" && len(resp.Diagnostics) > 0 {
|
||||||
|
for _, d := range resp.Diagnostics {
|
||||||
|
if !strings.Contains(d.Summary, tc.ExpectError) {
|
||||||
|
t.Fatalf("Unexpected error: %s/%s", d.Summary, d.Detail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := msgpack.Unmarshal(resp.PreparedConfig.Msgpack, block.ImpliedType())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.ExpectConfig.GoString() != val.GoString() {
|
||||||
|
t.Fatalf("\nexpected: %#v\ngot: %#v", tc.ExpectConfig, val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue