[sdk/go] Marshal output values (#7958)
This change adds support for marshaling outputs as output values in the Go SDK.
This commit is contained in:
parent
74dfa83de5
commit
9deb5ca0ec
|
@ -1108,7 +1108,6 @@ func (p *provider) Construct(info ConstructInfo, typ tokens.Type, name tokens.QN
|
|||
// To initially scope the use of this new feature, we only keep output values for
|
||||
// Construct and Call (when the client accepts them).
|
||||
KeepOutputValues: p.acceptOutputs,
|
||||
DontSkipOutputs: true,
|
||||
})
|
||||
if err != nil {
|
||||
return ConstructResult{}, err
|
||||
|
@ -1363,7 +1362,6 @@ func (p *provider) Call(tok tokens.ModuleMember, args resource.PropertyMap, info
|
|||
// To initially scope the use of this new feature, we only keep output values for
|
||||
// Construct and Call (when the client accepts them).
|
||||
KeepOutputValues: p.acceptOutputs,
|
||||
DontSkipOutputs: true,
|
||||
})
|
||||
if err != nil {
|
||||
return CallResult{}, err
|
||||
|
|
|
@ -40,7 +40,6 @@ type MarshalOptions struct {
|
|||
KeepResources bool // true if we are keeping resoures (otherwise we return raw urn).
|
||||
SkipInternalKeys bool // true to skip internal property keys (keys that start with "__") in the resulting map.
|
||||
KeepOutputValues bool // true if we are keeping output values.
|
||||
DontSkipOutputs bool // true to not skip outputs.
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -73,9 +72,7 @@ func MarshalProperties(props resource.PropertyMap, opts MarshalOptions) (*struct
|
|||
for _, key := range props.StableKeys() {
|
||||
v := props[key]
|
||||
logging.V(9).Infof("Marshaling property for RPC[%s]: %s=%v", opts.Label, key, v)
|
||||
if !opts.DontSkipOutputs && v.IsOutput() && !v.OutputValue().Known {
|
||||
logging.V(9).Infof("Skipping output property for RPC[%s]: %v", opts.Label, key)
|
||||
} else if opts.SkipNulls && v.IsNull() {
|
||||
if opts.SkipNulls && v.IsNull() {
|
||||
logging.V(9).Infof("Skipping null property for RPC[%s]: %s (as requested)", opts.Label, key)
|
||||
} else if opts.SkipInternalKeys && resource.IsInternalPropertyKey(key) {
|
||||
logging.V(9).Infof("Skipping internal property for RPC[%s]: %s (as requested)", opts.Label, key)
|
||||
|
|
|
@ -436,7 +436,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "empty (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{}),
|
||||
},
|
||||
|
@ -458,7 +458,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "unknown (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.MakeOutput(resource.NewStringProperty("")),
|
||||
},
|
||||
|
@ -480,7 +480,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "unknown with deps (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty(""),
|
||||
|
@ -515,7 +515,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "known (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("hello"),
|
||||
|
@ -543,7 +543,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "known with deps (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("hello"),
|
||||
|
@ -582,7 +582,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "secret (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("shhh"),
|
||||
|
@ -614,7 +614,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "secret with deps (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("shhh"),
|
||||
|
@ -657,7 +657,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "unknown secret (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("shhh"),
|
||||
|
@ -685,7 +685,7 @@ func TestMarshalProperties(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "unknown secret with deps (KeepOutputValues)",
|
||||
opts: MarshalOptions{DontSkipOutputs: true, KeepOutputValues: true},
|
||||
opts: MarshalOptions{KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("shhh"),
|
||||
|
@ -1695,48 +1695,6 @@ func TestMarshalPropertiesDontSkipOutputs(t *testing.T) {
|
|||
"value": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
},
|
||||
expected: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"nested": {
|
||||
Kind: &structpb.Value_StructValue{
|
||||
StructValue: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Output (KeepUnknowns, KeepOutputValues)",
|
||||
opts: MarshalOptions{KeepUnknowns: true, KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"message": resource.NewOutputProperty(resource.Output{}),
|
||||
"nested": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"value": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
},
|
||||
expected: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"nested": {
|
||||
Kind: &structpb.Value_StructValue{
|
||||
StructValue: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Output (KeepUnknowns, DontSkipOutputs)",
|
||||
opts: MarshalOptions{KeepUnknowns: true, DontSkipOutputs: true},
|
||||
props: resource.PropertyMap{
|
||||
"message": resource.NewOutputProperty(resource.Output{}),
|
||||
"nested": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"value": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
},
|
||||
expected: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"message": {
|
||||
|
@ -1757,8 +1715,8 @@ func TestMarshalPropertiesDontSkipOutputs(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "Output (KeepUnknowns, KeepOutputValues, DontSkipOutputs)",
|
||||
opts: MarshalOptions{KeepUnknowns: true, KeepOutputValues: true, DontSkipOutputs: true},
|
||||
name: "Output (KeepUnknowns, KeepOutputValues)",
|
||||
opts: MarshalOptions{KeepUnknowns: true, KeepOutputValues: true},
|
||||
props: resource.PropertyMap{
|
||||
"message": resource.NewOutputProperty(resource.Output{}),
|
||||
"nested": resource.NewObjectProperty(resource.PropertyMap{
|
||||
|
|
|
@ -51,7 +51,8 @@ type Context struct {
|
|||
engine pulumirpc.EngineClient
|
||||
engineConn *grpc.ClientConn
|
||||
|
||||
keepResources bool // true if resources should be marshaled as strongly-typed references.
|
||||
keepResources bool // true if resources should be marshaled as strongly-typed references.
|
||||
keepOutputValues bool // true if outputs should be marshaled as strongly-type output values.
|
||||
|
||||
rpcs int // the number of outstanding RPC requests.
|
||||
rpcsDone *sync.Cond // an event signaling completion of RPCs.
|
||||
|
@ -104,26 +105,37 @@ func NewContext(ctx context.Context, info RunInfo) (*Context, error) {
|
|||
engine = &mockEngine{}
|
||||
}
|
||||
|
||||
var keepResources bool
|
||||
if monitor != nil {
|
||||
supportsFeatureResp, err := monitor.SupportsFeature(ctx, &pulumirpc.SupportsFeatureRequest{
|
||||
Id: "resourceReferences",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("checking monitor features: %w", err)
|
||||
supportsFeature := func(id string) (bool, error) {
|
||||
if monitor != nil {
|
||||
resp, err := monitor.SupportsFeature(ctx, &pulumirpc.SupportsFeatureRequest{Id: id})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("checking monitor features: %w", err)
|
||||
}
|
||||
return resp.GetHasSupport(), nil
|
||||
}
|
||||
keepResources = supportsFeatureResp.GetHasSupport()
|
||||
return false, nil
|
||||
}
|
||||
|
||||
keepResources, err := supportsFeature("resourceReferences")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keepOutputValues, err := supportsFeature("outputValues")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
context := &Context{
|
||||
ctx: ctx,
|
||||
info: info,
|
||||
exports: make(map[string]Input),
|
||||
monitorConn: monitorConn,
|
||||
monitor: monitor,
|
||||
engineConn: engineConn,
|
||||
engine: engine,
|
||||
keepResources: keepResources,
|
||||
ctx: ctx,
|
||||
info: info,
|
||||
exports: make(map[string]Input),
|
||||
monitorConn: monitorConn,
|
||||
monitor: monitor,
|
||||
engineConn: engineConn,
|
||||
engine: engine,
|
||||
keepResources: keepResources,
|
||||
keepOutputValues: keepOutputValues,
|
||||
}
|
||||
context.rpcsDone = sync.NewCond(&context.rpcsLock)
|
||||
context.Log = &logState{
|
||||
|
@ -383,8 +395,9 @@ func (ctx *Context) Call(tok string, args Input, output Output, self Resource, o
|
|||
rpcArgs, err := plugin.MarshalProperties(
|
||||
resolvedArgs,
|
||||
ctx.withKeepOrRejectUnknowns(plugin.MarshalOptions{
|
||||
KeepSecrets: true,
|
||||
KeepResources: ctx.keepResources,
|
||||
KeepSecrets: true,
|
||||
KeepResources: ctx.keepResources,
|
||||
KeepOutputValues: ctx.keepOutputValues,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshaling args: %w", err)
|
||||
|
@ -1175,6 +1188,9 @@ func (ctx *Context) prepareResourceInputs(res Resource, props Input, t string, o
|
|||
ctx.withKeepOrRejectUnknowns(plugin.MarshalOptions{
|
||||
KeepSecrets: true,
|
||||
KeepResources: ctx.keepResources,
|
||||
// To initially scope the use of this new feature, we only keep output values when
|
||||
// remote is true (for multi-lang components).
|
||||
KeepOutputValues: remote && ctx.keepOutputValues,
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshaling properties: %w", err)
|
||||
|
|
|
@ -73,8 +73,14 @@ func (m *mockMonitor) newURN(parent, typ, name string) string {
|
|||
func (m *mockMonitor) SupportsFeature(ctx context.Context, in *pulumirpc.SupportsFeatureRequest,
|
||||
opts ...grpc.CallOption) (*pulumirpc.SupportsFeatureResponse, error) {
|
||||
|
||||
id := in.GetId()
|
||||
|
||||
// Support for "outputValues" is deliberately disabled for the mock monitor so
|
||||
// instances of `Output` don't show up in `MockResourceArgs` Inputs.
|
||||
hasSupport := id == "secrets" || id == "resourceReferences"
|
||||
|
||||
return &pulumirpc.SupportsFeatureResponse{
|
||||
HasSupport: true,
|
||||
HasSupport: hasSupport,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1277,8 +1277,14 @@ func TestConstructResult(t *testing.T) {
|
|||
resolvedProps, _, _, err := marshalInputs(state)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, resource.NewPropertyMapFromMap(map[string]interface{}{
|
||||
"foo": "hi",
|
||||
"someValue": "something",
|
||||
}), resolvedProps)
|
||||
assert.Equal(t, resource.PropertyMap{
|
||||
"foo": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("hi"),
|
||||
Known: true,
|
||||
}),
|
||||
"someValue": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("something"),
|
||||
Known: true,
|
||||
}),
|
||||
}, resolvedProps)
|
||||
}
|
||||
|
|
|
@ -214,23 +214,6 @@ const cannotAwaitFmt = "cannot marshal Output value of type %T; please use Apply
|
|||
|
||||
// marshalInput marshals an input value, returning its raw serializable value along with any dependencies.
|
||||
func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.PropertyValue, []Resource, error) {
|
||||
val, deps, secret, err := marshalInputAndDetermineSecret(v, destType, await)
|
||||
if err != nil {
|
||||
return val, deps, err
|
||||
}
|
||||
|
||||
if secret {
|
||||
return resource.MakeSecret(val), deps, nil
|
||||
}
|
||||
|
||||
return val, deps, nil
|
||||
}
|
||||
|
||||
// marshalInputAndDetermineSecret marshals an input value with information about secret status
|
||||
func marshalInputAndDetermineSecret(v interface{},
|
||||
destType reflect.Type,
|
||||
await bool) (resource.PropertyValue, []Resource, bool, error) {
|
||||
secret := false
|
||||
var deps []Resource
|
||||
for {
|
||||
valueType := reflect.TypeOf(v)
|
||||
|
@ -239,10 +222,17 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
if input, ok := v.(Input); ok {
|
||||
if inputType := reflect.ValueOf(input); inputType.Kind() == reflect.Ptr && inputType.IsNil() {
|
||||
// input type is a ptr type with a nil backing value
|
||||
return resource.PropertyValue{}, nil, secret, nil
|
||||
return resource.PropertyValue{}, nil, nil
|
||||
}
|
||||
valueType = input.ElementType()
|
||||
|
||||
// Handle cases where the destination is a ptr type whose element type is the same as the value type
|
||||
// (e.g. destType is *FooBar and valueType is FooBar).
|
||||
// This avoids calling the ToOutput method to convert the input to an output in this case.
|
||||
if valueType != destType && destType.Kind() == reflect.Ptr && valueType == destType.Elem() {
|
||||
destType = destType.Elem()
|
||||
}
|
||||
|
||||
// If the element type of the input is not identical to the type of the destination and the destination is
|
||||
// not the any type (i.e. interface{}), attempt to convert the input to an appropriately-typed output.
|
||||
if valueType != destType && destType != anyType {
|
||||
|
@ -253,51 +243,69 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
err := fmt.Errorf(
|
||||
"cannot marshal an input of type %T with element type %v as a value of type %v",
|
||||
input, valueType, destType)
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If the input is an Output, await its value. The returned value is fully resolved.
|
||||
if output, ok := input.(Output); ok {
|
||||
if !await {
|
||||
return resource.PropertyValue{}, nil, false, fmt.Errorf(cannotAwaitFmt, output)
|
||||
return resource.PropertyValue{}, nil, fmt.Errorf(cannotAwaitFmt, output)
|
||||
}
|
||||
|
||||
// Await the output.
|
||||
ov, known, outputSecret, outputDeps, err := output.getState().await(context.TODO())
|
||||
ov, known, secret, outputDeps, err := output.getState().await(context.TODO())
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
}
|
||||
secret = outputSecret
|
||||
|
||||
// If the value is unknown, return the appropriate sentinel.
|
||||
if !known {
|
||||
return resource.MakeComputed(resource.NewStringProperty("")), outputDeps, secret, nil
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
|
||||
v, deps = ov, outputDeps
|
||||
urnSet, err := expandDependencies(context.TODO(), outputDeps)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
urns := urnSet.sortedValues()
|
||||
var dependencies []resource.URN
|
||||
if len(urns) > 0 {
|
||||
dependencies = make([]resource.URN, len(urns))
|
||||
for i, urn := range urns {
|
||||
dependencies[i] = resource.URN(urn)
|
||||
}
|
||||
}
|
||||
|
||||
out := resource.Output{
|
||||
Known: known,
|
||||
Secret: secret,
|
||||
Dependencies: dependencies,
|
||||
}
|
||||
if known {
|
||||
out.Element, _, err = marshalInput(ov, destType, await)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
}
|
||||
return resource.NewOutputProperty(out), outputDeps, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If v is nil, just return that.
|
||||
if v == nil {
|
||||
return resource.PropertyValue{}, nil, secret, nil
|
||||
return resource.PropertyValue{}, nil, nil
|
||||
}
|
||||
|
||||
// Look for some well known types.
|
||||
switch v := v.(type) {
|
||||
case *asset:
|
||||
if v.invalid {
|
||||
return resource.PropertyValue{}, nil, false, fmt.Errorf("invalid asset")
|
||||
return resource.PropertyValue{}, nil, fmt.Errorf("invalid asset")
|
||||
}
|
||||
return resource.NewAssetProperty(&resource.Asset{
|
||||
Path: v.Path(),
|
||||
Text: v.Text(),
|
||||
URI: v.URI(),
|
||||
}), deps, secret, nil
|
||||
}), deps, nil
|
||||
case *archive:
|
||||
if v.invalid {
|
||||
return resource.PropertyValue{}, nil, false, fmt.Errorf("invalid archive")
|
||||
return resource.PropertyValue{}, nil, fmt.Errorf("invalid archive")
|
||||
}
|
||||
|
||||
var assets map[string]interface{}
|
||||
|
@ -306,7 +314,7 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
for k, a := range as {
|
||||
aa, _, err := marshalInput(a, anyType, await)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
assets[k] = aa.V
|
||||
}
|
||||
|
@ -315,13 +323,13 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
Assets: assets,
|
||||
Path: v.Path(),
|
||||
URI: v.URI(),
|
||||
}), deps, secret, nil
|
||||
}), deps, nil
|
||||
case Resource:
|
||||
deps = append(deps, v)
|
||||
|
||||
urn, known, secretURN, err := v.URN().awaitURN(context.Background())
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
contract.Assert(known)
|
||||
contract.Assert(!secretURN)
|
||||
|
@ -329,14 +337,14 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
if custom, ok := v.(CustomResource); ok {
|
||||
id, _, secretID, err := custom.ID().awaitID(context.Background())
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
contract.Assert(!secretID)
|
||||
|
||||
return resource.MakeCustomResourceReference(resource.URN(urn), resource.ID(id), ""), deps, secret, nil
|
||||
return resource.MakeCustomResourceReference(resource.URN(urn), resource.ID(id), ""), deps, nil
|
||||
}
|
||||
|
||||
return resource.MakeComponentResourceReference(resource.URN(urn), ""), deps, secret, nil
|
||||
return resource.MakeComponentResourceReference(resource.URN(urn), ""), deps, nil
|
||||
}
|
||||
|
||||
if destType.Kind() == reflect.Interface {
|
||||
|
@ -361,17 +369,17 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
|
||||
switch rv.Type().Kind() {
|
||||
case reflect.Bool:
|
||||
return resource.NewBoolProperty(rv.Bool()), deps, secret, nil
|
||||
return resource.NewBoolProperty(rv.Bool()), deps, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return resource.NewNumberProperty(float64(rv.Int())), deps, secret, nil
|
||||
return resource.NewNumberProperty(float64(rv.Int())), deps, nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return resource.NewNumberProperty(float64(rv.Uint())), deps, secret, nil
|
||||
return resource.NewNumberProperty(float64(rv.Uint())), deps, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return resource.NewNumberProperty(rv.Float()), deps, secret, nil
|
||||
return resource.NewNumberProperty(rv.Float()), deps, nil
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
// Dereference non-nil pointers and interfaces.
|
||||
if rv.IsNil() {
|
||||
return resource.PropertyValue{}, deps, secret, nil
|
||||
return resource.PropertyValue{}, deps, nil
|
||||
}
|
||||
if destType.Kind() == reflect.Ptr {
|
||||
destType = destType.Elem()
|
||||
|
@ -379,10 +387,10 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
v = rv.Elem().Interface()
|
||||
continue
|
||||
case reflect.String:
|
||||
return resource.NewStringProperty(rv.String()), deps, secret, nil
|
||||
return resource.NewStringProperty(rv.String()), deps, nil
|
||||
case reflect.Array, reflect.Slice:
|
||||
if rv.IsNil() {
|
||||
return resource.PropertyValue{}, deps, secret, nil
|
||||
return resource.PropertyValue{}, deps, nil
|
||||
}
|
||||
|
||||
destElem := destType.Elem()
|
||||
|
@ -393,22 +401,22 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
elem := rv.Index(i)
|
||||
e, d, err := marshalInput(elem.Interface(), destElem, await)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
if !e.IsNull() {
|
||||
arr = append(arr, e)
|
||||
}
|
||||
deps = append(deps, d...)
|
||||
}
|
||||
return resource.NewArrayProperty(arr), deps, secret, nil
|
||||
return resource.NewArrayProperty(arr), deps, nil
|
||||
case reflect.Map:
|
||||
if rv.Type().Key().Kind() != reflect.String {
|
||||
return resource.PropertyValue{}, nil, false,
|
||||
return resource.PropertyValue{}, nil,
|
||||
fmt.Errorf("expected map keys to be strings; got %v", rv.Type().Key())
|
||||
}
|
||||
|
||||
if rv.IsNil() {
|
||||
return resource.PropertyValue{}, deps, secret, nil
|
||||
return resource.PropertyValue{}, deps, nil
|
||||
}
|
||||
|
||||
destElem := destType.Elem()
|
||||
|
@ -419,14 +427,14 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
value := rv.MapIndex(key)
|
||||
mv, d, err := marshalInput(value.Interface(), destElem, await)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
if !mv.IsNull() {
|
||||
obj[resource.PropertyKey(key.String())] = mv
|
||||
}
|
||||
deps = append(deps, d...)
|
||||
}
|
||||
return resource.NewObjectProperty(obj), deps, secret, nil
|
||||
return resource.NewObjectProperty(obj), deps, nil
|
||||
case reflect.Struct:
|
||||
obj := resource.PropertyMap{}
|
||||
typ := rv.Type()
|
||||
|
@ -440,7 +448,7 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
|
||||
fv, d, err := marshalInput(rv.Field(i).Interface(), destField.Type, await)
|
||||
if err != nil {
|
||||
return resource.PropertyValue{}, nil, false, err
|
||||
return resource.PropertyValue{}, nil, err
|
||||
}
|
||||
|
||||
if !fv.IsNull() {
|
||||
|
@ -448,9 +456,9 @@ func marshalInputAndDetermineSecret(v interface{},
|
|||
}
|
||||
deps = append(deps, d...)
|
||||
}
|
||||
return resource.NewObjectProperty(obj), deps, secret, nil
|
||||
return resource.NewObjectProperty(obj), deps, nil
|
||||
}
|
||||
return resource.PropertyValue{}, nil, false, fmt.Errorf("unrecognized input property type: %v (%T)", v, v)
|
||||
return resource.PropertyValue{}, nil, fmt.Errorf("unrecognized input property type: %v (%T)", v, v)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,8 +499,17 @@ func unmarshalResourceReference(ctx *Context, ref resource.ResourceReference) (R
|
|||
|
||||
func unmarshalPropertyValue(ctx *Context, v resource.PropertyValue) (interface{}, bool, error) {
|
||||
switch {
|
||||
case v.IsComputed() || v.IsOutput():
|
||||
case v.IsComputed():
|
||||
return nil, false, nil
|
||||
case v.IsOutput():
|
||||
if !v.OutputValue().Known {
|
||||
return nil, v.OutputValue().Secret, nil
|
||||
}
|
||||
ov, _, err := unmarshalPropertyValue(ctx, v.OutputValue().Element)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return ov, v.OutputValue().Secret, nil
|
||||
case v.IsSecret():
|
||||
sv, _, err := unmarshalPropertyValue(ctx, v.SecretValue().Element)
|
||||
if err != nil {
|
||||
|
@ -573,7 +590,7 @@ func unmarshalOutput(ctx *Context, v resource.PropertyValue, dest reflect.Value)
|
|||
contract.Assert(dest.CanSet())
|
||||
|
||||
// Check for nils and unknowns. The destination will be left with the zero value.
|
||||
if v.IsNull() || v.IsComputed() || v.IsOutput() {
|
||||
if v.IsNull() || v.IsComputed() || (v.IsOutput() && !v.OutputValue().Known) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// nolint: unused,deadcode
|
||||
// nolint: unused,deadcode,lll
|
||||
package pulumi
|
||||
|
||||
import (
|
||||
|
@ -623,7 +623,7 @@ func (UntypedArgs) ElementType() reflect.Type {
|
|||
return reflect.TypeOf((*map[string]interface{})(nil)).Elem()
|
||||
}
|
||||
|
||||
func TestMapInputMarhsalling(t *testing.T) {
|
||||
func TestMapInputMarshalling(t *testing.T) {
|
||||
var theResource simpleCustomResource
|
||||
out := newOutput(nil, reflect.TypeOf((*StringOutput)(nil)).Elem(), &theResource)
|
||||
out.getState().resolve("outputty", true, false, nil)
|
||||
|
@ -645,17 +645,22 @@ func TestMapInputMarhsalling(t *testing.T) {
|
|||
})
|
||||
|
||||
cases := []struct {
|
||||
inputs Input
|
||||
depUrns []string
|
||||
inputs Input
|
||||
depUrns []string
|
||||
expectOutputValue bool
|
||||
}{
|
||||
{inputs: inputs1, depUrns: []string{""}},
|
||||
{inputs: inputs1, depUrns: []string{""}, expectOutputValue: true},
|
||||
{inputs: inputs2, depUrns: nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
resolved, _, depUrns, err := marshalInputs(c.inputs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "outputty", resolved["prop"].StringValue())
|
||||
if c.expectOutputValue {
|
||||
assert.Equal(t, "outputty", resolved["prop"].OutputValue().Element.StringValue())
|
||||
} else {
|
||||
assert.Equal(t, "outputty", resolved["prop"].StringValue())
|
||||
}
|
||||
assert.Equal(t, "foo", resolved["nested"].ObjectValue()["foo"].StringValue())
|
||||
assert.Equal(t, 42.0, resolved["nested"].ObjectValue()["bar"].NumberValue())
|
||||
assert.Equal(t, len(c.depUrns), len(depUrns))
|
||||
|
@ -880,3 +885,655 @@ func TestDependsOnComponent(t *testing.T) {
|
|||
_, deps = newResource("resI", DependsOn([]Resource{resG}))
|
||||
assert.Equal(t, []string{"resG"}, deps)
|
||||
}
|
||||
|
||||
func TestOutputValueMarshalling(t *testing.T) {
|
||||
ctx, err := NewContext(context.Background(), RunInfo{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
values := []struct {
|
||||
value interface{}
|
||||
expected resource.PropertyValue
|
||||
}{
|
||||
{value: nil, expected: resource.NewNullProperty()},
|
||||
{value: 0, expected: resource.NewNumberProperty(0)},
|
||||
{value: 1, expected: resource.NewNumberProperty(1)},
|
||||
{value: "", expected: resource.NewStringProperty("")},
|
||||
{value: "hi", expected: resource.NewStringProperty("hi")},
|
||||
{value: map[string]string{}, expected: resource.NewObjectProperty(resource.PropertyMap{})},
|
||||
{value: []string{}, expected: resource.NewArrayProperty(nil)},
|
||||
}
|
||||
for _, value := range values {
|
||||
for _, deps := range [][]resource.URN{nil, {"fakeURN1", "fakeURN2"}} {
|
||||
for _, known := range []bool{true, false} {
|
||||
for _, secret := range []bool{true, false} {
|
||||
var resources []Resource
|
||||
if len(deps) > 0 {
|
||||
for _, dep := range deps {
|
||||
resources = append(resources, ctx.newDependencyResource(URN(dep)))
|
||||
}
|
||||
}
|
||||
|
||||
out := ctx.newOutput(anyOutputType, resources...)
|
||||
out.getState().resolve(value.value, known, secret, nil)
|
||||
|
||||
expected := resource.Output{
|
||||
Known: known,
|
||||
Secret: secret,
|
||||
Dependencies: deps,
|
||||
}
|
||||
if known {
|
||||
expected.Element = value.expected
|
||||
}
|
||||
|
||||
name := fmt.Sprintf("value=%v, known=%v, secret=%v, deps=%v", value, known, secret, deps)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
inputs := Map{"value": out}
|
||||
expected := resource.PropertyMap{"value": resource.NewOutputProperty(expected)}
|
||||
actual, _, _, err := marshalInputs(inputs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type foo struct {
|
||||
TemplateOptions *TemplateOptions `pulumi:"templateOptions"`
|
||||
}
|
||||
|
||||
type fooArgs struct {
|
||||
TemplateOptions TemplateOptionsPtrInput
|
||||
}
|
||||
|
||||
func (fooArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*foo)(nil)).Elem()
|
||||
}
|
||||
|
||||
type TemplateOptions struct {
|
||||
Description *string `pulumi:"description"`
|
||||
TagSpecifications []TemplateTagSpecification `pulumi:"tagSpecifications"`
|
||||
}
|
||||
|
||||
type TemplateOptionsInput interface {
|
||||
Input
|
||||
|
||||
ToTemplateOptionsOutput() TemplateOptionsOutput
|
||||
ToTemplateOptionsOutputWithContext(context.Context) TemplateOptionsOutput
|
||||
}
|
||||
|
||||
type TemplateOptionsArgs struct {
|
||||
Description StringPtrInput
|
||||
TagSpecifications TemplateTagSpecificationArrayInput
|
||||
}
|
||||
|
||||
func (TemplateOptionsArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TemplateOptions)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i TemplateOptionsArgs) ToTemplateOptionsOutput() TemplateOptionsOutput {
|
||||
return i.ToTemplateOptionsOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i TemplateOptionsArgs) ToTemplateOptionsOutputWithContext(ctx context.Context) TemplateOptionsOutput {
|
||||
return ToOutputWithContext(ctx, i).(TemplateOptionsOutput)
|
||||
}
|
||||
|
||||
func (i TemplateOptionsArgs) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
|
||||
return i.ToTemplateOptionsPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i TemplateOptionsArgs) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
|
||||
return ToOutputWithContext(ctx, i).(TemplateOptionsOutput).ToTemplateOptionsPtrOutputWithContext(ctx)
|
||||
}
|
||||
|
||||
type TemplateOptionsPtrInput interface {
|
||||
Input
|
||||
|
||||
ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput
|
||||
ToTemplateOptionsPtrOutputWithContext(context.Context) TemplateOptionsPtrOutput
|
||||
}
|
||||
|
||||
type templateOptionsPtrType TemplateOptionsArgs
|
||||
|
||||
func TemplateOptionsPtr(v *TemplateOptionsArgs) TemplateOptionsPtrInput {
|
||||
return (*templateOptionsPtrType)(v)
|
||||
}
|
||||
|
||||
func (*templateOptionsPtrType) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**TemplateOptions)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i *templateOptionsPtrType) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
|
||||
return i.ToTemplateOptionsPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i *templateOptionsPtrType) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
|
||||
return ToOutputWithContext(ctx, i).(TemplateOptionsPtrOutput)
|
||||
}
|
||||
|
||||
type TemplateOptionsOutput struct{ *OutputState }
|
||||
|
||||
func (TemplateOptionsOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TemplateOptions)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TemplateOptionsOutput) ToTemplateOptionsOutput() TemplateOptionsOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TemplateOptionsOutput) ToTemplateOptionsOutputWithContext(ctx context.Context) TemplateOptionsOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TemplateOptionsOutput) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
|
||||
return o.ToTemplateOptionsPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o TemplateOptionsOutput) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, v TemplateOptions) *TemplateOptions {
|
||||
return &v
|
||||
}).(TemplateOptionsPtrOutput)
|
||||
}
|
||||
|
||||
type TemplateOptionsPtrOutput struct{ *OutputState }
|
||||
|
||||
func (TemplateOptionsPtrOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**TemplateOptions)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TemplateOptionsPtrOutput) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TemplateOptionsPtrOutput) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
type TemplateTagSpecification struct {
|
||||
Name *string `pulumi:"name"`
|
||||
Tags map[string]string `pulumi:"tags"`
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationInput interface {
|
||||
Input
|
||||
|
||||
ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput
|
||||
ToTemplateTagSpecificationOutputWithContext(context.Context) TemplateTagSpecificationOutput
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationArgs struct {
|
||||
Name StringPtrInput `pulumi:"name"`
|
||||
Tags StringMapInput `pulumi:"tags"`
|
||||
}
|
||||
|
||||
func (TemplateTagSpecificationArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TemplateTagSpecification)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i TemplateTagSpecificationArgs) ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput {
|
||||
return i.ToTemplateTagSpecificationOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i TemplateTagSpecificationArgs) ToTemplateTagSpecificationOutputWithContext(ctx context.Context) TemplateTagSpecificationOutput {
|
||||
return ToOutputWithContext(ctx, i).(TemplateTagSpecificationOutput)
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationArrayInput interface {
|
||||
Input
|
||||
|
||||
ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput
|
||||
ToTemplateTagSpecificationArrayOutputWithContext(context.Context) TemplateTagSpecificationArrayOutput
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationArray []TemplateTagSpecificationInput
|
||||
|
||||
func (TemplateTagSpecificationArray) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*[]TemplateTagSpecification)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i TemplateTagSpecificationArray) ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput {
|
||||
return i.ToTemplateTagSpecificationArrayOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i TemplateTagSpecificationArray) ToTemplateTagSpecificationArrayOutputWithContext(ctx context.Context) TemplateTagSpecificationArrayOutput {
|
||||
return ToOutputWithContext(ctx, i).(TemplateTagSpecificationArrayOutput)
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationOutput struct{ *OutputState }
|
||||
|
||||
func (TemplateTagSpecificationOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TemplateTagSpecification)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TemplateTagSpecificationOutput) ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TemplateTagSpecificationOutput) ToTemplateTagSpecificationOutputWithContext(ctx context.Context) TemplateTagSpecificationOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
type TemplateTagSpecificationArrayOutput struct{ *OutputState }
|
||||
|
||||
func (TemplateTagSpecificationArrayOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*[]TemplateTagSpecification)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TemplateTagSpecificationArrayOutput) ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TemplateTagSpecificationArrayOutput) ToTemplateTagSpecificationArrayOutputWithContext(ctx context.Context) TemplateTagSpecificationArrayOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func TestOutputValueMarshallingNested(t *testing.T) {
|
||||
ctx, err := NewContext(context.Background(), RunInfo{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
RegisterOutputType(TemplateOptionsOutput{})
|
||||
RegisterOutputType(TemplateOptionsPtrOutput{})
|
||||
RegisterOutputType(TemplateTagSpecificationOutput{})
|
||||
RegisterOutputType(TemplateTagSpecificationArrayOutput{})
|
||||
|
||||
templateOptionsPtrOutputType := reflect.TypeOf((*TemplateOptionsPtrOutput)(nil)).Elem()
|
||||
unknownTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
|
||||
unknownTemplateOptionsPtrOutput.getState().resolve(nil, false /*known*/, false /*secret*/, nil)
|
||||
|
||||
unknownSecretTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
|
||||
unknownSecretTemplateOptionsPtrOutput.getState().resolve(nil, false /*known*/, true /*secret*/, nil)
|
||||
|
||||
stringOutputType := reflect.TypeOf((*StringOutput)(nil)).Elem()
|
||||
unknownStringOutput := ctx.newOutput(stringOutputType).(StringOutput)
|
||||
unknownStringOutput.getState().resolve("", false /*known*/, false /*secret*/, nil)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input Input
|
||||
expected resource.PropertyValue
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: fooArgs{},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{}),
|
||||
},
|
||||
{
|
||||
name: "options empty",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{},
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options unknown",
|
||||
input: fooArgs{
|
||||
TemplateOptions: unknownTemplateOptionsPtrOutput,
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options unknown secret",
|
||||
input: fooArgs{
|
||||
TemplateOptions: unknownSecretTemplateOptionsPtrOutput,
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewOutputProperty(resource.Output{
|
||||
Secret: true,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options plain known description",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{
|
||||
Description: String("hello"),
|
||||
},
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"description": resource.NewStringProperty("hello"),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options plain known secret description",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{
|
||||
Description: ToSecret(String("hello")).(StringOutput),
|
||||
},
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"description": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewStringProperty("hello"),
|
||||
Known: true,
|
||||
Secret: true,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options output known secret description",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{
|
||||
Description: ToSecret(String("hello")).(StringOutput),
|
||||
}.ToTemplateOptionsOutput(),
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewOutputProperty(resource.Output{
|
||||
Element: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"description": resource.NewStringProperty("hello"),
|
||||
}),
|
||||
Known: true,
|
||||
Secret: true,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options plain unknown description",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{
|
||||
Description: unknownStringOutput,
|
||||
},
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"description": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "options tag specifications nested unknown",
|
||||
input: fooArgs{
|
||||
TemplateOptions: TemplateOptionsArgs{
|
||||
TagSpecifications: TemplateTagSpecificationArray{
|
||||
TemplateTagSpecificationArgs{
|
||||
Name: String("hello"),
|
||||
Tags: StringMap{
|
||||
"first": String("second"),
|
||||
"third": unknownStringOutput,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"tagSpecifications": resource.NewArrayProperty([]resource.PropertyValue{
|
||||
resource.NewObjectProperty(resource.PropertyMap{
|
||||
"name": resource.NewStringProperty("hello"),
|
||||
"tags": resource.NewObjectProperty(resource.PropertyMap{
|
||||
"first": resource.NewStringProperty("second"),
|
||||
"third": resource.NewOutputProperty(resource.Output{}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
inputs := Map{"value": tt.input}
|
||||
expected := resource.PropertyMap{"value": tt.expected}
|
||||
|
||||
actual, _, _, err := marshalInputs(inputs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type rubberTreeArgs struct {
|
||||
Size *TreeSize `pulumi:"size"`
|
||||
}
|
||||
type RubberTreeArgs struct {
|
||||
Size TreeSizePtrInput
|
||||
}
|
||||
|
||||
func (RubberTreeArgs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*rubberTreeArgs)(nil)).Elem()
|
||||
}
|
||||
|
||||
type TreeSize string
|
||||
|
||||
const (
|
||||
TreeSizeSmall = TreeSize("small")
|
||||
TreeSizeMedium = TreeSize("medium")
|
||||
TreeSizeLarge = TreeSize("large")
|
||||
)
|
||||
|
||||
func (TreeSize) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TreeSize)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (e TreeSize) ToTreeSizeOutput() TreeSizeOutput {
|
||||
return ToOutput(e).(TreeSizeOutput)
|
||||
}
|
||||
|
||||
func (e TreeSize) ToTreeSizeOutputWithContext(ctx context.Context) TreeSizeOutput {
|
||||
return ToOutputWithContext(ctx, e).(TreeSizeOutput)
|
||||
}
|
||||
|
||||
func (e TreeSize) ToTreeSizePtrOutput() TreeSizePtrOutput {
|
||||
return e.ToTreeSizePtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (e TreeSize) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
|
||||
return e.ToTreeSizeOutputWithContext(ctx).ToTreeSizePtrOutputWithContext(ctx)
|
||||
}
|
||||
|
||||
func (e TreeSize) ToStringOutput() StringOutput {
|
||||
return ToOutput(String(e)).(StringOutput)
|
||||
}
|
||||
|
||||
func (e TreeSize) ToStringOutputWithContext(ctx context.Context) StringOutput {
|
||||
return ToOutputWithContext(ctx, String(e)).(StringOutput)
|
||||
}
|
||||
|
||||
func (e TreeSize) ToStringPtrOutput() StringPtrOutput {
|
||||
return String(e).ToStringPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (e TreeSize) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
|
||||
return String(e).ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
|
||||
}
|
||||
|
||||
type TreeSizeOutput struct{ *OutputState }
|
||||
|
||||
func (TreeSizeOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*TreeSize)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToTreeSizeOutput() TreeSizeOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToTreeSizeOutputWithContext(ctx context.Context) TreeSizeOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToTreeSizePtrOutput() TreeSizePtrOutput {
|
||||
return o.ToTreeSizePtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, v TreeSize) *TreeSize {
|
||||
return &v
|
||||
}).(TreeSizePtrOutput)
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToStringOutput() StringOutput {
|
||||
return o.ToStringOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToStringOutputWithContext(ctx context.Context) StringOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, e TreeSize) string {
|
||||
return string(e)
|
||||
}).(StringOutput)
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToStringPtrOutput() StringPtrOutput {
|
||||
return o.ToStringPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o TreeSizeOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, e TreeSize) *string {
|
||||
v := string(e)
|
||||
return &v
|
||||
}).(StringPtrOutput)
|
||||
}
|
||||
|
||||
type TreeSizePtrOutput struct{ *OutputState }
|
||||
|
||||
func (TreeSizePtrOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((**TreeSize)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TreeSizePtrOutput) ToTreeSizePtrOutput() TreeSizePtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizePtrOutput) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizePtrOutput) Elem() TreeSizeOutput {
|
||||
return o.ApplyT(func(v *TreeSize) TreeSize {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
var ret TreeSize
|
||||
return ret
|
||||
}).(TreeSizeOutput)
|
||||
}
|
||||
|
||||
func (o TreeSizePtrOutput) ToStringPtrOutput() StringPtrOutput {
|
||||
return o.ToStringPtrOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (o TreeSizePtrOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
|
||||
return o.ApplyTWithContext(ctx, func(_ context.Context, e *TreeSize) *string {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
v := string(*e)
|
||||
return &v
|
||||
}).(StringPtrOutput)
|
||||
}
|
||||
|
||||
type TreeSizeInput interface {
|
||||
Input
|
||||
|
||||
ToTreeSizeOutput() TreeSizeOutput
|
||||
ToTreeSizeOutputWithContext(context.Context) TreeSizeOutput
|
||||
}
|
||||
|
||||
var treeSizePtrType = reflect.TypeOf((**TreeSize)(nil)).Elem()
|
||||
|
||||
type TreeSizePtrInput interface {
|
||||
Input
|
||||
|
||||
ToTreeSizePtrOutput() TreeSizePtrOutput
|
||||
ToTreeSizePtrOutputWithContext(context.Context) TreeSizePtrOutput
|
||||
}
|
||||
|
||||
type treeSizePtr string
|
||||
|
||||
func TreeSizePtr(v string) TreeSizePtrInput {
|
||||
return (*treeSizePtr)(&v)
|
||||
}
|
||||
|
||||
func (*treeSizePtr) ElementType() reflect.Type {
|
||||
return treeSizePtrType
|
||||
}
|
||||
|
||||
func (in *treeSizePtr) ToTreeSizePtrOutput() TreeSizePtrOutput {
|
||||
return ToOutput(in).(TreeSizePtrOutput)
|
||||
}
|
||||
|
||||
func (in *treeSizePtr) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
|
||||
return ToOutputWithContext(ctx, in).(TreeSizePtrOutput)
|
||||
}
|
||||
|
||||
type TreeSizeMapInput interface {
|
||||
Input
|
||||
|
||||
ToTreeSizeMapOutput() TreeSizeMapOutput
|
||||
ToTreeSizeMapOutputWithContext(context.Context) TreeSizeMapOutput
|
||||
}
|
||||
|
||||
type TreeSizeMap map[string]TreeSize
|
||||
|
||||
func (TreeSizeMap) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*map[string]TreeSize)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (i TreeSizeMap) ToTreeSizeMapOutput() TreeSizeMapOutput {
|
||||
return i.ToTreeSizeMapOutputWithContext(context.Background())
|
||||
}
|
||||
|
||||
func (i TreeSizeMap) ToTreeSizeMapOutputWithContext(ctx context.Context) TreeSizeMapOutput {
|
||||
return ToOutputWithContext(ctx, i).(TreeSizeMapOutput)
|
||||
}
|
||||
|
||||
type TreeSizeMapOutput struct{ *OutputState }
|
||||
|
||||
func (TreeSizeMapOutput) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*map[string]TreeSize)(nil)).Elem()
|
||||
}
|
||||
|
||||
func (o TreeSizeMapOutput) ToTreeSizeMapOutput() TreeSizeMapOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizeMapOutput) ToTreeSizeMapOutputWithContext(ctx context.Context) TreeSizeMapOutput {
|
||||
return o
|
||||
}
|
||||
|
||||
func (o TreeSizeMapOutput) MapIndex(k StringInput) TreeSizeOutput {
|
||||
return All(o, k).ApplyT(func(vs []interface{}) TreeSize {
|
||||
return vs[0].(map[string]TreeSize)[vs[1].(string)]
|
||||
}).(TreeSizeOutput)
|
||||
}
|
||||
|
||||
func TestOutputValueMarshallingEnums(t *testing.T) {
|
||||
_, err := NewContext(context.Background(), RunInfo{})
|
||||
assert.Nil(t, err)
|
||||
|
||||
RegisterOutputType(TreeSizeOutput{})
|
||||
RegisterOutputType(TreeSizePtrOutput{})
|
||||
RegisterOutputType(TreeSizeMapOutput{})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input Input
|
||||
expected resource.PropertyValue
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: &RubberTreeArgs{
|
||||
Size: TreeSize("medium"),
|
||||
},
|
||||
expected: resource.NewObjectProperty(resource.PropertyMap{
|
||||
"size": resource.NewStringProperty("medium"),
|
||||
}),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
inputs := Map{"value": tt.input}
|
||||
expected := resource.PropertyMap{"value": tt.expected}
|
||||
|
||||
actual, _, _, err := marshalInputs(inputs)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/blang/semver"
|
||||
|
@ -388,6 +389,69 @@ func TestRegisterResourceWithResourceReferences(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
type testMyRemoteComponentArgs struct {
|
||||
Inprop string `pulumi:"inprop"`
|
||||
}
|
||||
|
||||
type testMyRemoteComponentInputs struct {
|
||||
Inprop StringInput
|
||||
}
|
||||
|
||||
func (testMyRemoteComponentInputs) ElementType() reflect.Type {
|
||||
return reflect.TypeOf((*testMyRemoteComponentArgs)(nil)).Elem()
|
||||
}
|
||||
|
||||
type testMyRemoteComponent struct {
|
||||
ResourceState
|
||||
|
||||
Outprop StringOutput `pulumi:"outprop"`
|
||||
}
|
||||
|
||||
func TestRemoteComponent(t *testing.T) {
|
||||
mocks := &testMonitor{
|
||||
NewResourceF: func(args MockResourceArgs) (string, resource.PropertyMap, error) {
|
||||
|
||||
switch args.TypeToken {
|
||||
case "pkg:index:Instance":
|
||||
return "i-1234567890abcdef0", resource.PropertyMap{}, nil
|
||||
case "pkg:index:MyRemoteComponent":
|
||||
outprop := resource.NewStringProperty(fmt.Sprintf("output: %s", args.Inputs["inprop"].StringValue()))
|
||||
return args.Name + "_id", resource.PropertyMap{
|
||||
"inprop": args.Inputs["inprop"],
|
||||
"outprop": outprop,
|
||||
}, nil
|
||||
default:
|
||||
return "", nil, errors.Errorf("unknown resource %s", args.TypeToken)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
err := RunErr(func(ctx *Context) error {
|
||||
var instance testInstanceResource
|
||||
err := ctx.RegisterResource("pkg:index:Instance", "instance", &testInstanceResourceInputs{}, &instance)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var myremotecomponent testMyRemoteComponent
|
||||
err = ctx.RegisterRemoteComponentResource(
|
||||
"pkg:index:MyRemoteComponent", "myremotecomponent", &testMyRemoteComponentInputs{
|
||||
Inprop: Sprintf("hello: %v", instance.id),
|
||||
}, &myremotecomponent)
|
||||
assert.NoError(t, err)
|
||||
|
||||
val, known, secret, deps, err := await(myremotecomponent.Outprop)
|
||||
assert.NoError(t, err)
|
||||
stringVal, ok := val.(string)
|
||||
assert.True(t, ok)
|
||||
assert.True(t, strings.HasPrefix(stringVal, "output: hello: "))
|
||||
assert.True(t, known)
|
||||
assert.False(t, secret)
|
||||
assert.Equal(t, []Resource{&myremotecomponent}, deps)
|
||||
|
||||
return nil
|
||||
}, WithMocks("project", "stack", mocks))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWaitOrphanedApply(t *testing.T) {
|
||||
mocks := &testMonitor{
|
||||
NewResourceF: func(args MockResourceArgs) (string, resource.PropertyMap, error) {
|
||||
|
|
Loading…
Reference in a new issue