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.
285 lines
8.1 KiB
Go
285 lines
8.1 KiB
Go
// Licensed to Pulumi Corporation ("Pulumi") under one or more
|
|
// contributor license agreements. See the NOTICE file distributed with
|
|
// this work for additional information regarding copyright ownership.
|
|
// Pulumi licenses this file to You 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 resource
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
// ObjectDiff holds the results of diffing two object property maps.
|
|
type ObjectDiff struct {
|
|
Adds PropertyMap // properties in this map are created in the new.
|
|
Deletes PropertyMap // properties in this map are deleted from the new.
|
|
Sames PropertyMap // properties in this map are the same.
|
|
Updates map[PropertyKey]ValueDiff // properties in this map are changed in the new.
|
|
}
|
|
|
|
// Added returns true if the property 'k' has been added in the new property set.
|
|
func (diff *ObjectDiff) Added(k PropertyKey) bool {
|
|
_, has := diff.Adds[k]
|
|
return has
|
|
}
|
|
|
|
// Deleted returns true if the property 'k' has been deleted from the new property set.
|
|
func (diff *ObjectDiff) Deleted(k PropertyKey) bool {
|
|
_, has := diff.Deletes[k]
|
|
return has
|
|
}
|
|
|
|
// Updated returns true if the property 'k' has been changed between new and old property sets.
|
|
func (diff *ObjectDiff) Updated(k PropertyKey) bool {
|
|
_, has := diff.Updates[k]
|
|
return has
|
|
}
|
|
|
|
// Changed returns true if the property 'k' is known to be different between old and new.
|
|
func (diff *ObjectDiff) Changed(k PropertyKey) bool {
|
|
return diff.Added(k) || diff.Deleted(k) || diff.Updated(k)
|
|
}
|
|
|
|
// Same returns true if the property 'k' is *not* known to be different; note that this isn't the same as looking up in
|
|
// the Sames map, because it is possible the key is simply missing altogether (as is the case for nulls).
|
|
func (diff *ObjectDiff) Same(k PropertyKey) bool {
|
|
return !diff.Changed(k)
|
|
}
|
|
|
|
// Keys returns a stable snapshot of all keys known to this object, across adds, deletes, sames, and updates.
|
|
func (diff *ObjectDiff) Keys() []PropertyKey {
|
|
var ks []PropertyKey
|
|
for k := range diff.Adds {
|
|
ks = append(ks, k)
|
|
}
|
|
for k := range diff.Deletes {
|
|
ks = append(ks, k)
|
|
}
|
|
for k := range diff.Sames {
|
|
ks = append(ks, k)
|
|
}
|
|
for k := range diff.Updates {
|
|
ks = append(ks, k)
|
|
}
|
|
sort.Slice(ks, func(i, j int) bool { return ks[i] < ks[j] })
|
|
return ks
|
|
}
|
|
|
|
// ValueDiff holds the results of diffing two property values.
|
|
type ValueDiff struct {
|
|
Old PropertyValue // the old value.
|
|
New PropertyValue // the new value.
|
|
Array *ArrayDiff // the array's detailed diffs (only for arrays).
|
|
Object *ObjectDiff // the object's detailed diffs (only for objects).
|
|
}
|
|
|
|
// ArrayDiff holds the results of diffing two arrays of property values.
|
|
type ArrayDiff struct {
|
|
Adds map[int]PropertyValue // elements added in the new.
|
|
Deletes map[int]PropertyValue // elements deleted in the new.
|
|
Sames map[int]PropertyValue // elements the same in both.
|
|
Updates map[int]ValueDiff // elements that have changed in the new.
|
|
}
|
|
|
|
// Len computes the length of this array, taking into account adds, deletes, sames, and updates.
|
|
func (diff *ArrayDiff) Len() int {
|
|
len := 0
|
|
for i := range diff.Adds {
|
|
if i+1 > len {
|
|
len = i + 1
|
|
}
|
|
}
|
|
for i := range diff.Deletes {
|
|
if i+1 > len {
|
|
len = i + 1
|
|
}
|
|
}
|
|
for i := range diff.Sames {
|
|
if i+1 > len {
|
|
len = i + 1
|
|
}
|
|
}
|
|
for i := range diff.Updates {
|
|
if i+1 > len {
|
|
len = i + 1
|
|
}
|
|
}
|
|
return len
|
|
}
|
|
|
|
// Diff returns a diffset by comparing the property map to another; it returns nil if there are no diffs.
|
|
func (props PropertyMap) Diff(other PropertyMap) *ObjectDiff {
|
|
adds := make(PropertyMap)
|
|
deletes := make(PropertyMap)
|
|
sames := make(PropertyMap)
|
|
updates := make(map[PropertyKey]ValueDiff)
|
|
|
|
// First find any updates or deletes.
|
|
for k, old := range props {
|
|
if new, has := other[k]; has {
|
|
// If a new exists, use it; for output properties, however, ignore differences.
|
|
if new.IsOutput() {
|
|
sames[k] = old
|
|
} else if diff := old.Diff(new); diff != nil {
|
|
if !old.HasValue() {
|
|
adds[k] = new
|
|
} else if !new.HasValue() {
|
|
deletes[k] = old
|
|
} else {
|
|
updates[k] = *diff
|
|
}
|
|
} else {
|
|
sames[k] = old
|
|
}
|
|
} else if old.HasValue() {
|
|
// If there was no new property, it has been deleted.
|
|
deletes[k] = old
|
|
}
|
|
}
|
|
|
|
// Next find any additions not in the old map.
|
|
for k, new := range other {
|
|
if _, has := props[k]; !has && new.HasValue() {
|
|
adds[k] = new
|
|
}
|
|
}
|
|
|
|
// If no diffs were found, return nil; else return a diff structure.
|
|
if len(adds) == 0 && len(deletes) == 0 && len(updates) == 0 {
|
|
return nil
|
|
}
|
|
return &ObjectDiff{
|
|
Adds: adds,
|
|
Deletes: deletes,
|
|
Sames: sames,
|
|
Updates: updates,
|
|
}
|
|
}
|
|
|
|
// Diff returns a diff by comparing a single property value to another; it returns nil if there are no diffs.
|
|
func (v PropertyValue) Diff(other PropertyValue) *ValueDiff {
|
|
if v.IsArray() && other.IsArray() {
|
|
old := v.ArrayValue()
|
|
new := other.ArrayValue()
|
|
// If any elements exist in the new array but not the old, track them as adds.
|
|
adds := make(map[int]PropertyValue)
|
|
for i := len(old); i < len(new); i++ {
|
|
adds[i] = new[i]
|
|
}
|
|
// If any elements exist in the old array but not the new, track them as adds.
|
|
deletes := make(map[int]PropertyValue)
|
|
for i := len(new); i < len(old); i++ {
|
|
deletes[i] = old[i]
|
|
}
|
|
// Now if elements exist in both, track them as sames or updates.
|
|
sames := make(map[int]PropertyValue)
|
|
updates := make(map[int]ValueDiff)
|
|
for i := 0; i < len(old) && i < len(new); i++ {
|
|
if diff := old[i].Diff(new[i]); diff != nil {
|
|
updates[i] = *diff
|
|
} else {
|
|
sames[i] = old[i]
|
|
}
|
|
}
|
|
|
|
if len(adds) == 0 && len(deletes) == 0 && len(updates) == 0 {
|
|
return nil
|
|
}
|
|
return &ValueDiff{
|
|
Old: v,
|
|
New: other,
|
|
Array: &ArrayDiff{
|
|
Adds: adds,
|
|
Deletes: deletes,
|
|
Sames: sames,
|
|
Updates: updates,
|
|
},
|
|
}
|
|
}
|
|
if v.IsObject() && other.IsObject() {
|
|
old := v.ObjectValue()
|
|
new := other.ObjectValue()
|
|
if diff := old.Diff(new); diff != nil {
|
|
return &ValueDiff{
|
|
Old: v,
|
|
New: other,
|
|
Object: diff,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// If we got here, either the values are primitives, or they weren't the same type; do a simple diff.
|
|
if v.V == other.V {
|
|
return nil
|
|
}
|
|
return &ValueDiff{Old: v, New: other}
|
|
}
|
|
|
|
// DeepEquals returns true if this property map is deeply equal to the other property map; and false otherwise.
|
|
func (props PropertyMap) DeepEquals(other PropertyMap) bool {
|
|
// If any in props either doesn't exist, or is of a different value, return false.
|
|
for _, k := range props.StableKeys() {
|
|
v := props[k]
|
|
if p, has := other[k]; has {
|
|
if !v.DeepEquals(p) {
|
|
return false
|
|
}
|
|
} else if v.HasValue() {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// If the other map has properties that this map doesn't have, return false.
|
|
for _, k := range other.StableKeys() {
|
|
if _, has := props[k]; !has && other[k].HasValue() {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// DeepEquals returns true if this property map is deeply equal to the other property map; and false otherwise.
|
|
func (v PropertyValue) DeepEquals(other PropertyValue) bool {
|
|
// Arrays are equal if they are both of the same size and elements are deeply equal.
|
|
if v.IsArray() {
|
|
if !other.IsArray() {
|
|
return false
|
|
}
|
|
va := v.ArrayValue()
|
|
oa := other.ArrayValue()
|
|
if len(va) != len(oa) {
|
|
return false
|
|
}
|
|
for i, elem := range va {
|
|
if !elem.DeepEquals(oa[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Object values are equal if their contents are deeply equal.
|
|
if v.IsObject() {
|
|
if !other.IsObject() {
|
|
return false
|
|
}
|
|
vo := v.ObjectValue()
|
|
oa := other.ObjectValue()
|
|
return vo.DeepEquals(oa)
|
|
}
|
|
|
|
// For all other cases, primitives are equal if their values are equal.
|
|
return v.V == other.V
|
|
}
|