2017-06-26 23:46:34 +02:00
|
|
|
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
2017-06-08 18:26:06 +02:00
|
|
|
|
|
|
|
package resource
|
|
|
|
|
|
|
|
import (
|
2017-06-10 03:34:37 +02:00
|
|
|
"reflect"
|
|
|
|
|
2017-06-11 16:05:04 +02:00
|
|
|
"github.com/golang/glog"
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
"github.com/pulumi/lumi/pkg/compiler/symbols"
|
|
|
|
"github.com/pulumi/lumi/pkg/compiler/types"
|
2017-06-08 18:26:06 +02:00
|
|
|
"github.com/pulumi/lumi/pkg/compiler/types/predef"
|
|
|
|
"github.com/pulumi/lumi/pkg/eval/rt"
|
|
|
|
"github.com/pulumi/lumi/pkg/tokens"
|
|
|
|
"github.com/pulumi/lumi/pkg/util/contract"
|
|
|
|
)
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// IsResourceObject returns true if the given runtime object is a
|
|
|
|
func IsResourceObject(obj *rt.Object) bool {
|
|
|
|
return obj != nil && predef.IsResourceType(obj.Type())
|
2017-06-08 18:26:06 +02:00
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// Object is a live resource object, connected to state that may change due to evaluation.
|
|
|
|
type Object struct {
|
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-09 01:37:40 +02:00
|
|
|
obj *rt.Object // the resource's live object reference.
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
var _ Resource = (*Object)(nil)
|
|
|
|
|
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-09 01:37:40 +02:00
|
|
|
// NewObject creates a new resource object out of the runtime object provided. The context is used to resolve
|
2017-06-08 18:26:06 +02:00
|
|
|
// dependencies between resources and must contain all references that could be encountered.
|
2017-06-10 03:34:37 +02:00
|
|
|
func NewObject(obj *rt.Object) *Object {
|
|
|
|
contract.Assertf(IsResourceObject(obj), "Expected a resource type")
|
|
|
|
return &Object{obj: obj}
|
2017-06-08 18:26:06 +02:00
|
|
|
}
|
|
|
|
|
Implement `get` functions on all resources
This change implements the `get` function for resources. Per pulumi/lumi#83,
this allows Lumi scripts to actually read from the target environment.
For example, we can now look up a SecurityGroup from its ARN:
let group = aws.ec2.SecurityGroup.get(
"arn:aws:ec2:us-west-2:153052954103:security-group:sg-02150d79");
The returned object is a fully functional resource object. So, we can then
link it up with an EC2 instance, for example, in the usual ways:
let instance = new aws.ec2.Instance(..., {
securityGroups: [ group ],
});
This didn't require any changes to the RPC or provider model, since we
already implement the Get function.
There are a few loose ends; two are short term:
1) URNs are not rehydrated.
2) Query is not yet implemented.
One is mid-term:
3) We probably want a URN-based lookup function. But we will likely
wait until we tackle pulumi/lumi#109 before adding this.
And one is long term (and subtle):
4) These amount to I/O and are not repeatable! A change in the target
environment may cause a script to generate a different plan
intermittently. Most likely we want to apply a different kind of
deployment "policy" for such scripts. These are inching towards the
scripting model of pulumi/lumi#121, which is an entirely different
beast than the repeatable immutable infrastructure deployments.
Finally, it is worth noting that with this, we have some of the fundamental
underpinnings required to finally tackle "inference" (pulumi/lumi#142).
2017-06-20 02:24:00 +02:00
|
|
|
// NewEmptyObject allocates an empty resource object of a given type.
|
|
|
|
func NewEmptyObject(t symbols.Type) *Object {
|
|
|
|
contract.Assert(predef.IsResourceType(t))
|
|
|
|
return &Object{
|
|
|
|
obj: rt.NewObject(t, nil, nil, nil),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
func (r *Object) Obj() *rt.Object { return r.obj }
|
|
|
|
func (r *Object) Type() tokens.Type { return r.obj.Type().TypeToken() }
|
2017-06-08 18:26:06 +02:00
|
|
|
|
2017-06-11 15:52:56 +02:00
|
|
|
// ID fetches the object's ID.
|
|
|
|
func (r *Object) ID() ID {
|
2017-06-15 02:00:13 +02:00
|
|
|
if idobj := getIDObject(r.Obj()); idobj != nil && idobj.IsString() {
|
|
|
|
return ID(idobj.StringValue())
|
|
|
|
}
|
|
|
|
return ID("")
|
2017-06-11 15:52:56 +02:00
|
|
|
}
|
|
|
|
|
2017-06-11 17:09:20 +02:00
|
|
|
// HasID returns true if the object already has an ID assigned to it.
|
|
|
|
func (r *Object) HasID() bool {
|
2017-06-15 02:00:13 +02:00
|
|
|
return r.ID() != ""
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasComputedID returns true if the object has an ID, but is computed and its value is not known yet.
|
|
|
|
func (r *Object) HasComputedID() bool {
|
2017-06-13 15:55:13 +02:00
|
|
|
idobj := getIDObject(r.Obj())
|
2017-06-15 02:00:13 +02:00
|
|
|
return idobj != nil && idobj.IsComputed()
|
2017-06-11 17:09:20 +02:00
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// SetID assigns an ID to the target object. This must only happen once.
|
|
|
|
func (r *Object) SetID(id ID) {
|
|
|
|
prop := r.obj.GetPropertyAddr(IDProperty, true, true)
|
2017-06-13 15:55:13 +02:00
|
|
|
contract.Assertf(prop.Obj().IsNull() || prop.Obj().IsComputed(), "Unexpected double set on ID; previous=%v", prop)
|
2017-06-10 03:34:37 +02:00
|
|
|
prop.Set(rt.NewStringObject(id.String()))
|
|
|
|
}
|
|
|
|
|
2017-06-13 15:55:13 +02:00
|
|
|
// getIDObject fetches the ID off the target object, dynamically, given its runtime value.
|
|
|
|
func getIDObject(obj *rt.Object) *rt.Object {
|
|
|
|
contract.Assert(IsResourceObject(obj))
|
2017-06-11 15:52:56 +02:00
|
|
|
if idprop := obj.GetPropertyAddr(IDProperty, false, false); idprop != nil {
|
|
|
|
id := idprop.Obj()
|
|
|
|
contract.Assert(id != nil)
|
2017-06-13 15:55:13 +02:00
|
|
|
contract.Assert(id.IsString() || id.IsComputed())
|
|
|
|
return id
|
2017-06-11 15:52:56 +02:00
|
|
|
}
|
2017-06-13 15:55:13 +02:00
|
|
|
return nil
|
2017-06-11 15:52:56 +02:00
|
|
|
}
|
|
|
|
|
2017-06-15 02:00:13 +02:00
|
|
|
const (
|
2017-07-15 18:33:23 +02:00
|
|
|
// URNProperty is the special URN property name.
|
|
|
|
URNProperty = rt.PropertyKey("urn")
|
|
|
|
// URNPropertyKey is the special URN property name for resource maps.
|
|
|
|
URNPropertyKey = PropertyKey("urn")
|
2017-06-15 02:00:13 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// URN fetches the object's URN.
|
|
|
|
func (r *Object) URN() URN {
|
|
|
|
if urnobj := getURNObject(r.Obj()); urnobj != nil && urnobj.IsString() {
|
|
|
|
return URN(urnobj.StringValue())
|
|
|
|
}
|
|
|
|
return URN("")
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasURN returns true if the object has a URN assigned.
|
|
|
|
func (r *Object) HasURN() bool {
|
|
|
|
return r.URN() != ""
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// SetURN assignes a URN to the target object. This must only happen once.
|
2017-06-15 02:00:13 +02:00
|
|
|
func (r *Object) SetURN(urn URN) {
|
|
|
|
prop := r.obj.GetPropertyAddr(URNProperty, true, true)
|
|
|
|
contract.Assertf(prop.Obj().IsNull() || prop.Obj().IsComputed(), "Unexpected double set on URN; previous=%v", prop)
|
|
|
|
prop.Set(rt.NewStringObject(string(urn)))
|
2017-06-08 18:26:06 +02:00
|
|
|
}
|
2017-06-10 03:34:37 +02:00
|
|
|
|
2017-06-15 02:00:13 +02:00
|
|
|
// getURNObject fetches the URN off the target object, dynamically, given its runtime value.
|
|
|
|
func getURNObject(obj *rt.Object) *rt.Object {
|
|
|
|
contract.Assert(IsResourceObject(obj))
|
|
|
|
if urnprop := obj.GetPropertyAddr(URNProperty, false, false); urnprop != nil {
|
|
|
|
urn := urnprop.Obj()
|
|
|
|
contract.Assert(urn != nil)
|
|
|
|
contract.Assert(urn.IsString() || urn.IsComputed())
|
|
|
|
return urn
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2017-06-10 03:34:37 +02:00
|
|
|
|
2017-06-15 02:00:13 +02:00
|
|
|
// Update updates the target object URN, ID, and resource property map. This mutates the live object connected to this
|
|
|
|
// resource and also archives the resource object's present state in the form of a state snapshot.
|
|
|
|
func (r *Object) Update(urn URN, id ID, outputs PropertyMap) *State {
|
2017-06-10 03:34:37 +02:00
|
|
|
// First take a snapshot of the properties.
|
|
|
|
inputs := r.CopyProperties()
|
|
|
|
|
2017-06-15 02:00:13 +02:00
|
|
|
// Now assign the URN, ID, and copy everything in the property map, overwriting what exists.
|
|
|
|
r.SetURN(urn)
|
2017-06-13 15:55:13 +02:00
|
|
|
r.SetID(id)
|
2017-06-10 03:34:37 +02:00
|
|
|
r.SetProperties(outputs)
|
|
|
|
|
|
|
|
// Finally, return a state snapshot of the underlying object state.
|
|
|
|
return NewState(r.Type(), r.URN(), id, inputs, outputs)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopyProperties creates a property map out of a resource's runtime object. This is a snapshot and is completely
|
|
|
|
// disconnected from the object itself, such that any subsequent object updates will not be observed.
|
|
|
|
func (r *Object) CopyProperties() PropertyMap {
|
2017-06-13 15:55:13 +02:00
|
|
|
resobj := r.Obj()
|
|
|
|
return copyObject(resobj, resobj)
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SetProperties copies from a resource property map to the runtime object, overwriting properties as it goes.
|
|
|
|
func (r *Object) SetProperties(props PropertyMap) {
|
2017-06-15 02:00:13 +02:00
|
|
|
if props != nil {
|
|
|
|
setRuntimeProperties(r.Obj(), props)
|
|
|
|
}
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 15:55:13 +02:00
|
|
|
func copyObject(resobj *rt.Object, obj *rt.Object) PropertyMap {
|
2017-06-10 03:34:37 +02:00
|
|
|
contract.Assert(obj != nil)
|
|
|
|
props := obj.PropertyValues()
|
2017-06-13 15:55:13 +02:00
|
|
|
return copyObjectProperties(resobj, props)
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
Introduce an interface to read config
This change adds an engine gRPC interface, and associated implementation,
so that plugins may do interesting things that require "phoning home".
Previously, the engine would fire up plugins and talk to them directly,
but there was no way for a plugin to ask the engine to do anything.
The motivation here is so that plugins can read evaluator state, such
as config information, but this change also allows richer logging
functionality than previously possible. We will still auto-log any
stdout/stderr writes; however, explicit errors, warnings, informational,
and even debug messages may be written over the Log API.
2017-06-21 04:45:07 +02:00
|
|
|
// CopyObject flattens a single object into a serializable "JSON-like" property value.
|
|
|
|
func CopyObject(obj *rt.Object) PropertyValue {
|
|
|
|
return copyObjectProperty(nil, obj)
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// copyObjectProperty creates a single property value out of a runtime object. It returns false if the property could
|
|
|
|
// not be stored in a property (e.g., it is a function or other unrecognized or unserializable runtime object).
|
2017-06-13 15:55:13 +02:00
|
|
|
func copyObjectProperty(resobj *rt.Object, obj *rt.Object) PropertyValue {
|
2017-06-10 03:34:37 +02:00
|
|
|
t := obj.Type()
|
2017-06-11 15:52:56 +02:00
|
|
|
|
2017-07-14 21:28:43 +02:00
|
|
|
// If the object is a resource, marshal its ID.
|
2017-06-10 03:34:37 +02:00
|
|
|
if predef.IsResourceType(t) {
|
2017-06-13 15:55:13 +02:00
|
|
|
idobj := getIDObject(obj)
|
|
|
|
if idobj != nil && idobj.IsString() {
|
|
|
|
return NewStringProperty(idobj.StringValue())
|
2017-06-11 15:52:56 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 15:55:13 +02:00
|
|
|
// If an ID hasn't yet been assigned, we must be planning, and so this is a computed property.
|
|
|
|
return MakeComputed(NewStringProperty(""))
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
2017-07-14 21:28:43 +02:00
|
|
|
// If the object is an asset or archive type, recover that value.
|
|
|
|
if predef.IsResourceAssetType(t) {
|
|
|
|
a := NewAssetFromObject(obj)
|
|
|
|
return NewAssetProperty(a)
|
|
|
|
} else if predef.IsResourceArchiveType(t) {
|
|
|
|
a := NewArchiveFromObject(obj)
|
|
|
|
return NewArchiveProperty(a)
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// Serialize simple primitive types with their primitive equivalents.
|
|
|
|
switch t {
|
|
|
|
case types.Null:
|
2017-06-13 15:55:13 +02:00
|
|
|
return NewNullProperty()
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Bool:
|
2017-06-13 15:55:13 +02:00
|
|
|
return NewBoolProperty(obj.BoolValue())
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Number:
|
2017-06-13 15:55:13 +02:00
|
|
|
return NewNumberProperty(obj.NumberValue())
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.String:
|
2017-06-13 15:55:13 +02:00
|
|
|
return NewStringProperty(obj.StringValue())
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Object, types.Dynamic:
|
2017-06-13 15:55:13 +02:00
|
|
|
result := copyObject(nil, obj) // an object literal, clone it
|
|
|
|
return NewObjectProperty(result)
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize arrays, maps, and object instances in the obvious way.
|
|
|
|
switch t.(type) {
|
|
|
|
case *symbols.ArrayType:
|
|
|
|
// Make a new array, clone each element, and return the result.
|
|
|
|
var result []PropertyValue
|
|
|
|
for _, e := range *obj.ArrayValue() {
|
2017-06-13 15:55:13 +02:00
|
|
|
result = append(result, copyObjectProperty(nil, e.Obj()))
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
2017-06-13 15:55:13 +02:00
|
|
|
return NewArrayProperty(result)
|
2017-06-10 03:34:37 +02:00
|
|
|
case *symbols.MapType:
|
|
|
|
// Make a new map, clone each property value, and return the result.
|
|
|
|
props := obj.PropertyValues()
|
2017-06-13 15:55:13 +02:00
|
|
|
result := copyObjectProperties(nil, props)
|
|
|
|
return NewObjectProperty(result)
|
2017-06-10 03:34:37 +02:00
|
|
|
case *symbols.Class:
|
|
|
|
// Make a new object that contains a deep clone of the source.
|
2017-06-13 15:55:13 +02:00
|
|
|
result := copyObject(nil, obj)
|
|
|
|
return NewObjectProperty(result)
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If a computed value, we can propagate an unknown value, but only for certain cases.
|
|
|
|
if t.Computed() {
|
2017-06-13 15:55:13 +02:00
|
|
|
// See if this is an output property. An output property is a property that is set directly on the resource
|
|
|
|
// object that is computed from precisely a single dependency and no expression. Otherwise, it is computed.
|
2017-06-10 03:34:37 +02:00
|
|
|
var makeProperty func(PropertyValue) PropertyValue
|
2017-06-15 21:41:51 +02:00
|
|
|
if isOutputObject(resobj, obj) {
|
2017-06-10 03:34:37 +02:00
|
|
|
makeProperty = MakeOutput
|
2017-06-13 15:55:13 +02:00
|
|
|
} else {
|
|
|
|
makeProperty = MakeComputed
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
2017-06-13 15:55:13 +02:00
|
|
|
// Now just wrap the underlying object appropriately.
|
2017-06-10 03:34:37 +02:00
|
|
|
elem := t.(*symbols.ComputedType).Element
|
|
|
|
switch elem {
|
|
|
|
case types.Null:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewNullProperty())
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Bool:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewBoolProperty(false))
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Number:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewNumberProperty(0))
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.String:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewStringProperty(""))
|
2017-06-10 03:34:37 +02:00
|
|
|
case types.Object, types.Dynamic:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewObjectProperty(make(PropertyMap)))
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
switch elem.(type) {
|
|
|
|
case *symbols.ArrayType:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewArrayProperty(nil))
|
2017-06-10 03:34:37 +02:00
|
|
|
case *symbols.Class:
|
2017-06-13 15:55:13 +02:00
|
|
|
return makeProperty(NewObjectProperty(make(PropertyMap)))
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can safely skip serializing functions, however, anything else is unexpected at this point.
|
|
|
|
_, isfunc := t.(*symbols.FunctionType)
|
|
|
|
contract.Assertf(isfunc, "Unrecognized resource property object type '%v' (%v)", t, reflect.TypeOf(t))
|
2017-06-13 15:55:13 +02:00
|
|
|
return PropertyValue{}
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// copyObjectProperties copies a resource's properties.
|
2017-06-13 15:55:13 +02:00
|
|
|
func copyObjectProperties(resobj *rt.Object, props *rt.PropertyMap) PropertyMap {
|
2017-06-10 03:34:37 +02:00
|
|
|
// Walk the object's properties and serialize them in a stable order.
|
|
|
|
result := make(PropertyMap)
|
|
|
|
for _, k := range props.Stable() {
|
2017-06-13 15:55:13 +02:00
|
|
|
result[PropertyKey(k)] = copyObjectProperty(resobj, props.Get(k))
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2017-06-15 21:41:51 +02:00
|
|
|
// isOutputObject returns true if the object obj is a computed output property for resource object resobj.
|
|
|
|
func isOutputObject(resobj *rt.Object, obj *rt.Object) bool {
|
|
|
|
if obj.IsComputed() {
|
|
|
|
v := obj.ComputedValue()
|
|
|
|
return !v.Expr && len(v.Sources) == 1 && v.Sources[0] == resobj
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-06-10 03:34:37 +02:00
|
|
|
// setRuntimeProperties translates from a resource property map into the equivalent runtime objects, and stores them on
|
|
|
|
// the given runtime object.
|
|
|
|
func setRuntimeProperties(obj *rt.Object, props PropertyMap) {
|
|
|
|
for k, v := range props {
|
|
|
|
prop := obj.GetPropertyAddr(rt.PropertyKey(k), true, true)
|
2017-06-21 20:33:10 +02:00
|
|
|
// TODO[pulumi/lumi#260]: we are only setting if IsNull or IsComputed, to avoid certain shortcomings in our
|
|
|
|
// serialization format today. For example, if a resource ID appears, we must map it back to the runtime
|
|
|
|
// object. This means some resource outputs won't get reflected accurately. We will need to fix this.
|
2017-06-15 21:41:51 +02:00
|
|
|
pobj := prop.Obj()
|
|
|
|
if pobj.IsNull() || isOutputObject(obj, pobj) {
|
|
|
|
glog.V(9).Infof("Setting resource object property: %v=%v", k, v)
|
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 20:50:47 +02:00
|
|
|
val := createRuntimeProperty(v)
|
|
|
|
prop.Set(val)
|
2017-06-15 21:41:51 +02:00
|
|
|
} else {
|
|
|
|
glog.V(9).Infof("Skipping resource object property: %v=%v; existing=%v", k, v, pobj)
|
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 20:50:47 +02:00
|
|
|
}
|
2017-06-10 03:34:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// createRuntimeProperty translates a property value into a runtime object.
|
|
|
|
func createRuntimeProperty(v PropertyValue) *rt.Object {
|
|
|
|
if v.IsNull() {
|
|
|
|
return rt.Null
|
|
|
|
} else if v.IsBool() {
|
|
|
|
return rt.Bools[v.BoolValue()]
|
|
|
|
} else if v.IsNumber() {
|
|
|
|
return rt.NewNumberObject(v.NumberValue())
|
|
|
|
} else if v.IsString() {
|
|
|
|
return rt.NewStringObject(v.StringValue())
|
|
|
|
} else if v.IsArray() {
|
|
|
|
src := v.ArrayValue()
|
|
|
|
arr := make([]*rt.Pointer, len(src))
|
|
|
|
for i, elem := range src {
|
|
|
|
ve := createRuntimeProperty(elem)
|
|
|
|
arr[i] = rt.NewPointer(ve, false, nil, nil)
|
|
|
|
}
|
|
|
|
return rt.NewArrayObject(types.Dynamic, &arr)
|
|
|
|
}
|
|
|
|
|
|
|
|
contract.Assertf(v.IsObject(), "Expected an object, not a computed/output value")
|
|
|
|
obj := rt.NewObject(types.Dynamic, nil, nil, nil)
|
|
|
|
setRuntimeProperties(obj, v.ObjectValue())
|
|
|
|
return obj
|
|
|
|
}
|