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:
parent
4e2f94df95
commit
02c45f9f10
4 changed files with 116 additions and 82 deletions
|
@ -7,11 +7,13 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/pulumi/pulumi/pkg/backend"
|
"github.com/pulumi/pulumi/pkg/backend"
|
||||||
"github.com/pulumi/pulumi/pkg/diag"
|
"github.com/pulumi/pulumi/pkg/diag"
|
||||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||||
"github.com/pulumi/pulumi/pkg/engine"
|
"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/cmdutil"
|
||||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||||
)
|
)
|
||||||
|
@ -42,6 +44,8 @@ func displayEvents(action string,
|
||||||
return
|
return
|
||||||
case engine.PreludeEvent:
|
case engine.PreludeEvent:
|
||||||
displayPreludeEvent(os.Stdout, event.Payload.(engine.PreludeEventPayload), opts)
|
displayPreludeEvent(os.Stdout, event.Payload.(engine.PreludeEventPayload), opts)
|
||||||
|
case engine.SummaryEvent:
|
||||||
|
displaySummaryEvent(os.Stdout, event.Payload.(engine.SummaryEventPayload), opts)
|
||||||
case engine.StdoutColorEvent:
|
case engine.StdoutColorEvent:
|
||||||
payload := event.Payload.(engine.StdoutEventPayload)
|
payload := event.Payload.(engine.StdoutEventPayload)
|
||||||
out = os.Stdout
|
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
|
// nolint: gas
|
||||||
func displayPreludeEvent(out io.Writer, event engine.PreludeEventPayload, opts backend.DisplayOptions) {
|
func displayPreludeEvent(out io.Writer, event engine.PreludeEventPayload, opts backend.DisplayOptions) {
|
||||||
if opts.ShowConfig {
|
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)))
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -99,23 +99,9 @@ func deployLatest(info *planContext, opts deployOptions) (ResourceChanges, error
|
||||||
}
|
}
|
||||||
contract.Assert(summary != nil)
|
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.
|
// Print out the total number of steps performed (and their kinds), the duration, and any summary info.
|
||||||
resourceChanges = ResourceChanges(actions.Ops)
|
resourceChanges = ResourceChanges(actions.Ops)
|
||||||
if c := printChangeSummary(&footer, resourceChanges, false); c != 0 {
|
opts.Events <- updateSummaryEvent(actions.MaybeCorrupt, time.Since(start), resourceChanges)
|
||||||
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)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resourceChanges, err
|
return resourceChanges, err
|
||||||
|
|
|
@ -4,6 +4,7 @@ package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pulumi/pulumi/pkg/diag"
|
"github.com/pulumi/pulumi/pkg/diag"
|
||||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||||
|
@ -26,6 +27,7 @@ const (
|
||||||
StdoutColorEvent EventType = "stdoutcolor"
|
StdoutColorEvent EventType = "stdoutcolor"
|
||||||
DiagEvent EventType = "diag"
|
DiagEvent EventType = "diag"
|
||||||
PreludeEvent EventType = "prelude"
|
PreludeEvent EventType = "prelude"
|
||||||
|
SummaryEvent EventType = "summary"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cancelEvent() Event {
|
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
|
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 {
|
func preludeEvent(isPreview bool, cfg config.Map) Event {
|
||||||
configStringMap := make(map[string]string, len(cfg))
|
configStringMap := make(map[string]string, len(cfg))
|
||||||
for k, v := range 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 {
|
func stdOutEventWithColor(s fmt.Stringer) Event {
|
||||||
return Event{
|
return Event{
|
||||||
Type: StdoutColorEvent,
|
Type: StdoutColorEvent,
|
||||||
|
|
|
@ -198,11 +198,9 @@ func printPlan(result *planResult) (ResourceChanges, error) {
|
||||||
return nil, errors.New("One or more errors occurred during this preview")
|
return nil, errors.New("One or more errors occurred during this preview")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a summary of operation counts.
|
// Emit an event with a summary of operation counts.
|
||||||
var summary bytes.Buffer
|
|
||||||
changes := ResourceChanges(actions.Ops)
|
changes := ResourceChanges(actions.Ops)
|
||||||
printChangeSummary(&summary, changes, true)
|
result.Options.Events <- previewSummaryEvent(changes)
|
||||||
result.Options.Events <- stdOutEventWithColor(&summary)
|
|
||||||
return changes, nil
|
return changes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,69 +229,6 @@ func isRootStack(step deploy.Step) bool {
|
||||||
return step.URN().Type() == resource.RootStackType
|
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.
|
// 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,
|
func stepParentIndent(b *bytes.Buffer, step deploy.Step,
|
||||||
seen map[resource.URN]deploy.Step, shown map[resource.URN]bool,
|
seen map[resource.URN]deploy.Step, shown map[resource.URN]bool,
|
||||||
|
|
Loading…
Reference in a new issue