Export resource change counts from engine (#823)

This PR exports the aggregate resource changes for update and destroy operations. We'll use this information in #636 when summarizing previous updates.

I initially started with a new struct that had fields like `Created`, `Deleted`, `Unchanged`, etc. But it became cumbersome with the seven different type of resource operations we perform. So instead went with the more flexible `map[deploy.StepOp]int`.
This commit is contained in:
Chris Smith 2018-01-20 19:15:19 -08:00 committed by GitHub
commit 6f6fca7592
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 29 deletions

View file

@ -139,7 +139,7 @@ func (b *localBackend) Update(stackName tokens.QName, pkg *pack.Package, root st
go displayEvents(events, done, debug)
if err = engine.Deploy(update, events, opts); err != nil {
if _, err = engine.Deploy(update, events, opts); err != nil {
return err
}
@ -162,7 +162,7 @@ func (b *localBackend) Destroy(stackName tokens.QName, pkg *pack.Package, root s
go displayEvents(events, done, debug)
if err := engine.Destroy(update, events, opts); err != nil {
if _, err := engine.Destroy(update, events, opts); err != nil {
return err
}

View file

@ -29,7 +29,10 @@ type UpdateOptions struct {
Summary bool // true if we should only summarize resources and operations.
}
func Deploy(update Update, events chan<- Event, opts UpdateOptions) error {
// ResourceChanges contains the aggregate resource changes by operation type.
type ResourceChanges map[deploy.StepOp]int
func Deploy(update Update, events chan<- Event, opts UpdateOptions) (ResourceChanges, error) {
contract.Require(update != nil, "update")
contract.Require(events != nil, "events")
@ -37,7 +40,7 @@ func Deploy(update Update, events chan<- Event, opts UpdateOptions) error {
info, err := planContextFromUpdate(update)
if err != nil {
return err
return nil, err
}
defer info.Close()
@ -66,25 +69,27 @@ type deployOptions struct {
Diag diag.Sink // the sink to use for diag'ing.
}
func deployLatest(info *planContext, opts deployOptions) error {
func deployLatest(info *planContext, opts deployOptions) (ResourceChanges, error) {
result, err := plan(info, opts)
if err != nil {
return err
return nil, err
}
var resourceChanges ResourceChanges
if result != nil {
defer contract.IgnoreClose(result)
// Make the current working directory the same as the program's, and restore it upon exit.
done, err := result.Chdir()
if err != nil {
return err
return nil, err
}
defer done()
if opts.DryRun {
// If a dry run, just print the plan, don't actually carry out the deployment.
// If a dry run, just print the plan, don't actually carry out the deployment. (Reporting 0 changes.)
if err := printPlan(result); err != nil {
return err
return resourceChanges, err
}
} else {
// Otherwise, we will actually deploy the latest bits.
@ -99,7 +104,7 @@ func deployLatest(info *planContext, opts deployOptions) error {
summary, _, _, err := result.Walk(actions, false)
if err != nil && summary == nil {
// Something went wrong, and no changes were made.
return err
return resourceChanges, err
}
contract.Assert(summary != nil)
@ -107,7 +112,8 @@ func deployLatest(info *planContext, opts deployOptions) error {
var footer bytes.Buffer
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
if c := printChangeSummary(&footer, actions.Ops, false); c != 0 {
resourceChanges = ResourceChanges(actions.Ops)
if c := printChangeSummary(&footer, resourceChanges, false); c != 0 {
footer.WriteString(fmt.Sprintf("%vUpdate duration: %v%v\n",
colors.SpecUnimportant, time.Since(start), colors.Reset))
}
@ -121,15 +127,16 @@ func deployLatest(info *planContext, opts deployOptions) error {
opts.Events <- stdOutEventWithColor(&footer, opts.Color)
if err != nil {
return err
return resourceChanges, err
}
}
}
if !opts.Diag.Success() {
// If any error that wasn't printed above, be sure to make it evident in the output.
return goerr.New("One or more errors occurred during this update")
return resourceChanges, goerr.New("One or more errors occurred during this update")
}
return nil
return resourceChanges, nil
}
// deployActions pretty-prints the plan application process as it goes.

View file

@ -7,14 +7,14 @@ import (
"github.com/pulumi/pulumi/pkg/util/contract"
)
func Destroy(update Update, events chan<- Event, opts UpdateOptions) error {
func Destroy(update Update, events chan<- Event, opts UpdateOptions) (ResourceChanges, error) {
contract.Require(update != nil, "update")
defer func() { events <- cancelEvent() }()
info, err := planContextFromUpdate(update)
if err != nil {
return err
return nil, err
}
defer info.Close()

View file

@ -157,6 +157,7 @@ func (res *planResult) Close() error {
return res.Ctx.Close()
}
// printPlan prints the plan's result to the plan's Options.Events stream.
func printPlan(result *planResult) error {
// First print config/unchanged/etc. if necessary.
var prelude bytes.Buffer
@ -180,7 +181,7 @@ func printPlan(result *planResult) error {
// Print a summary of operation counts.
var summary bytes.Buffer
printChangeSummary(&summary, actions.Ops, true)
printChangeSummary(&summary, ResourceChanges(actions.Ops), true)
result.Options.Events <- stdOutEventWithColor(&summary, result.Options.Color)
return nil
}
@ -233,14 +234,15 @@ func printConfig(b *bytes.Buffer, cfg config.Map) {
}
}
func printChangeSummary(b *bytes.Buffer, counts map[deploy.StepOp]int, preview bool) int {
changes := 0
for op, c := range counts {
// printChangeSummary writes summary informatiom about the resoures changed to the provided buffer.
// Returns the total number of resources changed regardless of operation type.
func printChangeSummary(b *bytes.Buffer, changes ResourceChanges, preview bool) int {
changeCount := 0
for op, c := range changes {
if op != deploy.OpSame {
changes += c
changeCount += c
}
}
var kind string
if preview {
kind = "previewed"
@ -249,19 +251,19 @@ func printChangeSummary(b *bytes.Buffer, counts map[deploy.StepOp]int, preview b
}
var changesLabel string
if changes == 0 {
if changeCount == 0 {
kind = "required"
changesLabel = "no"
} else {
changesLabel = strconv.Itoa(changes)
changesLabel = strconv.Itoa(changeCount)
}
if changes > 0 || counts[deploy.OpSame] > 0 {
if changeCount > 0 || changes[deploy.OpSame] > 0 {
kind += ":"
}
b.WriteString(fmt.Sprintf("%vinfo%v: %v %v %v\n",
colors.SpecInfo, colors.Reset, changesLabel, plural("change", changes), kind))
colors.SpecInfo, colors.Reset, changesLabel, plural("change", changeCount), kind))
var planTo string
if preview {
@ -271,7 +273,7 @@ func printChangeSummary(b *bytes.Buffer, counts map[deploy.StepOp]int, preview b
// Now summarize all of the changes; we print sames a little differently.
for _, op := range deploy.StepOps {
if op != deploy.OpSame {
if c := counts[op]; c > 0 {
if c := changes[op]; c > 0 {
opDescription := string(op)
if !preview {
opDescription = op.PastTense()
@ -281,11 +283,11 @@ func printChangeSummary(b *bytes.Buffer, counts map[deploy.StepOp]int, preview b
}
}
}
if c := counts[deploy.OpSame]; c > 0 {
if c := changes[deploy.OpSame]; c > 0 {
b.WriteString(fmt.Sprintf(" %v %v unchanged\n", c, plural("resource", c)))
}
return changes
return changeCount
}
func plural(s string, c int) string {