Move summary printing out of the engine

The engine now emits a special type of summary event, which the CLI
displays.
This commit is contained in:
Matt Ellis 2018-02-01 21:15:09 -08:00
parent 4e2f94df95
commit 02c45f9f10
4 changed files with 116 additions and 82 deletions

View file

@ -7,11 +7,13 @@ import (
"io"
"os"
"sort"
"strconv"
"github.com/pulumi/pulumi/pkg/backend"
"github.com/pulumi/pulumi/pkg/diag"
"github.com/pulumi/pulumi/pkg/diag/colors"
"github.com/pulumi/pulumi/pkg/engine"
"github.com/pulumi/pulumi/pkg/resource/deploy"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pulumi/pulumi/pkg/util/contract"
)
@ -42,6 +44,8 @@ func displayEvents(action string,
return
case engine.PreludeEvent:
displayPreludeEvent(os.Stdout, event.Payload.(engine.PreludeEventPayload), opts)
case engine.SummaryEvent:
displaySummaryEvent(os.Stdout, event.Payload.(engine.SummaryEventPayload), opts)
case engine.StdoutColorEvent:
payload := event.Payload.(engine.StdoutEventPayload)
out = os.Stdout
@ -67,6 +71,75 @@ func displayEvents(action string,
}
}
// nolint: gas
func displaySummaryEvent(out io.Writer, event engine.SummaryEventPayload, opts backend.DisplayOptions) {
changes := event.ResourceChanges
changeCount := 0
for op, c := range changes {
if op != deploy.OpSame {
changeCount += c
}
}
var kind string
if event.IsPreview {
kind = "previewed"
} else {
kind = "performed"
}
var changesLabel string
if changeCount == 0 {
kind = "required"
changesLabel = "no"
} else {
changesLabel = strconv.Itoa(changeCount)
}
if changeCount > 0 || changes[deploy.OpSame] > 0 {
kind += ":"
}
fmt.Fprint(out, opts.Color.Colorize(fmt.Sprintf("%vinfo%v: %v %v %v\n",
colors.SpecInfo, colors.Reset, changesLabel, plural("change", changeCount), kind)))
var planTo string
if event.IsPreview {
planTo = "to "
}
// Now summarize all of the changes; we print sames a little differently.
for _, op := range deploy.StepOps {
if op != deploy.OpSame {
if c := changes[op]; c > 0 {
opDescription := string(op)
if !event.IsPreview {
opDescription = op.PastTense()
}
fmt.Fprint(out, opts.Color.Colorize(fmt.Sprintf(" %v%v %v %v%v%v\n",
op.Prefix(), c, plural("resource", c), planTo, opDescription, colors.Reset)))
}
}
}
if c := changes[deploy.OpSame]; c > 0 {
fmt.Fprintf(out, " %v %v unchanged\n", c, plural("resource", c))
}
// For actual deploys, we print some additional summary information
if !event.IsPreview {
if changeCount > 0 {
fmt.Fprint(out, opts.Color.Colorize(fmt.Sprintf("%vUpdate duration: %v%v\n",
colors.SpecUnimportant, event.Duration, colors.Reset)))
}
if event.MaybeCorrupt {
fmt.Fprint(out, opts.Color.Colorize(fmt.Sprintf(
"%vA catastrophic error occurred; resources states may be unknown%v\n",
colors.SpecAttention, colors.Reset)))
}
}
}
// nolint: gas
func displayPreludeEvent(out io.Writer, event engine.PreludeEventPayload, opts backend.DisplayOptions) {
if opts.ShowConfig {
@ -89,3 +162,10 @@ func displayPreludeEvent(out io.Writer, event engine.PreludeEventPayload, opts b
fmt.Fprint(out, opts.Color.Colorize(fmt.Sprintf("%v%v changes:%v\n", colors.SpecUnimportant, action, colors.Reset)))
}
func plural(s string, c int) string {
if c != 1 {
s += "s"
}
return s
}

View file

@ -99,23 +99,9 @@ func deployLatest(info *planContext, opts deployOptions) (ResourceChanges, error
}
contract.Assert(summary != nil)
// Print a summary.
var footer bytes.Buffer
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
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))
}
if actions.MaybeCorrupt {
footer.WriteString(fmt.Sprintf(
"%vA catastrophic error occurred; resources states may be unknown%v\n",
colors.SpecAttention, colors.Reset))
}
opts.Events <- stdOutEventWithColor(&footer)
opts.Events <- updateSummaryEvent(actions.MaybeCorrupt, time.Since(start), resourceChanges)
if err != nil {
return resourceChanges, err

View file

@ -4,6 +4,7 @@ package engine
import (
"fmt"
"time"
"github.com/pulumi/pulumi/pkg/diag"
"github.com/pulumi/pulumi/pkg/diag/colors"
@ -26,6 +27,7 @@ const (
StdoutColorEvent EventType = "stdoutcolor"
DiagEvent EventType = "diag"
PreludeEvent EventType = "prelude"
SummaryEvent EventType = "summary"
)
func cancelEvent() Event {
@ -49,6 +51,13 @@ type PreludeEventPayload struct {
Config map[string]string // the keys and values for config. For encrypted config, the values may be blinded
}
type SummaryEventPayload struct {
IsPreview bool // true if this summary is for a plan operation
MaybeCorrupt bool // true if one or more resources may be corrupt
Duration time.Duration // the duration of the entire update operation (zero values for previews)
ResourceChanges ResourceChanges // count of changed resources, useful for reporting
}
func preludeEvent(isPreview bool, cfg config.Map) Event {
configStringMap := make(map[string]string, len(cfg))
for k, v := range cfg {
@ -67,6 +76,30 @@ func preludeEvent(isPreview bool, cfg config.Map) Event {
}
}
func previewSummaryEvent(resourceChanges ResourceChanges) Event {
return Event{
Type: SummaryEvent,
Payload: SummaryEventPayload{
IsPreview: true,
MaybeCorrupt: false,
Duration: 0,
ResourceChanges: resourceChanges,
},
}
}
func updateSummaryEvent(maybeCorrupt bool, duration time.Duration, resourceChanges ResourceChanges) Event {
return Event{
Type: SummaryEvent,
Payload: SummaryEventPayload{
IsPreview: false,
MaybeCorrupt: maybeCorrupt,
Duration: duration,
ResourceChanges: resourceChanges,
},
}
}
func stdOutEventWithColor(s fmt.Stringer) Event {
return Event{
Type: StdoutColorEvent,

View file

@ -198,11 +198,9 @@ func printPlan(result *planResult) (ResourceChanges, error) {
return nil, errors.New("One or more errors occurred during this preview")
}
// Print a summary of operation counts.
var summary bytes.Buffer
// Emit an event with a summary of operation counts.
changes := ResourceChanges(actions.Ops)
printChangeSummary(&summary, changes, true)
result.Options.Events <- stdOutEventWithColor(&summary)
result.Options.Events <- previewSummaryEvent(changes)
return changes, nil
}
@ -231,69 +229,6 @@ func isRootStack(step deploy.Step) bool {
return step.URN().Type() == resource.RootStackType
}
// 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 {
changeCount += c
}
}
var kind string
if preview {
kind = "previewed"
} else {
kind = "performed"
}
var changesLabel string
if changeCount == 0 {
kind = "required"
changesLabel = "no"
} else {
changesLabel = strconv.Itoa(changeCount)
}
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", changeCount), kind))
var planTo string
if preview {
planTo = "to "
}
// Now summarize all of the changes; we print sames a little differently.
for _, op := range deploy.StepOps {
if op != deploy.OpSame {
if c := changes[op]; c > 0 {
opDescription := string(op)
if !preview {
opDescription = op.PastTense()
}
b.WriteString(fmt.Sprintf(" %v%v %v %v%v%v\n",
op.Prefix(), c, plural("resource", c), planTo, opDescription, colors.Reset))
}
}
}
if c := changes[deploy.OpSame]; c > 0 {
b.WriteString(fmt.Sprintf(" %v %v unchanged\n", c, plural("resource", c)))
}
return changeCount
}
func plural(s string, c int) string {
if c != 1 {
s += "s"
}
return s
}
// stepParentIndent computes a step's parent indentation. If print is true, it also prints parents as it goes.
func stepParentIndent(b *bytes.Buffer, step deploy.Step,
seen map[resource.URN]deploy.Step, shown map[resource.URN]bool,