This change improves our output formatting by generally adding fewer prefixes. As shown in pulumi/pulumi#359, we were being excessively verbose in many places, including prefixing every console.out with "langhost[nodejs].stdout: ", displaying full stack traces for simple errors like missing configuration, etc. Overall, this change includes the following: * Don't prefix stdout and stderr output from the program, other than the standard "info:" prefix. I experimented with various schemes here, but they all felt gratuitous. Simply emitting the output seems fine, especially as it's closer to what would happen if you just ran the program under node. * Do NOT make writes to stderr fail the plan/deploy. Previously we assumed that any console.errors, for instance, meant that the overall program should fail. This simply isn't how stderr is treated generally and meant you couldn't use certain logging techniques and libraries, among other things. * Do make sure that stderr writes in the program end up going to stderr in the Pulumi CLI output, however, so that redirection works as it should. This required a new Infoerr log level. * Make a small fix to the planning logic so we don't attempt to print the summary if an error occurs. * Finally, add a new error type, RunError, that when thrown and uncaught does not result in a full stack trace being printed. Anyone can use this, however, we currently use it for config errors so that we can terminate with a pretty error message, rather than the monstrosity shown in pulumi/pulumi#359.
79 lines
2.4 KiB
Go
79 lines
2.4 KiB
Go
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
package plugin
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
"github.com/pulumi/pulumi/pkg/resource"
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
|
lumirpc "github.com/pulumi/pulumi/sdk/proto/go"
|
|
)
|
|
|
|
const AnalyzerPluginPrefix = "pulumi-analyzer-"
|
|
|
|
// analyzer reflects an analyzer plugin, loaded dynamically for a single suite of checks.
|
|
type analyzer struct {
|
|
ctx *Context
|
|
name tokens.QName
|
|
plug *plugin
|
|
client lumirpc.AnalyzerClient
|
|
}
|
|
|
|
// NewAnalyzer binds to a given analyzer's plugin by name and creates a gRPC connection to it. If the associated plugin
|
|
// could not be found by name on the PATH, or an error occurs while creating the child process, an error is returned.
|
|
func NewAnalyzer(host Host, ctx *Context, name tokens.QName) (Analyzer, error) {
|
|
// Go ahead and attempt to load the plugin from the PATH.
|
|
srvexe := AnalyzerPluginPrefix + strings.Replace(string(name), tokens.QNameDelimiter, "_", -1)
|
|
plug, err := newPlugin(ctx, srvexe, fmt.Sprintf("%v (analyzer)", name), []string{host.ServerAddr()})
|
|
if err != nil {
|
|
return nil, err
|
|
} else if plug == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &analyzer{
|
|
ctx: ctx,
|
|
name: name,
|
|
plug: plug,
|
|
client: lumirpc.NewAnalyzerClient(plug.Conn),
|
|
}, nil
|
|
}
|
|
|
|
func (a *analyzer) Name() tokens.QName { return a.name }
|
|
|
|
// Analyze analyzes a single resource object, and returns any errors that it finds.
|
|
func (a *analyzer) Analyze(t tokens.Type, props resource.PropertyMap) ([]AnalyzeFailure, error) {
|
|
glog.V(7).Infof("analyzer[%v].Analyze(t=%v,#props=%v) executing", a.name, t, len(props))
|
|
mprops, err := MarshalProperties(props, MarshalOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp, err := a.client.Analyze(a.ctx.Request(), &lumirpc.AnalyzeRequest{
|
|
Type: string(t),
|
|
Properties: mprops,
|
|
})
|
|
if err != nil {
|
|
glog.V(7).Infof("analyzer[%v].Analyze(t=%v,...) failed: err=%v", a.name, t, err)
|
|
return nil, err
|
|
}
|
|
|
|
var failures []AnalyzeFailure
|
|
for _, failure := range resp.GetFailures() {
|
|
failures = append(failures, AnalyzeFailure{
|
|
Property: resource.PropertyKey(failure.Property),
|
|
Reason: failure.Reason,
|
|
})
|
|
}
|
|
glog.V(7).Infof("analyzer[%v].Analyze(t=%v,...) success: failures=#%v", a.name, t, len(failures))
|
|
return failures, nil
|
|
}
|
|
|
|
// Close tears down the underlying plugin RPC connection and process.
|
|
func (a *analyzer) Close() error {
|
|
return a.plug.Close()
|
|
}
|