Do not prompt for passphrase multiple times
The change does two things: - Reorders some calls in the CLI to prevent trying to create a secrets manager twice (which would end up prompting twice). - Adds a cache inside the passphrase secrets manager such that when decrypting a deployment, we can re-use the one created earlier in the update. This is sort of a hack, but is needed because otherwise we would fail to decrypt the deployment, meaning that if you had a secret value in your deployment *and* you were using local passphrase encryption *and* you had not set PULUMI_CONFIG_PASSPHRASE you would get an error asking you to do so. Fixes #2729
This commit is contained in:
parent
247edadd69
commit
c91ddf996b
|
@ -2,6 +2,9 @@
|
|||
|
||||
### Improvements
|
||||
|
||||
- Pulumi no longer prompts for your passphrase twice during operations when you
|
||||
are using the passphrase based secrets provider. (fixes [pulumi/pulumi#2729](https://github.com/pulumi/pulumi/issues/2729)).
|
||||
|
||||
## 0.17.11 (Released May 13, 2019)
|
||||
|
||||
### Major Changes
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/pulumi/pulumi/pkg/backend"
|
||||
"github.com/pulumi/pulumi/pkg/backend/display"
|
||||
"github.com/pulumi/pulumi/pkg/resource/config"
|
||||
"github.com/pulumi/pulumi/pkg/secrets"
|
||||
"github.com/pulumi/pulumi/pkg/tokens"
|
||||
"github.com/pulumi/pulumi/pkg/util/cmdutil"
|
||||
"github.com/pulumi/pulumi/pkg/workspace"
|
||||
|
@ -533,7 +534,7 @@ func looksLikeSecret(k config.Key, v string) bool {
|
|||
|
||||
// getStackConfiguration loads configuration information for a given stack. If stackConfigFile is non empty,
|
||||
// it is uses instead of the default configuration file for the stack
|
||||
func getStackConfiguration(stack backend.Stack) (backend.StackConfiguration, error) {
|
||||
func getStackConfiguration(stack backend.Stack, sm secrets.Manager) (backend.StackConfiguration, error) {
|
||||
workspaceStack, err := loadProjectStack(stack)
|
||||
if err != nil {
|
||||
return backend.StackConfiguration{}, errors.Wrap(err, "loading stack configuration")
|
||||
|
@ -549,7 +550,7 @@ func getStackConfiguration(stack backend.Stack) (backend.StackConfiguration, err
|
|||
}, nil
|
||||
}
|
||||
|
||||
crypter, err := getStackDencrypter(stack)
|
||||
crypter, err := sm.Decrypter()
|
||||
if err != nil {
|
||||
return backend.StackConfiguration{}, errors.Wrap(err, "getting configuration decrypter")
|
||||
}
|
||||
|
|
|
@ -98,16 +98,16 @@ func newDestroyCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
Parallel: parallel,
|
||||
|
|
|
@ -55,7 +55,12 @@ func newLogsCmd() *cobra.Command {
|
|||
return err
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting secrets manager")
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "getting stack configuration")
|
||||
}
|
||||
|
|
|
@ -98,16 +98,16 @@ func newPreviewCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
changes, res := s.Preview(commandContext(), backend.UpdateOperation{
|
||||
Proj: proj,
|
||||
Root: root,
|
||||
|
|
|
@ -64,7 +64,12 @@ func newQueryCmd() *cobra.Command {
|
|||
return result.FromError(err)
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
@ -76,6 +81,7 @@ func newQueryCmd() *cobra.Command {
|
|||
Root: root,
|
||||
Opts: opts,
|
||||
StackConfiguration: cfg,
|
||||
SecretsManager: sm,
|
||||
Scopes: cancellationScopes,
|
||||
})
|
||||
switch {
|
||||
|
|
|
@ -99,16 +99,16 @@ func newRefreshCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
Parallel: parallel,
|
||||
|
|
22
cmd/up.go
22
cmd/up.go
|
@ -92,16 +92,16 @@ func newUpCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
Parallel: parallel,
|
||||
|
@ -244,18 +244,16 @@ func newUpCmd() *cobra.Command {
|
|||
return result.FromError(errors.Wrap(err, "gathering environment metadata"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
// TODO(ellismg): Is there UX here what we want? Do we end up double prompting for a passphrase
|
||||
// when using passphrase based secrets management?
|
||||
sm, err := getStackSecretsManager(s)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting secrets manager"))
|
||||
}
|
||||
|
||||
cfg, err := getStackConfiguration(s, sm)
|
||||
if err != nil {
|
||||
return result.FromError(errors.Wrap(err, "getting stack configuration"))
|
||||
}
|
||||
|
||||
opts.Engine = engine.UpdateOptions{
|
||||
Analyzers: analyzers,
|
||||
Parallel: parallel,
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
|
@ -103,18 +104,38 @@ func (sm *localSecretsManager) Encrypter() (config.Encrypter, error) {
|
|||
return sm.crypter, nil
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
var cache map[string]secrets.Manager
|
||||
|
||||
func NewPassphaseSecretsManager(phrase string, state string) (secrets.Manager, error) {
|
||||
// check the cache first, if we have an already seen this state before, return a cached value
|
||||
lock.Lock()
|
||||
if cache == nil {
|
||||
cache = make(map[string]secrets.Manager)
|
||||
}
|
||||
cachedValue := cache[state]
|
||||
lock.Unlock()
|
||||
|
||||
if cachedValue != nil {
|
||||
return cachedValue, nil
|
||||
}
|
||||
|
||||
// wasn't in the cache so try to construct it and add it if there's no error.
|
||||
crypter, err := symmetricCrypterFromPhraseAndState(phrase, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &localSecretsManager{
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
sm := &localSecretsManager{
|
||||
crypter: crypter,
|
||||
state: localSecretsManagerState{
|
||||
Salt: state,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
cache[state] = sm
|
||||
return sm, nil
|
||||
}
|
||||
|
||||
type provider struct{}
|
||||
|
@ -162,11 +183,11 @@ func newLockedPasspharseSecretsManager(state localSecretsManagerState) secrets.M
|
|||
type errorCrypter struct{}
|
||||
|
||||
func (ec *errorCrypter) EncryptValue(v string) (string, error) {
|
||||
return "", errors.New("failed to encrypt: incorrect passphrase, please set PULUMI_CONFIG_PASSPHRASE to the" +
|
||||
return "", errors.New("failed to encrypt: incorrect passphrase, please set PULUMI_CONFIG_PASSPHRASE to the " +
|
||||
"correct passphrase")
|
||||
}
|
||||
|
||||
func (ec *errorCrypter) DecryptValue(v string) (string, error) {
|
||||
return "", errors.New("failed to decrypt: incorrect passphrase, please set PULUMI_CONFIG_PASSPHRASE to the" +
|
||||
return "", errors.New("failed to decrypt: incorrect passphrase, please set PULUMI_CONFIG_PASSPHRASE to the " +
|
||||
"correct passphrase")
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue