mirror of https://github.com/pulumi/pulumi.git
673 lines
24 KiB
Go
673 lines
24 KiB
Go
// Copyright 2016-2021, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/archive"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/asset"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/util/logging"
|
|
)
|
|
|
|
// MarshalOptions controls the marshaling of RPC structures.
|
|
type MarshalOptions struct {
|
|
Label string // an optional label for debugging.
|
|
SkipNulls bool // true to skip nulls altogether in the resulting map.
|
|
KeepUnknowns bool // true if we are keeping unknown values (otherwise we skip them).
|
|
RejectUnknowns bool // true if we should return errors on unknown values. Takes precedence over KeepUnknowns.
|
|
ElideAssetContents bool // true if we are eliding the contents of assets.
|
|
ComputeAssetHashes bool // true if we are computing missing asset hashes on the fly.
|
|
KeepSecrets bool // true if we are keeping secrets (otherwise we replace them with their underlying value).
|
|
RejectAssets bool // true if we should return errors on Asset and Archive values.
|
|
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.
|
|
UpgradeToOutputValues bool // true if secrets and unknowns should be upgraded to output values.
|
|
WorkingDirectory string // the optional working directory to use when serializing assets & archives.
|
|
}
|
|
|
|
const (
|
|
// UnknownBoolValue is a sentinel indicating that a bool property's value is not known, because it depends on
|
|
// a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownBoolValue = "1c4a061d-8072-4f0a-a4cb-0ff528b18fe7"
|
|
// UnknownNumberValue is a sentinel indicating that a number property's value is not known, because it depends on
|
|
// a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownNumberValue = "3eeb2bf0-c639-47a8-9e75-3b44932eb421"
|
|
// UnknownStringValue is a sentinel indicating that a string property's value is not known, because it depends on
|
|
// a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownStringValue = "04da6b54-80e4-46f7-96ec-b56ff0331ba9"
|
|
// UnknownArrayValue is a sentinel indicating that an array property's value is not known, because it depends on
|
|
// a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownArrayValue = "6a19a0b0-7e62-4c92-b797-7f8e31da9cc2"
|
|
// UnknownAssetValue is a sentinel indicating that an asset property's value is not known, because it depends on
|
|
// a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownAssetValue = "030794c1-ac77-496b-92df-f27374a8bd58"
|
|
// UnknownArchiveValue is a sentinel indicating that an archive property's value is not known, because it depends
|
|
// on a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownArchiveValue = "e48ece36-62e2-4504-bad9-02848725956a"
|
|
// UnknownObjectValue is a sentinel indicating that an archive property's value is not known, because it depends
|
|
// on a computation with values whose values themselves are not yet known (e.g., dependent upon an output property).
|
|
UnknownObjectValue = "dd056dcd-154b-4c76-9bd3-c8f88648b5ff"
|
|
)
|
|
|
|
// MarshalProperties marshals a resource's property map as a "JSON-like" protobuf structure.
|
|
func MarshalProperties(props resource.PropertyMap, opts MarshalOptions) (*structpb.Struct, error) {
|
|
fields := make(map[string]*structpb.Value)
|
|
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.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)
|
|
} else {
|
|
m, err := MarshalPropertyValue(key, v, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if m != nil {
|
|
fields[string(key)] = m
|
|
}
|
|
}
|
|
}
|
|
return &structpb.Struct{
|
|
Fields: fields,
|
|
}, nil
|
|
}
|
|
|
|
// MarshalPropertyValue marshals a single resource property value into its "JSON-like" value representation.
|
|
func MarshalPropertyValue(key resource.PropertyKey, v resource.PropertyValue,
|
|
opts MarshalOptions,
|
|
) (*structpb.Value, error) {
|
|
if v.IsNull() {
|
|
return MarshalNull(opts), nil
|
|
} else if v.IsBool() {
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_BoolValue{
|
|
BoolValue: v.BoolValue(),
|
|
},
|
|
}, nil
|
|
} else if v.IsNumber() {
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_NumberValue{
|
|
NumberValue: v.NumberValue(),
|
|
},
|
|
}, nil
|
|
} else if v.IsString() {
|
|
return MarshalString(v.StringValue(), opts), nil
|
|
} else if v.IsArray() {
|
|
var elems []*structpb.Value
|
|
for _, elem := range v.ArrayValue() {
|
|
e, err := MarshalPropertyValue(key, elem, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if e != nil {
|
|
elems = append(elems, e)
|
|
}
|
|
}
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_ListValue{
|
|
ListValue: &structpb.ListValue{Values: elems},
|
|
},
|
|
}, nil
|
|
} else if v.IsAsset() {
|
|
if opts.RejectAssets {
|
|
return nil, fmt.Errorf("unexpected Asset property value for %q", key)
|
|
}
|
|
return MarshalAsset(v.AssetValue(), opts)
|
|
} else if v.IsArchive() {
|
|
if opts.RejectAssets {
|
|
return nil, fmt.Errorf("unexpected Asset Archive property value for %q", key)
|
|
}
|
|
return MarshalArchive(v.ArchiveValue(), opts)
|
|
} else if v.IsObject() {
|
|
obj, err := MarshalProperties(v.ObjectValue(), opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return MarshalStruct(obj, opts), nil
|
|
} else if v.IsComputed() {
|
|
if opts.RejectUnknowns {
|
|
return nil, fmt.Errorf("unexpected unknown property value for %q", key)
|
|
} else if opts.KeepUnknowns {
|
|
if opts.KeepOutputValues && opts.UpgradeToOutputValues {
|
|
output := resource.NewObjectProperty(resource.PropertyMap{
|
|
resource.SigKey: resource.NewStringProperty(resource.OutputValueSig),
|
|
})
|
|
return MarshalPropertyValue(key, output, opts)
|
|
}
|
|
return marshalUnknownProperty(v.Input().Element, opts), nil
|
|
}
|
|
return nil, nil // return nil and the caller will ignore it.
|
|
} else if v.IsOutput() {
|
|
if !opts.KeepOutputValues {
|
|
result := v.OutputValue().Element
|
|
if !v.OutputValue().Known {
|
|
// Unknown outputs are marshaled the same as Computed.
|
|
result = resource.MakeComputed(resource.NewStringProperty(""))
|
|
}
|
|
if v.OutputValue().Secret {
|
|
result = resource.MakeSecret(result)
|
|
}
|
|
return MarshalPropertyValue(key, result, opts)
|
|
}
|
|
obj := resource.PropertyMap{
|
|
resource.SigKey: resource.NewStringProperty(resource.OutputValueSig),
|
|
}
|
|
if v.OutputValue().Known {
|
|
obj["value"] = v.OutputValue().Element
|
|
}
|
|
if v.OutputValue().Secret {
|
|
obj["secret"] = resource.NewBoolProperty(v.OutputValue().Secret)
|
|
}
|
|
if len(v.OutputValue().Dependencies) > 0 {
|
|
deps := make([]resource.PropertyValue, len(v.OutputValue().Dependencies))
|
|
for i, dep := range v.OutputValue().Dependencies {
|
|
deps[i] = resource.NewStringProperty(string(dep))
|
|
}
|
|
obj["dependencies"] = resource.NewArrayProperty(deps)
|
|
}
|
|
output := resource.NewObjectProperty(obj)
|
|
return MarshalPropertyValue(key, output, opts)
|
|
} else if v.IsSecret() {
|
|
if !opts.KeepSecrets {
|
|
logging.V(5).Infof("marshalling secret value as raw value as opts.KeepSecrets is false")
|
|
return MarshalPropertyValue(key, v.SecretValue().Element, opts)
|
|
}
|
|
if opts.KeepOutputValues && opts.UpgradeToOutputValues {
|
|
output := resource.NewObjectProperty(resource.PropertyMap{
|
|
resource.SigKey: resource.NewStringProperty(resource.OutputValueSig),
|
|
"secret": resource.NewBoolProperty(true),
|
|
"value": v.SecretValue().Element,
|
|
})
|
|
return MarshalPropertyValue(key, output, opts)
|
|
}
|
|
secret := resource.NewObjectProperty(resource.PropertyMap{
|
|
resource.SigKey: resource.NewStringProperty(resource.SecretSig),
|
|
"value": v.SecretValue().Element,
|
|
})
|
|
return MarshalPropertyValue(key, secret, opts)
|
|
} else if v.IsResourceReference() {
|
|
ref := v.ResourceReferenceValue()
|
|
if !opts.KeepResources {
|
|
val := string(ref.URN)
|
|
if !ref.ID.IsNull() {
|
|
return MarshalPropertyValue(key, ref.ID, opts)
|
|
}
|
|
logging.V(5).Infof("marshalling resource value as raw URN or ID as opts.KeepResources is false")
|
|
return MarshalString(val, opts), nil
|
|
}
|
|
m := resource.PropertyMap{
|
|
resource.SigKey: resource.NewStringProperty(resource.ResourceReferenceSig),
|
|
"urn": resource.NewStringProperty(string(ref.URN)),
|
|
}
|
|
if id, hasID := ref.IDString(); hasID {
|
|
m["id"] = resource.NewStringProperty(id)
|
|
}
|
|
if ref.PackageVersion != "" {
|
|
m["packageVersion"] = resource.NewStringProperty(ref.PackageVersion)
|
|
}
|
|
return MarshalPropertyValue(key, resource.NewObjectProperty(m), opts)
|
|
}
|
|
|
|
contract.Failf("Unrecognized property value in RPC[%s] for %q: %v (type=%v)",
|
|
opts.Label, key, v.V, reflect.TypeOf(v.V))
|
|
return nil, nil
|
|
}
|
|
|
|
// marshalUnknownProperty marshals an unknown property in a way that lets us recover its type on the other end.
|
|
func marshalUnknownProperty(elem resource.PropertyValue, opts MarshalOptions) *structpb.Value {
|
|
// Normal cases, these get sentinels.
|
|
if elem.IsBool() {
|
|
return MarshalString(UnknownBoolValue, opts)
|
|
} else if elem.IsNumber() {
|
|
return MarshalString(UnknownNumberValue, opts)
|
|
} else if elem.IsString() {
|
|
return MarshalString(UnknownStringValue, opts)
|
|
} else if elem.IsArray() {
|
|
return MarshalString(UnknownArrayValue, opts)
|
|
} else if elem.IsAsset() {
|
|
return MarshalString(UnknownAssetValue, opts)
|
|
} else if elem.IsArchive() {
|
|
return MarshalString(UnknownArchiveValue, opts)
|
|
} else if elem.IsObject() {
|
|
return MarshalString(UnknownObjectValue, opts)
|
|
}
|
|
|
|
// If for some reason we end up with a recursive computed/output, just keep digging.
|
|
if elem.IsComputed() {
|
|
return marshalUnknownProperty(elem.Input().Element, opts)
|
|
} else if elem.IsOutput() {
|
|
return marshalUnknownProperty(elem.OutputValue().Element, opts)
|
|
}
|
|
|
|
// Finally, if a null, we can guess its value! (the one and only...)
|
|
if elem.IsNull() {
|
|
return MarshalNull(opts)
|
|
}
|
|
|
|
contract.Failf("Unexpected output/computed property element in RPC[%s]: %v", opts.Label, elem)
|
|
return nil
|
|
}
|
|
|
|
// UnmarshalProperties unmarshals a "JSON-like" protobuf structure into a new resource property map.
|
|
func UnmarshalProperties(props *structpb.Struct, opts MarshalOptions) (resource.PropertyMap, error) {
|
|
result := make(resource.PropertyMap)
|
|
|
|
// First sort the keys so we enumerate them in order (in case errors happen, we want determinism).
|
|
var keys []string
|
|
if props != nil {
|
|
for k := range props.Fields {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
}
|
|
|
|
// And now unmarshal every field it into the map.
|
|
for _, key := range keys {
|
|
pk := resource.PropertyKey(key)
|
|
v, err := UnmarshalPropertyValue(pk, props.Fields[key], opts)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if v != nil {
|
|
logging.V(9).Infof("Unmarshaling property for RPC[%s]: %s=%v", opts.Label, key, v)
|
|
if opts.SkipNulls && v.IsNull() {
|
|
logging.V(9).Infof("Skipping unmarshaling for RPC[%s]: %s is null", opts.Label, key)
|
|
} else if opts.SkipInternalKeys && resource.IsInternalPropertyKey(pk) {
|
|
logging.V(9).Infof("Skipping unmarshaling for RPC[%s]: %s is internal", opts.Label, key)
|
|
} else {
|
|
result[pk] = *v
|
|
}
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// UnmarshalPropertyValue unmarshals a single "JSON-like" value into a new property value.
|
|
func UnmarshalPropertyValue(key resource.PropertyKey, v *structpb.Value,
|
|
opts MarshalOptions,
|
|
) (*resource.PropertyValue, error) {
|
|
contract.Assertf(v != nil, "a value is required")
|
|
|
|
switch v.Kind.(type) {
|
|
case *structpb.Value_NullValue:
|
|
m := resource.NewNullProperty()
|
|
return &m, nil
|
|
case *structpb.Value_BoolValue:
|
|
m := resource.NewBoolProperty(v.GetBoolValue())
|
|
return &m, nil
|
|
case *structpb.Value_NumberValue:
|
|
m := resource.NewNumberProperty(v.GetNumberValue())
|
|
return &m, nil
|
|
case *structpb.Value_StringValue:
|
|
// If it's a string, it could be an unknown property, or just a regular string.
|
|
s := v.GetStringValue()
|
|
if unk, isunk := unmarshalUnknownPropertyValue(s, opts); isunk {
|
|
if opts.RejectUnknowns {
|
|
return nil, fmt.Errorf("unexpected unknown property value for %q", key)
|
|
} else if opts.KeepUnknowns {
|
|
return &unk, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
m := resource.NewStringProperty(s)
|
|
return &m, nil
|
|
case *structpb.Value_ListValue:
|
|
lst := v.GetListValue()
|
|
elems := make([]resource.PropertyValue, len(lst.GetValues()))
|
|
for i, elem := range lst.GetValues() {
|
|
e, err := UnmarshalPropertyValue(key, elem, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
} else if e != nil {
|
|
if i == len(elems) {
|
|
elems = append(elems, *e)
|
|
} else {
|
|
elems[i] = *e
|
|
}
|
|
}
|
|
}
|
|
m := resource.NewArrayProperty(elems)
|
|
return &m, nil
|
|
case *structpb.Value_StructValue:
|
|
// Start by unmarshaling.
|
|
obj, err := UnmarshalProperties(v.GetStructValue(), opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Before returning it as an object, check to see if it's a known recoverable type.
|
|
objmap := obj.Mappable()
|
|
sig, hasSig := objmap[string(resource.SigKey)]
|
|
if !hasSig {
|
|
// This is a weakly-typed object map.
|
|
m := resource.NewObjectProperty(obj)
|
|
return &m, nil
|
|
}
|
|
|
|
switch sig {
|
|
case asset.AssetSig:
|
|
if opts.RejectAssets {
|
|
return nil, fmt.Errorf("unexpected Asset property value for %q", key)
|
|
}
|
|
asset, isasset, err := asset.Deserialize(objmap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// This can only be false with a non-nil error if there is a signature match. We've already verified the
|
|
// signature.
|
|
contract.Assertf(isasset, "value must be an asset")
|
|
if opts.ComputeAssetHashes {
|
|
if opts.WorkingDirectory == "" {
|
|
if err = asset.EnsureHash(); err != nil {
|
|
return nil, fmt.Errorf("failed to compute asset hash for %q: %w", key, err)
|
|
}
|
|
} else {
|
|
if err = asset.EnsureHashWithWD(opts.WorkingDirectory); err != nil {
|
|
return nil, fmt.Errorf("failed to compute asset hash for %q: %w", key, err)
|
|
}
|
|
}
|
|
}
|
|
m := resource.NewAssetProperty(asset)
|
|
return &m, nil
|
|
case archive.ArchiveSig:
|
|
if opts.RejectAssets {
|
|
return nil, fmt.Errorf("unexpected Asset Archive property value for %q", key)
|
|
}
|
|
archive, isarchive, err := archive.Deserialize(objmap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// This can only be false with a non-nil error if there is a signature match. We've already verified the
|
|
// signature.
|
|
contract.Assertf(isarchive, "value must be an archive")
|
|
if opts.ComputeAssetHashes {
|
|
if opts.WorkingDirectory == "" {
|
|
if err = archive.EnsureHash(); err != nil {
|
|
return nil, fmt.Errorf("failed to compute archive hash for %q: %w", key, err)
|
|
}
|
|
} else {
|
|
if err = archive.EnsureHashWithWD(opts.WorkingDirectory); err != nil {
|
|
return nil, fmt.Errorf("failed to compute archive hash for %q: %w", key, err)
|
|
}
|
|
}
|
|
}
|
|
m := resource.NewArchiveProperty(archive)
|
|
return &m, nil
|
|
case resource.SecretSig:
|
|
value, ok := obj["value"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("malformed RPC secret: missing value for %q", key)
|
|
}
|
|
return unmarshalSecretPropertyValue(value, opts), nil
|
|
case resource.ResourceReferenceSig:
|
|
urn, ok := obj["urn"]
|
|
if !ok {
|
|
return nil, fmt.Errorf("malformed resource reference for %q: missing urn", key)
|
|
}
|
|
if !urn.IsString() {
|
|
return nil, fmt.Errorf("malformed resource reference for %q: urn not a string", key)
|
|
}
|
|
|
|
id, hasID := "", false
|
|
if idProp, ok := obj["id"]; ok {
|
|
hasID = true
|
|
switch {
|
|
case idProp.IsString():
|
|
id = idProp.StringValue()
|
|
case idProp.IsComputed():
|
|
// Leave the ID empty to indicate that it is unknown.
|
|
case idProp.IsOutput():
|
|
if idProp.OutputValue().Known {
|
|
if !idProp.OutputValue().Element.IsString() {
|
|
return nil, fmt.Errorf("malformed resource reference for %q: id not a string", key)
|
|
}
|
|
id = idProp.OutputValue().Element.StringValue()
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("malformed resource reference for %q: id not a string", key)
|
|
}
|
|
}
|
|
|
|
var packageVersion string
|
|
if packageVersionProp, ok := obj["packageVersion"]; ok {
|
|
if !packageVersionProp.IsString() {
|
|
return nil, fmt.Errorf("malformed resource reference for %q: packageVersion not a string", key)
|
|
}
|
|
packageVersion = packageVersionProp.StringValue()
|
|
}
|
|
|
|
if !opts.KeepResources {
|
|
value := urn.StringValue()
|
|
if hasID {
|
|
isIDUnknown := id == ""
|
|
if isIDUnknown && opts.KeepUnknowns {
|
|
v := structpb.Value{
|
|
Kind: &structpb.Value_StringValue{StringValue: UnknownStringValue},
|
|
}
|
|
return UnmarshalPropertyValue(key, &v, opts)
|
|
}
|
|
value = id
|
|
}
|
|
r := resource.NewStringProperty(value)
|
|
return &r, nil
|
|
}
|
|
|
|
var ref resource.PropertyValue
|
|
if hasID {
|
|
ref = resource.MakeCustomResourceReference(resource.URN(urn.StringValue()), resource.ID(id), packageVersion)
|
|
} else {
|
|
ref = resource.MakeComponentResourceReference(resource.URN(urn.StringValue()), packageVersion)
|
|
}
|
|
return &ref, nil
|
|
case resource.OutputValueSig:
|
|
value, known := obj["value"]
|
|
|
|
var secret bool
|
|
if secretProp, ok := obj["secret"]; ok {
|
|
if !secretProp.IsBool() {
|
|
return nil, fmt.Errorf("malformed output value for %q: secret not a bool", key)
|
|
}
|
|
secret = secretProp.BoolValue()
|
|
}
|
|
|
|
if !opts.KeepOutputValues {
|
|
result := &value
|
|
if !known {
|
|
result, err = UnmarshalPropertyValue(key, &structpb.Value{
|
|
Kind: &structpb.Value_StringValue{StringValue: UnknownStringValue},
|
|
}, opts)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if secret && result != nil {
|
|
result = unmarshalSecretPropertyValue(*result, opts)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
var dependencies []resource.URN
|
|
if dependenciesProp, ok := obj["dependencies"]; ok {
|
|
if !dependenciesProp.IsArray() {
|
|
return nil, fmt.Errorf("malformed output value for %q: dependencies not an array", key)
|
|
}
|
|
dependencies = make([]resource.URN, len(dependenciesProp.ArrayValue()))
|
|
for i, dep := range dependenciesProp.ArrayValue() {
|
|
if !dep.IsString() {
|
|
return nil, fmt.Errorf(
|
|
"malformed output value for %q: element in dependencies not a string", key)
|
|
}
|
|
dependencies[i] = resource.URN(dep.StringValue())
|
|
}
|
|
}
|
|
|
|
output := resource.NewOutputProperty(resource.Output{
|
|
Element: value,
|
|
Known: known,
|
|
Secret: secret,
|
|
Dependencies: dependencies,
|
|
})
|
|
return &output, nil
|
|
default:
|
|
return nil, fmt.Errorf("unrecognized signature '%v' in property map for %q", sig, key)
|
|
}
|
|
|
|
default:
|
|
contract.Failf("Unrecognized structpb value kind in RPC[%s] for %q: %v", opts.Label, key, reflect.TypeOf(v.Kind))
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func unmarshalUnknownPropertyValue(s string, opts MarshalOptions) (resource.PropertyValue, bool) {
|
|
var elem resource.PropertyValue
|
|
var unknown bool
|
|
switch s {
|
|
case UnknownBoolValue:
|
|
elem, unknown = resource.NewBoolProperty(false), true
|
|
case UnknownNumberValue:
|
|
elem, unknown = resource.NewNumberProperty(0), true
|
|
case UnknownStringValue:
|
|
elem, unknown = resource.NewStringProperty(""), true
|
|
case UnknownArrayValue:
|
|
elem, unknown = resource.NewArrayProperty([]resource.PropertyValue{}), true
|
|
case UnknownAssetValue:
|
|
elem, unknown = resource.NewAssetProperty(&asset.Asset{}), true
|
|
case UnknownArchiveValue:
|
|
elem, unknown = resource.NewArchiveProperty(&archive.Archive{}), true
|
|
case UnknownObjectValue:
|
|
elem, unknown = resource.NewObjectProperty(make(resource.PropertyMap)), true
|
|
}
|
|
if unknown {
|
|
if opts.KeepOutputValues && opts.UpgradeToOutputValues {
|
|
return resource.NewOutputProperty(resource.Output{
|
|
Element: elem,
|
|
}), true
|
|
}
|
|
comp := resource.Computed{Element: elem}
|
|
return resource.NewComputedProperty(comp), true
|
|
}
|
|
return resource.PropertyValue{}, false
|
|
}
|
|
|
|
func unmarshalSecretPropertyValue(v resource.PropertyValue, opts MarshalOptions) *resource.PropertyValue {
|
|
if !opts.KeepSecrets {
|
|
logging.V(5).Infof("unmarshalling secret as raw value, as opts.KeepSecrets is false")
|
|
return &v
|
|
}
|
|
var s resource.PropertyValue
|
|
if opts.KeepOutputValues && opts.UpgradeToOutputValues {
|
|
s = resource.NewOutputProperty(resource.Output{
|
|
Element: v,
|
|
Secret: true,
|
|
Known: true,
|
|
})
|
|
} else {
|
|
s = resource.MakeSecret(v)
|
|
}
|
|
return &s
|
|
}
|
|
|
|
// MarshalNull marshals a nil to its protobuf form.
|
|
func MarshalNull(opts MarshalOptions) *structpb.Value {
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_NullValue{
|
|
NullValue: structpb.NullValue_NULL_VALUE,
|
|
},
|
|
}
|
|
}
|
|
|
|
// MarshalString marshals a string to its protobuf form.
|
|
func MarshalString(s string, opts MarshalOptions) *structpb.Value {
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_StringValue{
|
|
StringValue: s,
|
|
},
|
|
}
|
|
}
|
|
|
|
// MarshalStruct marshals a struct for use in a protobuf field where a value is expected.
|
|
func MarshalStruct(obj *structpb.Struct, opts MarshalOptions) *structpb.Value {
|
|
return &structpb.Value{
|
|
Kind: &structpb.Value_StructValue{
|
|
StructValue: obj,
|
|
},
|
|
}
|
|
}
|
|
|
|
// MarshalAsset marshals an asset into its wire form for resource provider plugins.
|
|
func MarshalAsset(v *asset.Asset, opts MarshalOptions) (*structpb.Value, error) {
|
|
// If we are not providing access to an asset's contents, we simply need to record the fact that this asset existed.
|
|
// Serialize the asset with only its hash (if present).
|
|
if opts.ElideAssetContents {
|
|
v = &asset.Asset{Hash: v.Hash}
|
|
} else {
|
|
// Ensure a hash is present if needed.
|
|
if v.Hash == "" && opts.ComputeAssetHashes {
|
|
if opts.WorkingDirectory == "" {
|
|
if err := v.EnsureHash(); err != nil {
|
|
return nil, fmt.Errorf("failed to compute asset hash: %w", err)
|
|
}
|
|
} else {
|
|
if err := v.EnsureHashWithWD(opts.WorkingDirectory); err != nil {
|
|
return nil, fmt.Errorf("failed to compute asset hash: %w", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// To marshal an asset, we need to first serialize it, and then marshal that.
|
|
sera := v.Serialize()
|
|
serap := resource.NewPropertyMapFromMap(sera)
|
|
pk := resource.PropertyKey(v.URI)
|
|
return MarshalPropertyValue(pk, resource.NewObjectProperty(serap), opts)
|
|
}
|
|
|
|
// MarshalArchive marshals an archive into its wire form for resource provider plugins.
|
|
func MarshalArchive(v *archive.Archive, opts MarshalOptions) (*structpb.Value, error) {
|
|
// If we are not providing access to an asset's contents, we simply need to record the fact that this asset existed.
|
|
// Serialize the asset with only its hash (if present).
|
|
if opts.ElideAssetContents {
|
|
v = &archive.Archive{Hash: v.Hash}
|
|
} else {
|
|
// Ensure a hash is present if needed.
|
|
if v.Hash == "" && opts.ComputeAssetHashes {
|
|
if opts.WorkingDirectory == "" {
|
|
if err := v.EnsureHash(); err != nil {
|
|
return nil, fmt.Errorf("failed to compute archive hash: %w", err)
|
|
}
|
|
} else {
|
|
if err := v.EnsureHashWithWD(opts.WorkingDirectory); err != nil {
|
|
return nil, fmt.Errorf("failed to compute archive hash: %w", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// To marshal an archive, we need to first serialize it, and then marshal that.
|
|
sera := v.Serialize()
|
|
serap := resource.NewPropertyMapFromMap(sera)
|
|
pk := resource.PropertyKey(v.URI)
|
|
return MarshalPropertyValue(pk, resource.NewObjectProperty(serap), opts)
|
|
}
|