This commit is contained in:
evanboyle 2020-02-10 14:44:43 -08:00
parent c219633bee
commit aa28ea962c
3 changed files with 141 additions and 98 deletions

View file

@ -300,7 +300,7 @@ func (ctx *Context) ReadResource(
ctx.endRPC(err)
}()
idToRead, known, err := id.ToIDOutput().awaitID(context.TODO())
idToRead, known, _, err := id.ToIDOutput().awaitID(context.TODO())
if !known || err != nil {
return
}
@ -313,13 +313,14 @@ func (ctx *Context) ReadResource(
logging.V(9).Infof("ReadResource(%s, %s): Goroutine spawned, RPC call being made", t, name)
resp, err := ctx.monitor.ReadResource(ctx.ctx, &pulumirpc.ReadResourceRequest{
Type: t,
Name: name,
Parent: inputs.parent,
Properties: inputs.rpcProps,
Provider: inputs.provider,
Id: string(idToRead),
Aliases: inputs.aliases,
Type: t,
Name: name,
Parent: inputs.parent,
Properties: inputs.rpcProps,
Provider: inputs.provider,
Id: string(idToRead),
Aliases: inputs.aliases,
AcceptSecrets: true,
})
if err != nil {
logging.V(9).Infof("ReadResource(%s, %s): error: %v", t, name, err)
@ -443,6 +444,7 @@ func (ctx *Context) RegisterResource(
CustomTimeouts: inputs.customTimeouts,
IgnoreChanges: inputs.ignoreChanges,
Aliases: inputs.aliases,
AcceptSecrets: true,
})
if err != nil {
logging.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err)
@ -652,7 +654,8 @@ func (state *resourceState) resolve(dryrun bool, err error, inputs *resourceInpu
if err = unmarshalOutput(v, dest); err != nil {
output.reject(err)
} else {
output.resolve(dest.Interface(), known)
// TODO(evanboyle) what to do here?
output.resolve(dest.Interface(), false, known)
}
}
}
@ -734,7 +737,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
// Await alias URNs
aliases := make([]string, len(resource.aliases))
for i, alias := range resource.aliases {
urn, _, err := alias.awaitURN(context.Background())
urn, _, _, err := alias.awaitURN(context.Background())
if err != nil {
return nil, errors.Wrap(err, "error waiting for alias URN to resolve")
}
@ -774,7 +777,7 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
var importID ID
if opts.Import != nil {
id, _, err := opts.Import.ToIDOutput().awaitID(context.TODO())
id, _, _, err := opts.Import.ToIDOutput().awaitID(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
}
@ -783,7 +786,7 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
var parentURN URN
if opts.Parent != nil {
urn, _, err := opts.Parent.URN().awaitURN(context.TODO())
urn, _, _, err := opts.Parent.URN().awaitURN(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
}
@ -794,7 +797,7 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
if opts.DependsOn != nil {
depURNs = make([]URN, len(opts.DependsOn))
for i, r := range opts.DependsOn {
urn, _, err := r.URN().awaitURN(context.TODO())
urn, _, _, err := r.URN().awaitURN(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
}
@ -821,11 +824,11 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
}
func (ctx *Context) resolveProviderReference(provider ProviderResource) (string, error) {
urn, _, err := provider.URN().awaitURN(context.TODO())
urn, _, _, err := provider.URN().awaitURN(context.TODO())
if err != nil {
return "", err
}
id, known, err := provider.ID().awaitID(context.TODO())
id, known, _, err := provider.ID().awaitID(context.TODO())
if err != nil {
return "", err
}
@ -899,7 +902,10 @@ func (ctx *Context) RegisterResourceOutputs(resource Resource, outs Map) error {
ctx.endRPC(err)
}()
urn, _, err := resource.URN().awaitURN(context.TODO())
// URN is coming back as nil here
urn, _, _, err := resource.URN().awaitURN(context.TODO())
logging.V(9).Infof("URN: --------------------------------------------------\n")
logging.V(9).Infof("URN: %v\n", urn)
if err != nil {
return
}

View file

@ -103,7 +103,7 @@ func marshalInputs(props Input) (resource.PropertyMap, map[string][]URN, []URN,
var deps []URN
pdepset := map[URN]bool{}
for _, dep := range resourceDeps {
depURN, _, err := dep.URN().awaitURN(context.TODO())
depURN, _, _, err := dep.URN().awaitURN(context.TODO())
if err != nil {
return nil, nil, nil, err
}
@ -136,6 +136,20 @@ 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) {
for {
valueType := reflect.TypeOf(v)
@ -152,25 +166,25 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
input, valueType = newOutput, destType
} else if !valueType.AssignableTo(destType) {
err := errors.Errorf("cannot marshal an input of type %T as a value of type %v", input, destType)
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, 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, errors.Errorf(cannotAwaitFmt, output)
return resource.PropertyValue{}, nil, false, errors.Errorf(cannotAwaitFmt, output)
}
// Await the output.
ov, known, err := output.await(context.TODO())
ov, known, secret, err := output.await(context.TODO())
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, secret, err
}
// If the value is unknown, return the appropriate sentinel.
if !known {
return resource.MakeComputed(resource.NewStringProperty("")), output.dependencies(), nil
return resource.MakeComputed(resource.NewStringProperty("")), output.dependencies(), secret, nil
}
v, deps = ov, output.dependencies()
@ -179,7 +193,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
// If v is nil, just return that.
if v == nil {
return resource.PropertyValue{}, nil, nil
return resource.PropertyValue{}, nil, false, nil
}
// Look for some well known types.
@ -189,7 +203,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
Path: v.Path(),
Text: v.Text(),
URI: v.URI(),
}), deps, nil
}), deps, false, nil
case *archive:
var assets map[string]interface{}
if as := v.Assets(); as != nil {
@ -197,7 +211,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
for k, a := range as {
aa, _, err := marshalInput(a, anyType, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
assets[k] = aa.V
}
@ -206,16 +220,16 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
Assets: assets,
Path: v.Path(),
URI: v.URI(),
}), deps, nil
}), deps, false, nil
case CustomResource:
deps = append(deps, v)
// Resources aren't serializable; instead, serialize a reference to ID, tracking as a dependency.
e, d, err := marshalInput(v.ID(), idType, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
return e, append(deps, d...), nil
return e, append(deps, d...), false, nil
}
contract.Assertf(valueType.AssignableTo(destType) || valueType.ConvertibleTo(destType),
@ -232,25 +246,25 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
rv := reflect.ValueOf(v)
switch rv.Type().Kind() {
case reflect.Bool:
return resource.NewBoolProperty(rv.Bool()), deps, nil
return resource.NewBoolProperty(rv.Bool()), deps, false, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return resource.NewNumberProperty(float64(rv.Int())), deps, nil
return resource.NewNumberProperty(float64(rv.Int())), deps, false, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return resource.NewNumberProperty(float64(rv.Uint())), deps, nil
return resource.NewNumberProperty(float64(rv.Uint())), deps, false, nil
case reflect.Float32, reflect.Float64:
return resource.NewNumberProperty(rv.Float()), deps, nil
return resource.NewNumberProperty(rv.Float()), deps, false, nil
case reflect.Ptr, reflect.Interface:
// Dereference non-nil pointers and interfaces.
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, false, nil
}
v, destType = rv.Elem().Interface(), destType.Elem()
continue
case reflect.String:
return resource.NewStringProperty(rv.String()), deps, nil
return resource.NewStringProperty(rv.String()), deps, false, nil
case reflect.Array, reflect.Slice:
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, false, nil
}
destElem := destType.Elem()
@ -261,22 +275,22 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
elem := rv.Index(i)
e, d, err := marshalInput(elem.Interface(), destElem, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !e.IsNull() {
arr = append(arr, e)
}
deps = append(deps, d...)
}
return resource.NewArrayProperty(arr), deps, nil
return resource.NewArrayProperty(arr), deps, false, nil
case reflect.Map:
if rv.Type().Key().Kind() != reflect.String {
return resource.PropertyValue{}, nil,
return resource.PropertyValue{}, nil, false,
errors.Errorf("expected map keys to be strings; got %v", rv.Type().Key())
}
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, false, nil
}
destElem := destType.Elem()
@ -287,14 +301,14 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
value := rv.MapIndex(key)
mv, d, err := marshalInput(value.Interface(), destElem, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !mv.IsNull() {
obj[resource.PropertyKey(key.String())] = mv
}
deps = append(deps, d...)
}
return resource.NewObjectProperty(obj), deps, nil
return resource.NewObjectProperty(obj), deps, false, nil
case reflect.Struct:
obj := resource.PropertyMap{}
typ := rv.Type()
@ -308,7 +322,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
fv, d, err := marshalInput(rv.Field(i).Interface(), destField.Type, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !fv.IsNull() {
@ -316,9 +330,9 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
}
deps = append(deps, d...)
}
return resource.NewObjectProperty(obj), deps, nil
return resource.NewObjectProperty(obj), deps, false, nil
}
return resource.PropertyValue{}, nil, errors.Errorf("unrecognized input property type: %v (%T)", v, v)
return resource.PropertyValue{}, nil, false, errors.Errorf("unrecognized input property type: %v (%T)", v, v)
}
}

View file

@ -38,12 +38,13 @@ type Output interface {
getState() *OutputState
dependencies() []Resource
fulfillValue(value reflect.Value, known bool, err error)
resolveValue(value reflect.Value, known bool)
fulfill(value interface{}, known bool, err error)
resolve(value interface{}, known bool)
fulfillValue(value reflect.Value, known bool, secret bool, err error)
resolveValue(value reflect.Value, known bool, secret bool)
fulfill(value interface{}, known bool, secret bool, err error)
resolve(value interface{}, known bool, secret bool)
reject(err error)
await(ctx context.Context) (interface{}, bool, error)
// TODO(evanboyle): await should probably be secret aware in some way?
await(ctx context.Context) (interface{}, bool, bool, error)
}
var outputType = reflect.TypeOf((*Output)(nil)).Elem()
@ -74,9 +75,10 @@ type OutputState struct {
state uint32 // one of output{Pending,Resolved,Rejected}
value interface{} // the value of this output if it is resolved.
err error // the error associated with this output if it is rejected.
known bool // true if this output's value is known.
value interface{} // the value of this output if it is resolved.
err error // the error associated with this output if it is rejected.
known bool // true if this output's value is known.
secret bool // true if this output's value is secret
element reflect.Type // the element type of this output.
deps []Resource // the dependencies associated with this output property.
@ -96,11 +98,11 @@ func (o *OutputState) dependencies() []Resource {
return o.deps
}
func (o *OutputState) fulfill(value interface{}, known bool, err error) {
o.fulfillValue(reflect.ValueOf(value), known, err)
func (o *OutputState) fulfill(value interface{}, known, secret bool, err error) {
o.fulfillValue(reflect.ValueOf(value), known, secret, err)
}
func (o *OutputState) fulfillValue(value reflect.Value, known bool, err error) {
func (o *OutputState) fulfillValue(value reflect.Value, known, secret bool, err error) {
if o == nil {
return
}
@ -116,45 +118,46 @@ func (o *OutputState) fulfillValue(value reflect.Value, known bool, err error) {
}
if err != nil {
o.state, o.err, o.known = outputRejected, err, true
o.state, o.err, o.known, o.secret = outputRejected, err, true, secret
} else {
if value.IsValid() {
reflect.ValueOf(&o.value).Elem().Set(value)
}
o.state, o.known = outputResolved, known
o.state, o.known, o.secret = outputResolved, known, secret
}
}
func (o *OutputState) resolve(value interface{}, known bool) {
o.fulfill(value, known, nil)
func (o *OutputState) resolve(value interface{}, known, secret bool) {
o.fulfill(value, known, secret, nil)
}
func (o *OutputState) resolveValue(value reflect.Value, known bool) {
o.fulfillValue(value, known, nil)
func (o *OutputState) resolveValue(value reflect.Value, known, secret bool) {
o.fulfillValue(value, known, secret, nil)
}
func (o *OutputState) reject(err error) {
o.fulfill(nil, true, err)
o.fulfill(nil, true, o.secret, err)
}
func (o *OutputState) await(ctx context.Context) (interface{}, bool, error) {
func (o *OutputState) await(ctx context.Context) (interface{}, bool, bool, error) {
for {
if o == nil {
// If the state is nil, treat its value as resolved and unknown.
return nil, false, nil
// TODO(evanboyle): what to do here?
return nil, false, false, nil
}
o.mutex.Lock()
for o.state == outputPending {
if ctx.Err() != nil {
return nil, true, ctx.Err()
return nil, true, o.secret, ctx.Err()
}
o.cond.Wait()
}
o.mutex.Unlock()
if !o.known || o.err != nil {
return nil, o.known, o.err
return nil, o.known, o.secret, o.err
}
// If the result is an Output, await it in turn.
@ -163,7 +166,7 @@ func (o *OutputState) await(ctx context.Context) (interface{}, bool, error) {
// the element type of the outer output. We should reconsider this.
ov, ok := o.value.(Output)
if !ok {
return o.value, true, nil
return o.value, true, o.secret, nil
}
o = ov.getState()
}
@ -213,6 +216,8 @@ func newOutput(typ reflect.Type, deps ...Resource) Output {
return output.Interface().(Output)
}
// TODO(evanboyle): not sure what to do here? Should this be two methods, NewOutput & NewSecret?
// NewOutput returns an output value that can be used to rendezvous with the production of a value or error. The
// function returns the output itself, plus two functions: one for resolving a value, and another for rejecting with an
// error; exactly one function must be called. This acts like a promise.
@ -220,7 +225,7 @@ func NewOutput() (Output, func(interface{}), func(error)) {
out := newOutputState(anyType)
resolve := func(v interface{}) {
out.resolve(v, true)
out.resolve(v, true, false)
}
reject := func(err error) {
out.reject(err)
@ -375,9 +380,9 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
result := newOutput(resultType, o.dependencies()...)
go func() {
v, known, err := o.await(ctx)
v, known, secret, err := o.await(ctx)
if err != nil || !known {
result.fulfill(nil, known, err)
result.fulfill(nil, known, secret, err)
return
}
@ -389,11 +394,22 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
}
// Fulfill the result.
result.fulfillValue(results[0], true, nil)
result.fulfillValue(results[0], true, secret, nil)
}()
return result
}
func SecretT(input interface{}) Output {
return SecretTWithContext(context.Background(), input)
}
func SecretTWithContext(ctx context.Context, input interface{}) Output {
o := ToOutputWithContext(ctx, input)
// not sure if this is totally right, the input value may resolve and clobber the secret state
o.getState().secret = true
return o
}
// All returns an ArrayOutput that will resolve when all of the provided inputs will resolve. Each element of the
// array will contain the resolved value of the corresponding output. The output will be rejected if any of the inputs
// is rejected.
@ -489,11 +505,12 @@ func callToOutputMethod(ctx context.Context, input reflect.Value, resolvedType r
return toOutputMethod.Call([]reflect.Value{reflect.ValueOf(ctx)})[0].Interface().(Output), true
}
func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, bool, error) {
contract.Assert(v.IsValid())
// TODO(evanboyle) not sure if this is right
if !resolved.CanSet() {
return true, nil
return true, false, nil
}
// If the value is an Input with of a different element type, turn it into an Output of the appropriate type and
@ -502,8 +519,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
if v.CanInterface() && valueType.Implements(inputType) {
input, isNonNil := v.Interface().(Input)
if !isNonNil {
// TODO(evanboyle) not sure if this is right
// A nil input is already fully-resolved.
return true, nil
return true, false, nil
}
valueType = input.ElementType()
@ -529,22 +547,23 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
// If the input is an Output, await its value. The returned value is fully resolved.
if output, ok := input.(Output); ok {
e, known, err := output.await(ctx)
e, known, secret, err := output.await(ctx)
if err != nil || !known {
return known, err
return known, secret, err
}
if !assignInput {
resolved.Set(reflect.ValueOf(e))
} else {
resolved.Set(reflect.ValueOf(input))
}
return true, nil
return true, secret, nil
}
// Check for types that are already fully-resolved.
if v, ok := getResolvedValue(input); ok {
resolved.Set(v)
return true, nil
// TODO(evanboyle) not sure if this is right
return true, false, nil
}
v, isInput = reflect.ValueOf(input), true
@ -572,7 +591,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
resolved = reflect.New(valueType).Elem()
}
known, err := true, error(nil)
known, secret, err := true, false, error(nil)
switch v.Kind() {
case reflect.Interface:
if !v.IsNil() {
@ -589,8 +608,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
numFields := typ.NumField()
for i := 0; i < numFields; i++ {
_, field := getMappedField(resolved, i)
fknown, ferr := awaitInputs(ctx, v.Field(i), field)
fknown, fsecret, ferr := awaitInputs(ctx, v.Field(i), field)
known = known && fknown
secret = secret || fsecret
if err == nil {
err = ferr
}
@ -598,8 +618,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
eknown, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
eknown, esecret, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
known = known && eknown
secret = secret || esecret
if err == nil {
err = eerr
}
@ -608,8 +629,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
l := v.Len()
resolved.Set(reflect.MakeSlice(resolved.Type(), l, l))
for i := 0; i < l; i++ {
eknown, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
eknown, esecret, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
known = known && eknown
secret = secret || esecret
if err == nil {
err = eerr
}
@ -620,13 +642,13 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
iter := v.MapRange()
for iter.Next() {
kv := reflect.New(resolvedKeyType).Elem()
kknown, kerr := awaitInputs(ctx, iter.Key(), kv)
kknown, ksecret, kerr := awaitInputs(ctx, iter.Key(), kv)
if err == nil {
err = kerr
}
vv := reflect.New(resolvedValueType).Elem()
vknown, verr := awaitInputs(ctx, iter.Value(), vv)
vknown, vsecret, verr := awaitInputs(ctx, iter.Value(), vv)
if err == nil {
err = verr
}
@ -636,6 +658,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
}
known = known && kknown && vknown
secret = secret || ksecret && vsecret
}
default:
if isInput {
@ -643,7 +666,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
}
resolved.Set(v)
}
return known, err
return known, secret, err
}
// ToOutput returns an Output that will resolve when all Inputs contained in the given value have resolved.
@ -668,13 +691,13 @@ func ToOutputWithContext(ctx context.Context, v interface{}) Output {
go func() {
element := reflect.New(resolvedType).Elem()
known, err := awaitInputs(ctx, reflect.ValueOf(v), element)
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), element)
if err != nil || !known {
result.fulfill(nil, known, err)
result.fulfill(nil, known, secret, err)
return
}
result.resolveValue(element, true)
result.resolveValue(element, true, secret)
}()
return result
}
@ -754,8 +777,8 @@ func AnyWithContext(ctx context.Context, v interface{}) AnyOutput {
out := newOutput(anyOutputType, gatherDependencies(v)...)
go func() {
var result interface{}
known, err := awaitInputs(ctx, reflect.ValueOf(v), reflect.ValueOf(&result).Elem())
out.fulfill(result, known, err)
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), reflect.ValueOf(&result).Elem())
out.fulfill(result, known, secret, err)
}()
return out.(AnyOutput)
}
@ -782,12 +805,12 @@ func (o IDOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOut
return o.ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
}
func (o IDOutput) awaitID(ctx context.Context) (ID, bool, error) {
id, known, err := o.await(ctx)
func (o IDOutput) awaitID(ctx context.Context) (ID, bool, bool, error) {
id, known, _, err := o.await(ctx)
if !known || err != nil {
return "", known, err
return "", known, false, err
}
return ID(convert(id, stringType).(string)), true, nil
return ID(convert(id, stringType).(string)), true, false, nil
}
func (in URN) ToStringPtrOutput() StringPtrOutput {
@ -806,12 +829,12 @@ func (o URNOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOu
return o.ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
}
func (o URNOutput) awaitURN(ctx context.Context) (URN, bool, error) {
id, known, err := o.await(ctx)
func (o URNOutput) awaitURN(ctx context.Context) (URN, bool, bool, error) {
id, known, _, err := o.await(ctx)
if !known || err != nil {
return "", known, err
return "", known, false, err
}
return URN(convert(id, stringType).(string)), true, nil
return URN(convert(id, stringType).(string)), true, false, nil
}
func convert(v interface{}, to reflect.Type) interface{} {