These changes contain a preliminary fix for #7359 in the Go SDK. The fix handles input values that are nested one level deep within maps and arrays, but does not handle other cases of nested input types.
This commit is contained in:
parent
e0902d2489
commit
398fb2f852
|
@ -9,6 +9,9 @@
|
|||
- [auto/nodejs] - Fail early when multiple versions of `@pulumi/pulumi` are detected in nodejs inline programs.'
|
||||
[#7349](https://github.com/pulumi/pulumi/pull/7349)
|
||||
|
||||
- [sdk/go] - Add preliminary support for unmarshaling plain arrays and maps of output values.
|
||||
[#7369](https://github.com/pulumi/pulumi/pull/7369)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [sdk/dotnet] - Fix swallowed nested exceptions with inline program, so they correctly bubble to the consumer.
|
||||
|
|
|
@ -218,29 +218,71 @@ func constructInputsCopyTo(ctx *Context, inputs map[string]interface{}, args int
|
|||
continue
|
||||
}
|
||||
|
||||
if field.Type.Implements(outputType) || field.Type.Implements(inputType) {
|
||||
handleField := func(typ reflect.Type, value resource.PropertyValue, deps []Resource) (reflect.Value, error) {
|
||||
resultType := anyOutputType
|
||||
if field.Type.Implements(outputType) {
|
||||
resultType = field.Type
|
||||
} else if field.Type.Implements(inputType) {
|
||||
toOutputMethodName := "To" + strings.TrimSuffix(field.Type.Name(), "Input") + "Output"
|
||||
if toOutputMethod, found := field.Type.MethodByName(toOutputMethodName); found {
|
||||
if typ.Implements(outputType) {
|
||||
resultType = typ
|
||||
} else if typ.Implements(inputType) {
|
||||
toOutputMethodName := "To" + strings.TrimSuffix(typ.Name(), "Input") + "Output"
|
||||
if toOutputMethod, found := typ.MethodByName(toOutputMethodName); found {
|
||||
mt := toOutputMethod.Type
|
||||
if mt.NumIn() == 0 && mt.NumOut() == 1 && mt.Out(0).Implements(outputType) {
|
||||
resultType = mt.Out(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
output := ctx.newOutput(resultType, ci.deps...)
|
||||
output := ctx.newOutput(resultType, deps...)
|
||||
dest := reflect.New(output.ElementType()).Elem()
|
||||
known := !ci.value.ContainsUnknowns()
|
||||
secret, err := unmarshalOutput(ctx, ci.value, dest)
|
||||
secret, err := unmarshalOutput(ctx, value, dest)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
output.getState().resolve(dest.Interface(), known, secret, nil)
|
||||
return reflect.ValueOf(output), nil
|
||||
}
|
||||
|
||||
isInputType := func(typ reflect.Type) bool {
|
||||
return typ.Implements(outputType) || typ.Implements(inputType)
|
||||
}
|
||||
|
||||
if isInputType(field.Type) {
|
||||
val, err := handleField(field.Type, ci.value, ci.deps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fieldV.Set(val)
|
||||
continue
|
||||
}
|
||||
|
||||
output.getState().resolve(dest.Interface(), known, secret, nil)
|
||||
fieldV.Set(reflect.ValueOf(output))
|
||||
if field.Type.Kind() == reflect.Slice && isInputType(field.Type.Elem()) {
|
||||
elemType := field.Type.Elem()
|
||||
length := len(ci.value.ArrayValue())
|
||||
dest := reflect.MakeSlice(field.Type, length, length)
|
||||
for i := 0; i < length; i++ {
|
||||
val, err := handleField(elemType, ci.value.ArrayValue()[i], ci.deps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest.Index(i).Set(val)
|
||||
}
|
||||
fieldV.Set(dest)
|
||||
continue
|
||||
}
|
||||
|
||||
if field.Type.Kind() == reflect.Map && isInputType(field.Type.Elem()) {
|
||||
elemType := field.Type.Elem()
|
||||
length := len(ci.value.ObjectValue())
|
||||
dest := reflect.MakeMapWithSize(field.Type, length)
|
||||
for k, v := range ci.value.ObjectValue() {
|
||||
key := reflect.ValueOf(string(k))
|
||||
val, err := handleField(elemType, v, ci.deps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dest.SetMapIndex(key, val)
|
||||
}
|
||||
fieldV.Set(dest)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
||||
)
|
||||
|
@ -130,6 +131,14 @@ type NestedMapArgs struct {
|
|||
Value map[string]Nested `pulumi:"value"`
|
||||
}
|
||||
|
||||
type PlainArrayArgs struct {
|
||||
Value []StringInput `pulumi:"value"`
|
||||
}
|
||||
|
||||
type PlainMapArgs struct {
|
||||
Value map[string]StringInput `pulumi:"value"`
|
||||
}
|
||||
|
||||
func TestConstructInputsCopyTo(t *testing.T) {
|
||||
var ctx Context
|
||||
|
||||
|
@ -143,6 +152,7 @@ func TestConstructInputsCopyTo(t *testing.T) {
|
|||
expectedValue interface{}
|
||||
expectedSecret bool
|
||||
expectedDeps []Resource
|
||||
typeOnly bool
|
||||
}{
|
||||
{
|
||||
input: resource.NewStringProperty("foo"),
|
||||
|
@ -339,23 +349,45 @@ func TestConstructInputsCopyTo(t *testing.T) {
|
|||
"b": {Foo: "4", Bar: 4},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: resource.NewArrayProperty([]resource.PropertyValue{
|
||||
resource.NewStringProperty("foo"),
|
||||
resource.NewStringProperty("bar"),
|
||||
}),
|
||||
args: &PlainArrayArgs{},
|
||||
expectedType: []StringInput{},
|
||||
typeOnly: true,
|
||||
},
|
||||
{
|
||||
input: resource.NewObjectProperty(resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"foo": "bar",
|
||||
"baz": "qux",
|
||||
})),
|
||||
args: &PlainMapArgs{},
|
||||
expectedType: map[string]StringInput{},
|
||||
typeOnly: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%T-%v-%T", test.args, test.input, test.expectedType), func(t *testing.T) {
|
||||
ctx, err := NewContext(context.Background(), RunInfo{})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
inputs := map[string]interface{}{
|
||||
"value": &constructInput{value: test.input, deps: test.deps},
|
||||
}
|
||||
err = constructInputsCopyTo(ctx, inputs, test.args)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := reflect.ValueOf(test.args).Elem().FieldByName("Value").Interface()
|
||||
assert.IsType(t, test.expectedType, result)
|
||||
require.IsType(t, test.expectedType, result)
|
||||
|
||||
if _, ok := test.expectedType.(Output); ok {
|
||||
value, known, secret, deps, err := await(result.(Output))
|
||||
if test.typeOnly {
|
||||
return
|
||||
}
|
||||
|
||||
if out, ok := result.(Output); ok {
|
||||
value, known, secret, deps, err := await(out)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.expectedValue, value)
|
||||
assert.True(t, known)
|
||||
|
|
Loading…
Reference in a new issue