From cd5707f483e57ce0c691c421fc6bee89e28807a1 Mon Sep 17 00:00:00 2001 From: joeduffy Date: Wed, 29 Aug 2018 17:06:48 -0700 Subject: [PATCH] Clarify refresh text The wording for refresh doesn't accurately convey that the operations aren't actually mutating your resources, but instead are simply changing your checkpoint state. This change (hopefully) helps in two ways: First, put text just before the prompt: Do you want to perform this refresh? No resources will be modified as part of this refresh; just your stack's state will be. Second, alter the summary ever-so-slightly, from: info: 2 changes performed: ~ 2 resources updated 3 resources unchanged to: info: 2 changes refreshed: ~ 2 resources updated 3 resources unchanged This reads just slightly better, and removes any sense of panic I might have otherwise had that my refresh just did something wrong. As I was in here, since I had to pass UpdateKind information to new places, I cleaned up the situation where we had three mostly-similar enums (but which actually diverged) and several areas where we were using untyped strings for this same information. Now there's just one. This fixes pulumi/pulumi#1551. --- pkg/apitype/history.go | 6 ++- pkg/backend/backend.go | 1 + pkg/backend/cloud/backend.go | 70 +++++++++++++++++------------- pkg/backend/cloud/client/api.go | 13 +----- pkg/backend/cloud/client/client.go | 16 +++---- pkg/backend/cloud/state.go | 10 ++--- pkg/backend/local/backend.go | 14 +++--- pkg/backend/local/display.go | 24 +++++----- pkg/backend/local/progress.go | 17 +++++--- pkg/backend/updates.go | 19 ++------ 10 files changed, 93 insertions(+), 97 deletions(-) diff --git a/pkg/apitype/history.go b/pkg/apitype/history.go index 76b682f51..e9af893dc 100644 --- a/pkg/apitype/history.go +++ b/pkg/apitype/history.go @@ -23,14 +23,16 @@ import "encoding/json" type UpdateKind string const ( - // DeployUpdate is the prototypical Pulumi program update. - DeployUpdate UpdateKind = "update" + // UpdateUpdate is the prototypical Pulumi program update. + UpdateUpdate UpdateKind = "update" // PreviewUpdate is a preview of an update, without impacting resources. PreviewUpdate UpdateKind = "preview" // RefreshUpdate is an update that came from a refresh operation. RefreshUpdate UpdateKind = "refresh" // DestroyUpdate is an update which removes all resources. DestroyUpdate UpdateKind = "destroy" + // ImportUpdate is an update that entails importing a raw checkpoint file. + ImportUpdate UpdateKind = "import" ) // UpdateResult is an enum for the result of the update. diff --git a/pkg/backend/backend.go b/pkg/backend/backend.go index 9db8e3aa2..3c998a8c2 100644 --- a/pkg/backend/backend.go +++ b/pkg/backend/backend.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/pulumi/pulumi/pkg/apitype" "github.com/pulumi/pulumi/pkg/engine" "github.com/pulumi/pulumi/pkg/operations" diff --git a/pkg/backend/cloud/backend.go b/pkg/backend/cloud/backend.go index a88eda99e..ad13c85c7 100644 --- a/pkg/backend/cloud/backend.go +++ b/pkg/backend/cloud/backend.go @@ -564,11 +564,11 @@ var ( previewText string text string }{ - string(client.UpdateKindPreview): {"update of", "Previewing"}, - string(client.UpdateKindUpdate): {"update of", "Updating"}, - string(client.UpdateKindRefresh): {"refresh of", "Refreshing"}, - string(client.UpdateKindDestroy): {"destroy of", "Destroying"}, - string(client.UpdateKindImport): {"import to", "Importing into"}, + string(apitype.PreviewUpdate): {"update of", "Previewing"}, + string(apitype.UpdateUpdate): {"update of", "Updating"}, + string(apitype.RefreshUpdate): {"refresh of", "Refreshing"}, + string(apitype.DestroyUpdate): {"destroy of", "Destroying"}, + string(apitype.ImportUpdate): {"import to", "Importing into"}, } ) @@ -603,14 +603,14 @@ func getStack(ctx context.Context, b *cloudBackend, stackRef backend.StackRefere return stack, nil } -func createDiff(events []engine.Event, displayOpts backend.DisplayOptions) string { +func createDiff(updateKind apitype.UpdateKind, events []engine.Event, displayOpts backend.DisplayOptions) string { buff := &bytes.Buffer{} seen := make(map[resource.URN]engine.StepEventMetadata) displayOpts.SummaryDiff = true for _, e := range events { - msg := local.RenderDiffEvent(e, seen, displayOpts) + msg := local.RenderDiffEvent(updateKind, e, seen, displayOpts) if msg != "" { if e.Type == engine.SummaryEvent { msg = "\n" + msg @@ -625,7 +625,7 @@ func createDiff(events []engine.Event, displayOpts backend.DisplayOptions) strin } func (b *cloudBackend) PreviewThenPrompt( - ctx context.Context, updateKind client.UpdateKind, stack backend.Stack, pkg *workspace.Project, root string, + ctx context.Context, updateKind apitype.UpdateKind, stack backend.Stack, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { @@ -662,7 +662,7 @@ func (b *cloudBackend) PreviewThenPrompt( } // If there are no changes, or we're auto-approving or just previewing, we can skip the confirmation prompt. - if opts.AutoApprove || updateKind == client.UpdateKindPreview { + if opts.AutoApprove || updateKind == apitype.PreviewUpdate { return changes, nil } @@ -671,7 +671,7 @@ func (b *cloudBackend) PreviewThenPrompt( } // confirmBeforeUpdating asks the user whether to proceed. A nil error means yes. -func confirmBeforeUpdating(updateKind client.UpdateKind, stack backend.Stack, +func confirmBeforeUpdating(updateKind apitype.UpdateKind, stack backend.Stack, events []engine.Event, opts backend.UpdateOptions) error { for { var response string @@ -692,10 +692,17 @@ func confirmBeforeUpdating(updateKind client.UpdateKind, stack backend.Stack, previewWarning = colors.SpecWarning + " without a preview" + colors.BrightWhite } + // Create a prompt. If this is a refresh, we'll add some extra text so it's clear we aren't updating resources. + prompt := "\b" + opts.Display.Color.Colorize( + colors.BrightWhite+fmt.Sprintf("Do you want to perform this %s%s?", + updateKind, previewWarning)+colors.Reset) + if updateKind == apitype.RefreshUpdate { + prompt += "\nNo resources will be modified as part of this refresh; just your stack's state will be." + } + + // Now prompt the user for a yes, no, or details, and then proceed accordingly. if err := survey.AskOne(&survey.Select{ - Message: "\b" + opts.Display.Color.Colorize( - colors.BrightWhite+fmt.Sprintf("Do you want to perform this %s%s?", - updateKind, previewWarning)+colors.Reset), + Message: prompt, Options: choices, Default: string(no), }, &response, nil); err != nil { @@ -711,7 +718,7 @@ func confirmBeforeUpdating(updateKind client.UpdateKind, stack backend.Stack, } if response == string(details) { - diff := createDiff(events, opts.Display) + diff := createDiff(updateKind, events, opts.Display) _, err := os.Stdout.WriteString(diff + "\n\n") contract.IgnoreError(err) continue @@ -720,7 +727,7 @@ func confirmBeforeUpdating(updateKind client.UpdateKind, stack backend.Stack, } func (b *cloudBackend) PreviewThenPromptThenExecute( - ctx context.Context, updateKind client.UpdateKind, stackRef backend.StackReference, pkg *workspace.Project, + ctx context.Context, updateKind apitype.UpdateKind, stackRef backend.StackReference, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { @@ -732,7 +739,7 @@ func (b *cloudBackend) PreviewThenPromptThenExecute( // Preview the operation to the user and ask them if they want to proceed. changes, err := b.PreviewThenPrompt(ctx, updateKind, stack, pkg, root, m, opts, scopes) - if err != nil || updateKind == client.UpdateKindPreview { + if err != nil || updateKind == apitype.PreviewUpdate { return changes, err } @@ -757,30 +764,30 @@ func (b *cloudBackend) Preview(ctx context.Context, stackRef backend.StackRefere // We can skip PreviewtTenPromptThenExecute, and just go straight to Execute. return b.updateStack( - ctx, client.UpdateKindPreview, stack, pkg, root, m, opts, nil, + ctx, apitype.PreviewUpdate, stack, pkg, root, m, opts, nil, true /*dryRun*/, persist, scopes) } func (b *cloudBackend) Update(ctx context.Context, stackRef backend.StackReference, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.PreviewThenPromptThenExecute(ctx, client.UpdateKindUpdate, stackRef, pkg, root, m, opts, scopes) + return b.PreviewThenPromptThenExecute(ctx, apitype.UpdateUpdate, stackRef, pkg, root, m, opts, scopes) } func (b *cloudBackend) Refresh(ctx context.Context, stackRef backend.StackReference, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.PreviewThenPromptThenExecute(ctx, client.UpdateKindRefresh, stackRef, pkg, root, m, opts, scopes) + return b.PreviewThenPromptThenExecute(ctx, apitype.RefreshUpdate, stackRef, pkg, root, m, opts, scopes) } func (b *cloudBackend) Destroy(ctx context.Context, stackRef backend.StackReference, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.PreviewThenPromptThenExecute(ctx, client.UpdateKindDestroy, stackRef, pkg, root, m, opts, scopes) + return b.PreviewThenPromptThenExecute(ctx, apitype.DestroyUpdate, stackRef, pkg, root, m, opts, scopes) } func (b *cloudBackend) createAndStartUpdate( - ctx context.Context, action client.UpdateKind, stackRef backend.StackReference, + ctx context.Context, action apitype.UpdateKind, stackRef backend.StackReference, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, dryRun bool) (client.UpdateIdentifier, int, string, error) { @@ -821,7 +828,7 @@ func (b *cloudBackend) createAndStartUpdate( return client.UpdateIdentifier{}, 0, "", err } // Any non-preview update will be considered part of the stack's update history. - if action != client.UpdateKindPreview { + if action != apitype.PreviewUpdate { logging.V(7).Infof("Stack %s being updated to version %d", stackRef, version) } @@ -830,7 +837,7 @@ func (b *cloudBackend) createAndStartUpdate( // updateStack performs a the provided type of update on a stack hosted in the Pulumi Cloud. func (b *cloudBackend) updateStack( - ctx context.Context, action client.UpdateKind, stack backend.Stack, pkg *workspace.Project, + ctx context.Context, action apitype.UpdateKind, stack backend.Stack, pkg *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, callerEventsOpt chan<- engine.Event, dryRun bool, persist bool, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { @@ -905,7 +912,7 @@ func getUpdateContents( } func (b *cloudBackend) runEngineAction( - ctx context.Context, action client.UpdateKind, stackRef backend.StackReference, pkg *workspace.Project, + ctx context.Context, action apitype.UpdateKind, stackRef backend.StackReference, pkg *workspace.Project, root string, opts backend.UpdateOptions, update client.UpdateIdentifier, token string, callerEventsOpt chan<- engine.Event, dryRun bool, persist bool, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { @@ -920,7 +927,8 @@ func (b *cloudBackend) runEngineAction( displayEvents := make(chan engine.Event) displayDone := make(chan bool) - go u.RecordAndDisplayEvents(getActionLabel(string(action), dryRun), displayEvents, displayDone, opts.Display) + go u.RecordAndDisplayEvents( + getActionLabel(string(action), dryRun), action, displayEvents, displayDone, opts.Display) engineEvents := make(chan engine.Event) @@ -950,13 +958,13 @@ func (b *cloudBackend) runEngineAction( } switch action { - case client.UpdateKindPreview: + case apitype.PreviewUpdate: changes, err = engine.Update(u, engineCtx, opts.Engine, true) - case client.UpdateKindUpdate: + case apitype.UpdateUpdate: changes, err = engine.Update(u, engineCtx, opts.Engine, dryRun) - case client.UpdateKindRefresh: + case apitype.RefreshUpdate: changes, err = engine.Refresh(u, engineCtx, opts.Engine, dryRun) - case client.UpdateKindDestroy: + case apitype.DestroyUpdate: changes, err = engine.Destroy(u, engineCtx, opts.Engine, dryRun) default: contract.Failf("Unrecognized action type: %s", action) @@ -1002,7 +1010,7 @@ func (b *cloudBackend) CancelCurrentUpdate(ctx context.Context, stackRef backend // NOTE: the update kind is not relevant; the same endpoint will work for updates of all kinds. updateID := client.UpdateIdentifier{ StackIdentifier: stackID, - UpdateKind: client.UpdateKindUpdate, + UpdateKind: apitype.UpdateUpdate, UpdateID: stack.ActiveUpdate, } return b.client.CancelUpdate(ctx, updateID) @@ -1029,7 +1037,7 @@ func (b *cloudBackend) GetHistory(ctx context.Context, stackRef backend.StackRef } beUpdates = append(beUpdates, backend.UpdateInfo{ - Kind: backend.UpdateKind(update.Kind), + Kind: update.Kind, Message: update.Message, Environment: update.Environment, Config: cfg, diff --git a/pkg/backend/cloud/client/api.go b/pkg/backend/cloud/client/api.go index 0c3828e86..e1c20f874 100644 --- a/pkg/backend/cloud/client/api.go +++ b/pkg/backend/cloud/client/api.go @@ -36,17 +36,6 @@ import ( "github.com/pulumi/pulumi/pkg/version" ) -// UpdateKind is an enum for describing the kinds of updates we support. -type UpdateKind string - -const ( - UpdateKindUpdate UpdateKind = "update" - UpdateKindPreview UpdateKind = "preview" - UpdateKindRefresh UpdateKind = "refresh" - UpdateKindDestroy UpdateKind = "destroy" - UpdateKindImport UpdateKind = "import" -) - // StackIdentifier is the set of data needed to identify a Pulumi Cloud stack. type StackIdentifier struct { Owner string @@ -57,7 +46,7 @@ type StackIdentifier struct { type UpdateIdentifier struct { StackIdentifier - UpdateKind UpdateKind + UpdateKind apitype.UpdateKind UpdateID string } diff --git a/pkg/backend/cloud/client/client.go b/pkg/backend/cloud/client/client.go index a33c9f8b7..211b05444 100644 --- a/pkg/backend/cloud/client/client.go +++ b/pkg/backend/cloud/client/client.go @@ -90,7 +90,7 @@ func getStackPath(stack StackIdentifier, components ...string) string { // getUpdatePath returns the API path to for the given stack with the given components joined with path separators // and appended to the update root. func getUpdatePath(update UpdateIdentifier, components ...string) string { - components = append([]string{string(update.UpdateKind), update.UpdateID}, components...) + components = append([]string{string(apitype.UpdateUpdate), update.UpdateID}, components...) return getStackPath(update.StackIdentifier, components...) } @@ -322,7 +322,7 @@ func (pc *Client) ImportStackDeployment(ctx context.Context, stack StackIdentifi return UpdateIdentifier{ StackIdentifier: stack, - UpdateKind: UpdateKindUpdate, + UpdateKind: apitype.UpdateUpdate, UpdateID: resp.UpdateID, }, nil } @@ -331,7 +331,7 @@ func (pc *Client) ImportStackDeployment(ctx context.Context, stack StackIdentifi // requires that the Pulumi program is uploaded, the provided getContents callback will be invoked to fetch the // contents of the Pulumi program. func (pc *Client) CreateUpdate( - ctx context.Context, kind UpdateKind, stack StackIdentifier, pkg *workspace.Project, cfg config.Map, + ctx context.Context, kind apitype.UpdateKind, stack StackIdentifier, pkg *workspace.Project, cfg config.Map, main string, m apitype.UpdateMetadata, opts engine.UpdateOptions, dryRun bool, getContents func() (io.ReadCloser, int64, error)) (UpdateIdentifier, error) { @@ -373,13 +373,13 @@ func (pc *Client) CreateUpdate( // Create the initial update object. var endpoint string switch kind { - case UpdateKindUpdate: + case apitype.UpdateUpdate: endpoint = "update" - case UpdateKindPreview: + case apitype.PreviewUpdate: endpoint = "preview" - case UpdateKindRefresh: + case apitype.RefreshUpdate: endpoint = "refresh" - case UpdateKindDestroy: + case apitype.DestroyUpdate: endpoint = "destroy" default: contract.Failf("Unknown kind: %s", kind) @@ -392,7 +392,7 @@ func (pc *Client) CreateUpdate( } // Now upload the program if necessary. - if kind != UpdateKindDestroy && updateResponse.UploadURL != "" { + if kind != apitype.DestroyUpdate && updateResponse.UploadURL != "" { uploadURL, err := url.Parse(updateResponse.UploadURL) if err != nil { return UpdateIdentifier{}, errors.Wrap(err, "parsing upload URL") diff --git a/pkg/backend/cloud/state.go b/pkg/backend/cloud/state.go index a673b80e0..6e3a347fb 100644 --- a/pkg/backend/cloud/state.go +++ b/pkg/backend/cloud/state.go @@ -137,7 +137,7 @@ func (u *cloudUpdate) Complete(status apitype.UpdateStatus) error { } func (u *cloudUpdate) recordEvent( - event engine.Event, seen map[resource.URN]engine.StepEventMetadata, + action apitype.UpdateKind, event engine.Event, seen map[resource.URN]engine.StepEventMetadata, opts backend.DisplayOptions) error { // If we don't have a token source, we can't perform any mutations. @@ -158,7 +158,7 @@ func (u *cloudUpdate) recordEvent( // Ensure we render events with raw colorization tags. Also, render these as 'diff' events so // the user has a rich diff-log they can see when the look at their logs in the service. opts.Color = colors.Raw - msg := local.RenderDiffEvent(event, seen, opts) + msg := local.RenderDiffEvent(action, event, seen, opts) if msg == "" { return nil } @@ -173,13 +173,13 @@ func (u *cloudUpdate) recordEvent( return u.backend.client.AppendUpdateLogEntry(u.context, u.update, kind, fields, token) } -func (u *cloudUpdate) RecordAndDisplayEvents(action string, +func (u *cloudUpdate) RecordAndDisplayEvents(op string, action apitype.UpdateKind, events <-chan engine.Event, done chan<- bool, opts backend.DisplayOptions) { // Start the local display processor. Display things however the options have been // set to display (i.e. diff vs progress). displayEvents := make(chan engine.Event) - go local.DisplayEvents(action, displayEvents, done, opts) + go local.DisplayEvents(op, action, displayEvents, done, opts) seen := make(map[resource.URN]engine.StepEventMetadata) for e := range events { @@ -187,7 +187,7 @@ func (u *cloudUpdate) RecordAndDisplayEvents(action string, displayEvents <- e // Then render and record the event for posterity. - if err := u.recordEvent(e, seen, opts); err != nil { + if err := u.recordEvent(action, e, seen, opts); err != nil { diagEvent := engine.Event{ Type: engine.DiagEvent, Payload: engine.DiagEventPayload{ diff --git a/pkg/backend/local/backend.go b/pkg/backend/local/backend.go index ad9c41304..461537cba 100644 --- a/pkg/backend/local/backend.go +++ b/pkg/backend/local/backend.go @@ -206,7 +206,7 @@ func (b *localBackend) GetLatestConfiguration(ctx context.Context, func (b *localBackend) Preview( _ context.Context, stackRef backend.StackReference, proj *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.performEngineOp("previewing", backend.PreviewUpdate, + return b.performEngineOp("previewing", apitype.PreviewUpdate, stackRef.StackName(), proj, root, m, opts, scopes, engine.Update) } @@ -225,27 +225,27 @@ func (b *localBackend) Update( if err = backend.ValidateStackProperties(string(stackName), tags); err != nil { return nil, errors.Wrap(err, "validating stack properties") } - return b.performEngineOp("updating", backend.DeployUpdate, + return b.performEngineOp("updating", apitype.UpdateUpdate, stackName, proj, root, m, opts, scopes, engine.Update) } func (b *localBackend) Refresh( _ context.Context, stackRef backend.StackReference, proj *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.performEngineOp("refreshing", backend.RefreshUpdate, + return b.performEngineOp("refreshing", apitype.RefreshUpdate, stackRef.StackName(), proj, root, m, opts, scopes, engine.Refresh) } func (b *localBackend) Destroy( _ context.Context, stackRef backend.StackReference, proj *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource) (engine.ResourceChanges, error) { - return b.performEngineOp("destroying", backend.DestroyUpdate, + return b.performEngineOp("destroying", apitype.DestroyUpdate, stackRef.StackName(), proj, root, m, opts, scopes, engine.Destroy) } type engineOpFunc func(engine.UpdateInfo, *engine.Context, engine.UpdateOptions, bool) (engine.ResourceChanges, error) -func (b *localBackend) performEngineOp(op string, kind backend.UpdateKind, +func (b *localBackend) performEngineOp(op string, kind apitype.UpdateKind, stackName tokens.QName, proj *workspace.Project, root string, m backend.UpdateMetadata, opts backend.UpdateOptions, scopes backend.CancellationScopeSource, performEngineOp engineOpFunc) (engine.ResourceChanges, error) { @@ -255,13 +255,13 @@ func (b *localBackend) performEngineOp(op string, kind backend.UpdateKind, } events := make(chan engine.Event) - dryRun := (kind == backend.PreviewUpdate) + dryRun := (kind == apitype.PreviewUpdate) cancelScope := scopes.NewScope(events, dryRun) defer cancelScope.Close() done := make(chan bool) - go DisplayEvents(op, events, done, opts.Display) + go DisplayEvents(op, kind, events, done, opts.Display) // Create the management machinery. persister := b.newSnapshotPersister(stackName) diff --git a/pkg/backend/local/display.go b/pkg/backend/local/display.go index bdca01a00..76c8e1fdd 100644 --- a/pkg/backend/local/display.go +++ b/pkg/backend/local/display.go @@ -24,6 +24,7 @@ import ( "strconv" "time" + "github.com/pulumi/pulumi/pkg/apitype" "github.com/pulumi/pulumi/pkg/backend" "github.com/pulumi/pulumi/pkg/diag" "github.com/pulumi/pulumi/pkg/diag/colors" @@ -38,13 +39,13 @@ import ( // it comes in. Once all events have been read from the channel and displayed, it closes the `done` // channel so the caller can await all the events being written. func DisplayEvents( - action string, events <-chan engine.Event, + op string, action apitype.UpdateKind, events <-chan engine.Event, done chan<- bool, opts backend.DisplayOptions) { if opts.DiffDisplay { - DisplayDiffEvents(action, events, done, opts) + DisplayDiffEvents(op, action, events, done, opts) } else { - DisplayProgressEvents(action, events, done, opts) + DisplayProgressEvents(op, action, events, done, opts) } } @@ -58,10 +59,10 @@ func (s *nopSpinner) Reset() { } // DisplayDiffEvents displays the engine events with the diff view. -func DisplayDiffEvents(action string, +func DisplayDiffEvents(op string, action apitype.UpdateKind, events <-chan engine.Event, done chan<- bool, opts backend.DisplayOptions) { - prefix := fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), action) + prefix := fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), op) var spinner cmdutil.Spinner var ticker *time.Ticker @@ -96,7 +97,7 @@ func DisplayDiffEvents(action string, } } - msg := RenderDiffEvent(event, seen, opts) + msg := RenderDiffEvent(action, event, seen, opts) if msg != "" && out != nil { fprintIgnoreError(out, msg) } @@ -108,8 +109,8 @@ func DisplayDiffEvents(action string, } } -func RenderDiffEvent( - event engine.Event, seen map[resource.URN]engine.StepEventMetadata, opts backend.DisplayOptions) string { +func RenderDiffEvent(action apitype.UpdateKind, event engine.Event, + seen map[resource.URN]engine.StepEventMetadata, opts backend.DisplayOptions) string { switch event.Type { case engine.CancelEvent: @@ -117,7 +118,7 @@ func RenderDiffEvent( case engine.PreludeEvent: return renderPreludeEvent(event.Payload.(engine.PreludeEventPayload), opts) case engine.SummaryEvent: - return renderSummaryEvent(event.Payload.(engine.SummaryEventPayload), opts) + return renderSummaryEvent(action, event.Payload.(engine.SummaryEventPayload), opts) case engine.ResourceOperationFailed: return renderResourceOperationFailedEvent(event.Payload.(engine.ResourceOperationFailedPayload), opts) case engine.ResourceOutputsEvent: @@ -147,7 +148,8 @@ func renderStdoutColorEvent( return opts.Color.Colorize(payload.Message) } -func renderSummaryEvent(event engine.SummaryEventPayload, opts backend.DisplayOptions) string { +func renderSummaryEvent( + action apitype.UpdateKind, event engine.SummaryEventPayload, opts backend.DisplayOptions) string { changes := event.ResourceChanges changeCount := 0 @@ -159,6 +161,8 @@ func renderSummaryEvent(event engine.SummaryEventPayload, opts backend.DisplayOp var kind string if event.IsPreview { kind = "previewed" + } else if action == apitype.RefreshUpdate { + kind = "refreshed" } else { kind = "performed" } diff --git a/pkg/backend/local/progress.go b/pkg/backend/local/progress.go index a4f3d8e0b..344736f37 100644 --- a/pkg/backend/local/progress.go +++ b/pkg/backend/local/progress.go @@ -25,6 +25,10 @@ import ( "unicode" "unicode/utf8" + "github.com/docker/docker/pkg/term" + "golang.org/x/crypto/ssh/terminal" + + "github.com/pulumi/pulumi/pkg/apitype" "github.com/pulumi/pulumi/pkg/backend" "github.com/pulumi/pulumi/pkg/diag" "github.com/pulumi/pulumi/pkg/diag/colors" @@ -34,9 +38,6 @@ import ( "github.com/pulumi/pulumi/pkg/tokens" "github.com/pulumi/pulumi/pkg/util/cmdutil" "github.com/pulumi/pulumi/pkg/util/contract" - - "github.com/docker/docker/pkg/term" - "golang.org/x/crypto/ssh/terminal" ) // Progress describes a message we want to show in the display. There are two types of messages, @@ -86,6 +87,9 @@ type ProgressDisplay struct { opts backend.DisplayOptions progressOutput chan<- Progress + // action is the kind of action (preview, update, refresh, etc) being performed. + action apitype.UpdateKind + // Whether or not we're previewing. We don't know what we are actually doing until // we get the initial 'prelude' event. // @@ -223,14 +227,14 @@ func (display *ProgressDisplay) writeBlankLine() { // DisplayProgressEvents displays the engine events with docker's progress view. func DisplayProgressEvents( - action string, events <-chan engine.Event, + op string, action apitype.UpdateKind, events <-chan engine.Event, done chan<- bool, opts backend.DisplayOptions) { // Create a ticker that will update all our status messages once a second. Any // in-flight resources will get a varying . .. ... ticker appended to them to // let the user know what is still being worked on. spinner, ticker := cmdutil.NewSpinnerAndTicker( - fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), action), + fmt.Sprintf("%s%s...", cmdutil.EmojiOr("✨ ", "@ "), op), nil, 1 /*timesPerSecond*/) // The channel we push progress messages into, and which DisplayProgressToStream pulls @@ -238,6 +242,7 @@ func DisplayProgressEvents( progressOutput := make(chan Progress) display := &ProgressDisplay{ + action: action, opts: opts, progressOutput: progressOutput, eventUrnToResourceRow: make(map[resource.URN]ResourceRow), @@ -718,7 +723,7 @@ func (display *ProgressDisplay) processEndSteps() { // print the summary if display.summaryEventPayload != nil { - msg := renderSummaryEvent(*display.summaryEventPayload, display.opts) + msg := renderSummaryEvent(display.action, *display.summaryEventPayload, display.opts) if !wroteDiagnosticHeader { display.writeBlankLine() diff --git a/pkg/backend/updates.go b/pkg/backend/updates.go index b37dbe0bf..51e8e0f84 100644 --- a/pkg/backend/updates.go +++ b/pkg/backend/updates.go @@ -15,6 +15,7 @@ package backend import ( + "github.com/pulumi/pulumi/pkg/apitype" "github.com/pulumi/pulumi/pkg/engine" "github.com/pulumi/pulumi/pkg/resource/config" ) @@ -28,20 +29,6 @@ type UpdateMetadata struct { Environment map[string]string `json:"environment"` } -// UpdateKind is an enum for the type of update performed. -type UpdateKind string - -const ( - // DeployUpdate is the prototypical Pulumi program update. - DeployUpdate UpdateKind = "update" - // PreviewUpdate is a preview of an update, without impacting resources. - PreviewUpdate UpdateKind = "preview" - // RefreshUpdate is an update that adopts a cloud's existing resource state. - RefreshUpdate UpdateKind = "refresh" - // DestroyUpdate is an update which removes all resources. - DestroyUpdate UpdateKind = "destroy" -) - // UpdateResult is an enum for the result of the update. type UpdateResult string @@ -92,8 +79,8 @@ const ( // UpdateInfo describes a previous update. type UpdateInfo struct { // Information known before an update is started. - Kind UpdateKind `json:"kind"` - StartTime int64 `json:"startTime"` + Kind apitype.UpdateKind `json:"kind"` + StartTime int64 `json:"startTime"` // Message is an optional message associated with the update. Message string `json:"message"`