Merge pull request #643 from pulumi/LateDecrypt

Decrypt configuration nearer to its use.
This commit is contained in:
Pat Gavlin 2017-12-04 17:27:59 -08:00 committed by GitHub
commit 94645c313a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 55 additions and 29 deletions

View file

@ -177,8 +177,13 @@ func (b *localBackend) GetLogs(stackName tokens.QName, query operations.LogQuery
contract.Assert(snap != nil)
contract.Assert(target != nil)
config, err := target.Config.Decrypt(target.Decrypter)
if err != nil {
return nil, err
}
components := operations.NewResourceTree(snap.Resources)
ops := components.OperationsProvider(target.Config)
ops := components.OperationsProvider(config)
logs, err := ops.GetLogs(query)
if logs == nil {
return nil, err

View file

@ -39,17 +39,7 @@ func (p localStackProvider) GetTarget(name tokens.QName) (*deploy.Target, error)
return nil, err
}
decryptedConfig := make(map[tokens.ModuleMember]string)
for k, v := range config {
decrypted, err := v.Value(p.decrypter)
if err != nil {
return nil, errors.Wrap(err, "could not decrypt configuration value")
}
decryptedConfig[k] = decrypted
}
return &deploy.Target{Name: name, Config: decryptedConfig}, nil
return &deploy.Target{Name: name, Config: config, Decrypter: p.decrypter}, nil
}
func (p localStackProvider) GetSnapshot(name tokens.QName) (*deploy.Snapshot, error) {

View file

@ -14,6 +14,7 @@ import (
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/diag/colors"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/config"
"github.com/pulumi/pulumi/pkg/resource/deploy"
"github.com/pulumi/pulumi/pkg/resource/plugin"
"github.com/pulumi/pulumi/pkg/tokens"
@ -185,16 +186,18 @@ func printPrelude(b *bytes.Buffer, result *planResult, planning bool) {
}
}
func printConfig(b *bytes.Buffer, config map[tokens.ModuleMember]string) {
func printConfig(b *bytes.Buffer, cfg config.Map) {
b.WriteString(fmt.Sprintf("%vConfiguration:%v\n", colors.SpecUnimportant, colors.Reset))
if config != nil {
if cfg != nil {
var keys []string
for key := range config {
for key := range cfg {
keys = append(keys, string(key))
}
sort.Strings(keys)
for _, key := range keys {
b.WriteString(fmt.Sprintf(" %v: %v\n", key, config[tokens.ModuleMember(key)]))
v, err := cfg[tokens.ModuleMember(key)].Value(config.NewBlindingDecrypter())
contract.Assert(err == nil)
b.WriteString(fmt.Sprintf(" %v: %v\n", key, v))
}
}
}

View file

@ -36,7 +36,7 @@ type Crypter interface {
// be used when you want to display configuration information to a user but don't want to prompt for a password
// so secrets will not be decrypted.
func NewBlindingDecrypter() Decrypter {
return &blindingDecrypter{}
return blindingDecrypter{}
}
type blindingDecrypter struct{}

View file

@ -12,6 +12,19 @@ import (
// Map is a bag of config stored in the settings file.
type Map map[tokens.ModuleMember]Value
// Decrypt returns the configuration as a map from module member to decrypted value.
func (m Map) Decrypt(decrypter Decrypter) (map[tokens.ModuleMember]string, error) {
r := map[tokens.ModuleMember]string{}
for k, c := range m {
v, err := c.Value(decrypter)
if err != nil {
return nil, err
}
r[k] = v
}
return r, nil
}
// HasSecureValue returns true if the config map contains a secure (encrypted) value.
func (m Map) HasSecureValue() bool {
for _, v := range m {

View file

@ -63,7 +63,7 @@ func (p *Plan) Start(opts Options) (*PlanIterator, error) {
func (p *Plan) configure() error {
var pkgs []string
pkgconfigs := make(map[tokens.Package]map[tokens.ModuleMember]string)
for k, v := range p.target.Config {
for k, c := range p.target.Config {
pkg := k.Package()
pkgs = append(pkgs, string(pkg))
pkgconfig, has := pkgconfigs[pkg]
@ -71,6 +71,10 @@ func (p *Plan) configure() error {
pkgconfig = make(map[tokens.ModuleMember]string)
pkgconfigs[pkg] = pkgconfig
}
v, err := c.Value(p.target.Decrypter)
if err != nil {
return err
}
pkgconfig[k] = v
}
sort.Strings(pkgs)

View file

@ -150,16 +150,24 @@ func (iter *evalSourceIterator) forkRun(opts Options) {
go func() {
// Next, launch the language plugin.
// IDEA: cache these so we reuse the same language plugin instance; if we do this, monitors must be per-run.
rt := iter.src.runinfo.Pkg.Runtime
langhost, err := iter.src.plugctx.Host.LanguageRuntime(rt, iter.mon.Address())
if err != nil {
err = errors.Wrapf(err, "failed to launch language host for '%v'", rt)
} else if langhost == nil {
err = errors.Errorf("could not load language plugin for '%v' from $PATH", rt)
} else {
run := func() error {
rt := iter.src.runinfo.Pkg.Runtime
langhost, err := iter.src.plugctx.Host.LanguageRuntime(rt, iter.mon.Address())
if err != nil {
return errors.Wrapf(err, "failed to launch language host for '%v'", rt)
} else if langhost == nil {
return errors.Errorf("could not load language plugin for '%v' from $PATH", rt)
}
// Make sure to clean up before exiting.
defer contract.IgnoreClose(langhost)
// Decrypt the configuration.
config, err := iter.src.runinfo.Target.Config.Decrypt(iter.src.runinfo.Target.Decrypter)
if err != nil {
return err
}
// Now run the actual program.
var progerr string
progerr, err = langhost.Run(plugin.RunInfo{
@ -168,7 +176,7 @@ func (iter *evalSourceIterator) forkRun(opts Options) {
Pwd: iter.src.runinfo.Pwd,
Program: iter.src.runinfo.Program,
Args: iter.src.runinfo.Args,
Config: iter.src.runinfo.Target.Config,
Config: config,
DryRun: iter.src.dryRun,
Parallel: opts.Parallel,
})
@ -176,10 +184,11 @@ func (iter *evalSourceIterator) forkRun(opts Options) {
// If the program had an unhandled error; propagate it to the caller.
err = errors.Errorf("an unhandled error occurred: %v", progerr)
}
return err
}
// Communicate the error, if it exists, or nil if the program exited cleanly.
iter.finChan <- err
iter.finChan <- run()
}()
}
}

View file

@ -3,11 +3,13 @@
package deploy
import (
"github.com/pulumi/pulumi/pkg/resource/config"
"github.com/pulumi/pulumi/pkg/tokens"
)
// Target represents information about a deployment target.
type Target struct {
Name tokens.QName // the target stack name.
Config map[tokens.ModuleMember]string // optional configuration key/values.
Name tokens.QName // the target stack name.
Config config.Map // optional configuration key/value pairs.
Decrypter config.Decrypter // decrypter for secret configuration values.
}