Emit richer events for resource steps
The engine now emits events with richer metadata during the ResourceOutputs and ResourcePre callbacks. The CLI can then use this information to decide if it should display the event or not and how much of the event to display. Options dealing with what to display and how to display it have moved into the CLI and the engine now emits all information for each event.
This commit is contained in:
parent
55383c46ba
commit
db079b1b0a
|
@ -65,16 +65,16 @@ func newDestroyCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
return s.Destroy(proj, root, debug, m, engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
DryRun: preview,
|
||||
Parallel: parallel,
|
||||
Analyzers: analyzers,
|
||||
DryRun: preview,
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
ShowReplacementSteps: showReplacementSteps,
|
||||
ShowSames: showSames,
|
||||
Summary: summary,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
})
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -53,16 +53,16 @@ func newPreviewCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
return s.Preview(proj, root, debug, engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
DryRun: true,
|
||||
Parallel: parallel,
|
||||
Analyzers: analyzers,
|
||||
DryRun: true,
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
ShowReplacementSteps: showReplacementSteps,
|
||||
ShowSames: showSames,
|
||||
Summary: summary,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
})
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -61,16 +61,16 @@ func newUpdateCmd() *cobra.Command {
|
|||
}
|
||||
|
||||
return s.Update(proj, root, debug, m, engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
DryRun: preview,
|
||||
Parallel: parallel,
|
||||
Analyzers: analyzers,
|
||||
DryRun: preview,
|
||||
Parallel: parallel,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
ShowReplacementSteps: showReplacementSteps,
|
||||
ShowSames: showSames,
|
||||
Summary: summary,
|
||||
Debug: debug,
|
||||
}, backend.DisplayOptions{
|
||||
Color: color.Colorization(),
|
||||
ShowConfig: showConfig,
|
||||
})
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -590,8 +590,8 @@ func (b *cloudBackend) makeProgramUpdateRequest(stackName tokens.QName, proj *wo
|
|||
DryRun: opts.DryRun,
|
||||
Parallel: opts.Parallel,
|
||||
ShowConfig: false, // This is a legacy option now, the engine will always emit config information
|
||||
ShowReplacementSteps: opts.ShowReplacementSteps,
|
||||
ShowSames: opts.ShowSames,
|
||||
ShowReplacementSteps: false, // This is a legacy option now, the engine will always emit this information
|
||||
ShowSames: false, // This is a legacy option now, the engine will always emit this information
|
||||
},
|
||||
Metadata: apitype.UpdateMetadata{
|
||||
Message: m.Message,
|
||||
|
|
|
@ -6,6 +6,9 @@ import "github.com/pulumi/pulumi/pkg/diag/colors"
|
|||
|
||||
// DisplayOptions controls how the output of events are rendered
|
||||
type DisplayOptions struct {
|
||||
Color colors.Colorization // colorization to apply to events
|
||||
ShowConfig bool // true if we should show configuration information before updating or previewing
|
||||
Color colors.Colorization // colorization to apply to events.
|
||||
ShowConfig bool // true if we should show configuration information.
|
||||
ShowReplacementSteps bool // true to show the replacement steps in the plan.
|
||||
ShowSames bool // true to show the resources that aren't updated in addition to updates.
|
||||
Summary bool // true if we should only summarize resources and operations.
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"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"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
|
@ -46,6 +47,13 @@ func displayEvents(action string,
|
|||
displayPreludeEvent(os.Stdout, event.Payload.(engine.PreludeEventPayload), opts)
|
||||
case engine.SummaryEvent:
|
||||
displaySummaryEvent(os.Stdout, event.Payload.(engine.SummaryEventPayload), opts)
|
||||
case engine.ResourceOperationFailed:
|
||||
displayResourceOperationFailedEvent(os.Stdout,
|
||||
event.Payload.(engine.ResourceOperationFailedPayload), opts)
|
||||
case engine.ResourceOutputsEvent:
|
||||
displayResourceOutputsEvent(os.Stdout, event.Payload.(engine.ResourceOutputsEventPayload), opts)
|
||||
case engine.ResourcePreEvent:
|
||||
displayResourcePreEvent(os.Stdout, event.Payload.(engine.ResourcePreEventPayload), opts)
|
||||
case engine.StdoutColorEvent:
|
||||
payload := event.Payload.(engine.StdoutEventPayload)
|
||||
out = os.Stdout
|
||||
|
@ -163,6 +171,67 @@ 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)))
|
||||
}
|
||||
|
||||
//nolint: gas
|
||||
func displayResourceOperationFailedEvent(out io.Writer,
|
||||
event engine.ResourceOperationFailedPayload, opts backend.DisplayOptions) {
|
||||
|
||||
fmt.Fprintf(out, "Step #%v failed [%v]: ", event.Steps+1, event.Metadata.Op)
|
||||
switch event.Status {
|
||||
case resource.StatusOK:
|
||||
fmt.Fprint(out, opts.Color.Colorize(
|
||||
fmt.Sprintf("%vprovider successfully recovered from this failure%v", colors.SpecNote, colors.Reset)))
|
||||
case resource.StatusUnknown:
|
||||
fmt.Fprint(out, opts.Color.Colorize(
|
||||
fmt.Sprintf("%vthis failure was catastrophic and the provider cannot guarantee recovery%v",
|
||||
colors.SpecAttention, colors.Reset)))
|
||||
default:
|
||||
contract.Failf("Unrecognized resource state: %v", event.Status)
|
||||
}
|
||||
fmt.Fprint(out, "\n")
|
||||
}
|
||||
|
||||
// nolint: gas
|
||||
func displayResourcePreEvent(out io.Writer, event engine.ResourcePreEventPayload, opts backend.DisplayOptions) {
|
||||
if shouldShow(event.Metadata, opts) || isRootStack(event.Metadata) {
|
||||
fmt.Fprint(out, opts.Color.Colorize(event.Summary))
|
||||
|
||||
if !opts.Summary {
|
||||
fmt.Fprint(out, event.Details)
|
||||
}
|
||||
|
||||
fmt.Fprint(out, opts.Color.Colorize(colors.Reset))
|
||||
}
|
||||
}
|
||||
|
||||
// nolint: gas
|
||||
func displayResourceOutputsEvent(out io.Writer, event engine.ResourceOutputsEventPayload, opts backend.DisplayOptions) {
|
||||
if (shouldShow(event.Metadata, opts) || isRootStack(event.Metadata)) && !opts.Summary {
|
||||
fmt.Fprint(out, opts.Color.Colorize(event.Text))
|
||||
}
|
||||
}
|
||||
|
||||
// isRootStack returns true if the step pertains to the rootmost stack component.
|
||||
func isRootStack(step engine.StepEventMetdata) bool {
|
||||
return step.URN.Type() == resource.RootStackType
|
||||
}
|
||||
|
||||
// shouldShow returns true if a step should show in the output.
|
||||
func shouldShow(step engine.StepEventMetdata, opts backend.DisplayOptions) bool {
|
||||
// For certain operations, whether they are tracked is controlled by flags (to cut down on superfluous output).
|
||||
if step.Op == deploy.OpSame {
|
||||
// If the op is the same, it is possible that the resource's metadata changed. In that case, still show it.
|
||||
if step.Old.Protect != step.New.Protect {
|
||||
return true
|
||||
}
|
||||
return opts.ShowSames
|
||||
} else if step.Op == deploy.OpCreateReplacement || step.Op == deploy.OpDeleteReplaced {
|
||||
return opts.ShowReplacementSteps
|
||||
} else if step.Op == deploy.OpReplace {
|
||||
return !opts.ShowReplacementSteps
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func plural(s string, c int) string {
|
||||
if c != 1 {
|
||||
s += "s"
|
||||
|
|
|
@ -3,14 +3,11 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/diag"
|
||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
|
@ -18,13 +15,10 @@ import (
|
|||
|
||||
// UpdateOptions contains all the settings for customizing how an update (deploy, preview, or destroy) is performed.
|
||||
type UpdateOptions struct {
|
||||
Analyzers []string // an optional set of analyzers to run as part of this deployment.
|
||||
DryRun bool // true if we should just print the plan without performing it.
|
||||
Parallel int // the degree of parallelism for resource operations (<=1 for serial).
|
||||
ShowReplacementSteps bool // true to show the replacement steps in the plan.
|
||||
ShowSames bool // true to show the resources that aren't updated in addition to updates.
|
||||
Summary bool // true if we should only summarize resources and operations.
|
||||
Debug bool // true if debugging output it enabled
|
||||
Analyzers []string // an optional set of analyzers to run as part of this deployment.
|
||||
DryRun bool // true if we should just print the plan without performing it.
|
||||
Parallel int // the degree of parallelism for resource operations (<=1 for serial).
|
||||
Debug bool // true if debugging output it enabled
|
||||
}
|
||||
|
||||
// ResourceChanges contains the aggregate resource changes by operation type.
|
||||
|
@ -139,12 +133,10 @@ func (acts *deployActions) OnResourceStepPre(step deploy.Step) (interface{}, err
|
|||
// Ensure we've marked this step as observed.
|
||||
acts.Seen[step.URN()] = step
|
||||
|
||||
// Report the beginning of the step if appropriate.
|
||||
if shouldShow(acts.Seen, step, acts.Opts) || isRootStack(step) {
|
||||
var b bytes.Buffer
|
||||
printStep(&b, step, acts.Seen, acts.Opts.Summary, false, acts.Opts.Debug)
|
||||
acts.Opts.Events <- stdOutEventWithColor(&b)
|
||||
}
|
||||
indent := stepParentIndent(step, acts.Seen)
|
||||
summary := getResourcePropertiesSummary(step, indent)
|
||||
details := getResourcePropertiesDetails(step, indent, false, acts.Opts.Debug)
|
||||
acts.Opts.Events <- resourcePreEvent(step, indent, summary, details)
|
||||
|
||||
// Inform the snapshot service that we are about to perform a step.
|
||||
return acts.Update.BeginMutation()
|
||||
|
@ -154,30 +146,16 @@ func (acts *deployActions) OnResourceStepPost(ctx interface{},
|
|||
step deploy.Step, status resource.Status, err error) error {
|
||||
assertSeen(acts.Seen, step)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
// Report the result of the step.
|
||||
stepop := step.Op()
|
||||
if err != nil {
|
||||
if status == resource.StatusUnknown {
|
||||
acts.MaybeCorrupt = true
|
||||
}
|
||||
|
||||
// Issue a true, bonafide error.
|
||||
acts.Opts.Diag.Errorf(diag.ErrorPlanApplyFailed, err)
|
||||
|
||||
// Print the state of the resource; we don't issue the error, because the deploy above will do that.
|
||||
stepnum := acts.Steps + 1
|
||||
b.WriteString(fmt.Sprintf("Step #%v failed [%v]: ", stepnum, stepop))
|
||||
switch status {
|
||||
case resource.StatusOK:
|
||||
b.WriteString(colors.SpecNote)
|
||||
b.WriteString("provider successfully recovered from this failure")
|
||||
case resource.StatusUnknown:
|
||||
b.WriteString(colors.SpecAttention)
|
||||
b.WriteString("this failure was catastrophic and the provider cannot guarantee recovery")
|
||||
acts.MaybeCorrupt = true
|
||||
default:
|
||||
contract.Failf("Unrecognized resource state: %v", status)
|
||||
}
|
||||
b.WriteString(colors.Reset)
|
||||
b.WriteString("\n")
|
||||
acts.Opts.Events <- resourceOperationFailedEvent(step, status, acts.Steps)
|
||||
} else {
|
||||
if step.Logical() {
|
||||
// Increment the counters.
|
||||
|
@ -186,13 +164,11 @@ func (acts *deployActions) OnResourceStepPost(ctx interface{},
|
|||
}
|
||||
|
||||
// Also show outputs here, since there might be some from the initial registration.
|
||||
if shouldShow(acts.Seen, step, acts.Opts) && !acts.Opts.Summary {
|
||||
printResourceOutputProperties(&b, step, acts.Seen, false, acts.Opts.Debug)
|
||||
}
|
||||
indent := stepParentIndent(step, acts.Seen)
|
||||
text := getResourceOutputsPropertiesString(step, indent, false, acts.Opts.Debug)
|
||||
acts.Opts.Events <- resourceOutputsEvent(step, indent, text)
|
||||
}
|
||||
|
||||
acts.Opts.Events <- stdOutEventWithColor(&b)
|
||||
|
||||
// Write out the current snapshot. Note that even if a failure has occurred, we should still have a
|
||||
// safe checkpoint. Note that any error that occurs when writing the checkpoint trumps the error reported above.
|
||||
return ctx.(SnapshotMutation).End(step.Iterator().Snap())
|
||||
|
@ -201,12 +177,9 @@ func (acts *deployActions) OnResourceStepPost(ctx interface{},
|
|||
func (acts *deployActions) OnResourceOutputs(step deploy.Step) error {
|
||||
assertSeen(acts.Seen, step)
|
||||
|
||||
// Print this step's output properties.
|
||||
if (shouldShow(acts.Seen, step, acts.Opts) || isRootStack(step)) && !acts.Opts.Summary {
|
||||
var b bytes.Buffer
|
||||
printResourceOutputProperties(&b, step, acts.Seen, false, acts.Opts.Debug)
|
||||
acts.Opts.Events <- stdOutEventWithColor(&b)
|
||||
}
|
||||
indent := stepParentIndent(step, acts.Seen)
|
||||
text := getResourceOutputsPropertiesString(step, indent, false, acts.Opts.Debug)
|
||||
acts.Opts.Events <- resourceOutputsEvent(step, indent, text)
|
||||
|
||||
// There's a chance there are new outputs that weren't written out last time.
|
||||
// We need to perform another snapshot write to ensure they get written out.
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pulumi/pulumi/pkg/diag"
|
||||
"github.com/pulumi/pulumi/pkg/diag/colors"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/config"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
"github.com/pulumi/pulumi/pkg/tokens"
|
||||
"github.com/pulumi/pulumi/pkg/util/contract"
|
||||
)
|
||||
|
||||
|
@ -23,11 +25,14 @@ type Event struct {
|
|||
type EventType string
|
||||
|
||||
const (
|
||||
CancelEvent EventType = "cancel"
|
||||
StdoutColorEvent EventType = "stdoutcolor"
|
||||
DiagEvent EventType = "diag"
|
||||
PreludeEvent EventType = "prelude"
|
||||
SummaryEvent EventType = "summary"
|
||||
CancelEvent EventType = "cancel"
|
||||
StdoutColorEvent EventType = "stdoutcolor"
|
||||
DiagEvent EventType = "diag"
|
||||
PreludeEvent EventType = "prelude"
|
||||
SummaryEvent EventType = "summary"
|
||||
ResourcePreEvent EventType = "resourcepre"
|
||||
ResourceOutputsEvent EventType = "resourceoutputs"
|
||||
ResourceOperationFailed EventType = "resourceoperationfailed"
|
||||
)
|
||||
|
||||
func cancelEvent() Event {
|
||||
|
@ -58,6 +63,107 @@ type SummaryEventPayload struct {
|
|||
ResourceChanges ResourceChanges // count of changed resources, useful for reporting
|
||||
}
|
||||
|
||||
type ResourceOperationFailedPayload struct {
|
||||
Metadata StepEventMetdata
|
||||
Status resource.Status
|
||||
Steps int
|
||||
}
|
||||
|
||||
type ResourceOutputsEventPayload struct {
|
||||
Metadata StepEventMetdata
|
||||
Indent int
|
||||
Text string
|
||||
}
|
||||
|
||||
type ResourcePreEventPayload struct {
|
||||
Metadata StepEventMetdata
|
||||
Indent int
|
||||
Summary string
|
||||
Details string
|
||||
}
|
||||
|
||||
type StepEventMetdata struct {
|
||||
Op deploy.StepOp // the operation performed by this step.
|
||||
URN resource.URN // the resource URN (for before and after).
|
||||
Type tokens.Type // the type affected by this step.
|
||||
Old *StepEventStateMetadata // the state of the resource before performing this step.
|
||||
New *StepEventStateMetadata // the state of the resource after performing this step.
|
||||
Res *StepEventStateMetadata // the latest state for the resource that is known (worst case, old).
|
||||
Logical bool // true if this step represents a logical operation in the program.
|
||||
}
|
||||
|
||||
type StepEventStateMetadata struct {
|
||||
Type tokens.Type // the resource's type.
|
||||
URN resource.URN // the resource's object urn, a human-friendly, unique name for the resource.
|
||||
Custom bool // true if the resource is custom, managed by a plugin.
|
||||
Delete bool // true if this resource is pending deletion due to a replacement.
|
||||
ID resource.ID // the resource's unique ID, assigned by the resource provider (or blank if none/uncreated).
|
||||
Parent resource.URN // an optional parent URN that this resource belongs to.
|
||||
Protect bool // true to "protect" this resource (protected resources cannot be deleted).
|
||||
}
|
||||
|
||||
func makeStepEventMetadata(step deploy.Step) StepEventMetdata {
|
||||
return StepEventMetdata{
|
||||
Op: step.Op(),
|
||||
URN: step.URN(),
|
||||
Type: step.Type(),
|
||||
Old: makeStepEventStateMetadata(step.Old()),
|
||||
New: makeStepEventStateMetadata(step.New()),
|
||||
Res: makeStepEventStateMetadata(step.Res()),
|
||||
Logical: step.Logical(),
|
||||
}
|
||||
}
|
||||
|
||||
func makeStepEventStateMetadata(state *resource.State) *StepEventStateMetadata {
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &StepEventStateMetadata{
|
||||
Type: state.Type,
|
||||
URN: state.URN,
|
||||
Custom: state.Custom,
|
||||
Delete: state.Delete,
|
||||
ID: state.ID,
|
||||
Parent: state.Parent,
|
||||
Protect: state.Protect,
|
||||
}
|
||||
}
|
||||
|
||||
func resourceOperationFailedEvent(step deploy.Step, status resource.Status, steps int) Event {
|
||||
return Event{
|
||||
Type: ResourceOperationFailed,
|
||||
Payload: ResourceOperationFailedPayload{
|
||||
Metadata: makeStepEventMetadata(step),
|
||||
Status: status,
|
||||
Steps: steps,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceOutputsEvent(step deploy.Step, indent int, text string) Event {
|
||||
return Event{
|
||||
Type: ResourceOutputsEvent,
|
||||
Payload: ResourceOutputsEventPayload{
|
||||
Metadata: makeStepEventMetadata(step),
|
||||
Indent: indent,
|
||||
Text: text,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourcePreEvent(step deploy.Step, indent int, summary string, details string) Event {
|
||||
return Event{
|
||||
Type: ResourcePreEvent,
|
||||
Payload: ResourcePreEventPayload{
|
||||
Metadata: makeStepEventMetadata(step),
|
||||
Indent: indent,
|
||||
Summary: summary,
|
||||
Details: details,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func preludeEvent(isPreview bool, cfg config.Map) Event {
|
||||
configStringMap := make(map[string]string, len(cfg))
|
||||
for k, v := range cfg {
|
||||
|
@ -100,16 +206,6 @@ func updateSummaryEvent(maybeCorrupt bool, duration time.Duration, resourceChang
|
|||
}
|
||||
}
|
||||
|
||||
func stdOutEventWithColor(s fmt.Stringer) Event {
|
||||
return Event{
|
||||
Type: StdoutColorEvent,
|
||||
Payload: StdoutEventPayload{
|
||||
Message: s.String(),
|
||||
Color: colors.Raw,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func diagDebugEvent(msg string) Event {
|
||||
return Event{
|
||||
Type: DiagEvent,
|
||||
|
|
|
@ -209,29 +209,6 @@ func assertSeen(seen map[resource.URN]deploy.Step, step deploy.Step) {
|
|||
contract.Assertf(has, "URN '%v' had not been marked as seen", step.URN())
|
||||
}
|
||||
|
||||
// shouldShow returns true if a step should show in the output.
|
||||
func shouldShow(seen map[resource.URN]deploy.Step, step deploy.Step, opts deployOptions) bool {
|
||||
|
||||
// For certain operations, whether they are tracked is controlled by flags (to cut down on superfluous output).
|
||||
if step.Op() == deploy.OpSame {
|
||||
// If the op is the same, it is possible that the resource's metadata changed. In that case, still show it.
|
||||
if step.Old().Protect != step.New().Protect {
|
||||
return true
|
||||
}
|
||||
return opts.ShowSames
|
||||
} else if step.Op() == deploy.OpCreateReplacement || step.Op() == deploy.OpDeleteReplaced {
|
||||
return opts.ShowReplacementSteps
|
||||
} else if step.Op() == deploy.OpReplace {
|
||||
return !opts.ShowReplacementSteps
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isRootStack returns true if the step pertains to the rootmost stack component.
|
||||
func isRootStack(step deploy.Step) bool {
|
||||
return step.URN().Type() == resource.RootStackType
|
||||
}
|
||||
|
||||
// stepParentIndent computes a step's parent indentation. If print is true, it also prints parents as it goes.
|
||||
func stepParentIndent(step deploy.Step, seen map[resource.URN]deploy.Step) int {
|
||||
indent := 0
|
||||
|
@ -249,39 +226,6 @@ func stepParentIndent(step deploy.Step, seen map[resource.URN]deploy.Step) int {
|
|||
return indent
|
||||
}
|
||||
|
||||
func printStep(b *bytes.Buffer, step deploy.Step, seen map[resource.URN]deploy.Step,
|
||||
summary bool, planning bool, debug bool) {
|
||||
op := step.Op()
|
||||
|
||||
// First, indent to the same level as this resource has parents, and toggle the level of detail accordingly.
|
||||
// TODO[pulumi/pulumi#340]: this isn't entirely correct. Conventionally, all children are created adjacent to
|
||||
// their parents, so this often does the right thing, but not always. For instance, we can have interleaved
|
||||
// infrastructure that gets emitted in the middle of the flow, making things look like they are parented
|
||||
// incorrectly. The real solution here is to have a more first class way of structuring the output.
|
||||
indent := stepParentIndent(step, seen)
|
||||
|
||||
// Print the indentation.
|
||||
b.WriteString(getIndentationString(indent, op, false))
|
||||
|
||||
// First, print out the operation's prefix.
|
||||
b.WriteString(op.Prefix())
|
||||
|
||||
// Next, print the resource type (since it is easy on the eyes and can be quickly identified).
|
||||
printStepHeader(b, step)
|
||||
|
||||
// Next print the resource URN, properties, etc.
|
||||
var replaces []resource.PropertyKey
|
||||
if step.Op() == deploy.OpCreateReplacement {
|
||||
replaces = step.(*deploy.CreateStep).Keys()
|
||||
} else if step.Op() == deploy.OpReplace {
|
||||
replaces = step.(*deploy.ReplaceStep).Keys()
|
||||
}
|
||||
printResourceProperties(b, step.URN(), step.Old(), step.New(), replaces, summary, planning, indent, op, debug)
|
||||
|
||||
// Reset the color -- we're done.
|
||||
b.WriteString(colors.Reset)
|
||||
}
|
||||
|
||||
func printStepHeader(b *bytes.Buffer, step deploy.Step) {
|
||||
var extra string
|
||||
old := step.Old()
|
||||
|
@ -337,16 +281,26 @@ func writeVerbatim(b *bytes.Buffer, op deploy.StepOp, value string) {
|
|||
writeWithIndentNoPrefix(b, 0, op, "%s", value)
|
||||
}
|
||||
|
||||
func printResourceProperties(
|
||||
b *bytes.Buffer, urn resource.URN, old *resource.State, new *resource.State,
|
||||
replaces []resource.PropertyKey, summary bool, planning bool, indent int, op deploy.StepOp, debug bool) {
|
||||
func getResourcePropertiesSummary(step deploy.Step, indent int) string {
|
||||
var b bytes.Buffer
|
||||
|
||||
indent++
|
||||
op := step.Op()
|
||||
urn := step.URN()
|
||||
old := step.Old()
|
||||
|
||||
// Print the indentation.
|
||||
b.WriteString(getIndentationString(indent, op, false))
|
||||
|
||||
// First, print out the operation's prefix.
|
||||
b.WriteString(op.Prefix())
|
||||
|
||||
// Next, print the resource type (since it is easy on the eyes and can be quickly identified).
|
||||
printStepHeader(&b, step)
|
||||
|
||||
// For these simple properties, print them as 'same' if they're just an update or replace.
|
||||
simplePropOp := considerSameIfNotCreateOrDelete(op)
|
||||
|
||||
// Print out the URN and, if present, the ID, as "pseudo-properties".
|
||||
// Print out the URN and, if present, the ID, as "pseudo-properties" and indent them.
|
||||
var id resource.ID
|
||||
if old != nil {
|
||||
id = old.ID
|
||||
|
@ -354,22 +308,40 @@ func printResourceProperties(
|
|||
|
||||
// Always print the ID and URN.
|
||||
if id != "" {
|
||||
writeWithIndentNoPrefix(b, indent, simplePropOp, "[id=%s]\n", string(id))
|
||||
writeWithIndentNoPrefix(&b, indent+1, simplePropOp, "[id=%s]\n", string(id))
|
||||
}
|
||||
if urn != "" {
|
||||
writeWithIndentNoPrefix(b, indent, simplePropOp, "[urn=%s]\n", urn)
|
||||
writeWithIndentNoPrefix(&b, indent+1, simplePropOp, "[urn=%s]\n", urn)
|
||||
}
|
||||
|
||||
// If not summarizing, print all of the properties associated with this resource.
|
||||
if !summary {
|
||||
if old == nil && new != nil {
|
||||
printObject(b, new.Inputs, planning, indent, op, false, debug)
|
||||
} else if new == nil && old != nil {
|
||||
printObject(b, old.Inputs, planning, indent, op, false, debug)
|
||||
} else {
|
||||
printOldNewDiffs(b, old.Inputs, new.Inputs, replaces, planning, indent, op, debug)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func getResourcePropertiesDetails(step deploy.Step, indent int, planning bool, debug bool) string {
|
||||
var b bytes.Buffer
|
||||
|
||||
// indent everything an additional level, like other properties.
|
||||
indent++
|
||||
|
||||
var replaces []resource.PropertyKey
|
||||
if step.Op() == deploy.OpCreateReplacement {
|
||||
replaces = step.(*deploy.CreateStep).Keys()
|
||||
} else if step.Op() == deploy.OpReplace {
|
||||
replaces = step.(*deploy.ReplaceStep).Keys()
|
||||
}
|
||||
|
||||
old := step.Old()
|
||||
new := step.New()
|
||||
|
||||
if old == nil && new != nil {
|
||||
printObject(&b, new.Inputs, planning, indent, step.Op(), false, debug)
|
||||
} else if new == nil && old != nil {
|
||||
printObject(&b, old.Inputs, planning, indent, step.Op(), false, debug)
|
||||
} else {
|
||||
printOldNewDiffs(&b, old.Inputs, new.Inputs, replaces, planning, indent, step.Op(), debug)
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func maxKey(keys []resource.PropertyKey) int {
|
||||
|
@ -401,18 +373,16 @@ func printObject(
|
|||
|
||||
// printResourceOutputProperties prints only those properties that either differ from the input properties or, if
|
||||
// there is an old snapshot of the resource, differ from the prior old snapshot's output properties.
|
||||
func printResourceOutputProperties(b *bytes.Buffer, step deploy.Step,
|
||||
seen map[resource.URN]deploy.Step, planning bool, debug bool) {
|
||||
func getResourceOutputsPropertiesString(step deploy.Step, indent int, planning bool, debug bool) string {
|
||||
var b bytes.Buffer
|
||||
|
||||
// Only certain kinds of steps have output properties associated with them.
|
||||
new := step.New()
|
||||
if new == nil || new.Outputs == nil {
|
||||
return
|
||||
return ""
|
||||
}
|
||||
op := considerSameIfNotCreateOrDelete(step.Op())
|
||||
|
||||
// Now compute the indentation level, in part based on the parents.
|
||||
indent := stepParentIndent(step, seen)
|
||||
|
||||
// First fetch all the relevant property maps that we may consult.
|
||||
ins := new.Inputs
|
||||
outs := new.Outputs
|
||||
|
@ -434,14 +404,16 @@ func printResourceOutputProperties(b *bytes.Buffer, step deploy.Step,
|
|||
|
||||
if print {
|
||||
if firstout {
|
||||
writeWithIndentNoPrefix(b, indent, op, "---outputs:---\n")
|
||||
writeWithIndentNoPrefix(&b, indent, op, "---outputs:---\n")
|
||||
firstout = false
|
||||
}
|
||||
printPropertyTitle(b, string(k), maxkey, indent, op, false)
|
||||
printPropertyValue(b, out, planning, indent, op, false, debug)
|
||||
printPropertyTitle(&b, string(k), maxkey, indent, op, false)
|
||||
printPropertyValue(&b, out, planning, indent, op, false, debug)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func considerSameIfNotCreateOrDelete(op deploy.StepOp) deploy.StepOp {
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
package engine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pulumi/pulumi/pkg/resource"
|
||||
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
||||
|
@ -81,12 +79,11 @@ func newPreviewActions(opts deployOptions) *previewActions {
|
|||
func (acts *previewActions) OnResourceStepPre(step deploy.Step) (interface{}, error) {
|
||||
acts.Seen[step.URN()] = step
|
||||
|
||||
// Print this step information (resource and all its properties).
|
||||
if shouldShow(acts.Seen, step, acts.Opts) || isRootStack(step) {
|
||||
var b bytes.Buffer
|
||||
printStep(&b, step, acts.Seen, acts.Opts.Summary, true, acts.Opts.Debug)
|
||||
acts.Opts.Events <- stdOutEventWithColor(&b)
|
||||
}
|
||||
indent := stepParentIndent(step, acts.Seen)
|
||||
summary := getResourcePropertiesSummary(step, indent)
|
||||
details := getResourcePropertiesDetails(step, indent, true, acts.Opts.Debug)
|
||||
acts.Opts.Events <- resourcePreEvent(step, indent, summary, details)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -101,10 +98,7 @@ func (acts *previewActions) OnResourceStepPost(ctx interface{},
|
|||
acts.Ops[step.Op()]++
|
||||
}
|
||||
|
||||
// Also show outputs here, since there might be some from the initial registration.
|
||||
if shouldShow(acts.Seen, step, acts.Opts) && !acts.Opts.Summary {
|
||||
_ = acts.OnResourceOutputs(step)
|
||||
}
|
||||
_ = acts.OnResourceOutputs(step)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -112,11 +106,8 @@ func (acts *previewActions) OnResourceStepPost(ctx interface{},
|
|||
func (acts *previewActions) OnResourceOutputs(step deploy.Step) error {
|
||||
assertSeen(acts.Seen, step)
|
||||
|
||||
// Print this step's output properties.
|
||||
if (shouldShow(acts.Seen, step, acts.Opts) || isRootStack(step)) && !acts.Opts.Summary {
|
||||
var b bytes.Buffer
|
||||
printResourceOutputProperties(&b, step, acts.Seen, true, acts.Opts.Debug)
|
||||
acts.Opts.Events <- stdOutEventWithColor(&b)
|
||||
}
|
||||
indent := stepParentIndent(step, acts.Seen)
|
||||
text := getResourceOutputsPropertiesString(step, indent, true, acts.Opts.Debug)
|
||||
acts.Opts.Events <- resourceOutputsEvent(step, indent, text)
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue