pulumi/pkg/backend/diy/backend_test.go

1593 lines
48 KiB
Go
Raw Normal View History

package diy
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
user "github.com/tweekmonster/luser"
"gocloud.dev/blob/fileblob"
"github.com/pulumi/pulumi/pkg/v3/backend"
"github.com/pulumi/pulumi/pkg/v3/operations"
"github.com/pulumi/pulumi/pkg/v3/resource/deploy"
"github.com/pulumi/pulumi/pkg/v3/resource/stack"
"github.com/pulumi/pulumi/pkg/v3/secrets/b64"
"github.com/pulumi/pulumi/pkg/v3/secrets/passphrase"
"github.com/pulumi/pulumi/sdk/v3/go/common/apitype"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag/colors"
"github.com/pulumi/pulumi/sdk/v3/go/common/encoding"
"github.com/pulumi/pulumi/sdk/v3/go/common/env"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/config"
"github.com/pulumi/pulumi/sdk/v3/go/common/testing/diagtest"
"github.com/pulumi/pulumi/sdk/v3/go/common/testing/iotest"
"github.com/pulumi/pulumi/sdk/v3/go/common/tokens"
Support always qualifying stack names (#11081) (#15857) <!--- 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. --> This commit adds the `--fully-qualify-stack-names` (or `-Q` for short) global command-line argument, which when supplied will always print stack names in the fully-qualified form of `organization/project/stack`. Fixes #11081. ## 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. -->
2024-04-04 10:11:46 +00:00
"github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil"
"github.com/pulumi/pulumi/sdk/v3/go/common/workspace"
)
Support always qualifying stack names (#11081) (#15857) <!--- 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. --> This commit adds the `--fully-qualify-stack-names` (or `-Q` for short) global command-line argument, which when supplied will always print stack names in the fully-qualified form of `organization/project/stack`. Fixes #11081. ## 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. -->
2024-04-04 10:11:46 +00:00
//nolint:paralleltest // mutates global configuration
func TestEnabledFullyQualifiedStackNames(t *testing.T) {
// Arrange
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
stackName := "organization/project-12345/stack-67890"
ref, err := b.ParseStackReference(stackName)
require.NoError(t, err)
s, err := b.CreateStack(ctx, ref, "", nil)
require.NoError(t, err)
previous := cmdutil.FullyQualifyStackNames
expected := stackName
// Act
cmdutil.FullyQualifyStackNames = true
defer func() { cmdutil.FullyQualifyStackNames = previous }()
actual := s.Ref().String()
// Assert
assert.Equal(t, expected, actual)
}
//nolint:paralleltest // mutates global configuration
func TestDisabledFullyQualifiedStackNames(t *testing.T) {
// Arrange
// Create a new project
projectDir := t.TempDir()
pyaml := filepath.Join(projectDir, "Pulumi.yaml")
err := os.WriteFile(pyaml, []byte("name: project-12345\nruntime: test"), 0o600)
require.NoError(t, err)
proj, err := workspace.LoadProject(pyaml)
require.NoError(t, err)
chdir(t, projectDir)
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), proj)
assert.NoError(t, err)
stackName := "organization/project-12345/stack-67890"
ref, err := b.ParseStackReference(stackName)
require.NoError(t, err)
s, err := b.CreateStack(ctx, ref, "", nil)
require.NoError(t, err)
previous := cmdutil.FullyQualifyStackNames
expected := "stack-67890"
// Act
cmdutil.FullyQualifyStackNames = false
defer func() { cmdutil.FullyQualifyStackNames = previous }()
actual := s.Ref().String()
// Assert
assert.Equal(t, expected, actual)
}
func TestMassageBlobPath(t *testing.T) {
t.Parallel()
testMassagePath := func(t *testing.T, s string, want string) {
add no_tmp_dir query parameter to file:// URLs (#15375) In gocloud.dev 0.34.0 the behaviour for file:// URLs changed, making pulumi fail when the state is supposed to be written to a mounted directory. See also https://github.com/pulumi/pulumi/issues/15352. Restore the old behaviour by adding a `no_tmp_dir=true` flag to the URL, unless the user provided it themselves. We also allow users to pass `no_tmp_dir=false` in the parameters, which will opt in to the new behaviour. This actually implements option 2 from the linked issue since it was easy enough to do, though I don't expect may users to make use of it, since I don't think a lot of people care about the contents of whatever directory the state is stored in anyway, so additional temp files there don't really matter. Fixes https://github.com/pulumi/pulumi/issues/15352 ## 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. --> --------- Co-authored-by: Justin Van Patten <jvp@justinvp.com>
2024-02-05 13:40:31 +00:00
t.Helper()
massaged, err := massageBlobPath(s)
assert.NoError(t, err)
assert.Equal(t, want, massaged,
"massageBlobPath(%s) didn't return expected result.\nWant: %q\nGot: %q", s, want, massaged)
}
// URLs not prefixed with "file://" are kept as-is. Also why we add FilePathPrefix as a prefix for other tests.
t.Run("NonFilePrefixed", func(t *testing.T) {
t.Parallel()
testMassagePath(t, "asdf-123", "asdf-123")
})
add no_tmp_dir query parameter to file:// URLs (#15375) In gocloud.dev 0.34.0 the behaviour for file:// URLs changed, making pulumi fail when the state is supposed to be written to a mounted directory. See also https://github.com/pulumi/pulumi/issues/15352. Restore the old behaviour by adding a `no_tmp_dir=true` flag to the URL, unless the user provided it themselves. We also allow users to pass `no_tmp_dir=false` in the parameters, which will opt in to the new behaviour. This actually implements option 2 from the linked issue since it was easy enough to do, though I don't expect may users to make use of it, since I don't think a lot of people care about the contents of whatever directory the state is stored in anyway, so additional temp files there don't really matter. Fixes https://github.com/pulumi/pulumi/issues/15352 ## 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. --> --------- Co-authored-by: Justin Van Patten <jvp@justinvp.com>
2024-02-05 13:40:31 +00:00
noTmpDirSuffix := "?no_tmp_dir=true"
// The home directory is converted into the user's actual home directory.
// Which requires even more tweaks to work on Windows.
t.Run("PrefixedWithTilde", func(t *testing.T) {
t.Parallel()
usr, err := user.Current()
if err != nil {
t.Fatalf("Unable to get current user: %v", err)
}
homeDir := usr.HomeDir
// When running on Windows, the "home directory" takes on a different meaning.
if runtime.GOOS == "windows" {
t.Logf("Running on %v", runtime.GOOS)
t.Run("NormalizeDirSeparator", func(t *testing.T) {
t.Parallel()
add no_tmp_dir query parameter to file:// URLs (#15375) In gocloud.dev 0.34.0 the behaviour for file:// URLs changed, making pulumi fail when the state is supposed to be written to a mounted directory. See also https://github.com/pulumi/pulumi/issues/15352. Restore the old behaviour by adding a `no_tmp_dir=true` flag to the URL, unless the user provided it themselves. We also allow users to pass `no_tmp_dir=false` in the parameters, which will opt in to the new behaviour. This actually implements option 2 from the linked issue since it was easy enough to do, though I don't expect may users to make use of it, since I don't think a lot of people care about the contents of whatever directory the state is stored in anyway, so additional temp files there don't really matter. Fixes https://github.com/pulumi/pulumi/issues/15352 ## 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. --> --------- Co-authored-by: Justin Van Patten <jvp@justinvp.com>
2024-02-05 13:40:31 +00:00
testMassagePath(t, FilePathPrefix+`C:\Users\steve\`, FilePathPrefix+"/C:/Users/steve"+noTmpDirSuffix)
})
newHomeDir := "/" + filepath.ToSlash(homeDir)
t.Logf("Changed homeDir to expect from %q to %q", homeDir, newHomeDir)
homeDir = newHomeDir
}
add no_tmp_dir query parameter to file:// URLs (#15375) In gocloud.dev 0.34.0 the behaviour for file:// URLs changed, making pulumi fail when the state is supposed to be written to a mounted directory. See also https://github.com/pulumi/pulumi/issues/15352. Restore the old behaviour by adding a `no_tmp_dir=true` flag to the URL, unless the user provided it themselves. We also allow users to pass `no_tmp_dir=false` in the parameters, which will opt in to the new behaviour. This actually implements option 2 from the linked issue since it was easy enough to do, though I don't expect may users to make use of it, since I don't think a lot of people care about the contents of whatever directory the state is stored in anyway, so additional temp files there don't really matter. Fixes https://github.com/pulumi/pulumi/issues/15352 ## 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. --> --------- Co-authored-by: Justin Van Patten <jvp@justinvp.com>
2024-02-05 13:40:31 +00:00
testMassagePath(t, FilePathPrefix+"~", FilePathPrefix+homeDir+noTmpDirSuffix)
testMassagePath(t, FilePathPrefix+"~/alpha/beta", FilePathPrefix+homeDir+"/alpha/beta"+noTmpDirSuffix)
})
t.Run("MakeAbsolute", func(t *testing.T) {
t.Parallel()
// Run the expected result through filepath.Abs, since on Windows we expect "C:\1\2".
expected := "/1/2"
abs, err := filepath.Abs(expected)
assert.NoError(t, err)
expected = filepath.ToSlash(abs)
if expected[0] != '/' {
expected = "/" + expected // A leading slash is added on Windows.
}
add no_tmp_dir query parameter to file:// URLs (#15375) In gocloud.dev 0.34.0 the behaviour for file:// URLs changed, making pulumi fail when the state is supposed to be written to a mounted directory. See also https://github.com/pulumi/pulumi/issues/15352. Restore the old behaviour by adding a `no_tmp_dir=true` flag to the URL, unless the user provided it themselves. We also allow users to pass `no_tmp_dir=false` in the parameters, which will opt in to the new behaviour. This actually implements option 2 from the linked issue since it was easy enough to do, though I don't expect may users to make use of it, since I don't think a lot of people care about the contents of whatever directory the state is stored in anyway, so additional temp files there don't really matter. Fixes https://github.com/pulumi/pulumi/issues/15352 ## 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. --> --------- Co-authored-by: Justin Van Patten <jvp@justinvp.com>
2024-02-05 13:40:31 +00:00
testMassagePath(t, FilePathPrefix+"/1/2/3/../4/..", FilePathPrefix+expected+noTmpDirSuffix)
})
t.Run("AlreadySuffixedWithNoTmpDir", func(t *testing.T) {
t.Parallel()
testMassagePath(t, FilePathPrefix+"/1?no_tmp_dir=yes", FilePathPrefix+"/1?no_tmp_dir=yes")
})
t.Run("AlreadySuffixedWithOtherQuery", func(t *testing.T) {
t.Parallel()
testMassagePath(t, FilePathPrefix+"/1?foo=bar", FilePathPrefix+"/1?foo=bar&no_tmp_dir=true")
})
t.Run("NoTmpDirFalseStripped", func(t *testing.T) {
t.Parallel()
testMassagePath(t, FilePathPrefix+"/1?no_tmp_dir=false", FilePathPrefix+"/1")
testMassagePath(t, FilePathPrefix+"/1?foo=bar&no_tmp_dir=false", FilePathPrefix+"/1?foo=bar")
})
}
func TestGetLogsForTargetWithNoSnapshot(t *testing.T) {
t.Parallel()
target := &deploy.Target{
Add tokens.StackName (#14487) <!--- 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. --> This adds a new type `tokens.StackName` which is a relatively strongly typed container for a stack name. The only weakly typed aspect of it is Go will always allow the "zero" value to be created for a struct, which for a stack name is the empty string which is invalid. To prevent introducing unexpected empty strings when working with stack names the `String()` method will panic for zero initialized stack names. Apart from the zero value, all other instances of `StackName` are via `ParseStackName` which returns a descriptive error if the string is not valid. This PR only updates "pkg/" to use this type. There are a number of places in "sdk/" which could do with this type as well, but there's no harm in doing a staggered roll out, and some parts of "sdk/" are user facing and will probably have to stay on the current `tokens.Name` and `tokens.QName` types. There are two places in the system where we panic on invalid stack names, both in the http backend. This _should_ be fine as we've had long standing validation that stacks created in the service are valid stack names. Just in case people have managed to introduce invalid stack names, there is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn off the validation _and_ panicing for stack names. Users can use that to temporarily disable the validation and continue working, but it should only be seen as a temporary measure. If they have invalid names they should rename them, or if they think they should be valid raise an issue with us to change the validation code. ## 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 - [ ] 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. --> - [ ] 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. -->
2023-11-15 07:44:54 +00:00
Name: tokens.MustParseStackName("test"),
Config: config.Map{},
Decrypter: config.NopDecrypter,
Snapshot: nil,
}
query := operations.LogQuery{}
res, err := GetLogsForTarget(target, query)
assert.NoError(t, err)
assert.Nil(t, res)
}
Allow anything in resource names (#14107) <!--- 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. --> Fixes https://github.com/pulumi/pulumi/issues/13968. Fixes https://github.com/pulumi/pulumi/issues/8949. This requires changing the parsing of URN's slightly, it is _very_ likely that providers will need to update to handle URNs like this correctly. This changes resource names to be `string` not `QName`. We never validated this before and it turns out that users have put all manner of text for resource names so we just updating the system to correctly reflect that. ## 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. -->
2023-11-20 08:59:00 +00:00
func makeUntypedDeployment(name string, phrase, state string) (*apitype.UntypedDeployment, error) {
return makeUntypedDeploymentTimestamp(name, phrase, state, nil, nil)
}
func makeUntypedDeploymentTimestamp(
Allow anything in resource names (#14107) <!--- 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. --> Fixes https://github.com/pulumi/pulumi/issues/13968. Fixes https://github.com/pulumi/pulumi/issues/8949. This requires changing the parsing of URN's slightly, it is _very_ likely that providers will need to update to handle URNs like this correctly. This changes resource names to be `string` not `QName`. We never validated this before and it turns out that users have put all manner of text for resource names so we just updating the system to correctly reflect that. ## 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. -->
2023-11-20 08:59:00 +00:00
name string,
phrase, state string,
created, modified *time.Time,
) (*apitype.UntypedDeployment, error) {
Add tests for stackChangeSecretsProviderCmd (#13791) <!--- 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. --> This command is currently untested. This adds a couple of basic tests to ensure that state and config secrets are changed. ## 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 - [ ] 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. --> - [ ] 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. -->
2023-08-31 08:56:38 +00:00
sm, err := passphrase.GetPassphraseSecretsManager(phrase, state)
if err != nil {
return nil, err
}
resources := []*resource.State{
{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", name),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("secret"): resource.MakeSecret(resource.NewStringProperty("s3cr3t")),
},
Created: created,
Modified: modified,
},
}
snap := deploy.NewSnapshot(deploy.Manifest{}, sm, resources, nil)
Lift context parameter to SerializeDeployment/Resource/Operations/Properties (#15929) <!--- 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. --> SerializePropertyValue needed a `context.Context` object to pass to the `config.Encrypter`. It was using `context.TODO()`, this change instead accepts a context on the parameters and lifts that up to SerializeProperties, SerializeResource, SerializeOperation, and SerializeDeployment. There were a few call sites for those methods that already had a context on hand, and they now pass that context. The other calls sites now use `context.TODO()`, we should continue to iterate in this area to ensure everywhere that needs a context has one passed in. ## 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 - [ ] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-04-15 07:45:46 +00:00
ctx := context.Background()
Lift context parameter to SerializeDeployment/Resource/Operations/Properties (#15929) <!--- 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. --> SerializePropertyValue needed a `context.Context` object to pass to the `config.Encrypter`. It was using `context.TODO()`, this change instead accepts a context on the parameters and lifts that up to SerializeProperties, SerializeResource, SerializeOperation, and SerializeDeployment. There were a few call sites for those methods that already had a context on hand, and they now pass that context. The other calls sites now use `context.TODO()`, we should continue to iterate in this area to ensure everywhere that needs a context has one passed in. ## 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 - [ ] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-04-15 07:45:46 +00:00
sdep, err := stack.SerializeDeployment(ctx, snap, false /* showSecrets */)
if err != nil {
return nil, err
}
data, err := json.Marshal(sdep)
if err != nil {
return nil, err
}
return &apitype.UntypedDeployment{
Version: 3,
Deployment: json.RawMessage(data),
}, nil
}
//nolint:paralleltest // mutates environment variables
func TestListStacksWithMultiplePassphrases(t *testing.T) {
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Create stack "a" and import a checkpoint with a secret
aStackRef, err := b.ParseStackReference("organization/project/a")
assert.NoError(t, err)
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
defer func() {
t.Setenv("PULUMI_CONFIG_PASSPHRASE", "abc123")
_, err := b.RemoveStack(ctx, aStack, true)
assert.NoError(t, err)
}()
deployment, err := makeUntypedDeployment("a", "abc123",
"v1:4iF78gb0nF0=:v1:Co6IbTWYs/UdrjgY:FSrAWOFZnj9ealCUDdJL7LrUKXX9BA==")
assert.NoError(t, err)
t.Setenv("PULUMI_CONFIG_PASSPHRASE", "abc123")
err = b.ImportDeployment(ctx, aStack, deployment)
assert.NoError(t, err)
// Create stack "b" and import a checkpoint with a secret
bStackRef, err := b.ParseStackReference("organization/project/b")
assert.NoError(t, err)
bStack, err := b.CreateStack(ctx, bStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, bStack)
defer func() {
t.Setenv("PULUMI_CONFIG_PASSPHRASE", "123abc")
_, err := b.RemoveStack(ctx, bStack, true)
assert.NoError(t, err)
}()
deployment, err = makeUntypedDeployment("b", "123abc",
"v1:C7H2a7/Ietk=:v1:yfAd1zOi6iY9DRIB:dumdsr+H89VpHIQWdB01XEFqYaYjAg==")
assert.NoError(t, err)
t.Setenv("PULUMI_CONFIG_PASSPHRASE", "123abc")
err = b.ImportDeployment(ctx, bStack, deployment)
assert.NoError(t, err)
// Remove the config passphrase so that we can no longer deserialize the checkpoints
err = os.Unsetenv("PULUMI_CONFIG_PASSPHRASE")
assert.NoError(t, err)
// Ensure that we can list the stacks we created even without a passphrase
stacks, outContToken, err := b.ListStacks(ctx, backend.ListStacksFilter{}, nil /* inContToken */)
assert.NoError(t, err)
assert.Nil(t, outContToken)
assert.Len(t, stacks, 2)
for _, stack := range stacks {
assert.NotNil(t, stack.ResourceCount())
assert.Equal(t, 1, *stack.ResourceCount())
}
}
func TestDrillError(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Get a non-existent stack and expect a nil error because it won't be found.
stackRef, err := b.ParseStackReference("organization/project/dev")
if err != nil {
t.Fatalf("unexpected error %v when parsing stack reference", err)
}
_, err = b.GetStack(ctx, stackRef)
assert.NoError(t, err)
}
func TestCancel(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Check that trying to cancel a stack that isn't created yet doesn't error
aStackRef, err := b.ParseStackReference("organization/project/a")
assert.NoError(t, err)
err = b.CancelCurrentUpdate(ctx, aStackRef)
assert.NoError(t, err)
// Check that trying to cancel a stack that isn't locked doesn't error
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
err = b.CancelCurrentUpdate(ctx, aStackRef)
assert.NoError(t, err)
// Locking and lock checks are only part of the internal interface
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
// Lock the stack and check CancelCurrentUpdate deletes the lock file
err = lb.Lock(ctx, aStackRef)
assert.NoError(t, err)
// check the lock file exists
lockExists, err := lb.bucket.Exists(ctx, lb.lockPath(aStackRef))
assert.NoError(t, err)
assert.True(t, lockExists)
// Call CancelCurrentUpdate
err = lb.CancelCurrentUpdate(ctx, aStackRef)
assert.NoError(t, err)
// Now check the lock file no longer exists
lockExists, err = lb.bucket.Exists(ctx, lb.lockPath(aStackRef))
assert.NoError(t, err)
assert.False(t, lockExists)
// Make another diy backend which will have a different lockId
ob, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
otherBackend, ok := ob.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
// Lock the stack with this new backend, then check that checkForLocks on the first backend now errors
err = otherBackend.Lock(ctx, aStackRef)
assert.NoError(t, err)
err = lb.checkForLock(ctx, aStackRef)
assert.Error(t, err)
// Now call CancelCurrentUpdate and check that checkForLocks no longer errors
err = lb.CancelCurrentUpdate(ctx, aStackRef)
assert.NoError(t, err)
err = lb.checkForLock(ctx, aStackRef)
assert.NoError(t, err)
}
func TestRemoveMakesBackups(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Grab the bucket interface to test with
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
// Check that creating a new stack doesn't make a backup file
aStackRef, err := lb.parseStackReference("organization/project/a")
assert.NoError(t, err)
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
// Check the stack file now exists, but the backup file doesn't
stackFileExists, err := lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
backupFileExists, err := lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef)+".bak")
assert.NoError(t, err)
assert.False(t, backupFileExists)
// Now remove the stack
removed, err := b.RemoveStack(ctx, aStack, false)
assert.NoError(t, err)
assert.False(t, removed)
// Check the stack file is now gone, but the backup file exists
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.False(t, stackFileExists)
backupFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef)+".bak")
assert.NoError(t, err)
assert.True(t, backupFileExists)
}
func TestRenameWorks(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Grab the bucket interface to test with
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
// Create a new stack
aStackRef, err := lb.parseStackReference("organization/project/a")
assert.NoError(t, err)
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
// Check the stack file now exists
stackFileExists, err := lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
// Fake up some history
err = lb.addToHistory(ctx, aStackRef, backend.UpdateInfo{Kind: apitype.DestroyUpdate})
assert.NoError(t, err)
// And pollute the history folder
err = lb.bucket.WriteAll(ctx, path.Join(aStackRef.HistoryDir(), "randomfile.txt"), []byte{0, 13}, nil)
assert.NoError(t, err)
// Rename the stack
bStackRefI, err := b.RenameStack(ctx, aStack, "organization/project/b")
assert.NoError(t, err)
assert.Equal(t, "organization/project/b", bStackRefI.String())
bStackRef := bStackRefI.(*diyBackendReference)
// Check the new stack file now exists and the old one is gone
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, bStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.False(t, stackFileExists)
// Rename again
bStack, err := b.GetStack(ctx, bStackRef)
assert.NoError(t, err)
cStackRefI, err := b.RenameStack(ctx, bStack, "organization/project/c")
assert.NoError(t, err)
assert.Equal(t, "organization/project/c", cStackRefI.String())
cStackRef := cStackRefI.(*diyBackendReference)
// Check the new stack file now exists and the old one is gone
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, cStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, bStackRef))
assert.NoError(t, err)
assert.False(t, stackFileExists)
// Check we can still get the history
history, err := b.GetHistory(ctx, cStackRef, 10, 0)
assert.NoError(t, err)
assert.Len(t, history, 1)
assert.Equal(t, apitype.DestroyUpdate, history[0].Kind)
}
func TestRenamePreservesIntegrity(t *testing.T) {
t.Parallel()
// Arrange.
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
stackRef, err := b.ParseStackReference("organization/project/a")
assert.NoError(t, err)
stk, err := b.CreateStack(ctx, stackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, stk)
rBase := &resource.State{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "base"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("p"): resource.NewStringProperty("v"),
},
}
rDependency := &resource.State{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "dependency"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("p"): resource.NewStringProperty("v"),
},
Dependencies: []resource.URN{rBase.URN},
}
rPropertyDependency := &resource.State{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "property-dependency"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("p"): resource.NewStringProperty("v"),
},
PropertyDependencies: map[resource.PropertyKey][]resource.URN{
resource.PropertyKey("p"): {rBase.URN},
},
}
rDeletedWith := &resource.State{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "deleted-with"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("p"): resource.NewStringProperty("v"),
},
DeletedWith: rBase.URN,
}
rParent := &resource.State{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "parent"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("p"): resource.NewStringProperty("v"),
},
Parent: rBase.URN,
}
resources := []*resource.State{
rBase,
rDependency,
rPropertyDependency,
rDeletedWith,
rParent,
}
snap := deploy.NewSnapshot(deploy.Manifest{}, nil, resources, nil)
ctx = context.Background()
sdep, err := stack.SerializeDeployment(ctx, snap, false)
assert.NoError(t, err)
data, err := encoding.JSON.Marshal(sdep)
assert.NoError(t, err)
err = b.ImportDeployment(ctx, stk, &apitype.UntypedDeployment{
Version: 3,
Deployment: json.RawMessage(data),
})
assert.NoError(t, err)
err = snap.VerifyIntegrity()
assert.NoError(t, err)
// Act.
renamedStackRef, err := b.RenameStack(ctx, stk, "organization/project/a-renamed")
assert.NoError(t, err)
// Assert.
renamedStk, err := b.GetStack(ctx, renamedStackRef)
assert.NoError(t, err)
assert.NotNil(t, renamedStk)
renamedSnap, err := renamedStk.Snapshot(ctx, nil)
assert.NoError(t, err)
err = renamedSnap.VerifyIntegrity()
assert.NoError(t, err)
}
2023-05-29 11:29:08 +00:00
func TestRenameProjectWorks(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
2023-05-29 11:29:08 +00:00
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Grab the bucket interface to test with
lb, ok := b.(*diyBackend)
2023-05-29 11:29:08 +00:00
assert.True(t, ok)
assert.NotNil(t, lb)
// Create a new stack
aStackRef, err := lb.parseStackReference("organization/project/a")
assert.NoError(t, err)
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
// Check the stack file now exists
stackFileExists, err := lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
// Fake up some history
err = lb.addToHistory(ctx, aStackRef, backend.UpdateInfo{Kind: apitype.DestroyUpdate})
assert.NoError(t, err)
// And pollute the history folder
err = lb.bucket.WriteAll(ctx, path.Join(aStackRef.HistoryDir(), "randomfile.txt"), []byte{0, 13}, nil)
assert.NoError(t, err)
// Rename the project and stack
bStackRefI, err := b.RenameStack(ctx, aStack, "organization/newProject/b")
assert.NoError(t, err)
assert.Equal(t, "organization/newProject/b", bStackRefI.String())
bStackRef := bStackRefI.(*diyBackendReference)
2023-05-29 11:29:08 +00:00
// Check the new stack file now exists and the old one is gone
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, bStackRef))
assert.NoError(t, err)
assert.True(t, stackFileExists)
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
assert.NoError(t, err)
assert.False(t, stackFileExists)
// Check we can still get the history
history, err := b.GetHistory(ctx, bStackRef, 10, 0)
assert.NoError(t, err)
assert.Len(t, history, 1)
assert.Equal(t, apitype.DestroyUpdate, history[0].Kind)
}
func TestLoginToNonExistingFolderFails(t *testing.T) {
t.Parallel()
fakeDir := "file://" + filepath.ToSlash(os.TempDir()) + "/non-existing"
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), fakeDir, nil)
assert.Error(t, err)
assert.Nil(t, b)
}
// TestParseEmptyStackFails demonstrates that ParseStackReference returns
// an error when the stack name is the empty string.TestParseEmptyStackFails
func TestParseEmptyStackFails(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
_, err = b.ParseStackReference("")
assert.Error(t, err)
}
// Regression test for https://github.com/pulumi/pulumi/issues/10439
func TestHtmlEscaping(t *testing.T) {
t.Parallel()
sm := b64.NewBase64SecretsManager()
resources := []*resource.State{
{
URN: resource.NewURN("a", "proj", "d:e:f", "a:b:c", "name"),
Type: "a:b:c",
Inputs: resource.PropertyMap{
resource.PropertyKey("html"): resource.NewStringProperty("<html@tags>"),
},
},
}
snap := deploy.NewSnapshot(deploy.Manifest{}, sm, resources, nil)
Lift context parameter to SerializeDeployment/Resource/Operations/Properties (#15929) <!--- 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. --> SerializePropertyValue needed a `context.Context` object to pass to the `config.Encrypter`. It was using `context.TODO()`, this change instead accepts a context on the parameters and lifts that up to SerializeProperties, SerializeResource, SerializeOperation, and SerializeDeployment. There were a few call sites for those methods that already had a context on hand, and they now pass that context. The other calls sites now use `context.TODO()`, we should continue to iterate in this area to ensure everywhere that needs a context has one passed in. ## 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 - [ ] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-04-15 07:45:46 +00:00
ctx := context.Background()
Lift context parameter to SerializeDeployment/Resource/Operations/Properties (#15929) <!--- 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. --> SerializePropertyValue needed a `context.Context` object to pass to the `config.Encrypter`. It was using `context.TODO()`, this change instead accepts a context on the parameters and lifts that up to SerializeProperties, SerializeResource, SerializeOperation, and SerializeDeployment. There were a few call sites for those methods that already had a context on hand, and they now pass that context. The other calls sites now use `context.TODO()`, we should continue to iterate in this area to ensure everywhere that needs a context has one passed in. ## 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 - [ ] I have formatted my code using `gofumpt` <!--- Please provide details if the checkbox below is to be left unchecked. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-04-15 07:45:46 +00:00
sdep, err := stack.SerializeDeployment(ctx, snap, false /* showSecrets */)
assert.NoError(t, err)
data, err := encoding.JSON.Marshal(sdep)
assert.NoError(t, err)
// Ensure data has the string contents "<html@tags>"", not "\u003chtml\u0026tags\u003e"
// ImportDeployment below should not modify the data
assert.Contains(t, string(data), "<html@tags>")
udep := &apitype.UntypedDeployment{
Version: 3,
Deployment: json.RawMessage(data),
}
// Login to a temp dir diy backend
tmpDir := t.TempDir()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Create stack "a" and import a checkpoint with a secret
aStackRef, err := b.ParseStackReference("organization/project/a")
assert.NoError(t, err)
aStack, err := b.CreateStack(ctx, aStackRef, "", nil)
assert.NoError(t, err)
assert.NotNil(t, aStack)
err = b.ImportDeployment(ctx, aStack, udep)
assert.NoError(t, err)
// Ensure the file has the string contents "<html@tags>"", not "\u003chtml\u0026tags\u003e"
// Grab the bucket interface to read the file with
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
chkpath := lb.stackPath(ctx, aStackRef.(*diyBackendReference))
bytes, err := lb.bucket.ReadAll(context.Background(), chkpath)
assert.NoError(t, err)
state := string(bytes)
assert.Contains(t, state, "<html@tags>")
}
func TestDIYBackendRejectsStackInitOptions(t *testing.T) {
t.Parallel()
// Here, we provide options that illegally specify a team on a
// backend that does not support teams. We expect this to create
// an error later when we call CreateStack.
illegalOptions := &backend.CreateStackOptions{Teams: []string{"red-team"}}
// • Create a mock diy backend
tmpDir := t.TempDir()
Enable perfsprint linter (#14813) <!--- 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. --> Prompted by a comment in another review: https://github.com/pulumi/pulumi/pull/14654#discussion_r1419995945 This lints that we don't use `fmt.Errorf` when `errors.New` will suffice, it also covers a load of other cases where `Sprintf` is sub-optimal. Most of these edits were made by running `perfsprint --fix`. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2023-12-12 12:19:42 +00:00
dirURI := "file://" + filepath.ToSlash(tmpDir)
diy, err := New(context.Background(), diagtest.LogSink(t), dirURI, nil)
assert.NoError(t, err)
ctx := context.Background()
// • Simulate `pulumi stack init`, passing non-nil init options
fakeStackRef, err := diy.ParseStackReference("organization/b/foobar")
assert.NoError(t, err)
_, err = diy.CreateStack(ctx, fakeStackRef, "", illegalOptions)
assert.ErrorIs(t, err, backend.ErrTeamsNotSupported)
}
func TestLegacyFolderStructure(t *testing.T) {
t.Parallel()
// Make a dummy stack file in the legacy location
tmpDir := t.TempDir()
err := os.MkdirAll(path.Join(tmpDir, ".pulumi", "stacks"), os.ModePerm)
require.NoError(t, err)
err = os.WriteFile(path.Join(tmpDir, ".pulumi", "stacks", "a.json"), []byte("{}"), os.ModePerm)
require.NoError(t, err)
// Login to a temp dir diy backend
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
require.NoError(t, err)
// Check the backend says it's NOT in project mode
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
assert.IsType(t, &legacyReferenceStore{}, lb.store)
// Check that list stack shows that stack
stacks, token, err := b.ListStacks(ctx, backend.ListStacksFilter{}, nil /* inContToken */)
assert.NoError(t, err)
assert.Nil(t, token)
assert.Len(t, stacks, 1)
assert.Equal(t, "a", stacks[0].Name().String())
// Create a new non-project stack
bRef, err := b.ParseStackReference("b")
assert.NoError(t, err)
assert.Equal(t, "b", bRef.String())
bStack, err := b.CreateStack(ctx, bRef, "", nil)
assert.NoError(t, err)
assert.Equal(t, "b", bStack.Ref().String())
assert.FileExists(t, path.Join(tmpDir, ".pulumi", "stacks", "b.json"))
}
func TestListStacksFilter(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
ctx := context.Background()
tmpDir := t.TempDir()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
require.NoError(t, err)
// Create two different project stack
aRef, err := b.ParseStackReference("organization/proj1/a")
assert.NoError(t, err)
_, err = b.CreateStack(ctx, aRef, "", nil)
assert.NoError(t, err)
bRef, err := b.ParseStackReference("organization/proj2/b")
assert.NoError(t, err)
_, err = b.CreateStack(ctx, bRef, "", nil)
assert.NoError(t, err)
// Check that list stack with a filter only shows one stack
filter := "proj1"
stacks, token, err := b.ListStacks(ctx, backend.ListStacksFilter{
Project: &filter,
}, nil /* inContToken */)
assert.NoError(t, err)
assert.Nil(t, token)
assert.Len(t, stacks, 1)
assert.Equal(t, "organization/proj1/a", stacks[0].Name().String())
}
func TestOptIntoLegacyFolderStructure(t *testing.T) {
t.Parallel()
tmpDir := t.TempDir()
ctx := context.Background()
s := make(env.MapStore)
s[env.DIYBackendLegacyLayout.Var().Name()] = "true"
b, err := newDIYBackend(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil,
&diyBackendOptions{Env: env.NewEnv(s)},
)
require.NoError(t, err)
// Verify that a new stack is created in the legacy location.
foo, err := b.ParseStackReference("foo")
require.NoError(t, err)
_, err = b.CreateStack(ctx, foo, "", nil)
require.NoError(t, err)
assert.FileExists(t, filepath.Join(tmpDir, ".pulumi", "stacks", "foo.json"))
}
// Verifies that the StackReference.String method
// takes the current project name into account,
// even if the current project name changes
// after the stack reference is created.
func TestStackReferenceString_currentProjectChange(t *testing.T) {
t.Parallel()
dir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(dir), nil)
require.NoError(t, err)
foo, err := b.ParseStackReference("organization/proj1/foo")
require.NoError(t, err)
bar, err := b.ParseStackReference("organization/proj2/bar")
require.NoError(t, err)
assert.Equal(t, "organization/proj1/foo", foo.String())
assert.Equal(t, "organization/proj2/bar", bar.String())
// Change the current project name
b.SetCurrentProject(&workspace.Project{Name: "proj1"})
assert.Equal(t, "foo", foo.String())
assert.Equal(t, "organization/proj2/bar", bar.String())
}
// Verifies that there's no data race in calling StackReference.String
// and diyBackend.SetCurrentProject concurrently.
func TestStackReferenceString_currentProjectChange_race(t *testing.T) {
t.Parallel()
const N = 1000
dir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(dir), nil)
require.NoError(t, err)
projects := make([]*workspace.Project, N)
refs := make([]backend.StackReference, N)
for i := 0; i < N; i++ {
name := fmt.Sprintf("proj%d", i)
projects[i] = &workspace.Project{Name: tokens.PackageName(name)}
refs[i], err = b.ParseStackReference(fmt.Sprintf("organization/%v/foo", name))
require.NoError(t, err)
}
// To exercise this data race, we'll have two goroutines.
// One goroutine will call StackReference.String repeatedly
// on all the stack references,
// and the other goroutine will call diyBackend.SetCurrentProject
// with all the projects.
var wg sync.WaitGroup
ready := make(chan struct{}) // both goroutines wait on this
wg.Add(1)
go func() {
defer wg.Done()
<-ready
for i := 0; i < N; i++ {
_ = refs[i].String()
}
}()
wg.Add(1)
go func() {
defer wg.Done()
<-ready
for i := 0; i < N; i++ {
b.SetCurrentProject(projects[i])
}
}()
close(ready) // start racing
wg.Wait()
}
func TestProjectFolderStructure(t *testing.T) {
t.Parallel()
// Login to a temp dir diy backend
// Make a dummy file in the legacy location which isn't a stack file, we should still automatically turn
// this into project mode.
tmpDir := t.TempDir()
err := os.MkdirAll(path.Join(tmpDir, ".pulumi", "plugins"), os.ModePerm)
require.NoError(t, err)
err = os.MkdirAll(path.Join(tmpDir, ".pulumi", "stacks"), os.ModePerm)
require.NoError(t, err)
err = os.WriteFile(path.Join(tmpDir, ".pulumi", "stacks", "a.txt"), []byte("{}"), os.ModePerm)
require.NoError(t, err)
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), nil)
assert.NoError(t, err)
// Check the backend says it's in project mode
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
assert.IsType(t, &projectReferenceStore{}, lb.store)
// Make a dummy stack file in the new project location
err = os.MkdirAll(path.Join(tmpDir, ".pulumi", "stacks", "testproj"), os.ModePerm)
assert.NoError(t, err)
err = os.WriteFile(path.Join(tmpDir, ".pulumi", "stacks", "testproj", "a.json"), []byte("{}"), os.ModePerm)
assert.NoError(t, err)
// Check that testproj is reported as existing
exists, err := b.DoesProjectExist(ctx, "", "testproj")
assert.NoError(t, err)
assert.True(t, exists)
// Check that list stack shows that stack
stacks, token, err := b.ListStacks(ctx, backend.ListStacksFilter{}, nil /* inContToken */)
assert.NoError(t, err)
assert.Nil(t, token)
assert.Len(t, stacks, 1)
assert.Equal(t, "organization/testproj/a", stacks[0].Name().String())
// Create a new project stack
bRef, err := b.ParseStackReference("organization/testproj/b")
assert.NoError(t, err)
assert.Equal(t, "organization/testproj/b", bRef.String())
bStack, err := b.CreateStack(ctx, bRef, "", nil)
assert.NoError(t, err)
assert.Equal(t, "organization/testproj/b", bStack.Ref().String())
assert.FileExists(t, path.Join(tmpDir, ".pulumi", "stacks", "testproj", "b.json"))
}
func chdir(t *testing.T, dir string) {
cwd, err := os.Getwd()
require.NoError(t, err)
require.NoError(t, os.Chdir(dir)) // Set directory
t.Cleanup(func() {
require.NoError(t, os.Chdir(cwd)) // Restore directory
restoredDir, err := os.Getwd()
require.NoError(t, err)
require.Equal(t, cwd, restoredDir)
})
}
//nolint:paralleltest // mutates cwd
func TestProjectNameMustMatch(t *testing.T) {
// Create a new project
projectDir := t.TempDir()
pyaml := filepath.Join(projectDir, "Pulumi.yaml")
err := os.WriteFile(pyaml, []byte("name: my-project\nruntime: test"), 0o600)
require.NoError(t, err)
proj, err := workspace.LoadProject(pyaml)
require.NoError(t, err)
chdir(t, projectDir)
// Login to a temp dir diy backend
tmpDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(tmpDir), proj)
require.NoError(t, err)
// Create a new implicit-project stack
aRef, err := b.ParseStackReference("a")
assert.NoError(t, err)
assert.Equal(t, "a", aRef.String())
aStack, err := b.CreateStack(ctx, aRef, "", nil)
assert.NoError(t, err)
assert.Equal(t, "a", aStack.Ref().String())
assert.FileExists(t, path.Join(tmpDir, ".pulumi", "stacks", "my-project", "a.json"))
// Create a new project stack with the wrong project name
bRef, err := b.ParseStackReference("organization/not-my-project/b")
assert.NoError(t, err)
assert.Equal(t, "organization/not-my-project/b", bRef.String())
bStack, err := b.CreateStack(ctx, bRef, "", nil)
assert.Error(t, err)
assert.Nil(t, bStack)
// Create a new project stack with the right project name
cRef, err := b.ParseStackReference("organization/my-project/c")
assert.NoError(t, err)
assert.Equal(t, "c", cRef.String())
cStack, err := b.CreateStack(ctx, cRef, "", nil)
assert.NoError(t, err)
assert.Equal(t, "c", cStack.Ref().String())
assert.FileExists(t, path.Join(tmpDir, ".pulumi", "stacks", "my-project", "c.json"))
}
func TestNew_legacyFileWarning(t *testing.T) {
t.Parallel()
// Verifies the names of files printed in warnings
// when legacy files are found while running in project mode.
tests := []struct {
desc string
files map[string]string
env env.MapStore
wantOut string
}{
{
desc: "no legacy stacks",
files: map[string]string{
// Should ignore non-stack files.
".pulumi/foo/extraneous_file": "",
},
},
{
desc: "legacy stacks",
files: map[string]string{
".pulumi/stacks/a.json": "{}",
".pulumi/stacks/b.json": "{}",
".pulumi/stacks/c.json.bak": "{}", // should ignore backup files
},
wantOut: "warning: Found legacy stack files in state store:\n" +
" - a\n" +
" - b\n" +
"Please run 'pulumi state upgrade' to migrate them to the new format.\n" +
Rename self-managed to diy-backend (#15268) <!--- 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. --> We're moving away from referring to filestate as "self managed" backends, preferring to refer to this as "DIY" backends going forward. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-01-30 09:00:15 +00:00
"Set PULUMI_DIY_BACKEND_NO_LEGACY_WARNING=1 to disable this warning.\n",
},
{
desc: "warning opt-out",
files: map[string]string{
".pulumi/stacks/a.json": "{}",
".pulumi/stacks/b.json": "{}",
},
env: map[string]string{
Rename self-managed to diy-backend (#15268) <!--- 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. --> We're moving away from referring to filestate as "self managed" backends, preferring to refer to this as "DIY" backends going forward. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-01-30 09:00:15 +00:00
"PULUMI_DIY_BACKEND_NO_LEGACY_WARNING": "true",
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.desc, func(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
ctx := context.Background()
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/meta.yaml", []byte("version: 1"), nil),
"write meta.yaml")
for path, contents := range tt.files {
require.NoError(t, bucket.WriteAll(ctx, path, []byte(contents), nil),
"write %q", path)
}
var buff bytes.Buffer
sink := diag.DefaultSink(io.Discard, &buff, diag.FormatOptions{Color: colors.Never})
_, err = newDIYBackend(ctx, sink, "file://"+filepath.ToSlash(stateDir), nil,
&diyBackendOptions{Env: env.NewEnv(tt.env)})
require.NoError(t, err)
assert.Equal(t, tt.wantOut, buff.String())
})
}
}
func TestLegacyUpgrade(t *testing.T) {
t.Parallel()
// Make a dummy stack file in the legacy location
tmpDir := t.TempDir()
err := os.MkdirAll(path.Join(tmpDir, ".pulumi", "stacks"), os.ModePerm)
require.NoError(t, err)
err = os.WriteFile(path.Join(tmpDir, ".pulumi", "stacks", "a.json"), []byte(`{
"latest": {
"resources": [
{
"type": "package:module:resource",
"urn": "urn:pulumi:stack::project::package:module:resource::name"
}
]
}
}`), os.ModePerm)
require.NoError(t, err)
var output bytes.Buffer
sink := diag.DefaultSink(&output, &output, diag.FormatOptions{Color: colors.Never})
// Login to a temp dir diy backend
ctx := context.Background()
b, err := New(ctx, sink, "file://"+filepath.ToSlash(tmpDir), nil)
require.NoError(t, err)
// Check the backend says it's NOT in project mode
lb, ok := b.(*diyBackend)
assert.True(t, ok)
assert.NotNil(t, lb)
assert.IsType(t, &legacyReferenceStore{}, lb.store)
err = lb.Upgrade(ctx, nil /* opts */)
require.NoError(t, err)
assert.IsType(t, &projectReferenceStore{}, lb.store)
assert.Contains(t, output.String(), "Upgraded 1 stack(s) to project mode")
// Check that a has been moved
aStackRef, err := lb.parseStackReference("organization/project/a")
require.NoError(t, err)
stackFileExists, err := lb.bucket.Exists(ctx, lb.stackPath(ctx, aStackRef))
require.NoError(t, err)
assert.True(t, stackFileExists)
// Write b.json and upgrade again
err = os.WriteFile(path.Join(tmpDir, ".pulumi", "stacks", "b.json"), []byte(`{
"latest": {
"resources": [
{
"type": "package:module:resource",
"urn": "urn:pulumi:stack::other-project::package:module:resource::name"
}
]
}
}`), os.ModePerm)
require.NoError(t, err)
err = lb.Upgrade(ctx, nil /* opts */)
require.NoError(t, err)
// Check that b has been moved
bStackRef, err := lb.parseStackReference("organization/other-project/b")
require.NoError(t, err)
stackFileExists, err = lb.bucket.Exists(ctx, lb.stackPath(ctx, bStackRef))
require.NoError(t, err)
assert.True(t, stackFileExists)
}
func TestLegacyUpgrade_partial(t *testing.T) {
t.Parallel()
// Verifies that we can upgrade a subset of stacks.
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
ctx := context.Background()
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/stacks/foo.json", []byte(`{
"latest": {
"resources": [
{
"type": "package:module:resource",
"urn": "urn:pulumi:stack::project::package:module:resource::name"
}
]
}
}`), nil))
require.NoError(t,
// no resources, can't guess project name
bucket.WriteAll(ctx, ".pulumi/stacks/bar.json",
[]byte(`{"latest": {"resources": []}}`), nil))
var buff bytes.Buffer
sink := diag.DefaultSink(io.Discard, &buff, diag.FormatOptions{Color: colors.Never})
b, err := New(ctx, sink, "file://"+filepath.ToSlash(stateDir), nil)
require.NoError(t, err)
require.NoError(t, b.Upgrade(ctx, nil /* opts */))
assert.Contains(t, buff.String(), `Skipping stack "bar": no project name found`)
exists, err := bucket.Exists(ctx, ".pulumi/stacks/project/foo.json")
require.NoError(t, err)
assert.True(t, exists, "foo was not migrated")
ref, err := b.ParseStackReference("organization/project/foo")
require.NoError(t, err)
assert.Equal(t, tokens.QName("organization/project/foo"), ref.FullyQualifiedName())
}
// When a stack project could not be determined,
// we should fill it in with ProjectsForDetachedStacks.
func TestLegacyUpgrade_ProjectsForDetachedStacks(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
// Write a few empty stacks.
// These stacks have no resources, so we can't guess the project name.
ctx := context.Background()
for _, stack := range []string{"foo", "bar", "baz"} {
statePath := path.Join(".pulumi", "stacks", stack+".json")
require.NoError(t,
bucket.WriteAll(ctx, statePath,
[]byte(`{"latest": {"resources": []}}`), nil),
"write stack %s", stack)
}
var stderr bytes.Buffer
sink := diag.DefaultSink(io.Discard, &stderr, diag.FormatOptions{Color: colors.Never})
b, err := New(ctx, sink, "file://"+filepath.ToSlash(stateDir), nil)
require.NoError(t, err, "initialize backend")
// For the first two stacks, we'll return project names to upgrade them.
// For the third stack, we will not set a project name, and it should be skipped.
err = b.Upgrade(ctx, &UpgradeOptions{
Add tokens.StackName (#14487) <!--- 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. --> This adds a new type `tokens.StackName` which is a relatively strongly typed container for a stack name. The only weakly typed aspect of it is Go will always allow the "zero" value to be created for a struct, which for a stack name is the empty string which is invalid. To prevent introducing unexpected empty strings when working with stack names the `String()` method will panic for zero initialized stack names. Apart from the zero value, all other instances of `StackName` are via `ParseStackName` which returns a descriptive error if the string is not valid. This PR only updates "pkg/" to use this type. There are a number of places in "sdk/" which could do with this type as well, but there's no harm in doing a staggered roll out, and some parts of "sdk/" are user facing and will probably have to stay on the current `tokens.Name` and `tokens.QName` types. There are two places in the system where we panic on invalid stack names, both in the http backend. This _should_ be fine as we've had long standing validation that stacks created in the service are valid stack names. Just in case people have managed to introduce invalid stack names, there is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn off the validation _and_ panicing for stack names. Users can use that to temporarily disable the validation and continue working, but it should only be seen as a temporary measure. If they have invalid names they should rename them, or if they think they should be valid raise an issue with us to change the validation code. ## 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 - [ ] 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. --> - [ ] 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. -->
2023-11-15 07:44:54 +00:00
ProjectsForDetachedStacks: func(stacks []tokens.StackName) (projects []tokens.Name, err error) {
assert.ElementsMatch(t, []tokens.StackName{
tokens.MustParseStackName("foo"),
tokens.MustParseStackName("bar"),
tokens.MustParseStackName("baz"),
}, stacks)
projects = make([]tokens.Name, len(stacks))
for idx, stack := range stacks {
Add tokens.StackName (#14487) <!--- 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. --> This adds a new type `tokens.StackName` which is a relatively strongly typed container for a stack name. The only weakly typed aspect of it is Go will always allow the "zero" value to be created for a struct, which for a stack name is the empty string which is invalid. To prevent introducing unexpected empty strings when working with stack names the `String()` method will panic for zero initialized stack names. Apart from the zero value, all other instances of `StackName` are via `ParseStackName` which returns a descriptive error if the string is not valid. This PR only updates "pkg/" to use this type. There are a number of places in "sdk/" which could do with this type as well, but there's no harm in doing a staggered roll out, and some parts of "sdk/" are user facing and will probably have to stay on the current `tokens.Name` and `tokens.QName` types. There are two places in the system where we panic on invalid stack names, both in the http backend. This _should_ be fine as we've had long standing validation that stacks created in the service are valid stack names. Just in case people have managed to introduce invalid stack names, there is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn off the validation _and_ panicing for stack names. Users can use that to temporarily disable the validation and continue working, but it should only be seen as a temporary measure. If they have invalid names they should rename them, or if they think they should be valid raise an issue with us to change the validation code. ## 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 - [ ] 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. --> - [ ] 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. -->
2023-11-15 07:44:54 +00:00
switch stack.String() {
case "foo":
projects[idx] = "proj1"
case "bar":
projects[idx] = "proj2"
case "baz":
// Leave baz detached.
}
}
return projects, nil
},
})
require.NoError(t, err)
for _, stack := range []string{"foo", "bar"} {
assert.NotContains(t, stderr.String(), fmt.Sprintf("Skipping stack %q", stack))
}
assert.Contains(t, stderr.String(), fmt.Sprintf("Skipping stack %q", "baz"))
wantFiles := []string{
".pulumi/stacks/proj1/foo.json",
".pulumi/stacks/proj2/bar.json",
".pulumi/stacks/baz.json",
}
for _, file := range wantFiles {
exists, err := bucket.Exists(ctx, file)
require.NoError(t, err, "exists(%q)", file)
assert.True(t, exists, "file %q must exist", file)
}
}
// When a stack project could not be determined
// and ProjectsForDetachedStacks returns an error,
// the upgrade should fail.
func TestLegacyUpgrade_ProjectsForDetachedStacks_error(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
ctx := context.Background()
// We have one stack with a guessable project name, and one without.
// If ProjectsForDetachedStacks returns an error, the upgrade should
// fail for both because the user likely cancelled the upgrade.
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/stacks/foo.json", []byte(`{
"latest": {
"resources": [
{
"type": "package:module:resource",
"urn": "urn:pulumi:stack::project::package:module:resource::name"
}
]
}
}`), nil))
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/stacks/bar.json",
[]byte(`{"latest": {"resources": []}}`), nil))
sink := diag.DefaultSink(io.Discard, iotest.LogWriter(t), diag.FormatOptions{Color: colors.Never})
b, err := New(ctx, sink, "file://"+filepath.ToSlash(stateDir), nil)
require.NoError(t, err)
giveErr := errors.New("canceled operation")
err = b.Upgrade(ctx, &UpgradeOptions{
Add tokens.StackName (#14487) <!--- 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. --> This adds a new type `tokens.StackName` which is a relatively strongly typed container for a stack name. The only weakly typed aspect of it is Go will always allow the "zero" value to be created for a struct, which for a stack name is the empty string which is invalid. To prevent introducing unexpected empty strings when working with stack names the `String()` method will panic for zero initialized stack names. Apart from the zero value, all other instances of `StackName` are via `ParseStackName` which returns a descriptive error if the string is not valid. This PR only updates "pkg/" to use this type. There are a number of places in "sdk/" which could do with this type as well, but there's no harm in doing a staggered roll out, and some parts of "sdk/" are user facing and will probably have to stay on the current `tokens.Name` and `tokens.QName` types. There are two places in the system where we panic on invalid stack names, both in the http backend. This _should_ be fine as we've had long standing validation that stacks created in the service are valid stack names. Just in case people have managed to introduce invalid stack names, there is the `PULUMI_DISABLE_VALIDATION` environment variable which will turn off the validation _and_ panicing for stack names. Users can use that to temporarily disable the validation and continue working, but it should only be seen as a temporary measure. If they have invalid names they should rename them, or if they think they should be valid raise an issue with us to change the validation code. ## 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 - [ ] 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. --> - [ ] 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. -->
2023-11-15 07:44:54 +00:00
ProjectsForDetachedStacks: func(stacks []tokens.StackName) (projects []tokens.Name, err error) {
assert.Equal(t, []tokens.StackName{
tokens.MustParseStackName("bar"),
}, stacks)
return nil, giveErr
},
})
require.Error(t, err)
assert.ErrorIs(t, err, giveErr)
wantFiles := []string{
".pulumi/stacks/foo.json",
".pulumi/stacks/bar.json",
}
for _, file := range wantFiles {
exists, err := bucket.Exists(ctx, file)
require.NoError(t, err, "exists(%q)", file)
assert.True(t, exists, "file %q must exist", file)
}
}
// If an upgrade failed because we couldn't write the meta.yaml,
// the stacks should be left in legacy mode.
func TestLegacyUpgrade_writeMetaError(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
ctx := context.Background()
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/stacks/foo.json", []byte(`{
"latest": {
"resources": [
{
"type": "package:module:resource",
"urn": "urn:pulumi:stack::project::package:module:resource::name"
}
]
}
}`), nil))
// To prevent a write to meta.yaml, we'll create a directory with that name.
// The system will reject creating a file with the same name.
require.NoError(t, os.MkdirAll(filepath.Join(stateDir, ".pulumi", "meta.yaml"), 0o755))
var buff bytes.Buffer
sink := diag.DefaultSink(io.Discard, &buff, diag.FormatOptions{Color: colors.Never})
b, err := New(ctx, sink, "file://"+filepath.ToSlash(stateDir), nil)
require.NoError(t, err)
require.Error(t, b.Upgrade(ctx, nil /* opts */))
stderr := buff.String()
assert.Contains(t, stderr, "error: Could not write new state metadata file")
assert.Contains(t, stderr, "Please verify that the storage is writable")
assert.FileExists(t, filepath.Join(stateDir, ".pulumi", "stacks", "foo.json"),
"foo.json should not have been upgraded")
}
func TestNew_unsupportedStoreVersion(t *testing.T) {
t.Parallel()
// Verifies that we fail to initialize a backend if the store version is
// newer than the CLI version.
stateDir := t.TempDir()
bucket, err := fileblob.OpenBucket(stateDir, nil)
require.NoError(t, err)
// Set up a meta.yaml "from the future".
ctx := context.Background()
require.NoError(t,
bucket.WriteAll(ctx, ".pulumi/meta.yaml", []byte("version: 999999999"), nil))
_, err = New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(stateDir), nil)
assert.ErrorContains(t, err, "state store unsupported")
assert.ErrorContains(t, err, "'meta.yaml' version (999999999) is not supported")
}
// TestSerializeTimestampRFC3339 captures our expectations that Created and Modified will be serialized to
// RFC3339.
func TestSerializeTimestampRFC3339(t *testing.T) {
t.Parallel()
created := time.Now().UTC()
modified := created.Add(time.Hour)
deployment, err := makeUntypedDeploymentTimestamp("b", "123abc",
"v1:C7H2a7/Ietk=:v1:yfAd1zOi6iY9DRIB:dumdsr+H89VpHIQWdB01XEFqYaYjAg==", &created, &modified)
assert.NoError(t, err)
createdStr := created.Format(time.RFC3339Nano)
modifiedStr := modified.Format(time.RFC3339Nano)
assert.Contains(t, string(deployment.Deployment), createdStr)
assert.Contains(t, string(deployment.Deployment), modifiedStr)
}
func TestUpgrade_manyFailures(t *testing.T) {
t.Parallel()
const (
numStacks = 100
badStackBody = `{"latest": {"resources": []}}`
)
tmpDir := t.TempDir()
bucket, err := fileblob.OpenBucket(tmpDir, nil)
require.NoError(t, err)
ctx := context.Background()
for i := 0; i < numStacks; i++ {
stackPath := path.Join(".pulumi", "stacks", fmt.Sprintf("stack-%d.json", i))
require.NoError(t, bucket.WriteAll(ctx, stackPath, []byte(badStackBody), nil))
}
var output bytes.Buffer
sink := diag.DefaultSink(io.Discard, &output, diag.FormatOptions{Color: colors.Never})
// Login to a temp dir diy backend
b, err := New(ctx, sink, "file://"+filepath.ToSlash(tmpDir), nil)
require.NoError(t, err)
require.NoError(t, b.Upgrade(ctx, nil /* opts */))
out := output.String()
for i := 0; i < numStacks; i++ {
assert.Contains(t, out, fmt.Sprintf(`Skipping stack "stack-%d"`, i))
}
}
func TestCreateStack_gzip(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
ctx := context.Background()
s := make(env.MapStore)
s[env.DIYBackendGzip.Var().Name()] = "true"
b, err := newDIYBackend(
ctx,
diagtest.LogSink(t), "file://"+filepath.ToSlash(stateDir),
&workspace.Project{Name: "testproj"},
&diyBackendOptions{Env: env.NewEnv(s)},
)
require.NoError(t, err)
fooRef, err := b.ParseStackReference("foo")
require.NoError(t, err)
_, err = b.CreateStack(ctx, fooRef, "", nil)
require.NoError(t, err)
Rename self-managed to diy-backend (#15268) <!--- 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. --> We're moving away from referring to filestate as "self managed" backends, preferring to refer to this as "DIY" backends going forward. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-01-30 09:00:15 +00:00
// With PULUMI_DIY_BACKEND_GZIP enabled,
// we'll store state into gzipped files.
assert.FileExists(t, filepath.Join(stateDir, ".pulumi", "stacks", "testproj", "foo.json.gz"))
}
func TestCreateStack_retainCheckpoints(t *testing.T) {
t.Parallel()
stateDir := t.TempDir()
ctx := context.Background()
s := make(env.MapStore)
s[env.DIYBackendRetainCheckpoints.Var().Name()] = "true"
b, err := newDIYBackend(
ctx,
diagtest.LogSink(t), "file://"+filepath.ToSlash(stateDir),
&workspace.Project{Name: "testproj"},
&diyBackendOptions{Env: env.NewEnv(s)},
)
require.NoError(t, err)
fooRef, err := b.ParseStackReference("foo")
require.NoError(t, err)
_, err = b.CreateStack(ctx, fooRef, "", nil)
require.NoError(t, err)
// With PULUMI_RETAIN_CHECKPOINTS enabled,
// we'll make copies of files under $orig.$timestamp.
// Since we can't predict the timestamp,
// we'll just check that there's at least one file
// with a timestamp extension.
got, err := filepath.Glob(
filepath.Join(stateDir, ".pulumi", "stacks", "testproj", "foo.json.*"))
require.NoError(t, err)
checkpointExtRe := regexp.MustCompile(`^\.[0-9]+$`)
var found bool
for _, f := range got {
if checkpointExtRe.MatchString(filepath.Ext(f)) {
found = true
break
}
}
assert.True(t, found,
"file with a timestamp extension not found in %v", got)
}
Validate snapshots from service on load (#14046) <!--- 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. --> In the filestate code we would always run loaded snapshots through VerifyIntegrity before using them. The httpstate didn't have any similar checks. This brings the two backends into alignment, reusing the option from before that was just for filestate (--disable-integrity-checking). At some point we should further align these so that httpstate also validates the snapshots it has written out, like filestate does today. ## 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 - [ ] 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. -->
2023-12-04 15:12:56 +00:00
//nolint:paralleltest // mutates global state
func TestDisableIntegrityChecking(t *testing.T) {
stateDir := t.TempDir()
ctx := context.Background()
b, err := New(ctx, diagtest.LogSink(t), "file://"+filepath.ToSlash(stateDir), &workspace.Project{Name: "testproj"})
require.NoError(t, err)
ref, err := b.ParseStackReference("stack")
require.NoError(t, err)
s, err := b.CreateStack(ctx, ref, "", nil)
require.NoError(t, err)
// make up a bad stack
deployment := apitype.UntypedDeployment{
Version: 3,
Deployment: json.RawMessage(`{
"resources": [
{
"urn": "urn:pulumi:stack::proj::type::name1",
"type": "type",
"parent": "urn:pulumi:stack::proj::type::name2"
},
{
"urn": "urn:pulumi:stack::proj::type::name2",
"type": "type"
}
]
}`),
}
// Import deployment doesn't verify the deployment
err = b.ImportDeployment(ctx, s, &deployment)
require.NoError(t, err)
backend.DisableIntegrityChecking = false
Add Base64SecretsProvider for testing (#15017) <!--- 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. --> To support plugin based secret providers like (e.g. AGE https://github.com/pulumi/pulumi/issues/11493) the default secrets provider will need to become more complicated, requiring access to a plugin host at least. As such for tests it will be simpler to have a basic secrets provider that only supports base64 and can be created without any setup. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-01-05 08:32:56 +00:00
snap, err := s.Snapshot(ctx, b64.Base64SecretsProvider)
Validate snapshots from service on load (#14046) <!--- 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. --> In the filestate code we would always run loaded snapshots through VerifyIntegrity before using them. The httpstate didn't have any similar checks. This brings the two backends into alignment, reusing the option from before that was just for filestate (--disable-integrity-checking). At some point we should further align these so that httpstate also validates the snapshots it has written out, like filestate does today. ## 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 - [ ] 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. -->
2023-12-04 15:12:56 +00:00
require.ErrorContains(t, err,
"child resource urn:pulumi:stack::proj::type::name1's parent urn:pulumi:stack::proj::type::name2 comes after it")
assert.Nil(t, snap)
backend.DisableIntegrityChecking = true
Add Base64SecretsProvider for testing (#15017) <!--- 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. --> To support plugin based secret providers like (e.g. AGE https://github.com/pulumi/pulumi/issues/11493) the default secrets provider will need to become more complicated, requiring access to a plugin host at least. As such for tests it will be simpler to have a basic secrets provider that only supports base64 and can be created without any setup. ## 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. --> - [ ] I have added tests that prove my fix is effective or that my feature works <!--- User-facing changes require a CHANGELOG entry. --> - [ ] 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. -->
2024-01-05 08:32:56 +00:00
snap, err = s.Snapshot(ctx, b64.Base64SecretsProvider)
Validate snapshots from service on load (#14046) <!--- 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. --> In the filestate code we would always run loaded snapshots through VerifyIntegrity before using them. The httpstate didn't have any similar checks. This brings the two backends into alignment, reusing the option from before that was just for filestate (--disable-integrity-checking). At some point we should further align these so that httpstate also validates the snapshots it has written out, like filestate does today. ## 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 - [ ] 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. -->
2023-12-04 15:12:56 +00:00
require.NoError(t, err)
assert.NotNil(t, snap)
}