[Go] Ensure Apply handles nil output values (#4268)

Fixes #4247.
This commit is contained in:
Luke Hoban 2020-04-01 17:39:01 -07:00 committed by GitHub
parent a0f615ad09
commit 9e37741916
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 66 additions and 4 deletions

View file

@ -2,8 +2,8 @@ CHANGELOG
=========
## HEAD (unreleased)
_(none)_
- Fix handling of `nil` values in Outputs in Go.
[#4268](https://github.com/pulumi/pulumi/pull/4268)
## 1.14.0 (2020-04-01)
- Fix error related to side-by-side versions of `@pulumi/pulumi`.

View file

@ -385,7 +385,11 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
}
// If we have a known value, run the applier to transform it.
results := fn.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(v)})
val := reflect.ValueOf(v)
if !val.IsValid() {
val = reflect.Zero(o.elementType())
}
results := fn.Call([]reflect.Value{reflect.ValueOf(ctx), val})
if len(results) == 2 && !results[1].IsNil() {
result.reject(results[1].Interface().(error))
return
@ -432,6 +436,10 @@ func AllWithContext(ctx context.Context, inputs ...interface{}) ArrayOutput {
}
func gatherDependencies(v interface{}) []Resource {
if v == nil {
return nil
}
depSet := make(map[Resource]struct{})
gatherDependencySet(reflect.ValueOf(v), depSet)
@ -557,7 +565,11 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, bool, er
return known, secret, err
}
if !assignInput {
resolved.Set(reflect.ValueOf(e))
val := reflect.ValueOf(e)
if !val.IsValid() {
val = reflect.Zero(output.ElementType())
}
resolved.Set(val)
} else {
resolved.Set(reflect.ValueOf(input))
}
@ -706,6 +718,11 @@ func toOutputWithContext(ctx context.Context, v interface{}, forceSecret bool) O
result := newOutput(resultType, gatherDependencies(v)...)
go func() {
if v == nil {
result.fulfill(nil, true, false, nil)
return
}
element := reflect.New(resolvedType).Elem()
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), element)
@ -794,6 +811,10 @@ func AnyWithContext(ctx context.Context, v interface{}) AnyOutput {
// Return an output that resolves when all nested inputs have resolved.
out := newOutput(anyOutputType, gatherDependencies(v)...)
go func() {
if v == nil {
out.fulfill(nil, true, false, nil)
return
}
var result interface{}
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), reflect.ValueOf(&result).Elem())
out.fulfill(result, known, secret, err)

View file

@ -393,3 +393,44 @@ func TestSecretApply(t *testing.T) {
}
}
func TestNil(t *testing.T) {
ao := Any(nil)
v, known, secret, err := await(ao)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nil, v)
o := ToOutput(nil)
v, known, secret, err = await(o)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nil, v)
o = ToOutput(ao)
v, known, secret, err = await(o)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nil, v)
ao = ToOutput("").ApplyT(func(v string) interface{} {
return nil
}).(AnyOutput)
v, known, secret, err = await(ao)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nil, v)
bo := ao.ApplyBool(func(x interface{}) bool {
return x == nil
})
v, known, secret, err = await(bo)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, true, v)
}