github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/backend/display/display.go (about) 1 // Copyright 2016-2018, Pulumi Corporation. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package display 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "os" 22 "time" 23 24 "github.com/pulumi/pulumi/pkg/v3/engine" 25 "github.com/pulumi/pulumi/pkg/v3/resource/deploy" 26 "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" 27 "github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors" 28 "github.com/pulumi/pulumi/sdk/v3/go/common/resource" 29 "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" 30 "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" 31 "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" 32 "github.com/pulumi/pulumi/sdk/v3/go/common/util/logging" 33 ) 34 35 // ShowEvents reads events from the `events` channel until it is closed, displaying each event as 36 // it comes in. Once all events have been read from the channel and displayed, it closes the `done` 37 // channel so the caller can await all the events being written. 38 func ShowEvents( 39 op string, action apitype.UpdateKind, stack tokens.Name, proj tokens.PackageName, 40 events <-chan engine.Event, done chan<- bool, opts Options, isPreview bool) { 41 42 if opts.EventLogPath != "" { 43 events, done = startEventLogger(events, done, opts) 44 } 45 46 streamPreview := cmdutil.IsTruthy(os.Getenv("PULUMI_ENABLE_STREAMING_JSON_PREVIEW")) 47 48 if opts.JSONDisplay { 49 if isPreview && !streamPreview { 50 ShowPreviewDigest(events, done, opts) 51 } else { 52 ShowJSONEvents(events, done, opts) 53 } 54 return 55 } 56 57 switch opts.Type { 58 case DisplayDiff: 59 ShowDiffEvents(op, events, done, opts) 60 case DisplayProgress: 61 ShowProgressEvents(op, action, stack, proj, events, done, opts, isPreview) 62 case DisplayQuery: 63 contract.Failf("DisplayQuery can only be used in query mode, which should be invoked " + 64 "directly instead of through ShowEvents") 65 case DisplayWatch: 66 ShowWatchEvents(op, events, done, opts) 67 default: 68 contract.Failf("Unknown display type %d", opts.Type) 69 } 70 } 71 72 func logJSONEvent(encoder *json.Encoder, event engine.Event, opts Options, seq int) error { 73 apiEvent, err := ConvertEngineEvent(event, false /* showSecrets */) 74 if err != nil { 75 return err 76 } 77 78 apiEvent.Sequence = seq 79 apiEvent.Timestamp = int(time.Now().Unix()) 80 // If opts.Color == "never" (i.e. NO_COLOR is specified or --color=never), clean up the color directives 81 // from the emitted events. 82 if opts.Color == colors.Never { 83 switch { 84 case apiEvent.DiagnosticEvent != nil: 85 apiEvent.DiagnosticEvent.Message = colors.Never.Colorize(apiEvent.DiagnosticEvent.Message) 86 apiEvent.DiagnosticEvent.Prefix = colors.Never.Colorize(apiEvent.DiagnosticEvent.Prefix) 87 apiEvent.DiagnosticEvent.Color = string(colors.Never) 88 case apiEvent.StdoutEvent != nil: 89 apiEvent.StdoutEvent.Message = colors.Never.Colorize(apiEvent.StdoutEvent.Message) 90 apiEvent.StdoutEvent.Color = string(colors.Never) 91 case apiEvent.PolicyEvent != nil: 92 apiEvent.PolicyEvent.Message = colors.Never.Colorize(apiEvent.PolicyEvent.Message) 93 apiEvent.PolicyEvent.Color = string(colors.Never) 94 } 95 } 96 97 return encoder.Encode(apiEvent) 98 } 99 100 func startEventLogger(events <-chan engine.Event, done chan<- bool, opts Options) (<-chan engine.Event, chan<- bool) { 101 // Before moving further, attempt to open the log file. 102 logFile, err := os.Create(opts.EventLogPath) 103 if err != nil { 104 logging.V(7).Infof("could not create event log: %v", err) 105 return events, done 106 } 107 108 outEvents, outDone := make(chan engine.Event), make(chan bool) 109 go func() { 110 defer close(done) 111 defer func() { 112 contract.IgnoreError(logFile.Close()) 113 }() 114 115 sequence := 0 116 encoder := json.NewEncoder(logFile) 117 encoder.SetEscapeHTML(false) 118 for e := range events { 119 if err = logJSONEvent(encoder, e, opts, sequence); err != nil { 120 logging.V(7).Infof("failed to log event: %v", err) 121 } 122 sequence++ 123 124 outEvents <- e 125 126 if e.Type == engine.CancelEvent { 127 break 128 } 129 } 130 131 <-outDone 132 }() 133 134 return outEvents, outDone 135 } 136 137 type nopSpinner struct { 138 } 139 140 func (s *nopSpinner) Tick() { 141 } 142 143 func (s *nopSpinner) Reset() { 144 } 145 146 // isRootStack returns true if the step pertains to the rootmost stack component. 147 func isRootStack(step engine.StepEventMetadata) bool { 148 return isRootURN(step.URN) 149 } 150 151 func isRootURN(urn resource.URN) bool { 152 return urn != "" && urn.Type() == resource.RootStackType 153 } 154 155 // shouldShow returns true if a step should show in the output. 156 func shouldShow(step engine.StepEventMetadata, opts Options) bool { 157 // For certain operations, whether they are tracked is controlled by flags (to cut down on superfluous output). 158 if step.Op == deploy.OpSame { 159 // If the op is the same, it is possible that the resource's metadata changed. In that case, still show it. 160 if step.Old.Protect != step.New.Protect { 161 return true 162 } 163 return opts.ShowSameResources 164 } 165 166 // For logical replacement operations, only show them during progress-style updates (since this is integrated 167 // into the resource status update), or if it is requested explicitly (for diffs and JSON outputs). 168 if (opts.Type == DisplayDiff || opts.JSONDisplay) && !step.Logical && !opts.ShowReplacementSteps { 169 return false 170 } 171 172 // Otherwise, default to showing the operation. 173 return true 174 } 175 176 func fprintfIgnoreError(w io.Writer, format string, a ...interface{}) { 177 _, err := fmt.Fprintf(w, format, a...) 178 contract.IgnoreError(err) 179 } 180 181 func fprintIgnoreError(w io.Writer, a ...interface{}) { 182 _, err := fmt.Fprint(w, a...) 183 contract.IgnoreError(err) 184 }