This change adds a new manifest section to the checkpoint files. The existing time moves into it, and we add to it the version of the Pulumi CLI that created it, along with the names, types, and versions of all plugins used to generate the file. There is a magic cookie that we also use during verification. This is to help keep us sane when debugging problems "in the wild," and I'm sure we will add more to it over time (checksum, etc). For example, after an up, you can now see this in `pulumi stack`: ``` Current stack is demo: Last updated at 2017-12-01 13:48:49.815740523 -0800 PST Pulumi version v0.8.3-79-g1ab99ad Plugin pulumi-provider-aws [resource] version v0.8.3-22-g4363e77 Plugin pulumi-langhost-nodejs [language] version v0.8.3-79-g77bb6b6 Checkpoint file is /Users/joeduffy/dev/code/src/github.com/pulumi/pulumi-aws/.pulumi/stacks/webserver/demo.json ``` This addresses pulumi/pulumi#628.
97 lines
3.3 KiB
Go
97 lines
3.3 KiB
Go
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
|
|
|
package plugin
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/golang/glog"
|
|
pbempty "github.com/golang/protobuf/ptypes/empty"
|
|
|
|
"github.com/pulumi/pulumi/pkg/tokens"
|
|
pulumirpc "github.com/pulumi/pulumi/sdk/proto/go"
|
|
)
|
|
|
|
const LanguagePluginPrefix = "pulumi-langhost-"
|
|
|
|
// langhost reflects a language host plugin, loaded dynamically for a single language/runtime pair.
|
|
type langhost struct {
|
|
ctx *Context
|
|
runtime string
|
|
plug *plugin
|
|
client pulumirpc.LanguageRuntimeClient
|
|
}
|
|
|
|
// NewLanguageRuntime binds to a language's runtime plugin and then creates a gRPC connection to it. If the
|
|
// plugin could not be found, or an error occurs while creating the child process, an error is returned.
|
|
func NewLanguageRuntime(host Host, ctx *Context, runtime string, monitorAddr string) (LanguageRuntime, error) {
|
|
// Go ahead and attempt to load the plugin from the PATH.
|
|
srvexe := LanguagePluginPrefix + strings.Replace(runtime, tokens.QNameDelimiter, "_", -1)
|
|
plug, err := newPlugin(ctx, srvexe, runtime, []string{monitorAddr, host.ServerAddr()})
|
|
if err != nil {
|
|
return nil, err
|
|
} else if plug == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
return &langhost{
|
|
ctx: ctx,
|
|
runtime: runtime,
|
|
plug: plug,
|
|
client: pulumirpc.NewLanguageRuntimeClient(plug.Conn),
|
|
}, nil
|
|
}
|
|
|
|
func (h *langhost) Runtime() string { return h.runtime }
|
|
|
|
// Run executes a program in the language runtime for planning or deployment purposes. If info.DryRun is true,
|
|
// the code must not assume that side-effects or final values resulting from resource deployments are actually
|
|
// available. If it is false, on the other hand, a real deployment is occurring and it may safely depend on these.
|
|
func (h *langhost) Run(info RunInfo) (string, error) {
|
|
glog.V(7).Infof("langhost[%v].Run(pwd=%v,program=%v,#args=%v,proj=%s,stack=%v,#config=%v,dryrun=%v) executing",
|
|
h.runtime, info.Pwd, info.Program, len(info.Args), info.Project, info.Stack, len(info.Config), info.DryRun)
|
|
config := make(map[string]string)
|
|
for k, v := range info.Config {
|
|
config[string(k)] = v
|
|
}
|
|
resp, err := h.client.Run(h.ctx.Request(), &pulumirpc.RunRequest{
|
|
Pwd: info.Pwd,
|
|
Program: info.Program,
|
|
Args: info.Args,
|
|
Project: info.Project,
|
|
Stack: info.Stack,
|
|
Config: config,
|
|
DryRun: info.DryRun,
|
|
Parallel: int32(info.Parallel),
|
|
})
|
|
if err != nil {
|
|
glog.V(7).Infof("langhost[%v].Run(pwd=%v,program=%v,...,dryrun=%v) failed: err=%v",
|
|
h.runtime, info.Pwd, info.Program, info.DryRun, err)
|
|
return "", err
|
|
}
|
|
|
|
progerr := resp.GetError()
|
|
glog.V(7).Infof("langhost[%v].RunPlan(pwd=%v,program=%v,...,dryrun=%v) success: progerr=%v",
|
|
h.runtime, info.Pwd, info.Program, info.DryRun, progerr)
|
|
return progerr, nil
|
|
}
|
|
|
|
// GetPluginInfo returns this plugin's information.
|
|
func (h *langhost) GetPluginInfo() (Info, error) {
|
|
glog.V(7).Infof("langhost[%v].GetPluginInfo() executing", h.runtime)
|
|
resp, err := h.client.GetPluginInfo(h.ctx.Request(), &pbempty.Empty{})
|
|
if err != nil {
|
|
glog.V(7).Infof("langhost[%v].GetPluginInfo() failed: err=%v", h.runtime, err)
|
|
return Info{}, err
|
|
}
|
|
return Info{
|
|
Name: h.plug.Bin,
|
|
Type: LanguageType,
|
|
Version: resp.Version,
|
|
}, nil
|
|
}
|
|
|
|
// Close tears down the underlying plugin RPC connection and process.
|
|
func (h *langhost) Close() error {
|
|
return h.plug.Close()
|
|
}
|