Track which updates triggered a replacement
This change tracks which updates triggered a replacement. This enables better output and diagnostics. For example, we now colorize those properties differently in the output. This makes it easier to diagnose why an unexpected resource might be getting deleted and recreated.
This commit is contained in:
parent
f0d9b12a3c
commit
523c669a03
62
cmd/husk.go
62
cmd/husk.go
|
@ -621,7 +621,7 @@ func printUnchanged(b *bytes.Buffer, plan resource.Plan, summary bool) {
|
|||
for _, res := range plan.Unchanged() {
|
||||
b.WriteString(" ") // simulate the 2 spaces for +, -, etc.
|
||||
printResourceHeader(b, res, nil, "")
|
||||
printResourceProperties(b, res, nil, nil, summary, "")
|
||||
printResourceProperties(b, res, nil, nil, nil, summary, "")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -632,7 +632,13 @@ func printStep(b *bytes.Buffer, step resource.Step, summary bool, indent string)
|
|||
// Next print the resource moniker, properties, etc.
|
||||
printResourceHeader(b, step.Old(), step.New(), indent)
|
||||
b.WriteString(step.Op().Suffix())
|
||||
printResourceProperties(b, step.Old(), step.New(), step.NewProps(), summary, indent)
|
||||
var replaces []resource.PropertyKey
|
||||
if step.Old() != nil {
|
||||
m := step.Old().Moniker()
|
||||
replaceMap := step.Plan().Replaces()
|
||||
replaces = replaceMap[m]
|
||||
}
|
||||
printResourceProperties(b, step.Old(), step.New(), step.NewProps(), replaces, summary, indent)
|
||||
|
||||
// Finally make sure to reset the color.
|
||||
b.WriteString(colors.Reset)
|
||||
|
@ -651,7 +657,7 @@ func printResourceHeader(b *bytes.Buffer, old resource.Resource, new resource.Re
|
|||
}
|
||||
|
||||
func printResourceProperties(b *bytes.Buffer, old resource.Resource, new resource.Resource,
|
||||
computed resource.PropertyMap, summary bool, indent string) {
|
||||
computed resource.PropertyMap, replaces []resource.PropertyKey, summary bool, indent string) {
|
||||
indent += detailsIndent
|
||||
|
||||
// Print out the moniker and, if present, the ID, as "pseudo-properties".
|
||||
|
@ -677,7 +683,7 @@ func printResourceProperties(b *bytes.Buffer, old resource.Resource, new resourc
|
|||
printObject(b, old.Properties(), indent)
|
||||
} else {
|
||||
contract.Assert(computed != nil) // use computed properties for diffs.
|
||||
printOldNewDiffs(b, old.Properties(), computed, indent)
|
||||
printOldNewDiffs(b, old.Properties(), computed, replaces, indent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -747,16 +753,18 @@ func printArrayElemHeader(b *bytes.Buffer, i int, indent string) string {
|
|||
return newIndent
|
||||
}
|
||||
|
||||
func printOldNewDiffs(b *bytes.Buffer, olds resource.PropertyMap, news resource.PropertyMap, indent string) {
|
||||
func printOldNewDiffs(b *bytes.Buffer, olds resource.PropertyMap, news resource.PropertyMap,
|
||||
replaces []resource.PropertyKey, indent string) {
|
||||
// Get the full diff structure between the two, and print it (recursively).
|
||||
if diff := olds.Diff(news); diff != nil {
|
||||
printObjectDiff(b, *diff, indent)
|
||||
printObjectDiff(b, *diff, replaces, false, indent)
|
||||
} else {
|
||||
printObject(b, news, indent)
|
||||
}
|
||||
}
|
||||
|
||||
func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff, indent string) {
|
||||
func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff,
|
||||
replaces []resource.PropertyKey, causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
// Compute the maximum with of property keys so we can justify everything.
|
||||
|
@ -768,6 +776,15 @@ func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff, indent string) {
|
|||
}
|
||||
}
|
||||
|
||||
// If a list of what causes a resource to get replaced exist, create a handy map.
|
||||
var replaceMap map[resource.PropertyKey]bool
|
||||
if len(replaces) > 0 {
|
||||
replaceMap = make(map[resource.PropertyKey]bool)
|
||||
for _, k := range replaces {
|
||||
replaceMap[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
// To print an object diff, enumerate the keys in stable order, and print each property independently.
|
||||
for _, k := range keys {
|
||||
title := func(id string) { printPropertyTitle(b, k, maxkey, id) }
|
||||
|
@ -786,7 +803,10 @@ func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff, indent string) {
|
|||
b.WriteString(colors.Reset)
|
||||
}
|
||||
} else if update, isupdate := diff.Updates[k]; isupdate {
|
||||
printPropertyValueDiff(b, title, update, indent)
|
||||
if !causedReplace && replaceMap != nil {
|
||||
causedReplace = replaceMap[k]
|
||||
}
|
||||
printPropertyValueDiff(b, title, update, causedReplace, indent)
|
||||
} else if same := diff.Sames[k]; shouldPrintPropertyValue(same) {
|
||||
title(indent)
|
||||
printPropertyValue(b, diff.Sames[k], indent)
|
||||
|
@ -794,13 +814,15 @@ func printObjectDiff(b *bytes.Buffer, diff resource.ObjectDiff, indent string) {
|
|||
}
|
||||
}
|
||||
|
||||
func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.ValueDiff, indent string) {
|
||||
func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.ValueDiff,
|
||||
causedReplace bool, indent string) {
|
||||
contract.Assert(len(indent) > 2)
|
||||
|
||||
if diff.Array != nil {
|
||||
title(indent)
|
||||
a := diff.Array
|
||||
b.WriteString("[\n")
|
||||
|
||||
a := diff.Array
|
||||
for i := 0; i < a.Len(); i++ {
|
||||
_, newIndent := getArrayElemHeader(b, i, indent)
|
||||
title := func(id string) { printArrayElemHeader(b, i, id) }
|
||||
|
@ -815,7 +837,7 @@ func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.V
|
|||
printPropertyValue(b, delete, deleteIndent(newIndent))
|
||||
b.WriteString(colors.Reset)
|
||||
} else if update, isupdate := a.Updates[i]; isupdate {
|
||||
printPropertyValueDiff(b, title, update, indent)
|
||||
printPropertyValueDiff(b, title, update, causedReplace, indent)
|
||||
} else {
|
||||
title(indent)
|
||||
printPropertyValue(b, a.Sames[i], newIndent)
|
||||
|
@ -825,7 +847,7 @@ func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.V
|
|||
} else if diff.Object != nil {
|
||||
title(indent)
|
||||
b.WriteString("{\n")
|
||||
printObjectDiff(b, *diff.Object, indent+" ")
|
||||
printObjectDiff(b, *diff.Object, nil, causedReplace, indent+" ")
|
||||
b.WriteString(fmt.Sprintf("%s}\n", indent))
|
||||
} else if diff.Old.IsResource() && diff.New.IsResource() && diff.New.ResourceValue().Replacement() {
|
||||
// If the old and new are both resources, and the new is a replacement, show this in a special way (+-).
|
||||
|
@ -837,13 +859,25 @@ func printPropertyValueDiff(b *bytes.Buffer, title func(string), diff resource.V
|
|||
// If we ended up here, the two values either differ by type, or they have different primitive values. We will
|
||||
// simply emit a deletion line followed by an addition line.
|
||||
if shouldPrintPropertyValue(diff.Old) {
|
||||
b.WriteString(resource.OpUpdate.Color())
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpDelete.Color() // this property triggered replacement; color as a delete
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(deleteIndent(indent))
|
||||
printPropertyValue(b, diff.Old, deleteIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
if shouldPrintPropertyValue(diff.New) {
|
||||
b.WriteString(resource.OpUpdate.Color())
|
||||
var color string
|
||||
if causedReplace {
|
||||
color = resource.OpCreate.Color() // this property triggered replacement; color as a create
|
||||
} else {
|
||||
color = resource.OpUpdate.Color()
|
||||
}
|
||||
b.WriteString(color)
|
||||
title(addIndent(indent))
|
||||
printPropertyValue(b, diff.New, addIndent(indent))
|
||||
b.WriteString(colors.Reset)
|
||||
|
|
|
@ -118,10 +118,18 @@ func (p *instanceProvider) UpdateImpact(
|
|||
|
||||
// Now check the diff for updates to any fields (none of them are updateable).
|
||||
// TODO: we should permit changes to security groups for non-EC2-classic VMs that are in VPCs.
|
||||
var replaces []string
|
||||
diff := olds.Diff(news)
|
||||
replace := diff.Diff(instanceImageID) || diff.Diff(instanceType) ||
|
||||
diff.Diff(instanceSecurityGroups) || diff.Diff(instanceKeyName)
|
||||
return &cocorpc.UpdateImpactResponse{Replace: replace}, nil
|
||||
if diff.Diff(instanceImageID) {
|
||||
replaces = append(replaces, instanceImageID)
|
||||
}
|
||||
if diff.Diff(instanceType) {
|
||||
replaces = append(replaces, instanceType)
|
||||
}
|
||||
if diff.Diff(instanceKeyName) {
|
||||
replaces = append(replaces, instanceKeyName)
|
||||
}
|
||||
return &cocorpc.UpdateImpactResponse{Replaces: replaces}, nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
|
|
|
@ -114,13 +114,13 @@ func (p *sgProvider) Update(ctx context.Context, req *cocorpc.UpdateRequest) (*p
|
|||
|
||||
// Provided it's okay, unmarshal, validate, and diff the properties.
|
||||
id := req.GetId()
|
||||
oldgrp, newgrp, diff, replace, err := unmarshalSecurityGroupProperties(req.GetOlds(), req.GetNews())
|
||||
oldgrp, newgrp, diff, replaces, err := unmarshalSecurityGroupProperties(req.GetOlds(), req.GetNews())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If this was a replacement, the UpdateImpact routine should have rejected it.
|
||||
if replace {
|
||||
if len(replaces) > 0 {
|
||||
return nil, errors.New("this update requires a resource replacement")
|
||||
}
|
||||
|
||||
|
@ -220,12 +220,12 @@ func (p *sgProvider) UpdateImpact(
|
|||
ctx context.Context, req *cocorpc.UpdateRequest) (*cocorpc.UpdateImpactResponse, error) {
|
||||
contract.Assert(req.GetType() == string(SecurityGroup))
|
||||
// First unmarshal and validate the properties.
|
||||
_, _, _, replace, err := unmarshalSecurityGroupProperties(req.GetOlds(), req.GetNews())
|
||||
_, _, _, replaces, err := unmarshalSecurityGroupProperties(req.GetOlds(), req.GetNews())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cocorpc.UpdateImpactResponse{
|
||||
Replace: replace,
|
||||
Replaces: replaces,
|
||||
// TODO: serialize the otherproperties that will be updated.
|
||||
}, nil
|
||||
}
|
||||
|
@ -233,23 +233,32 @@ func (p *sgProvider) UpdateImpact(
|
|||
// unmarshalSecurityGroupProperties unmarshals old and new properties, diffs them and checks whether resource
|
||||
// replacement is necessary. If an error occurs, the returned error is non-nil.
|
||||
func unmarshalSecurityGroupProperties(olds *pbstruct.Struct,
|
||||
news *pbstruct.Struct) (*securityGroup, *securityGroup, *resource.ObjectDiff, bool, error) {
|
||||
news *pbstruct.Struct) (*securityGroup, *securityGroup, *resource.ObjectDiff, []string, error) {
|
||||
// Deserialize the old/new properties and validate them before bothering to diff them.
|
||||
oldprops := resource.UnmarshalProperties(olds)
|
||||
oldgrp, err := newSecurityGroup(oldprops, true)
|
||||
if err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
newprops := resource.UnmarshalProperties(news)
|
||||
newgrp, err := newSecurityGroup(newprops, true)
|
||||
if err != nil {
|
||||
return nil, nil, nil, false, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Now diff the properties to determine whether this must be recreated.
|
||||
var replaces []string
|
||||
diff := oldprops.Diff(newprops)
|
||||
replace := diff.Diff(securityGroupName) || diff.Diff(securityGroupDescription) || diff.Diff(securityGroupVPCID)
|
||||
return oldgrp, newgrp, diff, replace, nil
|
||||
if diff.Diff(securityGroupName) {
|
||||
replaces = append(replaces, securityGroupName)
|
||||
}
|
||||
if diff.Diff(securityGroupDescription) {
|
||||
replaces = append(replaces, securityGroupDescription)
|
||||
}
|
||||
if diff.Diff(securityGroupVPCID) {
|
||||
replaces = append(replaces, securityGroupVPCID)
|
||||
}
|
||||
return oldgrp, newgrp, diff, replaces, nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
|
@ -284,11 +293,11 @@ type securityGroup struct {
|
|||
}
|
||||
|
||||
const (
|
||||
securityGroupName resource.PropertyKey = "name"
|
||||
securityGroupDescription = "groupDescription"
|
||||
securityGroupVPCID = "vpc"
|
||||
securityGroupEgress = "securityGroupEgress"
|
||||
securityGroupIngress = "securityGroupIngress"
|
||||
securityGroupName = "name"
|
||||
securityGroupDescription = "groupDescription"
|
||||
securityGroupVPCID = "vpc"
|
||||
securityGroupEgress = "securityGroupEgress"
|
||||
securityGroupIngress = "securityGroupIngress"
|
||||
)
|
||||
|
||||
// newSecurityGroup creates a new instance bag of state, validating required properties if asked to do so.
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
type Plan interface {
|
||||
Empty() bool // true if the plan is empty.
|
||||
Steps() Step // the first step to perform, linked to the rest.
|
||||
Replaces() map[Moniker][]PropertyKey // resources being replaced and their properties.
|
||||
Unchanged() map[Resource]Resource // the resources untouched by this plan.
|
||||
Apply(prog Progress) (Snapshot, error, Step, ResourceState) // performs the operations specified in this plan.
|
||||
}
|
||||
|
@ -34,6 +35,7 @@ type Progress interface {
|
|||
|
||||
// Step is a specification for a deployment operation.
|
||||
type Step interface {
|
||||
Plan() Plan // the plan this step belongs to.
|
||||
Op() StepOp // the operation that will be performed.
|
||||
Logical() bool // true if this is a logical step, rather than a physical one.
|
||||
Old() Resource // the old resource state, if any, before performing this step.
|
||||
|
@ -124,18 +126,20 @@ func NewPlan(ctx *Context, old Snapshot, new Snapshot) (Plan, error) {
|
|||
}
|
||||
|
||||
type plan struct {
|
||||
ctx *Context // this plan's context.
|
||||
ns tokens.QName // the husk/namespace target being deployed into.
|
||||
pkg tokens.Package // the package from which this snapshot came.
|
||||
args core.Args // the arguments used to compile this package.
|
||||
first *step // the first step to take.
|
||||
unchanged map[Resource]Resource // the resources that are remaining the same without modification.
|
||||
ctx *Context // this plan's context.
|
||||
ns tokens.QName // the husk/namespace target being deployed into.
|
||||
pkg tokens.Package // the package from which this snapshot came.
|
||||
args core.Args // the arguments used to compile this package.
|
||||
first *step // the first step to take.
|
||||
replaces map[Moniker][]PropertyKey // resources being replaced and their properties.
|
||||
unchanged map[Resource]Resource // the resources that are remaining the same without modification.
|
||||
}
|
||||
|
||||
var _ Plan = (*plan)(nil)
|
||||
|
||||
func (p *plan) Unchanged() map[Resource]Resource { return p.unchanged }
|
||||
func (p *plan) Empty() bool { return p.Steps() == nil }
|
||||
func (p *plan) Replaces() map[Moniker][]PropertyKey { return p.replaces }
|
||||
func (p *plan) Unchanged() map[Resource]Resource { return p.unchanged }
|
||||
func (p *plan) Empty() bool { return p.Steps() == nil }
|
||||
|
||||
func (p *plan) Steps() Step {
|
||||
if p.first == nil {
|
||||
|
@ -282,7 +286,7 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) (*plan, error) {
|
|||
// cascading impact on subsequent updates too, since those IDs must trigger recreations, etc.
|
||||
contract.Assert(old.Type() == new.Type())
|
||||
computed := new.Properties().ReplaceResources(func(r Moniker) Moniker {
|
||||
if pb.Replaces[r] {
|
||||
if pb.Replace(r) {
|
||||
// If the resource is being replaced, simply mangle the moniker so that it's different; this value
|
||||
// won't actually be used for anything other than the diffing algorithms below.
|
||||
r = r.Replace()
|
||||
|
@ -298,13 +302,13 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) (*plan, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
replace, _, err := prov.UpdateImpact(old.ID(), old.Type(), old.Properties(), computed)
|
||||
replaces, _, err := prov.UpdateImpact(old.ID(), old.Type(), old.Properties(), computed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Now create a step and vertex of the right kind.
|
||||
if replace {
|
||||
if len(replaces) > 0 {
|
||||
// To perform a replacement, create a creation, deletion, and add the appropriate edges. Namely:
|
||||
//
|
||||
// - Replacement depends on creation
|
||||
|
@ -313,7 +317,11 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) (*plan, error) {
|
|||
// - Deletion depends on updating all existing dependencies (ensured through usual update logic)
|
||||
//
|
||||
// This ensures the right sequencing, with the replacement node acting as a juncture in the graph.
|
||||
pb.Replaces[m] = true
|
||||
replkeys := make([]PropertyKey, len(replaces))
|
||||
for i, repl := range replaces {
|
||||
replkeys[i] = PropertyKey(repl)
|
||||
}
|
||||
pb.Replaces[m] = replkeys
|
||||
create := newReplaceCreateStep(pb.P, new)
|
||||
pb.Creates[m] = newPlanVertex(create)
|
||||
replace := newReplaceStep(pb.P, old, new, computed)
|
||||
|
@ -371,17 +379,17 @@ func newPlan(ctx *Context, old Snapshot, new Snapshot) (*plan, error) {
|
|||
|
||||
// planBuilder records a lot of the necessary information during the creation of a plan.
|
||||
type planBuilder struct {
|
||||
P *plan // the plan under construction.
|
||||
Olds map[Moniker]Resource // a map of moniker to old resource.
|
||||
OldRes []Resource // a flat list of old resources (in topological order).
|
||||
News map[Moniker]Resource // a map of moniker to new resource.
|
||||
NewRes []Resource // a flat list of new resources (in topological order).
|
||||
Depends map[Moniker][]Moniker // a map of moniker to all existing (old) dependencies.
|
||||
Creates map[Moniker]*planVertex // a map of pending creates to their associated vertex.
|
||||
Updates map[Moniker]*planVertex // a map of pending updates to their associated vertex.
|
||||
Deletes map[Moniker]*planVertex // a map of pending deletes to their associated vertex.
|
||||
Replaces map[Moniker]bool // a set of monikers that are scheduled for replacement.
|
||||
Unchanged map[Resource]Resource // a map of unchanged resources to their ID-stamped state.
|
||||
P *plan // the plan under construction.
|
||||
Olds map[Moniker]Resource // a map of moniker to old resource.
|
||||
OldRes []Resource // a flat list of old resources (in topological order).
|
||||
News map[Moniker]Resource // a map of moniker to new resource.
|
||||
NewRes []Resource // a flat list of new resources (in topological order).
|
||||
Depends map[Moniker][]Moniker // a map of moniker to all existing (old) dependencies.
|
||||
Creates map[Moniker]*planVertex // a map of pending creates to their associated vertex.
|
||||
Updates map[Moniker]*planVertex // a map of pending updates to their associated vertex.
|
||||
Deletes map[Moniker]*planVertex // a map of pending deletes to their associated vertex.
|
||||
Replaces map[Moniker][]PropertyKey // a map of monikers scheduled for replacement to properties being replaced.
|
||||
Unchanged map[Resource]Resource // a map of unchanged resources to their ID-stamped state.
|
||||
}
|
||||
|
||||
// newPlanBuilder initializes a fresh plan state instance, ready to use for planning.
|
||||
|
@ -427,38 +435,13 @@ func newPlanBuilder(ctx *Context, old Snapshot, new Snapshot) *planBuilder {
|
|||
Creates: make(map[Moniker]*planVertex),
|
||||
Updates: make(map[Moniker]*planVertex),
|
||||
Deletes: make(map[Moniker]*planVertex),
|
||||
Replaces: make(map[Moniker]bool),
|
||||
Replaces: make(map[Moniker][]PropertyKey),
|
||||
Unchanged: make(map[Resource]Resource),
|
||||
}
|
||||
}
|
||||
|
||||
// Plan finishes the plan building and returns the resulting, completed plan (or non-nil error if it fails).
|
||||
func (pb *planBuilder) Plan() (*plan, error) {
|
||||
// For all plan vertices with no ins, make them root nodes.
|
||||
var roots []*planEdge
|
||||
for _, vs := range []map[Moniker]*planVertex{pb.Creates, pb.Updates, pb.Deletes} {
|
||||
for _, v := range vs {
|
||||
if len(v.Ins()) == 0 {
|
||||
roots = append(roots, &planEdge{to: v})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now topologically sort the steps in the order they must execute, thread the plan together, and return it.
|
||||
g := newPlanGraph(roots)
|
||||
topdag, err := graph.Topsort(g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var prev *step
|
||||
for _, v := range topdag {
|
||||
insertStep(&prev, v.Data().(*step))
|
||||
}
|
||||
|
||||
// Remember the unchanged nodes.
|
||||
pb.P.unchanged = pb.Unchanged
|
||||
|
||||
return pb.P, nil
|
||||
func (pb *planBuilder) Replace(m Moniker) bool {
|
||||
return len(pb.Replaces[m]) > 0
|
||||
}
|
||||
|
||||
func (pb *planBuilder) ConnectCreate(m Moniker, v *planVertex) {
|
||||
|
@ -514,6 +497,36 @@ func (pb *planBuilder) ConnectDelete(m Moniker, v *planVertex) {
|
|||
}
|
||||
}
|
||||
|
||||
// Plan finishes the plan building and returns the resulting, completed plan (or non-nil error if it fails).
|
||||
func (pb *planBuilder) Plan() (*plan, error) {
|
||||
// For all plan vertices with no ins, make them root nodes.
|
||||
var roots []*planEdge
|
||||
for _, vs := range []map[Moniker]*planVertex{pb.Creates, pb.Updates, pb.Deletes} {
|
||||
for _, v := range vs {
|
||||
if len(v.Ins()) == 0 {
|
||||
roots = append(roots, &planEdge{to: v})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now topologically sort the steps in the order they must execute, thread the plan together, and return it.
|
||||
g := newPlanGraph(roots)
|
||||
topdag, err := graph.Topsort(g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var prev *step
|
||||
for _, v := range topdag {
|
||||
insertStep(&prev, v.Data().(*step))
|
||||
}
|
||||
|
||||
// Remember extra information useful for plan consumers.
|
||||
pb.P.replaces = pb.Replaces
|
||||
pb.P.unchanged = pb.Unchanged
|
||||
|
||||
return pb.P, nil
|
||||
}
|
||||
|
||||
type step struct {
|
||||
p *plan // this step's plan.
|
||||
op StepOp // the operation to perform.
|
||||
|
@ -525,6 +538,7 @@ type step struct {
|
|||
|
||||
var _ Step = (*step)(nil)
|
||||
|
||||
func (s *step) Plan() Plan { return s.p }
|
||||
func (s *step) Op() StepOp { return s.op }
|
||||
func (s *step) Logical() bool { return s.op == OpReplace }
|
||||
func (s *step) Old() Resource { return s.old }
|
||||
|
|
|
@ -231,7 +231,8 @@ func (p *Plugin) Update(id ID, t tokens.Type, olds PropertyMap, news PropertyMap
|
|||
}
|
||||
|
||||
// UpdateImpact checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *Plugin) UpdateImpact(id ID, t tokens.Type, olds PropertyMap, news PropertyMap) (bool, PropertyMap, error) {
|
||||
func (p *Plugin) UpdateImpact(id ID, t tokens.Type,
|
||||
olds PropertyMap, news PropertyMap) ([]string, PropertyMap, error) {
|
||||
contract.Requiref(id != "", "id", "not empty")
|
||||
contract.Requiref(t != "", "t", "not empty")
|
||||
|
||||
|
@ -251,14 +252,14 @@ func (p *Plugin) UpdateImpact(id ID, t tokens.Type, olds PropertyMap, news Prope
|
|||
resp, err := p.client.UpdateImpact(p.ctx.Request(), req)
|
||||
if err != nil {
|
||||
glog.V(7).Infof("Plugin[%v].UpdateImpact(id=%v,t=%v,...) failed: %v", p.pkg, id, t, err)
|
||||
return false, nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
replace := resp.GetReplace()
|
||||
impacts := UnmarshalProperties(resp.GetImpacts())
|
||||
glog.V(7).Infof("Plugin[%v].Update(id=%v,t=%v,...) success: replace=%v #impacts=%v",
|
||||
p.pkg, id, t, replace, len(impacts))
|
||||
return replace, impacts, nil
|
||||
replaces := resp.GetReplaces()
|
||||
changes := UnmarshalProperties(resp.GetChanges())
|
||||
glog.V(7).Infof("Plugin[%v].Update(id=%v,t=%v,...) success: #replaces=%v #changes=%v",
|
||||
p.pkg, id, t, len(replaces), len(changes))
|
||||
return replaces, changes, nil
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource.
|
||||
|
|
|
@ -32,7 +32,7 @@ type Provider interface {
|
|||
// Update updates an existing resource with new values.
|
||||
Update(id ID, t tokens.Type, olds PropertyMap, news PropertyMap) (error, ResourceState)
|
||||
// UpdateImpact checks what impacts a hypothetical update will have on the resource's properties.
|
||||
UpdateImpact(id ID, t tokens.Type, olds PropertyMap, news PropertyMap) (bool, PropertyMap, error)
|
||||
UpdateImpact(id ID, t tokens.Type, olds PropertyMap, news PropertyMap) ([]string, PropertyMap, error)
|
||||
// Delete tears down an existing resource.
|
||||
Delete(id ID, t tokens.Type) (error, ResourceState)
|
||||
}
|
||||
|
|
|
@ -181,8 +181,8 @@ func (m *UpdateRequest) GetNews() *google_protobuf1.Struct {
|
|||
}
|
||||
|
||||
type UpdateImpactResponse struct {
|
||||
Replace bool `protobuf:"varint,1,opt,name=replace" json:"replace,omitempty"`
|
||||
Impacts *google_protobuf1.Struct `protobuf:"bytes,2,opt,name=impacts" json:"impacts,omitempty"`
|
||||
Replaces []string `protobuf:"bytes,1,rep,name=replaces" json:"replaces,omitempty"`
|
||||
Changes *google_protobuf1.Struct `protobuf:"bytes,2,opt,name=changes" json:"changes,omitempty"`
|
||||
}
|
||||
|
||||
func (m *UpdateImpactResponse) Reset() { *m = UpdateImpactResponse{} }
|
||||
|
@ -190,16 +190,16 @@ func (m *UpdateImpactResponse) String() string { return proto.Compact
|
|||
func (*UpdateImpactResponse) ProtoMessage() {}
|
||||
func (*UpdateImpactResponse) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} }
|
||||
|
||||
func (m *UpdateImpactResponse) GetReplace() bool {
|
||||
func (m *UpdateImpactResponse) GetReplaces() []string {
|
||||
if m != nil {
|
||||
return m.Replace
|
||||
return m.Replaces
|
||||
}
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *UpdateImpactResponse) GetImpacts() *google_protobuf1.Struct {
|
||||
func (m *UpdateImpactResponse) GetChanges() *google_protobuf1.Struct {
|
||||
if m != nil {
|
||||
return m.Impacts
|
||||
return m.Changes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -498,32 +498,32 @@ var _ResourceProvider_serviceDesc = grpc.ServiceDesc{
|
|||
func init() { proto.RegisterFile("provider.proto", fileDescriptor1) }
|
||||
|
||||
var fileDescriptor1 = []byte{
|
||||
// 426 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x53, 0x4d, 0xab, 0xd3, 0x40,
|
||||
0x14, 0x6d, 0x62, 0x68, 0xf5, 0xf6, 0x03, 0x19, 0x9e, 0xef, 0x85, 0xa8, 0x50, 0x66, 0xf5, 0x40,
|
||||
0xc8, 0xa3, 0x2d, 0x45, 0xd0, 0xa5, 0x4a, 0x71, 0x23, 0x12, 0x71, 0x23, 0x6e, 0xd2, 0xc9, 0xb5,
|
||||
0x04, 0x92, 0xce, 0x38, 0x33, 0x51, 0xfa, 0x23, 0xfc, 0xcb, 0x22, 0xc9, 0x24, 0x71, 0x92, 0x52,
|
||||
0x5b, 0x17, 0x6f, 0x97, 0xdc, 0x39, 0x73, 0xee, 0xb9, 0x73, 0xce, 0x85, 0x99, 0x90, 0xfc, 0x47,
|
||||
0x9a, 0xa0, 0x0c, 0x85, 0xe4, 0x9a, 0x93, 0x11, 0xe3, 0x8c, 0x4b, 0xc1, 0x82, 0xa7, 0x3b, 0xce,
|
||||
0x77, 0x19, 0xde, 0x55, 0xe5, 0x6d, 0xf1, 0xed, 0x0e, 0x73, 0xa1, 0x0f, 0x06, 0x15, 0x3c, 0xeb,
|
||||
0x1f, 0x2a, 0x2d, 0x0b, 0xa6, 0xcd, 0x29, 0xfd, 0x02, 0xe3, 0x0f, 0x71, 0x8e, 0x11, 0x7e, 0x2f,
|
||||
0x50, 0x69, 0x42, 0xc0, 0xd3, 0x07, 0x81, 0xbe, 0x33, 0x77, 0x6e, 0x1f, 0x45, 0xd5, 0x37, 0x79,
|
||||
0x09, 0x20, 0x24, 0x17, 0x28, 0x75, 0x8a, 0xca, 0x77, 0xe7, 0xce, 0xed, 0x78, 0x79, 0x13, 0x1a,
|
||||
0xd6, 0xb0, 0x61, 0x0d, 0x3f, 0x55, 0xac, 0x91, 0x05, 0xa5, 0x14, 0x26, 0x86, 0x5b, 0x09, 0xbe,
|
||||
0x57, 0x58, 0x92, 0xef, 0xe3, 0xbc, 0x25, 0x2f, 0xbf, 0xe9, 0x57, 0x98, 0xbe, 0x91, 0x18, 0xeb,
|
||||
0xfb, 0x51, 0x30, 0x87, 0x59, 0xc3, 0x5e, 0x6b, 0x98, 0x81, 0x9b, 0x26, 0x35, 0xb9, 0x9b, 0x26,
|
||||
0x74, 0x01, 0xe3, 0x08, 0xe3, 0xa4, 0xe9, 0xde, 0x3b, 0x6e, 0xd5, 0xb8, 0x7f, 0xd5, 0xd0, 0x0d,
|
||||
0x4c, 0xcc, 0x95, 0x9a, 0xb2, 0xab, 0xce, 0xb9, 0x5c, 0xdd, 0x2f, 0x07, 0xa6, 0x9f, 0x45, 0x62,
|
||||
0x0d, 0x7f, 0x41, 0x7b, 0xf2, 0x02, 0x3c, 0x9e, 0x25, 0xca, 0x7f, 0xf0, 0xef, 0x46, 0x15, 0xa8,
|
||||
0x04, 0xef, 0xf1, 0xa7, 0xf2, 0xbd, 0x33, 0xe0, 0x12, 0x44, 0x19, 0x5c, 0x19, 0x39, 0xef, 0x73,
|
||||
0x11, 0x33, 0xdd, 0x0e, 0xe8, 0xc3, 0x48, 0xa2, 0xc8, 0x62, 0x66, 0x5c, 0x79, 0x18, 0x35, 0xbf,
|
||||
0x64, 0x01, 0xa3, 0xb4, 0xc2, 0x9e, 0x75, 0xa5, 0xc1, 0xd1, 0x15, 0x4c, 0xdf, 0x62, 0x86, 0xff,
|
||||
0x35, 0xf3, 0xf2, 0xb7, 0x0b, 0x8f, 0x23, 0x54, 0xbc, 0x90, 0x0c, 0x3f, 0xd6, 0x4b, 0x40, 0xd6,
|
||||
0xe0, 0x95, 0xf1, 0x22, 0x57, 0x61, 0xbd, 0x07, 0xa1, 0x95, 0xe4, 0xe0, 0x49, 0xaf, 0x6a, 0x66,
|
||||
0xa1, 0x03, 0xf2, 0x1a, 0x86, 0x26, 0x13, 0xe4, 0xba, 0x85, 0x74, 0x22, 0x18, 0xdc, 0x1c, 0xd5,
|
||||
0xdb, 0xcb, 0x6b, 0xf0, 0x4a, 0xef, 0xad, 0x9e, 0x56, 0x7a, 0xac, 0x9e, 0x76, 0x40, 0xe8, 0x80,
|
||||
0xbc, 0x82, 0xa1, 0x79, 0x59, 0xab, 0x67, 0xc7, 0xf9, 0xe0, 0xfa, 0xe8, 0xe1, 0xde, 0x95, 0x3b,
|
||||
0x4c, 0x07, 0x64, 0x03, 0x13, 0xdb, 0x95, 0x93, 0x0c, 0xcf, 0x7b, 0xf5, 0xae, 0x89, 0x46, 0x84,
|
||||
0x79, 0x79, 0x8b, 0xa2, 0x63, 0xc5, 0x69, 0x11, 0xdb, 0x61, 0x55, 0x59, 0xfd, 0x09, 0x00, 0x00,
|
||||
0xff, 0xff, 0xf3, 0xeb, 0x1f, 0x7c, 0x83, 0x04, 0x00, 0x00,
|
||||
// 431 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x53, 0x5d, 0x8b, 0xd3, 0x40,
|
||||
0x14, 0x6d, 0xb2, 0xa1, 0xeb, 0xde, 0x7e, 0x20, 0xc3, 0xba, 0x1b, 0xa2, 0x42, 0x99, 0xa7, 0x82,
|
||||
0x90, 0x65, 0x77, 0x29, 0x82, 0x3e, 0xaa, 0x14, 0x5f, 0x44, 0x22, 0xbe, 0x88, 0x2f, 0xe9, 0xe4,
|
||||
0x5a, 0x03, 0x49, 0x66, 0x9c, 0x99, 0x28, 0xfd, 0x11, 0xfe, 0x65, 0x91, 0x64, 0x92, 0x38, 0x49,
|
||||
0xa9, 0xad, 0x0f, 0xbe, 0x25, 0x77, 0xce, 0x9c, 0x7b, 0xee, 0x9c, 0x73, 0x61, 0x2e, 0x24, 0xff,
|
||||
0x9e, 0x26, 0x28, 0x43, 0x21, 0xb9, 0xe6, 0xe4, 0x9c, 0x71, 0xc6, 0xa5, 0x60, 0xc1, 0xe3, 0x2d,
|
||||
0xe7, 0xdb, 0x0c, 0x6f, 0xea, 0xf2, 0xa6, 0xfc, 0x72, 0x83, 0xb9, 0xd0, 0x3b, 0x83, 0x0a, 0x9e,
|
||||
0x0c, 0x0f, 0x95, 0x96, 0x25, 0xd3, 0xe6, 0x94, 0x7e, 0x82, 0xc9, 0xbb, 0x38, 0xc7, 0x08, 0xbf,
|
||||
0x95, 0xa8, 0x34, 0x21, 0xe0, 0xe9, 0x9d, 0x40, 0xdf, 0x59, 0x38, 0xcb, 0x8b, 0xa8, 0xfe, 0x26,
|
||||
0xcf, 0x01, 0x84, 0xe4, 0x02, 0xa5, 0x4e, 0x51, 0xf9, 0xee, 0xc2, 0x59, 0x4e, 0xee, 0xae, 0x43,
|
||||
0xc3, 0x1a, 0xb6, 0xac, 0xe1, 0x87, 0x9a, 0x35, 0xb2, 0xa0, 0x94, 0xc2, 0xd4, 0x70, 0x2b, 0xc1,
|
||||
0x0b, 0x85, 0x15, 0x79, 0x11, 0xe7, 0x1d, 0x79, 0xf5, 0x4d, 0x3f, 0xc3, 0xec, 0x95, 0xc4, 0x58,
|
||||
0xff, 0x1f, 0x05, 0x0b, 0x98, 0xb7, 0xec, 0x8d, 0x86, 0x39, 0xb8, 0x69, 0xd2, 0x90, 0xbb, 0x69,
|
||||
0x42, 0x6f, 0x61, 0x12, 0x61, 0x9c, 0xb4, 0xdd, 0x07, 0xc7, 0x9d, 0x1a, 0xf7, 0x8f, 0x1a, 0xba,
|
||||
0x86, 0xa9, 0xb9, 0xd2, 0x50, 0xf6, 0xd5, 0x39, 0xa7, 0xab, 0xfb, 0xe9, 0xc0, 0xec, 0xa3, 0x48,
|
||||
0xac, 0xe1, 0x4f, 0x68, 0x4f, 0x9e, 0x81, 0xc7, 0xb3, 0x44, 0xf9, 0x67, 0x7f, 0x6f, 0x54, 0x83,
|
||||
0x2a, 0x70, 0x81, 0x3f, 0x94, 0xef, 0x1d, 0x01, 0x57, 0x20, 0x8a, 0x70, 0x69, 0xe4, 0xbc, 0xcd,
|
||||
0x45, 0xcc, 0x74, 0x37, 0x60, 0x00, 0x0f, 0x24, 0x8a, 0x2c, 0x66, 0xf5, 0x78, 0x67, 0xcb, 0x8b,
|
||||
0xa8, 0xfb, 0x27, 0xb7, 0x70, 0xce, 0xbe, 0xc6, 0xc5, 0xf6, 0xb8, 0x2f, 0x2d, 0x8e, 0xde, 0xc3,
|
||||
0xec, 0x35, 0x66, 0xf8, 0x4f, 0x53, 0xdf, 0xfd, 0x72, 0xe1, 0x61, 0x84, 0x8a, 0x97, 0x92, 0xe1,
|
||||
0xfb, 0x66, 0x0d, 0xc8, 0x0a, 0xbc, 0x2a, 0x60, 0xe4, 0x32, 0x6c, 0x36, 0x21, 0xb4, 0xb2, 0x1c,
|
||||
0x3c, 0x1a, 0x54, 0xcd, 0x34, 0x74, 0x44, 0x5e, 0xc2, 0xd8, 0xa4, 0x82, 0x5c, 0x75, 0x90, 0x5e,
|
||||
0x08, 0x83, 0xeb, 0xbd, 0x7a, 0x77, 0x79, 0x05, 0x5e, 0xe5, 0xbe, 0xd5, 0xd3, 0xca, 0x8f, 0xd5,
|
||||
0xd3, 0x8e, 0x08, 0x1d, 0x91, 0x17, 0x30, 0x36, 0x6f, 0x6b, 0xf5, 0xec, 0x79, 0x1f, 0x5c, 0xed,
|
||||
0x3d, 0xdc, 0x9b, 0x6a, 0x8b, 0xe9, 0x88, 0xac, 0x61, 0x6a, 0xfb, 0x72, 0x90, 0xe1, 0xe9, 0xa0,
|
||||
0xde, 0xb7, 0xd1, 0x88, 0x30, 0x2f, 0x6f, 0x51, 0xf4, 0xac, 0x38, 0x2c, 0x62, 0x33, 0xae, 0x2b,
|
||||
0xf7, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x5e, 0xb9, 0x34, 0x85, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -1259,12 +1259,19 @@ proto.cocorpc.UpdateRequest.prototype.hasNews = function() {
|
|||
* @constructor
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, proto.cocorpc.UpdateImpactResponse.repeatedFields_, null);
|
||||
};
|
||||
goog.inherits(proto.cocorpc.UpdateImpactResponse, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
proto.cocorpc.UpdateImpactResponse.displayName = 'proto.cocorpc.UpdateImpactResponse';
|
||||
}
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.repeatedFields_ = [1];
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
|
@ -1293,8 +1300,8 @@ proto.cocorpc.UpdateImpactResponse.prototype.toObject = function(opt_includeInst
|
|||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.toObject = function(includeInstance, msg) {
|
||||
var f, obj = {
|
||||
replace: jspb.Message.getFieldWithDefault(msg, 1, false),
|
||||
impacts: (f = msg.getImpacts()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f)
|
||||
replacesList: jspb.Message.getField(msg, 1),
|
||||
changes: (f = msg.getChanges()) && google_protobuf_struct_pb.Struct.toObject(includeInstance, f)
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
|
@ -1332,13 +1339,13 @@ proto.cocorpc.UpdateImpactResponse.deserializeBinaryFromReader = function(msg, r
|
|||
var field = reader.getFieldNumber();
|
||||
switch (field) {
|
||||
case 1:
|
||||
var value = /** @type {boolean} */ (reader.readBool());
|
||||
msg.setReplace(value);
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addReplaces(value);
|
||||
break;
|
||||
case 2:
|
||||
var value = new google_protobuf_struct_pb.Struct;
|
||||
reader.readMessage(value,google_protobuf_struct_pb.Struct.deserializeBinaryFromReader);
|
||||
msg.setImpacts(value);
|
||||
msg.setChanges(value);
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
|
@ -1368,14 +1375,14 @@ proto.cocorpc.UpdateImpactResponse.prototype.serializeBinary = function() {
|
|||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.serializeBinaryToWriter = function(message, writer) {
|
||||
var f = undefined;
|
||||
f = message.getReplace();
|
||||
if (f) {
|
||||
writer.writeBool(
|
||||
f = message.getReplacesList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
1,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getImpacts();
|
||||
f = message.getChanges();
|
||||
if (f != null) {
|
||||
writer.writeMessage(
|
||||
2,
|
||||
|
@ -1387,40 +1394,54 @@ proto.cocorpc.UpdateImpactResponse.serializeBinaryToWriter = function(message, w
|
|||
|
||||
|
||||
/**
|
||||
* optional bool replace = 1;
|
||||
* Note that Boolean fields may be set to 0/1 when serialized from a Java server.
|
||||
* You should avoid comparisons like {@code val === true/false} in those cases.
|
||||
* @return {boolean}
|
||||
* repeated string replaces = 1;
|
||||
* If you change this array by adding, removing or replacing elements, or if you
|
||||
* replace the array itself, then you must call the setter to update it.
|
||||
* @return {!Array.<string>}
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.getReplace = function() {
|
||||
return /** @type {boolean} */ (jspb.Message.getFieldWithDefault(this, 1, false));
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.getReplacesList = function() {
|
||||
return /** @type {!Array.<string>} */ (jspb.Message.getField(this, 1));
|
||||
};
|
||||
|
||||
|
||||
/** @param {boolean} value */
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.setReplace = function(value) {
|
||||
jspb.Message.setField(this, 1, value);
|
||||
/** @param {!Array.<string>} value */
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.setReplacesList = function(value) {
|
||||
jspb.Message.setField(this, 1, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional google.protobuf.Struct impacts = 2;
|
||||
* @param {!string} value
|
||||
* @param {number=} opt_index
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.addReplaces = function(value, opt_index) {
|
||||
jspb.Message.addToRepeatedField(this, 1, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.clearReplacesList = function() {
|
||||
this.setReplacesList([]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* optional google.protobuf.Struct changes = 2;
|
||||
* @return {?proto.google.protobuf.Struct}
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.getImpacts = function() {
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.getChanges = function() {
|
||||
return /** @type{?proto.google.protobuf.Struct} */ (
|
||||
jspb.Message.getWrapperField(this, google_protobuf_struct_pb.Struct, 2));
|
||||
};
|
||||
|
||||
|
||||
/** @param {?proto.google.protobuf.Struct|undefined} value */
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.setImpacts = function(value) {
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.setChanges = function(value) {
|
||||
jspb.Message.setWrapperField(this, 2, value);
|
||||
};
|
||||
|
||||
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.clearImpacts = function() {
|
||||
this.setImpacts(undefined);
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.clearChanges = function() {
|
||||
this.setChanges(undefined);
|
||||
};
|
||||
|
||||
|
||||
|
@ -1428,7 +1449,7 @@ proto.cocorpc.UpdateImpactResponse.prototype.clearImpacts = function() {
|
|||
* Returns whether this field is set.
|
||||
* @return {!boolean}
|
||||
*/
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.hasImpacts = function() {
|
||||
proto.cocorpc.UpdateImpactResponse.prototype.hasChanges = function() {
|
||||
return jspb.Message.getField(this, 2) != null;
|
||||
};
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ message UpdateRequest {
|
|||
}
|
||||
|
||||
message UpdateImpactResponse {
|
||||
bool replace = 1; // true if this update triggers replacement of the resource, false otherwise.
|
||||
google.protobuf.Struct impacts = 2; // the full set of properties that will be altered by this operation.
|
||||
repeated string replaces = 1; // if this update requires a replacement, the set of properties triggering it.
|
||||
google.protobuf.Struct changes = 2; // the set of properties that will be changed (but don't require a replacement).
|
||||
}
|
||||
|
||||
message DeleteRequest {
|
||||
|
|
Loading…
Reference in a new issue