// Copyright 2016-2021, Pulumi Corporation. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package auto import ( "bytes" "context" "fmt" "math/rand" "os" "os/exec" "path/filepath" "regexp" "strings" "testing" "time" "github.com/stretchr/testify/require" "github.com/blang/semver" "github.com/stretchr/testify/assert" "github.com/pulumi/pulumi/sdk/v3/go/auto/events" "github.com/pulumi/pulumi/sdk/v3/go/auto/optdestroy" "github.com/pulumi/pulumi/sdk/v3/go/auto/optpreview" "github.com/pulumi/pulumi/sdk/v3/go/auto/optrefresh" "github.com/pulumi/pulumi/sdk/v3/go/auto/optremove" "github.com/pulumi/pulumi/sdk/v3/go/auto/optup" "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" "github.com/pulumi/pulumi/sdk/v3/go/pulumi" "github.com/pulumi/pulumi/sdk/v3/go/pulumi/config" ) var pulumiOrg = getTestOrg() const pName = "testproj" const agent = "pulumi/pulumi/test" func TestWorkspaceSecretsProvider(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) opts := []LocalWorkspaceOption{ SecretsProvider("passphrase"), EnvVars(map[string]string{ "PULUMI_CONFIG_PASSPHRASE": "password", }), } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }, opts...) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { err := os.Unsetenv("PULUMI_CONFIG_PASSPHRASE") assert.Nil(t, err, "failed to unset EnvVar.") // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() passwordVal := "Password1234!" err = s.SetConfig(ctx, "MySecretDatabasePassword", ConfigValue{Value: passwordVal, Secret: true}) if err != nil { t.Errorf("setConfig failed, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- get config -- conf, err := s.GetConfig(ctx, "MySecretDatabasePassword") if err != nil { t.Errorf("GetConfig failed, err: %v", err) t.FailNow() } assert.Equal(t, passwordVal, conf.Value) assert.Equal(t, true, conf.Secret) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestRemoveWithForce(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize pDir := filepath.Join(".", "test", "testproj") s, err := NewStackLocalSource(ctx, stackName, pDir) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // Set environment variables scoped to the workspace. envvars := map[string]string{ "foo": "bar", "barfoo": "foobar", } err = s.Workspace().SetEnvVars(envvars) assert.Nil(t, err, "failed to set environment values") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after setting many") s.Workspace().SetEnvVar("bar", "buzz") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment value after setting") s.Workspace().UnsetEnvVar("bar") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after unsetting.") // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) const permalinkSearchStr = "https://app.pulumi.com" var startRegex = regexp.MustCompile(permalinkSearchStr) permalink, err := GetPermalink(res.StdOut) assert.Nil(t, err, "failed to get permalink.") assert.True(t, startRegex.MatchString(permalink)) if err = s.Workspace().RemoveStack(ctx, stackName, optremove.Force()); err != nil { t.Errorf("remove stack with force failed") t.FailNow() } // to make sure stack was removed err = s.Workspace().SelectStack(ctx, s.Name()) assert.Error(t, err) assert.True(t, strings.Contains(err.Error(), "no stack named")) } func TestNewStackLocalSource(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize pDir := filepath.Join(".", "test", "testproj") s, err := NewStackLocalSource(ctx, stackName, pDir) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // Set environment variables scoped to the workspace. envvars := map[string]string{ "foo": "bar", "barfoo": "foobar", } err = s.Workspace().SetEnvVars(envvars) assert.Nil(t, err, "failed to set environment values") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after setting many") s.Workspace().SetEnvVar("bar", "buzz") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment value after setting") s.Workspace().UnsetEnvVar("bar") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after unsetting.") // -- pulumi up -- res, err := s.Up(ctx, optup.UserAgent(agent)) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) const permalinkSearchStr = "https://app.pulumi.com" var startRegex = regexp.MustCompile(permalinkSearchStr) permalink, err := GetPermalink(res.StdOut) assert.Nil(t, err, "failed to get permalink.") assert.True(t, startRegex.MatchString(permalink)) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh), optpreview.UserAgent(agent)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx, optrefresh.UserAgent(agent)) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx, optdestroy.UserAgent(agent)) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestUpsertStackLocalSource(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize pDir := filepath.Join(".", "test", "testproj") s, err := UpsertStackLocalSource(ctx, stackName, pDir) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // Set environment variables scoped to the workspace. envvars := map[string]string{ "foo": "bar", "barfoo": "foobar", } err = s.Workspace().SetEnvVars(envvars) assert.Nil(t, err, "failed to set environment values") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after setting many") s.Workspace().SetEnvVar("bar", "buzz") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment value after setting") s.Workspace().UnsetEnvVar("bar") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after unsetting.") // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func rangeIn(low, hi int) int { rand.Seed(time.Now().UnixNano()) return low + rand.Intn(hi-low) //nolint:gosec } func TestNewStackRemoteSource(t *testing.T) { ctx := context.Background() pName := "go_remote_proj" sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } repo := GitRepo{ URL: "https://github.com/pulumi/test-repo.git", ProjectPath: "goproj", } // initialize s, err := NewStackRemoteSource(ctx, stackName, repo) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestUpsertStackRemoteSource(t *testing.T) { ctx := context.Background() pName := "go_remote_proj" sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } repo := GitRepo{ URL: "https://github.com/pulumi/test-repo.git", ProjectPath: "goproj", } // initialize s, err := UpsertStackRemoteSource(ctx, stackName, repo) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestNewStackRemoteSourceWithSetup(t *testing.T) { ctx := context.Background() pName := "go_remote_proj" sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } binName := "examplesBinary" repo := GitRepo{ URL: "https://github.com/pulumi/test-repo.git", ProjectPath: "goproj", Setup: func(ctx context.Context, workspace Workspace) error { cmd := exec.Command("go", "build", "-o", binName, "main.go") cmd.Dir = workspace.WorkDir() return cmd.Run() }, } project := workspace.Project{ Name: tokens.PackageName(pName), Runtime: workspace.NewProjectRuntimeInfo("go", map[string]interface{}{ "binary": binName, }), } // initialize s, err := NewStackRemoteSource(ctx, stackName, repo, Project(project)) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestUpsertStackRemoteSourceWithSetup(t *testing.T) { ctx := context.Background() pName := "go_remote_proj" sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } binName := "examplesBinary" repo := GitRepo{ URL: "https://github.com/pulumi/test-repo.git", ProjectPath: "goproj", Setup: func(ctx context.Context, workspace Workspace) error { cmd := exec.Command("go", "build", "-o", binName, "main.go") cmd.Dir = workspace.WorkDir() return cmd.Run() }, } project := workspace.Project{ Name: tokens.PackageName(pName), Runtime: workspace.NewProjectRuntimeInfo("go", map[string]interface{}{ "binary": binName, }), } // initialize or select s, err := UpsertStackRemoteSource(ctx, stackName, repo, Project(project)) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestNewStackInlineSource(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx, optup.UserAgent(agent)) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) assert.Greater(t, res.Summary.Version, 0) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh), optpreview.UserAgent(agent)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx, optrefresh.UserAgent(agent)) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx, optdestroy.UserAgent(agent)) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestUpsertStackInlineSource(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize or select s, err := UpsertStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) // -- pulumi refresh -- ref, err := s.Refresh(ctx) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestNestedStackFails(t *testing.T) { // FIXME: see https://github.com/pulumi/pulumi/issues/5301 t.Skip("skipping test, see pulumi/pulumi#5301") testCtx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) parentstackName := FullyQualifiedStackName(pulumiOrg, "parent", sName) nestedstackName := FullyQualifiedStackName(pulumiOrg, "nested", sName) nestedStack, err := NewStackInlineSource(testCtx, nestedstackName, "nested", func(ctx *pulumi.Context) error { ctx.Export("exp_static", pulumi.String("foo")) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } // initialize s, err := NewStackInlineSource(testCtx, parentstackName, "parent", func(ctx *pulumi.Context) error { _, err := nestedStack.Up(testCtx) return err }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(testCtx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") err = nestedStack.Workspace().RemoveStack(testCtx, nestedStack.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() result, err := s.Up(testCtx) t.Log(result) assert.Error(t, err) assert.Contains(t, err.Error(), "nested stack operations are not supported") // -- pulumi destroy -- dRes, err := s.Destroy(testCtx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) dRes, err = nestedStack.Destroy(testCtx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestProgressStreams(t *testing.T) { ctx := context.Background() pName := "inline_progress_streams" sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- var upOut bytes.Buffer res, err := s.Up(ctx, optup.ProgressStreams(&upOut)) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, upOut.String(), res.StdOut, "expected stdout writers to contain same contents") // -- pulumi refresh -- var refOut bytes.Buffer ref, err := s.Refresh(ctx, optrefresh.ProgressStreams(&refOut)) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, refOut.String(), ref.StdOut, "expected stdout writers to contain same contents") // -- pulumi destroy -- var desOut bytes.Buffer dRes, err := s.Destroy(ctx, optdestroy.ProgressStreams(&desOut)) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, desOut.String(), dRes.StdOut, "expected stdout writers to contain same contents") } func TestImportExportStack(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // -- pulumi up -- _, err = s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } // -- pulumi stack export -- state, err := s.Export(ctx) if err != nil { t.Errorf("export failed, err: %v", err) t.FailNow() } // -- pulumi stack import -- err = s.Import(ctx, state) if err != nil { t.Errorf("import failed, err: %v", err) t.FailNow() } // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestNestedConfig(t *testing.T) { if getTestOrg() != "pulumi-test" { return } ctx := context.Background() stackName := FullyQualifiedStackName(pulumiOrg, "nested_config", "dev") // initialize pDir := filepath.Join(".", "test", "nested_config") s, err := UpsertStackLocalSource(ctx, stackName, pDir) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } // Also retrieve the stack settings directly from the yaml file and // make sure the config agrees with the config loaded by Pulumi. stackSettings, err := s.Workspace().StackSettings(ctx, stackName) require.NoError(t, err) confKeys := map[string]bool{} for k := range stackSettings.Config { confKeys[k.String()] = true } allConfig, err := s.GetAllConfig(ctx) if err != nil { t.Errorf("failed to get config, err: %v", err) t.FailNow() } allConfKeys := map[string]bool{} for k := range allConfig { allConfKeys[k] = true } assert.Equal(t, confKeys, allConfKeys) assert.NotEmpty(t, confKeys) outerVal, ok := allConfig["nested_config:outer"] assert.True(t, ok) assert.True(t, outerVal.Secret) assert.JSONEq(t, "{\"inner\":\"my_secret\", \"other\": \"something_else\"}", outerVal.Value) listVal, ok := allConfig["nested_config:myList"] assert.True(t, ok) assert.False(t, listVal.Secret) assert.JSONEq(t, "[\"one\",\"two\",\"three\"]", listVal.Value) outer, err := s.GetConfig(ctx, "outer") if err != nil { t.Errorf("failed to get config, err: %v", err) t.FailNow() } assert.True(t, outer.Secret) assert.JSONEq(t, "{\"inner\":\"my_secret\", \"other\": \"something_else\"}", outer.Value) list, err := s.GetConfig(ctx, "myList") if err != nil { t.Errorf("failed to get config, err: %v", err) t.FailNow() } assert.False(t, list.Secret) assert.JSONEq(t, "[\"one\",\"two\",\"three\"]", list.Value) } func TestStructuredOutput(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize pDir := filepath.Join(".", "test", "testproj") s, err := UpsertStackLocalSource(ctx, stackName, pDir) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } // Set environment variables scoped to the workspace. envvars := map[string]string{ "foo": "bar", "barfoo": "foobar", } err = s.Workspace().SetEnvVars(envvars) assert.Nil(t, err, "failed to set environment values") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after setting many") s.Workspace().SetEnvVar("bar", "buzz") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment value after setting") s.Workspace().UnsetEnvVar("bar") envvars = s.Workspace().GetEnvVars() assert.NotNil(t, envvars, "failed to get environment values after unsetting.") // -- pulumi up -- var upEvents []events.EngineEvent upCh := make(chan events.EngineEvent) go collectEvents(upCh, &upEvents) res, err := s.Up(ctx, optup.EventStreams(upCh)) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, 3, len(res.Outputs), "expected two plain outputs") assert.Equal(t, "foo", res.Outputs["exp_static"].Value) assert.False(t, res.Outputs["exp_static"].Secret) assert.Equal(t, "abc", res.Outputs["exp_cfg"].Value) assert.False(t, res.Outputs["exp_cfg"].Secret) assert.Equal(t, "secret", res.Outputs["exp_secret"].Value) assert.True(t, res.Outputs["exp_secret"].Secret) assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) assert.True(t, containsSummary(upEvents)) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) prev, err := s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } assert.Equal(t, 1, prev.ChangeSummary[apitype.OpSame]) steps := countSteps(previewEvents) assert.Equal(t, 1, steps) assert.True(t, containsSummary(previewEvents)) // -- pulumi refresh -- var refreshEvents []events.EngineEvent refCh := make(chan events.EngineEvent) go collectEvents(refCh, &refreshEvents) ref, err := s.Refresh(ctx, optrefresh.EventStreams(refCh)) if err != nil { t.Errorf("refresh failed, err: %v", err) t.FailNow() } assert.Equal(t, "refresh", ref.Summary.Kind) assert.Equal(t, "succeeded", ref.Summary.Result) assert.True(t, containsSummary(refreshEvents)) // -- pulumi destroy -- var destroyEvents []events.EngineEvent desCh := make(chan events.EngineEvent) go collectEvents(desCh, &destroyEvents) dRes, err := s.Destroy(ctx, optdestroy.EventStreams(desCh)) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) assert.True(t, containsSummary(destroyEvents)) } func TestSupportsStackOutputs(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "bar": ConfigValue{ Value: "abc", }, "buzz": ConfigValue{ Value: "secret", Secret: true, }, } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") nestedObj := pulumi.Map{ "not_a_secret": pulumi.String("foo"), "is_a_secret": pulumi.ToSecret("iamsecret"), } ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) ctx.Export("nested_obj", nestedObj) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } assertOutputs := func(t *testing.T, outputs OutputMap) { assert.Equal(t, 4, len(outputs), "expected four outputs") assert.Equal(t, "foo", outputs["exp_static"].Value) assert.False(t, outputs["exp_static"].Secret) assert.Equal(t, "abc", outputs["exp_cfg"].Value) assert.False(t, outputs["exp_cfg"].Secret) assert.Equal(t, "secret", outputs["exp_secret"].Value) assert.True(t, outputs["exp_secret"].Secret) assert.Equal(t, map[string]interface{}{ "is_a_secret": "iamsecret", "not_a_secret": "foo", }, outputs["nested_obj"].Value) assert.True(t, outputs["nested_obj"].Secret) } initialOutputs, err := s.Outputs(ctx) if err != nil { t.Errorf("failed to get initial outputs, err: %v", err) t.FailNow() } assert.Equal(t, 0, len(initialOutputs)) // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) assert.Greater(t, res.Summary.Version, 0) assertOutputs(t, res.Outputs) outputsAfterUp, err := s.Outputs(ctx) if err != nil { t.Errorf("failed to get outputs after up, err: %v", err) t.FailNow() } assertOutputs(t, outputsAfterUp) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) outputsAfterDestroy, err := s.Outputs(ctx) if err != nil { t.Errorf("failed to get outputs after destroy, err: %v", err) t.FailNow() } assert.Equal(t, 0, len(outputsAfterDestroy)) } func TestPulumiVersion(t *testing.T) { ctx := context.Background() ws, err := NewLocalWorkspace(ctx) if err != nil { t.Errorf("failed to create workspace, err: %v", err) t.FailNow() } version := ws.PulumiVersion() assert.NotEqual(t, "v0.0.0", version) assert.Regexp(t, `(\d+\.)(\d+\.)(\d+)(-.*)?`, version) } const PARSE = `Unable to parse` const MAJOR = `Major version mismatch.` const MINIMUM = `Minimum version requirement failed.` var minVersionTests = []struct { name string currentVersion string expectedError string optOut bool }{ { "higher_major", "100.0.0", MAJOR, false, }, { "lower_major", "1.0.0", MINIMUM, false, }, { "higher_minor", "2.2.0", MINIMUM, false, }, { "lower_minor", "2.1.0", MINIMUM, false, }, { "equal_minor_higher_patch", "2.2.2", MINIMUM, false, }, { "equal_minor_equal_patch", "2.2.1", MINIMUM, false, }, { "equal_minor_lower_patch", "2.2.0", MINIMUM, false, }, { "equal_minor_equal_patch_prerelease", // Note that prerelease < release so this case will error "2.21.1-alpha.1234", MINIMUM, false, }, { "opt_out_of_check_would_fail_otherwise", "2.2.0", "", true, }, { "opt_out_of_check_would_succeed_otherwise", "2.2.0", "", true, }, { "unparsable_version", "invalid", PARSE, false, }, { "opt_out_unparsable_version", "invalid", "", true, }, } func TestMinimumVersion(t *testing.T) { for _, tt := range minVersionTests { t.Run(tt.name, func(t *testing.T) { minVersion := semver.Version{Major: 2, Minor: 21, Patch: 1} _, err := parseAndValidatePulumiVersion(minVersion, tt.currentVersion, tt.optOut) if tt.expectedError != "" { assert.Error(t, err) assert.Regexp(t, tt.expectedError, err.Error()) } else { assert.Nil(t, err) } }) } } func TestProjectSettingsRespected(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) pName := "correct_project" stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) badProjectName := "project_was_overwritten" stack, err := NewStackInlineSource(ctx, stackName, badProjectName, func(ctx *pulumi.Context) error { return nil }, WorkDir(filepath.Join(".", "test", pName))) defer func() { // -- pulumi stack rm -- err = stack.Workspace().RemoveStack(ctx, stack.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() assert.Nil(t, err) projectSettings, err := stack.workspace.ProjectSettings(ctx) assert.Nil(t, err) assert.Equal(t, projectSettings.Name, tokens.PackageName("correct_project")) assert.Equal(t, *projectSettings.Description, "This is a description") } func TestSaveStackSettings(t *testing.T) { ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) opts := []LocalWorkspaceOption{ SecretsProvider("passphrase"), EnvVars(map[string]string{ "PULUMI_CONFIG_PASSPHRASE": "password", }), } // initialize s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") ctx.Export("exp_static", pulumi.String("foo")) ctx.Export("exp_cfg", pulumi.String(c.Get("bar"))) ctx.Export("exp_secret", c.GetSecret("buzz")) return nil }, opts...) require.NoError(t, err, "failed to initialize stack, err: %v", err) defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() // first load settings for created stack stackConfig, err := s.Workspace().StackSettings(ctx, stackName) require.NoError(t, err) stackConfig.SecretsProvider = "passphrase" assert.NoError(t, s.Workspace().SaveStackSettings(ctx, stackName, stackConfig)) // -- pulumi up -- res, err := s.Up(ctx) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } assert.Equal(t, "update", res.Summary.Kind) assert.Equal(t, "succeeded", res.Summary.Result) reloaded, err := s.workspace.StackSettings(ctx, stackName) assert.NoError(t, err) assert.Equal(t, stackConfig, reloaded) // -- pulumi destroy -- dRes, err := s.Destroy(ctx) if err != nil { t.Errorf("destroy failed, err: %v", err) t.FailNow() } assert.Equal(t, "destroy", dRes.Summary.Kind) assert.Equal(t, "succeeded", dRes.Summary.Result) } func TestConfigSecretWarnings(t *testing.T) { // TODO[pulumi/pulumi#7127]: Re-enabled the warning. t.Skip("Temporarily skipping test until we've re-enabled the warning - pulumi/pulumi#7127") ctx := context.Background() sName := fmt.Sprintf("int_test%d", rangeIn(10000000, 99999999)) stackName := FullyQualifiedStackName(pulumiOrg, pName, sName) cfg := ConfigMap{ "plainstr1": ConfigValue{Value: "1"}, "plainstr2": ConfigValue{Value: "2"}, "plainstr3": ConfigValue{Value: "3"}, "plainstr4": ConfigValue{Value: "4"}, "plainstr5": ConfigValue{Value: "5"}, "plainstr6": ConfigValue{Value: "6"}, "plainstr7": ConfigValue{Value: "7"}, "plainstr8": ConfigValue{Value: "8"}, "plainstr9": ConfigValue{Value: "9"}, "plainstr10": ConfigValue{Value: "10"}, "plainstr11": ConfigValue{Value: "11"}, "plainstr12": ConfigValue{Value: "12"}, "plainbool1": ConfigValue{Value: "true"}, "plainbool2": ConfigValue{Value: "true"}, "plainbool3": ConfigValue{Value: "true"}, "plainbool4": ConfigValue{Value: "true"}, "plainbool5": ConfigValue{Value: "true"}, "plainbool6": ConfigValue{Value: "true"}, "plainbool7": ConfigValue{Value: "true"}, "plainbool8": ConfigValue{Value: "true"}, "plainbool9": ConfigValue{Value: "true"}, "plainbool10": ConfigValue{Value: "true"}, "plainbool11": ConfigValue{Value: "true"}, "plainbool12": ConfigValue{Value: "true"}, "plainint1": ConfigValue{Value: "1"}, "plainint2": ConfigValue{Value: "2"}, "plainint3": ConfigValue{Value: "3"}, "plainint4": ConfigValue{Value: "4"}, "plainint5": ConfigValue{Value: "5"}, "plainint6": ConfigValue{Value: "6"}, "plainint7": ConfigValue{Value: "7"}, "plainint8": ConfigValue{Value: "8"}, "plainint9": ConfigValue{Value: "9"}, "plainint10": ConfigValue{Value: "10"}, "plainint11": ConfigValue{Value: "11"}, "plainint12": ConfigValue{Value: "12"}, "plainfloat1": ConfigValue{Value: "1.1"}, "plainfloat2": ConfigValue{Value: "2.2"}, "plainfloat3": ConfigValue{Value: "3.3"}, "plainfloat4": ConfigValue{Value: "4.4"}, "plainfloat5": ConfigValue{Value: "5.5"}, "plainfloat6": ConfigValue{Value: "6.6"}, "plainfloat7": ConfigValue{Value: "7.7"}, "plainfloat8": ConfigValue{Value: "8.8"}, "plainfloat9": ConfigValue{Value: "9.9"}, "plainfloat10": ConfigValue{Value: "10.1"}, "plainfloat11": ConfigValue{Value: "11.11"}, "plainfloat12": ConfigValue{Value: "12.12"}, "plainobj1": ConfigValue{Value: "{}"}, "plainobj2": ConfigValue{Value: "{}"}, "plainobj3": ConfigValue{Value: "{}"}, "plainobj4": ConfigValue{Value: "{}"}, "plainobj5": ConfigValue{Value: "{}"}, "plainobj6": ConfigValue{Value: "{}"}, "plainobj7": ConfigValue{Value: "{}"}, "plainobj8": ConfigValue{Value: "{}"}, "plainobj9": ConfigValue{Value: "{}"}, "plainobj10": ConfigValue{Value: "{}"}, "plainobj11": ConfigValue{Value: "{}"}, "plainobj12": ConfigValue{Value: "{}"}, "str1": ConfigValue{Value: "1", Secret: true}, "str2": ConfigValue{Value: "2", Secret: true}, "str3": ConfigValue{Value: "3", Secret: true}, "str4": ConfigValue{Value: "4", Secret: true}, "str5": ConfigValue{Value: "5", Secret: true}, "str6": ConfigValue{Value: "6", Secret: true}, "str7": ConfigValue{Value: "7", Secret: true}, "str8": ConfigValue{Value: "8", Secret: true}, "str9": ConfigValue{Value: "9", Secret: true}, "str10": ConfigValue{Value: "10", Secret: true}, "str11": ConfigValue{Value: "11", Secret: true}, "str12": ConfigValue{Value: "12", Secret: true}, "bool1": ConfigValue{Value: "true", Secret: true}, "bool2": ConfigValue{Value: "true", Secret: true}, "bool3": ConfigValue{Value: "true", Secret: true}, "bool4": ConfigValue{Value: "true", Secret: true}, "bool5": ConfigValue{Value: "true", Secret: true}, "bool6": ConfigValue{Value: "true", Secret: true}, "bool7": ConfigValue{Value: "true", Secret: true}, "bool8": ConfigValue{Value: "true", Secret: true}, "bool9": ConfigValue{Value: "true", Secret: true}, "bool10": ConfigValue{Value: "true", Secret: true}, "bool11": ConfigValue{Value: "true", Secret: true}, "bool12": ConfigValue{Value: "true", Secret: true}, "int1": ConfigValue{Value: "1", Secret: true}, "int2": ConfigValue{Value: "2", Secret: true}, "int3": ConfigValue{Value: "3", Secret: true}, "int4": ConfigValue{Value: "4", Secret: true}, "int5": ConfigValue{Value: "5", Secret: true}, "int6": ConfigValue{Value: "6", Secret: true}, "int7": ConfigValue{Value: "7", Secret: true}, "int8": ConfigValue{Value: "8", Secret: true}, "int9": ConfigValue{Value: "9", Secret: true}, "int10": ConfigValue{Value: "10", Secret: true}, "int11": ConfigValue{Value: "11", Secret: true}, "int12": ConfigValue{Value: "12", Secret: true}, "float1": ConfigValue{Value: "1.1", Secret: true}, "float2": ConfigValue{Value: "2.2", Secret: true}, "float3": ConfigValue{Value: "3.3", Secret: true}, "float4": ConfigValue{Value: "4.4", Secret: true}, "float5": ConfigValue{Value: "5.5", Secret: true}, "float6": ConfigValue{Value: "6.6", Secret: true}, "float7": ConfigValue{Value: "7.7", Secret: true}, "float8": ConfigValue{Value: "8.8", Secret: true}, "float9": ConfigValue{Value: "9.9", Secret: true}, "float10": ConfigValue{Value: "10.1", Secret: true}, "float11": ConfigValue{Value: "11.11", Secret: true}, "float12": ConfigValue{Value: "12.12", Secret: true}, "obj1": ConfigValue{Value: "{}", Secret: true}, "obj2": ConfigValue{Value: "{}", Secret: true}, "obj3": ConfigValue{Value: "{}", Secret: true}, "obj4": ConfigValue{Value: "{}", Secret: true}, "obj5": ConfigValue{Value: "{}", Secret: true}, "obj6": ConfigValue{Value: "{}", Secret: true}, "obj7": ConfigValue{Value: "{}", Secret: true}, "obj8": ConfigValue{Value: "{}", Secret: true}, "obj9": ConfigValue{Value: "{}", Secret: true}, "obj10": ConfigValue{Value: "{}", Secret: true}, "obj11": ConfigValue{Value: "{}", Secret: true}, "obj12": ConfigValue{Value: "{}", Secret: true}, } // initialize //nolint:errcheck s, err := NewStackInlineSource(ctx, stackName, pName, func(ctx *pulumi.Context) error { c := config.New(ctx, "") config.Get(ctx, "plainstr1") config.Require(ctx, "plainstr2") config.Try(ctx, "plainstr3") config.GetSecret(ctx, "plainstr4") config.RequireSecret(ctx, "plainstr5") config.TrySecret(ctx, "plainstr6") c.Get("plainstr7") c.Require("plainstr8") c.Try("plainstr9") c.GetSecret("plainstr10") c.RequireSecret("plainstr11") c.TrySecret("plainstr12") config.GetBool(ctx, "plainbool1") config.RequireBool(ctx, "plainbool2") config.TryBool(ctx, "plainbool3") config.GetSecretBool(ctx, "plainbool4") config.RequireSecretBool(ctx, "plainbool5") config.TrySecretBool(ctx, "plainbool6") c.GetBool("plainbool7") c.RequireBool("plainbool8") c.TryBool("plainbool9") c.GetSecretBool("plainbool10") c.RequireSecretBool("plainbool11") c.TrySecretBool("plainbool12") config.GetInt(ctx, "plainint1") config.RequireInt(ctx, "plainint2") config.TryInt(ctx, "plainint3") config.GetSecretInt(ctx, "plainint4") config.RequireSecretInt(ctx, "plainint5") config.TrySecretInt(ctx, "plainint6") c.GetInt("plainint7") c.RequireInt("plainint8") c.TryInt("plainint9") c.GetSecretInt("plainint10") c.RequireSecretInt("plainint11") c.TrySecretInt("plainint12") config.GetFloat64(ctx, "plainfloat1") config.RequireFloat64(ctx, "plainfloat2") config.TryFloat64(ctx, "plainfloat3") config.GetSecretFloat64(ctx, "plainfloat4") config.RequireSecretFloat64(ctx, "plainfloat5") config.TrySecretFloat64(ctx, "plainfloat6") c.GetFloat64("plainfloat7") c.RequireFloat64("plainfloat8") c.TryFloat64("plainfloat9") c.GetSecretFloat64("plainfloat10") c.RequireSecretFloat64("plainfloat11") c.TrySecretFloat64("plainfloat12") var obj interface{} config.GetObject(ctx, "plainobjj1", &obj) config.RequireObject(ctx, "plainobj2", &obj) config.TryObject(ctx, "plainobj3", &obj) config.GetSecretObject(ctx, "plainobj4", &obj) config.RequireSecretObject(ctx, "plainobj5", &obj) config.TrySecretObject(ctx, "plainobj6", &obj) c.GetObject("plainobjj7", &obj) c.RequireObject("plainobj8", &obj) c.TryObject("plainobj9", &obj) c.GetSecretObject("plainobj10", &obj) c.RequireSecretObject("plainobj11", &obj) c.TrySecretObject("plainobj12", &obj) config.Get(ctx, "str1") config.Require(ctx, "str2") config.Try(ctx, "str3") config.GetSecret(ctx, "str4") config.RequireSecret(ctx, "str5") config.TrySecret(ctx, "str6") c.Get("str7") c.Require("str8") c.Try("str9") c.GetSecret("str10") c.RequireSecret("str11") c.TrySecret("str12") config.GetBool(ctx, "bool1") config.RequireBool(ctx, "bool2") config.TryBool(ctx, "bool3") config.GetSecretBool(ctx, "bool4") config.RequireSecretBool(ctx, "bool5") config.TrySecretBool(ctx, "bool6") c.GetBool("bool7") c.RequireBool("bool8") c.TryBool("bool9") c.GetSecretBool("bool10") c.RequireSecretBool("bool11") c.TrySecretBool("bool12") config.GetInt(ctx, "int1") config.RequireInt(ctx, "int2") config.TryInt(ctx, "int3") config.GetSecretInt(ctx, "int4") config.RequireSecretInt(ctx, "int5") config.TrySecretInt(ctx, "int6") c.GetInt("int7") c.RequireInt("int8") c.TryInt("int9") c.GetSecretInt("int10") c.RequireSecretInt("int11") c.TrySecretInt("int12") config.GetFloat64(ctx, "float1") config.RequireFloat64(ctx, "float2") config.TryFloat64(ctx, "float3") config.GetSecretFloat64(ctx, "float4") config.RequireSecretFloat64(ctx, "float5") config.TrySecretFloat64(ctx, "float6") c.GetFloat64("float7") c.RequireFloat64("float8") c.TryFloat64("float9") c.GetSecretFloat64("float10") c.RequireSecretFloat64("float11") c.TrySecretFloat64("float12") config.GetObject(ctx, "obj1", &obj) config.RequireObject(ctx, "obj2", &obj) config.TryObject(ctx, "obj3", &obj) config.GetSecretObject(ctx, "obj4", &obj) config.RequireSecretObject(ctx, "obj5", &obj) config.TrySecretObject(ctx, "obj6", &obj) c.GetObject("obj7", &obj) c.RequireObject("obj8", &obj) c.TryObject("obj9", &obj) c.GetSecretObject("obj10", &obj) c.RequireSecretObject("obj11", &obj) c.TrySecretObject("obj12", &obj) return nil }) if err != nil { t.Errorf("failed to initialize stack, err: %v", err) t.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(t, err, "failed to remove stack. Resources have leaked.") }() err = s.SetAllConfig(ctx, cfg) if err != nil { t.Errorf("failed to set config, err: %v", err) t.FailNow() } validate := func(engineEvents []events.EngineEvent) { expectedWarnings := []string{ "Configuration 'testproj:str1' value is a secret; use `GetSecret` instead of `Get`", "Configuration 'testproj:str2' value is a secret; use `RequireSecret` instead of `Require`", "Configuration 'testproj:str3' value is a secret; use `TrySecret` instead of `Try`", "Configuration 'testproj:str7' value is a secret; use `GetSecret` instead of `Get`", "Configuration 'testproj:str8' value is a secret; use `RequireSecret` instead of `Require`", "Configuration 'testproj:str9' value is a secret; use `TrySecret` instead of `Try`", "Configuration 'testproj:bool1' value is a secret; use `GetSecretBool` instead of `GetBool`", "Configuration 'testproj:bool2' value is a secret; use `RequireSecretBool` instead of `RequireBool`", "Configuration 'testproj:bool3' value is a secret; use `TrySecretBool` instead of `TryBool`", "Configuration 'testproj:bool7' value is a secret; use `GetSecretBool` instead of `GetBool`", "Configuration 'testproj:bool8' value is a secret; use `RequireSecretBool` instead of `RequireBool`", "Configuration 'testproj:bool9' value is a secret; use `TrySecretBool` instead of `TryBool`", "Configuration 'testproj:int1' value is a secret; use `GetSecretInt` instead of `GetInt`", "Configuration 'testproj:int2' value is a secret; use `RequireSecretInt` instead of `RequireInt`", "Configuration 'testproj:int3' value is a secret; use `TrySecretInt` instead of `TryInt`", "Configuration 'testproj:int7' value is a secret; use `GetSecretInt` instead of `GetInt`", "Configuration 'testproj:int8' value is a secret; use `RequireSecretInt` instead of `RequireInt`", "Configuration 'testproj:int9' value is a secret; use `TrySecretInt` instead of `TryInt`", "Configuration 'testproj:float1' value is a secret; use `GetSecretFloat64` instead of `GetFloat64`", "Configuration 'testproj:float2' value is a secret; use `RequireSecretFloat64` instead of `RequireFloat64`", "Configuration 'testproj:float3' value is a secret; use `TrySecretFloat64` instead of `TryFloat64`", "Configuration 'testproj:float7' value is a secret; use `GetSecretFloat64` instead of `GetFloat64`", "Configuration 'testproj:float8' value is a secret; use `RequireSecretFloat64` instead of `RequireFloat64`", "Configuration 'testproj:float9' value is a secret; use `TrySecretFloat64` instead of `TryFloat64`", "Configuration 'testproj:obj1' value is a secret; use `GetSecretObject` instead of `GetObject`", "Configuration 'testproj:obj2' value is a secret; use `RequireSecretObject` instead of `RequireObject`", "Configuration 'testproj:obj3' value is a secret; use `TrySecretObject` instead of `TryObject`", "Configuration 'testproj:obj7' value is a secret; use `GetSecretObject` instead of `GetObject`", "Configuration 'testproj:obj8' value is a secret; use `RequireSecretObject` instead of `RequireObject`", "Configuration 'testproj:obj9' value is a secret; use `TrySecretObject` instead of `TryObject`", } for _, warning := range expectedWarnings { var found bool for _, event := range engineEvents { if event.DiagnosticEvent != nil && event.DiagnosticEvent.Severity == "warning" && strings.Contains(event.DiagnosticEvent.Message, warning) { found = true break } } assert.True(t, found, "expected warning %q", warning) } // These keys should not be in any warning messages. unexpectedWarnings := []string{ "plainstr1", "plainstr2", "plainstr3", "plainstr4", "plainstr5", "plainstr6", "plainstr7", "plainstr8", "plainstr9", "plainstr10", "plainstr11", "plainstr12", "plainbool1", "plainbool2", "plainbool3", "plainbool4", "plainbool5", "plainbool6", "plainbool7", "plainbool8", "plainbool9", "plainbool10", "plainbool11", "plainbool12", "plainint1", "plainint2", "plainint3", "plainint4", "plainint5", "plainint6", "plainint7", "plainint8", "plainint9", "plainint10", "plainint11", "plainint12", "plainfloat1", "plainfloat2", "plainfloat3", "plainfloat4", "plainfloat5", "plainfloat6", "plainfloat7", "plainfloat8", "plainfloat9", "plainfloat10", "plainfloat11", "plainfloat12", "plainobj1", "plainobj2", "plainobj3", "plainobj4", "plainobj5", "plainobj6", "plainobj7", "plainobj8", "plainobj9", "plainobj10", "plainobj11", "plainobj12", } for _, warning := range unexpectedWarnings { for _, event := range engineEvents { if event.DiagnosticEvent != nil { assert.NotContains(t, event.DiagnosticEvent.Message, warning) } } } } // -- pulumi up -- var upEvents []events.EngineEvent upCh := make(chan events.EngineEvent) go collectEvents(upCh, &upEvents) _, err = s.Up(ctx, optup.EventStreams(upCh)) if err != nil { t.Errorf("up failed, err: %v", err) t.FailNow() } validate(upEvents) // -- pulumi preview -- var previewEvents []events.EngineEvent prevCh := make(chan events.EngineEvent) go collectEvents(prevCh, &previewEvents) _, err = s.Preview(ctx, optpreview.EventStreams(prevCh)) if err != nil { t.Errorf("preview failed, err: %v", err) t.FailNow() } validate(previewEvents) } func BenchmarkBulkSetConfigMixed(b *testing.B) { ctx := context.Background() stackName := FullyQualifiedStackName(pulumiOrg, "set_config_mixed", "dev") // initialize s, err := NewStackInlineSource(ctx, stackName, "set_config_mixed", func(ctx *pulumi.Context) error { return nil }) if err != nil { b.Errorf("failed to initialize stack, err: %v", err) b.FailNow() } cfg := ConfigMap{ "one": ConfigValue{Value: "one", Secret: true}, "two": ConfigValue{Value: "two"}, "three": ConfigValue{Value: "three", Secret: true}, "four": ConfigValue{Value: "four"}, "five": ConfigValue{Value: "five", Secret: true}, "six": ConfigValue{Value: "six"}, "seven": ConfigValue{Value: "seven", Secret: true}, "eight": ConfigValue{Value: "eight"}, "nine": ConfigValue{Value: "nine", Secret: true}, "ten": ConfigValue{Value: "ten"}, "eleven": ConfigValue{Value: "one", Secret: true}, "twelve": ConfigValue{Value: "two"}, "thirteen": ConfigValue{Value: "three", Secret: true}, "fourteen": ConfigValue{Value: "four"}, "fifteen": ConfigValue{Value: "five", Secret: true}, "sixteen": ConfigValue{Value: "six"}, "seventeen": ConfigValue{Value: "seven", Secret: true}, "eighteen": ConfigValue{Value: "eight"}, "nineteen": ConfigValue{Value: "nine", Secret: true}, "twenty": ConfigValue{Value: "ten"}, "one1": ConfigValue{Value: "one", Secret: true}, "two1": ConfigValue{Value: "two"}, "three1": ConfigValue{Value: "three", Secret: true}, "four1": ConfigValue{Value: "four"}, "five1": ConfigValue{Value: "five", Secret: true}, "six1": ConfigValue{Value: "six"}, "seven1": ConfigValue{Value: "seven", Secret: true}, "eight1": ConfigValue{Value: "eight"}, "nine1": ConfigValue{Value: "nine", Secret: true}, "ten1": ConfigValue{Value: "ten"}, "eleven1": ConfigValue{Value: "one", Secret: true}, "twelve1": ConfigValue{Value: "two"}, "thirteen1": ConfigValue{Value: "three", Secret: true}, "fourteen1": ConfigValue{Value: "four"}, "fifteen1": ConfigValue{Value: "five", Secret: true}, "sixteen1": ConfigValue{Value: "six"}, "seventeen1": ConfigValue{Value: "seven", Secret: true}, "eighteen1": ConfigValue{Value: "eight"}, "nineteen1": ConfigValue{Value: "nine", Secret: true}, "twenty1": ConfigValue{Value: "ten"}, } err = s.SetAllConfig(ctx, cfg) if err != nil { b.Errorf("failed to set config, err: %v", err) b.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(b, err, "failed to remove stack. Resources have leaked.") }() } func BenchmarkBulkSetConfigPlain(b *testing.B) { ctx := context.Background() stackName := FullyQualifiedStackName(pulumiOrg, "set_config_plain", "dev") // initialize s, err := NewStackInlineSource(ctx, stackName, "set_config_plain", func(ctx *pulumi.Context) error { return nil }) if err != nil { b.Errorf("failed to initialize stack, err: %v", err) b.FailNow() } cfg := ConfigMap{ "one": ConfigValue{Value: "one"}, "two": ConfigValue{Value: "two"}, "three": ConfigValue{Value: "three"}, "four": ConfigValue{Value: "four"}, "five": ConfigValue{Value: "five"}, "six": ConfigValue{Value: "six"}, "seven": ConfigValue{Value: "seven"}, "eight": ConfigValue{Value: "eight"}, "nine": ConfigValue{Value: "nine"}, "ten": ConfigValue{Value: "ten"}, "eleven": ConfigValue{Value: "one"}, "twelve": ConfigValue{Value: "two"}, "thirteen": ConfigValue{Value: "three"}, "fourteen": ConfigValue{Value: "four"}, "fifteen": ConfigValue{Value: "five"}, "sixteen": ConfigValue{Value: "six"}, "seventeen": ConfigValue{Value: "seven"}, "eighteen": ConfigValue{Value: "eight"}, "nineteen": ConfigValue{Value: "nine"}, "twenty": ConfigValue{Value: "ten"}, "one1": ConfigValue{Value: "one"}, "two1": ConfigValue{Value: "two"}, "three1": ConfigValue{Value: "three"}, "four1": ConfigValue{Value: "four"}, "five1": ConfigValue{Value: "five"}, "six1": ConfigValue{Value: "six"}, "seven1": ConfigValue{Value: "seven"}, "eight1": ConfigValue{Value: "eight"}, "nine1": ConfigValue{Value: "nine"}, "ten1": ConfigValue{Value: "ten"}, "eleven1": ConfigValue{Value: "one"}, "twelve1": ConfigValue{Value: "two"}, "thirteen1": ConfigValue{Value: "three"}, "fourteen1": ConfigValue{Value: "four"}, "fifteen1": ConfigValue{Value: "five"}, "sixteen1": ConfigValue{Value: "six"}, "seventeen1": ConfigValue{Value: "seven"}, "eighteen1": ConfigValue{Value: "eight"}, "nineteen1": ConfigValue{Value: "nine"}, "twenty1": ConfigValue{Value: "ten"}, } err = s.SetAllConfig(ctx, cfg) if err != nil { b.Errorf("failed to set config, err: %v", err) b.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(b, err, "failed to remove stack. Resources have leaked.") }() } func BenchmarkBulkSetConfigSecret(b *testing.B) { ctx := context.Background() stackName := FullyQualifiedStackName(pulumiOrg, "set_config_plain", "dev") // initialize s, err := NewStackInlineSource(ctx, stackName, "set_config_plain", func(ctx *pulumi.Context) error { return nil }) if err != nil { b.Errorf("failed to initialize stack, err: %v", err) b.FailNow() } cfg := ConfigMap{ "one": ConfigValue{Value: "one", Secret: true}, "two": ConfigValue{Value: "two", Secret: true}, "three": ConfigValue{Value: "three", Secret: true}, "four": ConfigValue{Value: "four", Secret: true}, "five": ConfigValue{Value: "five", Secret: true}, "six": ConfigValue{Value: "six", Secret: true}, "seven": ConfigValue{Value: "seven", Secret: true}, "eight": ConfigValue{Value: "eight", Secret: true}, "nine": ConfigValue{Value: "nine", Secret: true}, "ten": ConfigValue{Value: "ten", Secret: true}, "eleven": ConfigValue{Value: "one", Secret: true}, "twelve": ConfigValue{Value: "two", Secret: true}, "thirteen": ConfigValue{Value: "three", Secret: true}, "fourteen": ConfigValue{Value: "four", Secret: true}, "fifteen": ConfigValue{Value: "five", Secret: true}, "sixteen": ConfigValue{Value: "six", Secret: true}, "seventeen": ConfigValue{Value: "seven", Secret: true}, "eighteen": ConfigValue{Value: "eight", Secret: true}, "nineteen": ConfigValue{Value: "nine", Secret: true}, "1twenty": ConfigValue{Value: "ten", Secret: true}, "one1": ConfigValue{Value: "one", Secret: true}, "two1": ConfigValue{Value: "two", Secret: true}, "three1": ConfigValue{Value: "three", Secret: true}, "four1": ConfigValue{Value: "four", Secret: true}, "five1": ConfigValue{Value: "five", Secret: true}, "six1": ConfigValue{Value: "six", Secret: true}, "seven1": ConfigValue{Value: "seven", Secret: true}, "eight1": ConfigValue{Value: "eight", Secret: true}, "nine1": ConfigValue{Value: "nine", Secret: true}, "ten1": ConfigValue{Value: "ten", Secret: true}, "eleven1": ConfigValue{Value: "one", Secret: true}, "twelve1": ConfigValue{Value: "two", Secret: true}, "thirteen1": ConfigValue{Value: "three", Secret: true}, "fourteen1": ConfigValue{Value: "four", Secret: true}, "fifteen1": ConfigValue{Value: "five", Secret: true}, "sixteen1": ConfigValue{Value: "six", Secret: true}, "seventeen1": ConfigValue{Value: "seven", Secret: true}, "eighteen1": ConfigValue{Value: "eight", Secret: true}, "nineteen1": ConfigValue{Value: "nine", Secret: true}, "twenty1": ConfigValue{Value: "ten", Secret: true}, } err = s.SetAllConfig(ctx, cfg) if err != nil { b.Errorf("failed to set config, err: %v", err) b.FailNow() } defer func() { // -- pulumi stack rm -- err = s.Workspace().RemoveStack(ctx, s.Name()) assert.Nil(b, err, "failed to remove stack. Resources have leaked.") }() } func getTestOrg() string { testOrg := "pulumi-test" if _, set := os.LookupEnv("PULUMI_TEST_ORG"); set { testOrg = os.Getenv("PULUMI_TEST_ORG") } return testOrg } func countSteps(log []events.EngineEvent) int { steps := 0 for _, e := range log { if e.ResourcePreEvent != nil { steps++ } } return steps } func containsSummary(log []events.EngineEvent) bool { hasSummary := false for _, e := range log { if e.SummaryEvent != nil { hasSummary = true } } return hasSummary } func collectEvents(eventChannel <-chan events.EngineEvent, events *[]events.EngineEvent) { for { event, ok := <-eventChannel if !ok { return } *events = append(*events, event) } }