2018-03-21 20:43:21 +01:00
|
|
|
// Copyright 2018, Pulumi Corporation. All rights reserved.
|
2017-10-05 23:08:46 +02:00
|
|
|
|
|
|
|
package engine
|
|
|
|
|
|
|
|
import (
|
2018-03-05 20:39:50 +01:00
|
|
|
"bytes"
|
|
|
|
"regexp"
|
2018-02-02 06:15:09 +01:00
|
|
|
"time"
|
2017-10-05 23:08:46 +02:00
|
|
|
|
|
|
|
"github.com/pulumi/pulumi/pkg/diag"
|
2017-12-18 20:42:32 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/diag/colors"
|
2018-02-04 10:18:06 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource"
|
2018-01-31 22:07:40 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource/config"
|
2018-02-04 10:18:06 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/resource/deploy"
|
|
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
2018-01-31 22:07:40 +01:00
|
|
|
"github.com/pulumi/pulumi/pkg/util/contract"
|
2017-10-05 23:08:46 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Event represents an event generated by the engine during an operation. The underlying
|
|
|
|
// type for the `Payload` field will differ depending on the value of the `Type` field
|
|
|
|
type Event struct {
|
2017-10-21 02:28:35 +02:00
|
|
|
Type EventType
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload interface{}
|
|
|
|
}
|
|
|
|
|
2017-10-21 02:28:35 +02:00
|
|
|
// EventType is the kind of event being emitted.
|
|
|
|
type EventType string
|
|
|
|
|
|
|
|
const (
|
2018-02-04 10:18:06 +01:00
|
|
|
CancelEvent EventType = "cancel"
|
|
|
|
StdoutColorEvent EventType = "stdoutcolor"
|
|
|
|
DiagEvent EventType = "diag"
|
|
|
|
PreludeEvent EventType = "prelude"
|
|
|
|
SummaryEvent EventType = "summary"
|
2018-03-09 18:59:57 +01:00
|
|
|
ResourcePreEvent EventType = "resource-pre"
|
|
|
|
ResourceOutputsEvent EventType = "resource-outputs"
|
|
|
|
ResourceOperationFailed EventType = "resource-operationfailed"
|
2017-10-21 02:28:35 +02:00
|
|
|
)
|
|
|
|
|
2017-10-23 00:52:00 +02:00
|
|
|
func cancelEvent() Event {
|
|
|
|
return Event{Type: CancelEvent}
|
|
|
|
}
|
|
|
|
|
2017-10-05 23:08:46 +02:00
|
|
|
// DiagEventPayload is the payload for an event with type `diag`
|
|
|
|
type DiagEventPayload struct {
|
|
|
|
Message string
|
2017-12-18 20:42:32 +01:00
|
|
|
Color colors.Colorization
|
|
|
|
Severity diag.Severity
|
2017-10-05 23:08:46 +02:00
|
|
|
}
|
|
|
|
|
2017-12-14 20:53:02 +01:00
|
|
|
type StdoutEventPayload struct {
|
|
|
|
Message string
|
2017-12-18 20:42:32 +01:00
|
|
|
Color colors.Colorization
|
2017-10-05 23:08:46 +02:00
|
|
|
}
|
|
|
|
|
2018-01-31 22:07:40 +01:00
|
|
|
type PreludeEventPayload struct {
|
|
|
|
IsPreview bool // true if this prelude is for a plan operation
|
|
|
|
Config map[string]string // the keys and values for config. For encrypted config, the values may be blinded
|
|
|
|
}
|
|
|
|
|
2018-02-02 06:15:09 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-02-04 10:18:06 +01:00
|
|
|
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).
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func makeEventEmitter(events chan<- Event, update Update) eventEmitter {
|
|
|
|
var f filter = &nopFilter{}
|
|
|
|
|
|
|
|
target := update.GetTarget()
|
|
|
|
if target.Config.HasSecureValue() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
for _, v := range target.Config {
|
|
|
|
if !v.Secure() {
|
|
|
|
continue
|
|
|
|
}
|
2018-03-10 04:33:00 +01:00
|
|
|
secret, err := v.Value(target.Decrypter)
|
|
|
|
contract.AssertNoError(err)
|
|
|
|
|
|
|
|
// For short secrets, don't actually add them to the filter, this is a trade-off we make to prevent
|
|
|
|
// displaying `[secret]`. Travis does a similar thing, for example.
|
|
|
|
if len(secret) < 3 {
|
|
|
|
continue
|
|
|
|
}
|
2018-03-05 20:39:50 +01:00
|
|
|
if b.Len() > 0 {
|
|
|
|
b.WriteRune('|')
|
|
|
|
}
|
|
|
|
|
|
|
|
b.WriteString(regexp.QuoteMeta(secret))
|
|
|
|
}
|
2018-03-10 04:33:00 +01:00
|
|
|
if b.Len() > 0 {
|
|
|
|
f = ®exFilter{re: regexp.MustCompile(b.String())}
|
|
|
|
}
|
2018-03-05 20:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return eventEmitter{
|
|
|
|
Chan: events,
|
|
|
|
Filter: f,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type eventEmitter struct {
|
|
|
|
Chan chan<- Event
|
|
|
|
Filter filter
|
|
|
|
}
|
|
|
|
|
2018-02-04 10:18:06 +01:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
type filter interface {
|
|
|
|
Filter(s string) string
|
|
|
|
}
|
|
|
|
|
|
|
|
type nopFilter struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *nopFilter) Filter(s string) string {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
type regexFilter struct {
|
|
|
|
re *regexp.Regexp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *regexFilter) Filter(s string) string {
|
|
|
|
return f.re.ReplaceAllLiteralString(s, "[secret]")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *eventEmitter) resourceOperationFailedEvent(step deploy.Step, status resource.Status, steps int) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2018-02-04 10:18:06 +01:00
|
|
|
Type: ResourceOperationFailed,
|
|
|
|
Payload: ResourceOperationFailedPayload{
|
|
|
|
Metadata: makeStepEventMetadata(step),
|
|
|
|
Status: status,
|
|
|
|
Steps: steps,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) resourceOutputsEvent(step deploy.Step, indent int, text string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2018-02-04 10:18:06 +01:00
|
|
|
Type: ResourceOutputsEvent,
|
|
|
|
Payload: ResourceOutputsEventPayload{
|
|
|
|
Metadata: makeStepEventMetadata(step),
|
|
|
|
Indent: indent,
|
2018-03-05 20:39:50 +01:00
|
|
|
Text: e.Filter.Filter(text),
|
2018-02-04 10:18:06 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) resourcePreEvent(step deploy.Step, indent int, summary string, details string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2018-02-04 10:18:06 +01:00
|
|
|
Type: ResourcePreEvent,
|
|
|
|
Payload: ResourcePreEventPayload{
|
|
|
|
Metadata: makeStepEventMetadata(step),
|
|
|
|
Indent: indent,
|
2018-03-05 20:39:50 +01:00
|
|
|
Summary: e.Filter.Filter(summary),
|
|
|
|
Details: e.Filter.Filter(details),
|
2018-02-04 10:18:06 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) preludeEvent(isPreview bool, cfg config.Map) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
2018-01-31 22:07:40 +01:00
|
|
|
configStringMap := make(map[string]string, len(cfg))
|
|
|
|
for k, v := range cfg {
|
|
|
|
keyString := k.String()
|
|
|
|
valueString, err := v.Value(config.NewBlindingDecrypter())
|
|
|
|
contract.AssertNoError(err)
|
|
|
|
configStringMap[keyString] = valueString
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
e.Chan <- Event{
|
2018-01-31 22:07:40 +01:00
|
|
|
Type: PreludeEvent,
|
|
|
|
Payload: PreludeEventPayload{
|
|
|
|
IsPreview: isPreview,
|
|
|
|
Config: configStringMap,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) previewSummaryEvent(resourceChanges ResourceChanges) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2018-02-02 06:15:09 +01:00
|
|
|
Type: SummaryEvent,
|
|
|
|
Payload: SummaryEventPayload{
|
|
|
|
IsPreview: true,
|
|
|
|
MaybeCorrupt: false,
|
|
|
|
Duration: 0,
|
|
|
|
ResourceChanges: resourceChanges,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) updateSummaryEvent(maybeCorrupt bool,
|
|
|
|
duration time.Duration, resourceChanges ResourceChanges) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2018-02-02 06:15:09 +01:00
|
|
|
Type: SummaryEvent,
|
|
|
|
Payload: SummaryEventPayload{
|
|
|
|
IsPreview: false,
|
|
|
|
MaybeCorrupt: maybeCorrupt,
|
|
|
|
Duration: duration,
|
|
|
|
ResourceChanges: resourceChanges,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) diagDebugEvent(msg string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2017-10-21 02:28:35 +02:00
|
|
|
Type: DiagEvent,
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload: DiagEventPayload{
|
2018-03-05 20:39:50 +01:00
|
|
|
Message: e.Filter.Filter(msg),
|
2018-01-31 18:41:42 +01:00
|
|
|
Color: colors.Raw,
|
2017-12-18 20:42:32 +01:00
|
|
|
Severity: diag.Debug,
|
2017-10-05 23:08:46 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) diagInfoEvent(msg string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2017-10-21 02:28:35 +02:00
|
|
|
Type: DiagEvent,
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload: DiagEventPayload{
|
2018-03-05 20:39:50 +01:00
|
|
|
Message: e.Filter.Filter(msg),
|
2018-01-31 18:41:42 +01:00
|
|
|
Color: colors.Raw,
|
2017-12-18 20:42:32 +01:00
|
|
|
Severity: diag.Info,
|
2017-10-05 23:08:46 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) diagInfoerrEvent(msg string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2017-10-21 02:28:35 +02:00
|
|
|
Type: DiagEvent,
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload: DiagEventPayload{
|
2018-03-05 20:39:50 +01:00
|
|
|
Message: e.Filter.Filter(msg),
|
2018-01-31 18:41:42 +01:00
|
|
|
Color: colors.Raw,
|
2017-12-18 20:42:32 +01:00
|
|
|
Severity: diag.Infoerr,
|
2017-10-05 23:08:46 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) diagErrorEvent(msg string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2017-10-21 02:28:35 +02:00
|
|
|
Type: DiagEvent,
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload: DiagEventPayload{
|
2018-03-05 20:39:50 +01:00
|
|
|
Message: e.Filter.Filter(msg),
|
2018-01-31 18:41:42 +01:00
|
|
|
Color: colors.Raw,
|
2017-12-18 20:42:32 +01:00
|
|
|
Severity: diag.Error,
|
2017-10-05 23:08:46 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-05 20:39:50 +01:00
|
|
|
func (e *eventEmitter) diagWarningEvent(msg string) {
|
|
|
|
contract.Requiref(e != nil, "e", "!= nil")
|
|
|
|
|
|
|
|
e.Chan <- Event{
|
2017-10-21 02:28:35 +02:00
|
|
|
Type: DiagEvent,
|
2017-10-05 23:08:46 +02:00
|
|
|
Payload: DiagEventPayload{
|
2018-03-05 20:39:50 +01:00
|
|
|
Message: e.Filter.Filter(msg),
|
2018-01-31 18:41:42 +01:00
|
|
|
Color: colors.Raw,
|
2017-12-18 20:42:32 +01:00
|
|
|
Severity: diag.Warning,
|
2017-10-05 23:08:46 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|