Skip to content

Commit

Permalink
Fix TestDestroySetsEncryptionsalt test and resulting bug (#15432)
Browse files Browse the repository at this point in the history
<!--- 
Thanks so much for your contribution! If this is your first time
contributing, please ensure that you have read the
[CONTRIBUTING](https://github.com/pulumi/pulumi/blob/master/CONTRIBUTING.md)
documentation.
-->

# Description

<!--- Please include a summary of the change and which issue is fixed.
Please also include relevant motivation and context. -->

While working on some other secret manager changes I found that
`TestDestroySetsEncryptedkey` wasn't actually fully testing what we
thought it was.

Firstly it was a bad name, we're checking for `encryptionsalt` being set
not `encryptedkey`. But more importantly it wasn't checking that the
salt stayed the same.

Turned out `destroy` was loading the stack config, seeing no
`encryptionsalt` and so new'ing up a brand new passphrase secret manager
and state and then saving that to the stack config.

This is now fixed that the test asserts that the salt is exactly what's
expected and I've fixed up the engine code to do this correctly.

## Checklist

- [x] I have run `make tidy` to update any new dependencies
- [x] I have run `make lint` to verify my code passes the lint check
  - [x] I have formatted my code using `gofumpt`

<!--- Please provide details if the checkbox below is to be left
unchecked. -->
- [x] I have added tests that prove my fix is effective or that my
feature works
<!--- 
User-facing changes require a CHANGELOG entry.
-->
- [x] I have run `make changelog` and committed the
`changelog/pending/<file>` documenting my change
<!--
If the change(s) in this PR is a modification of an existing call to the
Pulumi Cloud,
then the service should honor older versions of the CLI where this
change would not exist.
You must then bump the API version in
/pkg/backend/httpstate/client/api.go, as well as add
it to the service.
-->
- [ ] Yes, there are changes in this PR that warrants bumping the Pulumi
Cloud API version
<!-- @pulumi employees: If yes, you must submit corresponding changes in
the service repo. -->
  • Loading branch information
Frassle committed Feb 15, 2024
1 parent 0211ab4 commit 60f1abc
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: cli
description: Fix `pulumi destroy` to fill in stack config with the secret config from state, not fresh secret config.
8 changes: 5 additions & 3 deletions pkg/cmd/pulumi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1147,8 +1147,10 @@ func looksLikeSecret(k config.Key, v string) bool {
(info.Entropy >= (entropyThreshold/2) && entropyPerChar >= entropyPerCharThreshold)
}

func getAndSaveSecretsManager(stack backend.Stack, workspaceStack *workspace.ProjectStack) (secrets.Manager, error) {
sm, needsSave, err := getStackSecretsManager(stack, workspaceStack)
func getAndSaveSecretsManager(
stack backend.Stack, workspaceStack *workspace.ProjectStack, fallbackManager secrets.Manager,
) (secrets.Manager, error) {
sm, needsSave, err := getStackSecretsManager(stack, workspaceStack, fallbackManager)
if err != nil {
return nil, fmt.Errorf("get stack secrets manager: %w", err)
}
Expand Down Expand Up @@ -1286,7 +1288,7 @@ func getStackConfigurationWithFallback(
}
}

sm, err := getAndSaveSecretsManager(stack, workspaceStack)
sm, err := getAndSaveSecretsManager(stack, workspaceStack, fallbackSecretsManager)
if err != nil {
if fallbackSecretsManager != nil {
sm = fallbackSecretsManager
Expand Down
29 changes: 25 additions & 4 deletions pkg/cmd/pulumi/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

func getStackEncrypter(s backend.Stack, ps *workspace.ProjectStack) (config.Encrypter, bool, error) {
sm, needsSave, err := getStackSecretsManager(s, ps)
sm, needsSave, err := getStackSecretsManager(s, ps, nil)
if err != nil {
return nil, false, err
}
Expand All @@ -42,7 +42,7 @@ func getStackEncrypter(s backend.Stack, ps *workspace.ProjectStack) (config.Encr
}

func getStackDecrypter(s backend.Stack, ps *workspace.ProjectStack) (config.Decrypter, bool, error) {
sm, needsSave, err := getStackSecretsManager(s, ps)
sm, needsSave, err := getStackSecretsManager(s, ps, nil)
if err != nil {
return nil, false, err
}
Expand All @@ -54,7 +54,9 @@ func getStackDecrypter(s backend.Stack, ps *workspace.ProjectStack) (config.Decr
return dec, needsSave, nil
}

func getStackSecretsManager(s backend.Stack, ps *workspace.ProjectStack) (secrets.Manager, bool, error) {
func getStackSecretsManager(
s backend.Stack, ps *workspace.ProjectStack, fallbackManager secrets.Manager,
) (secrets.Manager, bool, error) {
oldConfig := deepcopy.Copy(ps).(*workspace.ProjectStack)

var sm secrets.Manager
Expand All @@ -66,7 +68,26 @@ func getStackSecretsManager(s backend.Stack, ps *workspace.ProjectStack) (secret
sm, err = passphrase.NewPromptingPassphraseSecretsManager(
ps, false /* rotateSecretsProvider */)
} else {
sm, err = s.DefaultSecretManager(ps)
if fallbackManager != nil {
sm = fallbackManager

// We need to ensure the fallback manager picked saves to the stack state. TODO: It would be
// really nice if the format of secrets state in the config file matched what managers reported
// for state. That would go well with the pluginification of secret providers as well, but for now
// just switch on the secret provider type and ask it to fill in the config file for us.
if sm.Type() == passphrase.Type {
err = passphrase.EditProjectStack(ps, sm.State())
} else if sm.Type() == cloud.Type {
err = cloud.EditProjectStack(ps, sm.State())
} else {
// Anything else assume we can just clear all the secret bits
ps.EncryptionSalt = ""
ps.SecretsProvider = ""
ps.EncryptedKey = ""
}
} else {
sm, err = s.DefaultSecretManager(ps)
}
}
if err != nil {
return nil, false, err
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/pulumi/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ func promptForConfig(
return nil, fmt.Errorf("loading stack config: %w", err)
}

sm, needsSave, err := getStackSecretsManager(stack, ps)
sm, needsSave, err := getStackSecretsManager(stack, ps, nil)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/pulumi/stack_change_secrets_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ func migrateOldConfigAndCheckpointToNewSecretsProvider(ctx context.Context,
}

// Get the newly created secrets manager for the stack
newSecretsManager, needsSave, err := getStackSecretsManager(currentStack, reloadedProjectStack)
newSecretsManager, needsSave, err := getStackSecretsManager(currentStack, reloadedProjectStack, nil)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/stretchr/testify v1.8.4
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
gopkg.in/yaml.v2 v2.4.0
)

require (
Expand Down
28 changes: 19 additions & 9 deletions tests/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

//nolint:paralleltest // mutates environment variables
Expand Down Expand Up @@ -434,7 +435,7 @@ func TestStackBackups(t *testing.T) {
}

//nolint:paralleltest // mutates environment variables
func TestDestroySetsEncryptedkey(t *testing.T) {
func TestDestroySetsEncryptionsalt(t *testing.T) {
e := ptesting.NewEnvironment(t)
defer func() {
if !t.Failed() {
Expand All @@ -443,6 +444,8 @@ func TestDestroySetsEncryptedkey(t *testing.T) {
}()

const stackName = "imulup"
stackFile := filepath.Join(e.RootPath, "Pulumi.imulup.yaml")
var expectedSalt string

// Set up the environment.
{
Expand All @@ -462,10 +465,15 @@ func TestDestroySetsEncryptedkey(t *testing.T) {

// Now run pulumi up.
e.RunCommand("pulumi", "up", "--non-interactive", "--yes", "--skip-preview")
}

wd := e.RootPath
stackFile := filepath.Join(wd, "Pulumi.imulup.yaml")
// See what the encryptionsalt is
stackYAML, err := os.ReadFile(stackFile)
require.NoError(t, err)
var stackConfig workspace.ProjectStack
err = yaml.Unmarshal(stackYAML, &stackConfig)
require.NoError(t, err)
expectedSalt = stackConfig.EncryptionSalt
}

// Remove `encryptionsalt` from `Pulumi.imulup.yaml`.
preamble := "secretsprovider: passphrase\n"
Expand All @@ -475,11 +483,13 @@ func TestDestroySetsEncryptedkey(t *testing.T) {
// Now run pulumi destroy.
e.RunCommand("pulumi", "destroy", "--non-interactive", "--yes", "--skip-preview")

// Check that the stack file has `encryptionsalt` set.
data, err := os.ReadFile(stackFile)
assert.NoError(t, err, "reading Pulumi.imulup.yaml")
s := string(data)
assert.Contains(t, s, "encryptionsalt", "should contain encryptionsalt")
// Check that the stack file has the right `encryptionsalt` set.
stackYAML, err := os.ReadFile(stackFile)
require.NoError(t, err)
var stackConfig workspace.ProjectStack
err = yaml.Unmarshal(stackYAML, &stackConfig)
require.NoError(t, err)
assert.Equal(t, expectedSalt, stackConfig.EncryptionSalt)

e.RunCommand("pulumi", "stack", "rm", "--yes")
}
Expand Down

0 comments on commit 60f1abc

Please sign in to comment.