2018-05-22 19:43:36 +00:00
|
|
|
// Copyright 2016-2018, 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.
|
2017-02-20 19:18:47 +00:00
|
|
|
|
Overhaul resources, planning, and environments
This change, part of pulumi/lumi#90, overhauls quite a bit of the
core resource, planning, environments, and related areas.
The biggest amount of movement comes from the splitting of pkg/resource
into multiple sub-packages. This results in:
- pkg/resource: just the core resource data structures.
- pkg/resource/deployment: all planning and deployment logic.
- pkg/resource/environment: all environment, configuration, and
serialized checkpoint structures and logic.
- pkg/resource/plugin: all dynamically loaded analyzer and
provider logic, including the actual loading and RPC mechanisms.
This also splits the resource abstraction up. We now have:
- resource.Resource: a shared interface.
- resource.Object: a resource that is connected to a live object
that will periodically observe mutations due to ongoing
evaluation of computations. Snapshots of its state may be
taken; however, this is purely a "pre-planning" abstraction.
- resource.State: a snapshot of a resource's state that is frozen.
In other words, it is no longer connected to a live object.
This is what will store provider outputs (ID and properties),
and is what may be serialized into a deployment record.
The branch is in a half-baked state as of this change; more changes
are to come...
2017-06-08 23:37:40 +00:00
|
|
|
package plugin
|
2017-02-20 19:18:47 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
|
|
|
|
|
|
|
structpb "github.com/golang/protobuf/ptypes/struct"
|
2017-09-14 23:40:44 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-02-20 19:18:47 +00:00
|
|
|
|
2017-09-22 02:18:21 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource"
|
|
|
|
"github.com/pulumi/pulumi/pkg/util/contract"
|
2018-05-15 22:28:00 +00:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/logging"
|
2017-02-20 19:18:47 +00:00
|
|
|
)
|
|
|
|
|
2017-02-24 23:41:56 +00:00
|
|
|
// MarshalOptions controls the marshaling of RPC structures.
|
|
|
|
type MarshalOptions struct {
|
2017-12-15 15:22:49 +00:00
|
|
|
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).
|
2018-01-09 20:21:47 +00:00
|
|
|
RejectUnknowns bool // true if we should return errors on unknown values. Takes precedence over KeepUnknowns.
|
2017-12-15 15:22:49 +00:00
|
|
|
ElideAssetContents bool // true if we are eliding the contents of assets.
|
|
|
|
ComputeAssetHashes bool // true if we are computing missing asset hashes on the fly.
|
2017-02-24 23:41:56 +00:00
|
|
|
}
|
|
|
|
|
2017-07-20 16:06:39 +00:00
|
|
|
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.
|
2017-09-14 23:40:44 +00:00
|
|
|
func MarshalProperties(props resource.PropertyMap, opts MarshalOptions) (*structpb.Struct, error) {
|
2017-07-20 16:06:39 +00:00
|
|
|
fields := make(map[string]*structpb.Value)
|
Make more progress on the new deployment model
This change restructures a lot more pertaining to deployments, snapshots,
environments, and the like.
The most notable change is that the notion of a deploy.Source is introduced,
which splits the responsibility between the deploy.Plan -- which simply
understands how to compute and carry out deployment plans -- and the idea
of something that can produce new objects on-demand during deployment.
The primary such implementation is evalSource, which encapsulates an
interpreter and takes a package, args, and config map, and proceeds to run
the interpreter in a distinct goroutine. It synchronizes as needed to
poke and prod the interpreter along its path to create new resource objects.
There are two other sources, however. First, a nullSource, which simply
refuses to create new objects. This can be handy when writing isolated
tests but is also used to simulate the "empty" environment as necessary to
do a complete teardown of the target environment. Second, a fixedSource,
which takes a pre-computed array of objects, and hands those, in order, to
the planning engine; this is mostly useful as a testing technique.
Boatloads of code is now changed and updated in the various CLI commands.
This further chugs along towards pulumi/lumi#90. The end is in sight.
2017-06-10 18:50:47 +00:00
|
|
|
for _, key := range props.StableKeys() {
|
2017-07-01 18:51:52 +00:00
|
|
|
v := props[key]
|
2018-05-15 22:28:00 +00:00
|
|
|
logging.V(9).Infof("Marshaling property for RPC[%s]: %s=%v", opts.Label, key, v)
|
2017-07-01 18:51:52 +00:00
|
|
|
if v.IsOutput() {
|
2018-05-15 22:28:00 +00:00
|
|
|
logging.V(9).Infof("Skipping output property for RPC[%s]: %v", opts.Label, key)
|
2017-07-01 18:51:52 +00:00
|
|
|
} else if opts.SkipNulls && v.IsNull() {
|
2018-05-15 22:28:00 +00:00
|
|
|
logging.V(9).Infof("Skipping null property for RPC[%s]: %s (as requested)", opts.Label, key)
|
2017-07-20 16:06:39 +00:00
|
|
|
} else {
|
2017-09-14 23:40:44 +00:00
|
|
|
m, err := MarshalPropertyValue(v, opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-11-03 20:40:15 +00:00
|
|
|
} else if m != nil {
|
|
|
|
fields[string(key)] = m
|
2017-09-14 23:40:44 +00:00
|
|
|
}
|
2017-02-24 23:41:56 +00:00
|
|
|
}
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
2017-07-20 16:06:39 +00:00
|
|
|
return &structpb.Struct{
|
|
|
|
Fields: fields,
|
2017-09-14 23:40:44 +00:00
|
|
|
}, nil
|
Initial support for output properties (1 of 3)
This change includes approximately 1/3rd of the change necessary
to support output properties, as per pulumi/lumi#90.
In short, the runtime now has a new hidden type, Latent<T>, which
represents a "speculative" value, whose eventual type will be T,
that we can use during evaluation in various ways. Namely,
operations against Latent<T>s generally produce new Latent<U>s.
During planning, any Latent<T>s that end up in resource properties
are transformed into "unknown" property values. An unknown property
value is legal only during planning-time activities, such as Check,
Name, and InspectChange. As a result, those RPC interfaces have
been updated to include lookaside maps indicating which properties
have unknown values. My intent is to add some helper functions to
make dealing with this circumstance more correct-by-construction.
For now, using an unresolved Latent<T> in a conditional will lead
to an error. See pulumi/lumi#67. Speculating beyond these -- by
supporting iterative planning and application -- is something we
want to support eventually, but it makes sense to do that as an
additive change beyond this initial support. That is a missing 1/3.
Finally, the other missing 1/3rd which will happen much sooner
than the rest is restructuing plan application so that it will
correctly observe resolution of Latent<T> values. Right now, the
evaluation happens in one single pass, prior to the application, and
so Latent<T>s never actually get witnessed in a resolved state.
2017-05-24 00:32:59 +00:00
|
|
|
}
|
|
|
|
|
2017-07-20 16:06:39 +00:00
|
|
|
// MarshalPropertyValue marshals a single resource property value into its "JSON-like" value representation.
|
2017-09-14 23:40:44 +00:00
|
|
|
func MarshalPropertyValue(v resource.PropertyValue, opts MarshalOptions) (*structpb.Value, error) {
|
2017-02-20 19:18:47 +00:00
|
|
|
if v.IsNull() {
|
2017-09-14 23:40:44 +00:00
|
|
|
return MarshalNull(opts), nil
|
2017-02-20 19:18:47 +00:00
|
|
|
} else if v.IsBool() {
|
|
|
|
return &structpb.Value{
|
|
|
|
Kind: &structpb.Value_BoolValue{
|
2017-02-28 19:02:33 +00:00
|
|
|
BoolValue: v.BoolValue(),
|
2017-02-20 19:18:47 +00:00
|
|
|
},
|
2017-09-14 23:40:44 +00:00
|
|
|
}, nil
|
2017-02-20 19:18:47 +00:00
|
|
|
} else if v.IsNumber() {
|
|
|
|
return &structpb.Value{
|
|
|
|
Kind: &structpb.Value_NumberValue{
|
2017-02-28 19:02:33 +00:00
|
|
|
NumberValue: v.NumberValue(),
|
2017-02-20 19:18:47 +00:00
|
|
|
},
|
2017-09-14 23:40:44 +00:00
|
|
|
}, nil
|
2017-02-20 19:18:47 +00:00
|
|
|
} else if v.IsString() {
|
2017-09-14 23:40:44 +00:00
|
|
|
return MarshalString(v.StringValue(), opts), nil
|
2017-02-20 19:18:47 +00:00
|
|
|
} else if v.IsArray() {
|
|
|
|
var elems []*structpb.Value
|
|
|
|
for _, elem := range v.ArrayValue() {
|
2017-09-14 23:40:44 +00:00
|
|
|
e, err := MarshalPropertyValue(elem, opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
elems = append(elems, e)
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
|
|
|
return &structpb.Value{
|
|
|
|
Kind: &structpb.Value_ListValue{
|
2017-02-28 19:02:33 +00:00
|
|
|
ListValue: &structpb.ListValue{Values: elems},
|
2017-02-20 19:18:47 +00:00
|
|
|
},
|
2017-09-14 23:40:44 +00:00
|
|
|
}, nil
|
2017-07-14 19:28:43 +00:00
|
|
|
} else if v.IsAsset() {
|
2017-07-17 17:38:57 +00:00
|
|
|
return MarshalAsset(v.AssetValue(), opts)
|
2017-07-14 19:28:43 +00:00
|
|
|
} else if v.IsArchive() {
|
2017-07-17 17:38:57 +00:00
|
|
|
return MarshalArchive(v.ArchiveValue(), opts)
|
2017-02-20 19:18:47 +00:00
|
|
|
} else if v.IsObject() {
|
2017-09-14 23:40:44 +00:00
|
|
|
obj, err := MarshalProperties(v.ObjectValue(), opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return MarshalStruct(obj, opts), nil
|
2017-05-25 20:19:20 +00:00
|
|
|
} else if v.IsComputed() {
|
2018-01-09 20:21:47 +00:00
|
|
|
if opts.RejectUnknowns {
|
|
|
|
return nil, errors.New("unexpected unknown property value")
|
|
|
|
} else if opts.KeepUnknowns {
|
2018-02-05 22:44:23 +00:00
|
|
|
return marshalUnknownProperty(v.Input().Element, opts), nil
|
2017-09-14 23:40:44 +00:00
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, nil // return nil and the caller will ignore it.
|
2017-05-25 20:19:20 +00:00
|
|
|
} else if v.IsOutput() {
|
2017-07-20 16:06:39 +00:00
|
|
|
// Note that at the moment we don't differentiate between computed and output properties on the wire. As
|
|
|
|
// a result, they will show up as computed on the other end. This distinction isn't currently interesting.
|
2017-11-03 20:40:15 +00:00
|
|
|
if opts.KeepUnknowns {
|
2017-09-14 23:40:44 +00:00
|
|
|
return marshalUnknownProperty(v.OutputValue().Element, opts), nil
|
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, nil // return nil and the caller will ignore it.
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
2017-04-08 14:44:02 +00:00
|
|
|
|
2017-12-15 15:22:49 +00:00
|
|
|
contract.Failf("Unrecognized property value in RPC[%s]: %v (type=%v)", opts.Label, v.V, reflect.TypeOf(v.V))
|
2017-09-14 23:40:44 +00:00
|
|
|
return nil, nil
|
2017-07-20 16:06:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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() {
|
2018-02-05 22:44:23 +00:00
|
|
|
return marshalUnknownProperty(elem.Input().Element, opts)
|
2017-07-20 16:06:39 +00:00
|
|
|
} 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)
|
|
|
|
}
|
|
|
|
|
2017-12-15 15:22:49 +00:00
|
|
|
contract.Failf("Unexpected output/computed property element in RPC[%s]: %v", opts.Label, elem)
|
2017-07-20 16:06:39 +00:00
|
|
|
return nil
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
|
|
|
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
// UnmarshalProperties unmarshals a "JSON-like" protobuf structure into a new resource property map.
|
2017-09-14 23:40:44 +00:00
|
|
|
func UnmarshalProperties(props *structpb.Struct, opts MarshalOptions) (resource.PropertyMap, error) {
|
Overhaul resources, planning, and environments
This change, part of pulumi/lumi#90, overhauls quite a bit of the
core resource, planning, environments, and related areas.
The biggest amount of movement comes from the splitting of pkg/resource
into multiple sub-packages. This results in:
- pkg/resource: just the core resource data structures.
- pkg/resource/deployment: all planning and deployment logic.
- pkg/resource/environment: all environment, configuration, and
serialized checkpoint structures and logic.
- pkg/resource/plugin: all dynamically loaded analyzer and
provider logic, including the actual loading and RPC mechanisms.
This also splits the resource abstraction up. We now have:
- resource.Resource: a shared interface.
- resource.Object: a resource that is connected to a live object
that will periodically observe mutations due to ongoing
evaluation of computations. Snapshots of its state may be
taken; however, this is purely a "pre-planning" abstraction.
- resource.State: a snapshot of a resource's state that is frozen.
In other words, it is no longer connected to a live object.
This is what will store provider outputs (ID and properties),
and is what may be serialized into a deployment record.
The branch is in a half-baked state as of this change; more changes
are to come...
2017-06-08 23:37:40 +00:00
|
|
|
result := make(resource.PropertyMap)
|
2017-02-20 19:18:47 +00:00
|
|
|
|
|
|
|
// First sort the keys so we enumerate them in order (in case errors happen, we want determinism).
|
|
|
|
var keys []string
|
2017-07-15 01:08:33 +00:00
|
|
|
if props != nil {
|
|
|
|
for k := range props.Fields {
|
|
|
|
keys = append(keys, k)
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// And now unmarshal every field it into the map.
|
2017-07-01 18:51:52 +00:00
|
|
|
for _, key := range keys {
|
|
|
|
pk := resource.PropertyKey(key)
|
2017-09-14 23:40:44 +00:00
|
|
|
v, err := UnmarshalPropertyValue(props.Fields[key], opts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-11-03 20:40:15 +00:00
|
|
|
} else if v != nil {
|
2018-05-15 22:28:00 +00:00
|
|
|
logging.V(9).Infof("Unmarshaling property for RPC[%s]: %s=%v", opts.Label, key, v)
|
2017-11-03 20:40:15 +00:00
|
|
|
if opts.SkipNulls && v.IsNull() {
|
2018-05-15 22:28:00 +00:00
|
|
|
logging.V(9).Infof("Skipping unmarshaling for RPC[%s]: %s is null", opts.Label, key)
|
2017-11-03 20:40:15 +00:00
|
|
|
} else {
|
|
|
|
result[pk] = *v
|
|
|
|
}
|
2017-07-01 18:51:52 +00:00
|
|
|
}
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 23:40:44 +00:00
|
|
|
return result, nil
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
|
|
|
|
2017-07-14 19:28:43 +00:00
|
|
|
// UnmarshalPropertyValue unmarshals a single "JSON-like" value into a new property value.
|
2017-11-03 20:40:15 +00:00
|
|
|
func UnmarshalPropertyValue(v *structpb.Value, opts MarshalOptions) (*resource.PropertyValue, error) {
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
contract.Assert(v != nil)
|
|
|
|
|
|
|
|
switch v.Kind.(type) {
|
|
|
|
case *structpb.Value_NullValue:
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewNullProperty()
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
case *structpb.Value_BoolValue:
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewBoolProperty(v.GetBoolValue())
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
case *structpb.Value_NumberValue:
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewNumberProperty(v.GetNumberValue())
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
case *structpb.Value_StringValue:
|
2017-07-20 16:06:39 +00:00
|
|
|
// 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 {
|
2018-01-09 20:21:47 +00:00
|
|
|
if opts.RejectUnknowns {
|
|
|
|
return nil, errors.New("unexpected unknown property value")
|
|
|
|
} else if opts.KeepUnknowns {
|
2017-11-03 20:40:15 +00:00
|
|
|
return &unk, nil
|
2017-09-14 23:40:44 +00:00
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, nil
|
2017-07-20 16:06:39 +00:00
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewStringProperty(s)
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
case *structpb.Value_ListValue:
|
|
|
|
// If there's already an array, prefer to swap elements within it.
|
Overhaul resources, planning, and environments
This change, part of pulumi/lumi#90, overhauls quite a bit of the
core resource, planning, environments, and related areas.
The biggest amount of movement comes from the splitting of pkg/resource
into multiple sub-packages. This results in:
- pkg/resource: just the core resource data structures.
- pkg/resource/deployment: all planning and deployment logic.
- pkg/resource/environment: all environment, configuration, and
serialized checkpoint structures and logic.
- pkg/resource/plugin: all dynamically loaded analyzer and
provider logic, including the actual loading and RPC mechanisms.
This also splits the resource abstraction up. We now have:
- resource.Resource: a shared interface.
- resource.Object: a resource that is connected to a live object
that will periodically observe mutations due to ongoing
evaluation of computations. Snapshots of its state may be
taken; however, this is purely a "pre-planning" abstraction.
- resource.State: a snapshot of a resource's state that is frozen.
In other words, it is no longer connected to a live object.
This is what will store provider outputs (ID and properties),
and is what may be serialized into a deployment record.
The branch is in a half-baked state as of this change; more changes
are to come...
2017-06-08 23:37:40 +00:00
|
|
|
var elems []resource.PropertyValue
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
lst := v.GetListValue()
|
|
|
|
for i, elem := range lst.GetValues() {
|
2017-09-14 23:40:44 +00:00
|
|
|
e, err := UnmarshalPropertyValue(elem, opts)
|
|
|
|
if err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, err
|
|
|
|
} else if e != nil {
|
|
|
|
if i == len(elems) {
|
|
|
|
elems = append(elems, *e)
|
|
|
|
} else {
|
|
|
|
elems[i] = *e
|
|
|
|
}
|
2017-09-14 23:40:44 +00:00
|
|
|
}
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewArrayProperty(elems)
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
case *structpb.Value_StructValue:
|
2017-07-14 19:28:43 +00:00
|
|
|
// Start by unmarshaling.
|
2017-09-14 23:40:44 +00:00
|
|
|
obj, err := UnmarshalProperties(v.GetStructValue(), opts)
|
|
|
|
if err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, err
|
2017-09-14 23:40:44 +00:00
|
|
|
}
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
|
2017-07-14 19:28:43 +00:00
|
|
|
// Before returning it as an object, check to see if it's a known recoverable type.
|
2017-07-17 17:38:57 +00:00
|
|
|
objmap := obj.Mappable()
|
2017-10-22 22:54:44 +00:00
|
|
|
asset, isasset, err := resource.DeserializeAsset(objmap)
|
2017-10-22 20:39:21 +00:00
|
|
|
if err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, err
|
2017-10-22 20:39:21 +00:00
|
|
|
} else if isasset {
|
|
|
|
if opts.ComputeAssetHashes {
|
|
|
|
if err = asset.EnsureHash(); err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, errors.Wrapf(err, "failed to compute asset hash")
|
2017-10-22 20:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewAssetProperty(asset)
|
|
|
|
return &m, nil
|
2017-10-22 20:39:21 +00:00
|
|
|
}
|
2017-10-22 22:54:44 +00:00
|
|
|
archive, isarchive, err := resource.DeserializeArchive(objmap)
|
2017-10-22 20:39:21 +00:00
|
|
|
if err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, err
|
2017-10-22 20:39:21 +00:00
|
|
|
} else if isarchive {
|
|
|
|
if opts.ComputeAssetHashes {
|
|
|
|
if err = archive.EnsureHash(); err != nil {
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, errors.Wrapf(err, "failed to compute archive hash")
|
2017-10-22 20:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewArchiveProperty(archive)
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
}
|
2017-11-03 20:40:15 +00:00
|
|
|
m := resource.NewObjectProperty(obj)
|
|
|
|
return &m, nil
|
Unmap properties on the receive side
This change makes progress on a few things with respect to properly
receiving properties on the engine side, coming from the provider side,
of the RPC boundary. The issues here are twofold:
1. Properties need to get unmapped using a JSON-tag-sensitive
marshaler, so that they are cased properly, etc. For that, we
have a new mapper.Unmap function (which is ultra lame -- see
pulumi/lumi#138).
2. We have the reverse problem with respect to resource IDs: on
the send side, we must translate from URNs (which the engine
knows about) and provider IDs (which the provider knows about);
similarly, then, on the receive side, we must translate from
provider IDs back into URNs.
As a result of these getting fixed, we can now properly marshal the
resulting properties back into the resource object during the plan
execution, alongside propagating and memoizing its ID.
2017-05-31 18:51:46 +00:00
|
|
|
|
|
|
|
default:
|
2017-12-15 15:22:49 +00:00
|
|
|
contract.Failf("Unrecognized structpb value kind in RPC[%s]: %v", opts.Label, reflect.TypeOf(v.Kind))
|
2017-11-03 20:40:15 +00:00
|
|
|
return nil, nil
|
2017-07-14 19:28:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 16:06:39 +00:00
|
|
|
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:
|
2017-10-22 20:39:21 +00:00
|
|
|
elem, unknown = resource.NewAssetProperty(&resource.Asset{}), true
|
2017-07-20 16:06:39 +00:00
|
|
|
case UnknownArchiveValue:
|
2017-10-22 20:39:21 +00:00
|
|
|
elem, unknown = resource.NewArchiveProperty(&resource.Archive{}), true
|
2017-07-20 16:06:39 +00:00
|
|
|
case UnknownObjectValue:
|
|
|
|
elem, unknown = resource.NewObjectProperty(make(resource.PropertyMap)), true
|
|
|
|
}
|
|
|
|
if unknown {
|
|
|
|
comp := resource.Computed{Element: elem}
|
|
|
|
return resource.NewComputedProperty(comp), true
|
|
|
|
}
|
|
|
|
return resource.PropertyValue{}, false
|
|
|
|
}
|
|
|
|
|
2017-07-14 19:28:43 +00:00
|
|
|
// 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.
|
2017-10-22 20:39:21 +00:00
|
|
|
func MarshalAsset(v *resource.Asset, opts MarshalOptions) (*structpb.Value, error) {
|
2017-11-21 01:38:09 +00:00
|
|
|
// 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).
|
2017-11-12 19:45:13 +00:00
|
|
|
if opts.ElideAssetContents {
|
|
|
|
v = &resource.Asset{Hash: v.Hash}
|
|
|
|
} else {
|
|
|
|
// Ensure a hash is present if needed.
|
|
|
|
if v.Hash == "" && opts.ComputeAssetHashes {
|
|
|
|
if err := v.EnsureHash(); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to compute asset hash")
|
|
|
|
}
|
2017-10-22 20:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 17:38:57 +00:00
|
|
|
// To marshal an asset, we need to first serialize it, and then marshal that.
|
|
|
|
sera := v.Serialize()
|
|
|
|
serap := resource.NewPropertyMapFromMap(sera)
|
|
|
|
return MarshalPropertyValue(resource.NewObjectProperty(serap), opts)
|
2017-07-14 19:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalArchive marshals an archive into its wire form for resource provider plugins.
|
2017-10-22 20:39:21 +00:00
|
|
|
func MarshalArchive(v *resource.Archive, opts MarshalOptions) (*structpb.Value, error) {
|
2017-11-21 01:38:09 +00:00
|
|
|
// 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).
|
2017-11-12 19:45:13 +00:00
|
|
|
if opts.ElideAssetContents {
|
|
|
|
v = &resource.Archive{Hash: v.Hash}
|
|
|
|
} else {
|
|
|
|
// Ensure a hash is present if needed.
|
|
|
|
if v.Hash == "" && opts.ComputeAssetHashes {
|
|
|
|
if err := v.EnsureHash(); err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "failed to compute archive hash")
|
|
|
|
}
|
2017-10-22 20:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 17:38:57 +00:00
|
|
|
// To marshal an archive, we need to first serialize it, and then marshal that.
|
|
|
|
sera := v.Serialize()
|
|
|
|
serap := resource.NewPropertyMapFromMap(sera)
|
|
|
|
return MarshalPropertyValue(resource.NewObjectProperty(serap), opts)
|
2017-02-20 19:18:47 +00:00
|
|
|
}
|