diff --git a/CHANGELOG.md b/CHANGELOG.md index 802509287f92..7a0b80847187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # Changelog +## 3.73.0 (2023-06-22) + + +### Features + +- [programgen] Allow traversing unknown properties from resources when skipping resource type checking + [#13180](https://github.com/pulumi/pulumi/pull/13180) + + +### Bug Fixes + +- [backend/filestate] Fix auto-opt-in to project mode. + [#13243](https://github.com/pulumi/pulumi/pull/13243) + +- [cli] `pulumi convert` will now cleanup temporary pulumi-convert directories when the command is finished. + [#13185](https://github.com/pulumi/pulumi/pull/13185) + +- [cli] Fix Markdown formatting issues in command usage. + [#13225](https://github.com/pulumi/pulumi/pull/13225) + +- [cli] Fix `stack rm` removing config files for the wrong project. + [#13227](https://github.com/pulumi/pulumi/pull/13227) + +- [cli/config] No longer error on directory read permissions when searching for project files. + [#13211](https://github.com/pulumi/pulumi/pull/13211) + +- [cli/display] Fix diff display partially parsing JSON/YAML from strings. + +- [cli/display] Fix large integers displaying in scientific notation. + [#13209](https://github.com/pulumi/pulumi/pull/13209) + +- [cli/display] Update summary is now correctly shown when `advisory` and `disabled` policy events are encountered. + [#13218](https://github.com/pulumi/pulumi/pull/13218) + +- [cli/display] Fix formatting bugs in display causing text like (MISSING) showing in output. + [#13228](https://github.com/pulumi/pulumi/pull/13228) + +- [cli/display] On Windows, make `pulumi state unprotect` command suggestion use double-quotes instead of single-quotes. + [#13236](https://github.com/pulumi/pulumi/pull/13236) + +- [cli/new] `pulumi new` now correctly supports numeric stack names. + [#13220](https://github.com/pulumi/pulumi/pull/13220) + +- [cli/new] Fix empty config values being added to the config file as part of `new`. + [#13233](https://github.com/pulumi/pulumi/pull/13233) + +- [cli/plugin] Fixes the output of plugin rm --yes command to explicitly say that plugins were removed + [#13216](https://github.com/pulumi/pulumi/pull/13216) + +- [engine] Fix wildcards in IgnoreChanges. + [#13005](https://github.com/pulumi/pulumi/pull/13005) + +- [engine] Fix ignoreChanges setting ignore array indexes to zero. + [#13005](https://github.com/pulumi/pulumi/pull/13005) + +- [sdk/nodejs] Write port to stdout as a string so Node doesn't colorize the output + [#13204](https://github.com/pulumi/pulumi/pull/13204) + +- [sdk/python] Allow tuples as Sequence input values to resources. + [#13210](https://github.com/pulumi/pulumi/pull/13210) + +- [sdkgen/python] Python SDK only prints a Function Invoke result's deprecation messages when using getters rather than on instantiation. + [#13213](https://github.com/pulumi/pulumi/pull/13213) + + +### Miscellaneous + +- [cli] Make no retry attempts for the Pulumi new version query. This should speed up the CLI in certain environments. + [#13215](https://github.com/pulumi/pulumi/pull/13215) + ## 3.72.2 (2023-06-17) diff --git a/changelog/pending/20230523--engine--fix-wildcards-in-ignorechanges.yaml b/changelog/pending/20230523--engine--fix-wildcards-in-ignorechanges.yaml deleted file mode 100644 index a9c3f46956f2..000000000000 --- a/changelog/pending/20230523--engine--fix-wildcards-in-ignorechanges.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: engine - description: Fix wildcards in IgnoreChanges. diff --git a/changelog/pending/20230526--engine--fix-ignorechanges-setting-ignore-array-indexs-to-zero.yaml b/changelog/pending/20230526--engine--fix-ignorechanges-setting-ignore-array-indexs-to-zero.yaml deleted file mode 100644 index 53d9df2d8591..000000000000 --- a/changelog/pending/20230526--engine--fix-ignorechanges-setting-ignore-array-indexs-to-zero.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: engine - description: Fix ignoreChanges setting ignore array indexes to zero. diff --git a/changelog/pending/20230601--cli-state--the-upgrade-command-now-prompts-the-user.yaml b/changelog/pending/20230601--cli-state--the-upgrade-command-now-prompts-the-user.yaml new file mode 100644 index 000000000000..4a69b19a6bee --- /dev/null +++ b/changelog/pending/20230601--cli-state--the-upgrade-command-now-prompts-the-user.yaml @@ -0,0 +1,4 @@ +changes: +- type: feat + scope: cli/state + description: The upgrade command now prompts the user to supply project names for stacks for which the project name could not be automatically guessed. diff --git a/changelog/pending/20230616--cli--pulumi-convert-will-now-cleanup-temporary-pulumi-convert-directories-when-the-command-is-finished.yaml b/changelog/pending/20230616--cli--pulumi-convert-will-now-cleanup-temporary-pulumi-convert-directories-when-the-command-is-finished.yaml deleted file mode 100644 index 98b441687d58..000000000000 --- a/changelog/pending/20230616--cli--pulumi-convert-will-now-cleanup-temporary-pulumi-convert-directories-when-the-command-is-finished.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli - description: `pulumi convert` will now cleanup temporary pulumi-convert directories when the command is finished. diff --git a/changelog/pending/20230616--programgen--allow-traversing-unknown-properties-from-resources-when-skipping-resouce-type-checking.yaml b/changelog/pending/20230616--programgen--allow-traversing-unknown-properties-from-resources-when-skipping-resouce-type-checking.yaml deleted file mode 100644 index c92e95c92fd4..000000000000 --- a/changelog/pending/20230616--programgen--allow-traversing-unknown-properties-from-resources-when-skipping-resouce-type-checking.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: feat - scope: programgen - description: Allow traversing unknown properties from resources when skipping resource type checking diff --git a/changelog/pending/20230619--sdk-nodejs--write-port-to-stdout-as-a-string-so-node-doesnt-colorize-the-output.yaml b/changelog/pending/20230619--sdk-nodejs--write-port-to-stdout-as-a-string-so-node-doesnt-colorize-the-output.yaml deleted file mode 100644 index 79e65a60873c..000000000000 --- a/changelog/pending/20230619--sdk-nodejs--write-port-to-stdout-as-a-string-so-node-doesnt-colorize-the-output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdk/nodejs - description: Write port to stdout as a string so Node doesn't colorize the output diff --git a/changelog/pending/20230620--cli--fix-markdown-formatting-issues-in-command-usage.yaml b/changelog/pending/20230620--cli--fix-markdown-formatting-issues-in-command-usage.yaml deleted file mode 100644 index 803aa34dd9ce..000000000000 --- a/changelog/pending/20230620--cli--fix-markdown-formatting-issues-in-command-usage.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli - description: Fix Markdown formatting issues in command usage. diff --git a/changelog/pending/20230620--cli--make-no-retry-attempts-for-the-pulumi-new-version-query.yaml b/changelog/pending/20230620--cli--make-no-retry-attempts-for-the-pulumi-new-version-query.yaml deleted file mode 100644 index 17e8f263b925..000000000000 --- a/changelog/pending/20230620--cli--make-no-retry-attempts-for-the-pulumi-new-version-query.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: chore - scope: cli - description: Make no retry attempts for the Pulumi new version query. This should speed up the CLI in certain environments. diff --git a/changelog/pending/20230620--cli-config--no-longer-error-on-directory-read-permissions-when-searching-for-project-files.yaml b/changelog/pending/20230620--cli-config--no-longer-error-on-directory-read-permissions-when-searching-for-project-files.yaml deleted file mode 100644 index 7902324b2d88..000000000000 --- a/changelog/pending/20230620--cli-config--no-longer-error-on-directory-read-permissions-when-searching-for-project-files.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/config - description: No longer error on directory read permissions when searching for project files. diff --git a/changelog/pending/20230620--cli-display--fix-diff-display-partially-parsing-json-yaml-from-strings.yaml b/changelog/pending/20230620--cli-display--fix-diff-display-partially-parsing-json-yaml-from-strings.yaml deleted file mode 100644 index 78bf607b1bbd..000000000000 --- a/changelog/pending/20230620--cli-display--fix-diff-display-partially-parsing-json-yaml-from-strings.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/display - description: Fix diff display partially parsing JSON/YAML from strings. diff --git a/changelog/pending/20230620--cli-display--fix-large-integers-displaying-in-scientific-notation.yaml b/changelog/pending/20230620--cli-display--fix-large-integers-displaying-in-scientific-notation.yaml deleted file mode 100644 index 8b366cdb9b7f..000000000000 --- a/changelog/pending/20230620--cli-display--fix-large-integers-displaying-in-scientific-notation.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/display - description: Fix large integers displaying in scientific notation. diff --git a/changelog/pending/20230620--cli-display--update-summary-is-now-correctly-shown-when-advisory-and-disabled-policy-events-are-encountered.yaml b/changelog/pending/20230620--cli-display--update-summary-is-now-correctly-shown-when-advisory-and-disabled-policy-events-are-encountered.yaml deleted file mode 100644 index 6711dcaab20d..000000000000 --- a/changelog/pending/20230620--cli-display--update-summary-is-now-correctly-shown-when-advisory-and-disabled-policy-events-are-encountered.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/display - description: Update summary is now correctly shown when `advisory` and `disabled` policy events are encountered. diff --git a/changelog/pending/20230620--cli-new--pulumi-new-now-correctly-supports-numeric-stack-names.yaml b/changelog/pending/20230620--cli-new--pulumi-new-now-correctly-supports-numeric-stack-names.yaml deleted file mode 100644 index 807174775f43..000000000000 --- a/changelog/pending/20230620--cli-new--pulumi-new-now-correctly-supports-numeric-stack-names.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/new - description: `pulumi new` now correctly supports numeric stack names. diff --git a/changelog/pending/20230620--cli-plugin--plugin-rm-yes-command-to-say-plugins-were-removed.yaml b/changelog/pending/20230620--cli-plugin--plugin-rm-yes-command-to-say-plugins-were-removed.yaml deleted file mode 100644 index 63773c3fb003..000000000000 --- a/changelog/pending/20230620--cli-plugin--plugin-rm-yes-command-to-say-plugins-were-removed.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/plugin - description: Fixes the output of plugin rm --yes command to explicitly say that plugins were removed diff --git a/changelog/pending/20230620--sdk-python--allow-tuples-as-sequence-input-values-to-resources.yaml b/changelog/pending/20230620--sdk-python--allow-tuples-as-sequence-input-values-to-resources.yaml deleted file mode 100644 index 6288ddf4b0c1..000000000000 --- a/changelog/pending/20230620--sdk-python--allow-tuples-as-sequence-input-values-to-resources.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdk/python - description: Allow tuples as Sequence input values to resources. diff --git a/changelog/pending/20230620--sdkgen-python--python-sdk-only-prints-a-function-invoke-results-deprecation-messages-when-using-getters-rather-than-on-instantiation.yaml b/changelog/pending/20230620--sdkgen-python--python-sdk-only-prints-a-function-invoke-results-deprecation-messages-when-using-getters-rather-than-on-instantiation.yaml deleted file mode 100644 index 6749495414b4..000000000000 --- a/changelog/pending/20230620--sdkgen-python--python-sdk-only-prints-a-function-invoke-results-deprecation-messages-when-using-getters-rather-than-on-instantiation.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: sdkgen/python - description: Python SDK only prints a Function Invoke result's deprecation messages when using getters rather than on instantiation. diff --git a/changelog/pending/20230621--cli--fix-stack-rm-removing-config-files-for-the-wrong-project.yaml b/changelog/pending/20230621--cli--fix-stack-rm-removing-config-files-for-the-wrong-project.yaml deleted file mode 100644 index 11feecc273e5..000000000000 --- a/changelog/pending/20230621--cli--fix-stack-rm-removing-config-files-for-the-wrong-project.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli - description: Fix `stack rm` removing config files for the wrong project. diff --git a/changelog/pending/20230621--cli-display--fix-formatting-bugs-in-display-causing-text-like-missing-showing-in-output.yaml b/changelog/pending/20230621--cli-display--fix-formatting-bugs-in-display-causing-text-like-missing-showing-in-output.yaml deleted file mode 100644 index 1eaff904af37..000000000000 --- a/changelog/pending/20230621--cli-display--fix-formatting-bugs-in-display-causing-text-like-missing-showing-in-output.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/display - description: Fix formatting bugs in display causing text like (MISSING) showing in output. diff --git a/changelog/pending/20230621--cli-display--on-windows-make-pulumi-state-unprotect-command-suggestion-use-double-quotes-instead-of-single-quotes.yaml b/changelog/pending/20230621--cli-display--on-windows-make-pulumi-state-unprotect-command-suggestion-use-double-quotes-instead-of-single-quotes.yaml deleted file mode 100644 index f5a251251d31..000000000000 --- a/changelog/pending/20230621--cli-display--on-windows-make-pulumi-state-unprotect-command-suggestion-use-double-quotes-instead-of-single-quotes.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/display - description: On Windows, make `pulumi state unprotect` command suggestion use double-quotes instead of single-quotes. diff --git a/changelog/pending/20230621--cli-new--fix-empty-config-values-being-added-to-the-config-file-as-part-of-new.yaml b/changelog/pending/20230621--cli-new--fix-empty-config-values-being-added-to-the-config-file-as-part-of-new.yaml deleted file mode 100644 index 8327c27b7bcc..000000000000 --- a/changelog/pending/20230621--cli-new--fix-empty-config-values-being-added-to-the-config-file-as-part-of-new.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: cli/new - description: Fix empty config values being added to the config file as part of `new`. diff --git a/changelog/pending/20230622--backend-filestate--fix-auto-opt-in-to-project-mode.yaml b/changelog/pending/20230622--backend-filestate--fix-auto-opt-in-to-project-mode.yaml deleted file mode 100644 index 724deea41ec0..000000000000 --- a/changelog/pending/20230622--backend-filestate--fix-auto-opt-in-to-project-mode.yaml +++ /dev/null @@ -1,4 +0,0 @@ -changes: -- type: fix - scope: backend/filestate - description: Fix auto-opt-in to project mode. diff --git a/changelog/pending/20230622--cli-state--disallow-renaming-resources-to-invalid-names-that-will-corrupt-the-state.yaml b/changelog/pending/20230622--cli-state--disallow-renaming-resources-to-invalid-names-that-will-corrupt-the-state.yaml new file mode 100644 index 000000000000..cc05fd60b6cf --- /dev/null +++ b/changelog/pending/20230622--cli-state--disallow-renaming-resources-to-invalid-names-that-will-corrupt-the-state.yaml @@ -0,0 +1,4 @@ +changes: +- type: fix + scope: cli/state + description: Disallow renaming resources to invalid names that will corrupt the state. diff --git a/pkg/cmd/pulumi/state_rename.go b/pkg/cmd/pulumi/state_rename.go index 67197ce2845d..86d475fb3d86 100644 --- a/pkg/cmd/pulumi/state_rename.go +++ b/pkg/cmd/pulumi/state_rename.go @@ -23,6 +23,7 @@ import ( "github.com/pulumi/pulumi/pkg/v3/resource/deploy/providers" "github.com/pulumi/pulumi/pkg/v3/resource/edit" "github.com/pulumi/pulumi/sdk/v3/go/common/resource" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" "github.com/pulumi/pulumi/sdk/v3/go/common/util/result" @@ -50,6 +51,11 @@ func stateRenameOperation(urn resource.URN, newResourceName string, opts display return errors.New("The input URN does not correspond to an existing resource") } + if !tokens.IsQName(newResourceName) { + return fmt.Errorf("invalid name %q: "+ + "resource names may only contain alphanumerics, underscores, hyphens, dots, and slashes", newResourceName) + } + inputResource := existingResources[0] oldUrn := inputResource.URN // update the URN with only the name part changed diff --git a/pkg/cmd/pulumi/state_rename_test.go b/pkg/cmd/pulumi/state_rename_test.go index 8ba4ec64749f..2676b595cc05 100644 --- a/pkg/cmd/pulumi/state_rename_test.go +++ b/pkg/cmd/pulumi/state_rename_test.go @@ -67,6 +67,44 @@ func TestRenameProvider(t *testing.T) { } } +func TestStateRename_invalidName(t *testing.T) { + t.Parallel() + + prov := resource.URN("urn:pulumi:dev::xxx-dev::kubernetes::provider") + res := resource.URN("urn:pulumi:dev::xxx-dev::kubernetes:core/v1:Namespace::amazon_cloudwatchNamespace") + + snap := deploy.Snapshot{ + Resources: []*resource.State{ + { + URN: prov, + ID: "provider-id", + Type: "pulumi:provider:kubernetes", + }, + { + URN: res, + ID: "res-id", + Type: "kubernetes:core/v1:Namespace", + }, + }, + } + require.NoError(t, snap.VerifyIntegrity(), + "invalid test: snapshot is already broken") + + err := stateRenameOperation( + res, + "urn:pulumi:dev::xxx-dev::eks:index:Cluster$kubernetes:core/v1:Namespace::amazon_cloudwatchNamespace", + display.Options{}, + &snap, + ) + require.Error(t, err) + assert.ErrorContains(t, err, "invalid name") + assert.ErrorContains(t, err, "names may only contain alphanumerics") + + // The state must still be valid, and the resource name unchanged. + require.NoError(t, snap.VerifyIntegrity(), "snapshot is broken after rename") + assert.Equal(t, res, snap.Resources[1].URN) +} + // Regression test for https://github.com/pulumi/pulumi/issues/13179. // // Defines a state with a two resources, one parented to the other, diff --git a/pkg/cmd/pulumi/state_upgrade.go b/pkg/cmd/pulumi/state_upgrade.go index d184a3f161e0..d9f497187d62 100644 --- a/pkg/cmd/pulumi/state_upgrade.go +++ b/pkg/cmd/pulumi/state_upgrade.go @@ -20,10 +20,14 @@ import ( "io" "os" + survey "github.com/AlecAivazis/survey/v2" + "github.com/AlecAivazis/survey/v2/terminal" "github.com/pulumi/pulumi/pkg/v3/backend" "github.com/pulumi/pulumi/pkg/v3/backend/display" "github.com/pulumi/pulumi/pkg/v3/backend/filestate" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/pulumi/pulumi/sdk/v3/go/common/util/cmdutil" + "github.com/pulumi/pulumi/sdk/v3/go/common/util/contract" "github.com/pulumi/pulumi/sdk/v3/go/common/util/result" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" @@ -54,6 +58,7 @@ This only has an effect on self-managed backends. type stateUpgradeCmd struct { Stdin io.Reader // defaults to os.Stdin Stdout io.Writer // defaults to os.Stdout + Stderr io.Writer // defaults to os.Stderr // Used to mock out the currentBackend function for testing. // Defaults to currentBackend function. @@ -67,6 +72,9 @@ func (cmd *stateUpgradeCmd) Run(ctx context.Context) error { if cmd.Stdin == nil { cmd.Stdin = os.Stdin } + if cmd.Stderr == nil { + cmd.Stderr = os.Stderr + } if cmd.currentBackend == nil { cmd.currentBackend = currentBackend @@ -101,5 +109,88 @@ func (cmd *stateUpgradeCmd) Run(ctx context.Context) error { return nil } - return lb.Upgrade(ctx, nil /*opts*/) + var opts filestate.UpgradeOptions + // If we're in interactive mode, prompt for the project name + // for each stack that doesn't have one. + if cmdutil.Interactive() { + opts.ProjectsForDetachedStacks = cmd.projectsForDetachedStacks + } + return lb.Upgrade(ctx, &opts) +} + +func (cmd *stateUpgradeCmd) projectsForDetachedStacks(stacks []tokens.Name) ([]tokens.Name, error) { + projects := make([]tokens.Name, len(stacks)) + err := (&stateUpgradeProjectNameWidget{ + Stdin: cmd.Stdin, + Stdout: cmd.Stdout, + Stderr: cmd.Stderr, + }).Prompt(stacks, projects) + return projects, err +} + +// stateUpgradeProjectNameWidget is a widget that prompts the user +// for a project name for every stack that doesn't have one. +// +// It is used by the 'pulumi state upgrade' command +// when it encounters stacks without a project name. +type stateUpgradeProjectNameWidget struct { + Stdin io.Reader // required + Stdout io.Writer // required + Stderr io.Writer // required +} + +// Prompt prompts the user for a project name for each stack +// and stores the result in the corresponding index of projects. +// +// The length of projects must be equal to the length of stacks. +func (w *stateUpgradeProjectNameWidget) Prompt(stacks, projects []tokens.Name) error { + contract.Assertf(len(stacks) == len(projects), + "length of stacks (%d) must equal length of projects (%d)", len(stacks), len(projects)) + + if len(stacks) == 0 { + // Nothing to prompt for. + return nil + } + + stdin, ok1 := w.Stdin.(terminal.FileReader) + stdout, ok2 := w.Stdout.(terminal.FileWriter) + if !ok1 || !ok2 { + // We're not using a real terminal, so we can't prompt. + // Pretend we're in non-interactive mode. + return nil + } + + fmt.Fprintln(stdout, "Found stacks without a project name.") + fmt.Fprintln(stdout, "Please enter a project name for each stack, or enter to skip that stack.") + for i, stack := range stacks { + var project string + err := survey.AskOne( + &survey.Input{ + Message: fmt.Sprintf("Stack %s", stack), + Help: "Enter a name for the project, or press enter to skip", + }, + &project, + survey.WithStdio(stdin, stdout, w.Stderr), + survey.WithValidator(w.validateProject), + ) + if err != nil { + return fmt.Errorf("prompt for %q: %w", stack, err) + } + + projects[i] = tokens.Name(project) + } + + return nil +} + +func (w *stateUpgradeProjectNameWidget) validateProject(ans any) error { + proj, ok := ans.(string) + contract.Assertf(ok, "widget should have a string output, got %T", ans) + + if proj == "" { + // The user wants to skip this stack. + return nil + } + + return tokens.ValidateProjectName(proj) } diff --git a/pkg/cmd/pulumi/state_upgrade_test.go b/pkg/cmd/pulumi/state_upgrade_test.go index 73882fc8c5c1..312ab54f4a82 100644 --- a/pkg/cmd/pulumi/state_upgrade_test.go +++ b/pkg/cmd/pulumi/state_upgrade_test.go @@ -5,12 +5,19 @@ import ( "context" "errors" "io" + "runtime" "strings" "testing" + "time" + "github.com/Netflix/go-expect" + "github.com/creack/pty" + "github.com/hinshun/vt10x" "github.com/pulumi/pulumi/pkg/v3/backend" "github.com/pulumi/pulumi/pkg/v3/backend/display" "github.com/pulumi/pulumi/pkg/v3/backend/filestate" + "github.com/pulumi/pulumi/sdk/v3/go/common/testing/iotest" + "github.com/pulumi/pulumi/sdk/v3/go/common/tokens" "github.com/pulumi/pulumi/sdk/v3/go/common/workspace" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -155,6 +162,172 @@ func TestStateUpgradeCmd_Run_backendError(t *testing.T) { assert.ErrorIs(t, err, giveErr) } +//nolint:paralleltest // subtests have shared state +func TestStateUpgradeProjectNameWidget(t *testing.T) { + t.Parallel() + + // Checks the behavior of the prompt for project names + // when they're missing. + + if runtime.GOOS == "windows" { + t.Skip("Skipping: Cannot create pseudo-terminal on Windows") + } + + // This is difficult to test because of how terminal-based this is. + // To test this: + // + // - We set up a pseduo-terminal (with the pty package). + // This will tell survey that it's running in an interactive terminal. + // - We connect that to the expect package, + // which lets us simulate user input and read the output. + // - Lastly, expect doesn't actually interpret terminal escape sequences, + // so we pass the output of survey through a vt100 terminal emulator + // (with the vt10x package), allowing expect to operate on plain text. + + ptty, tty, err := pty.Open() + require.NoError(t, err, "creating pseudo-terminal") + + console, err := expect.NewConsole( + expect.WithStdin(ptty), + expect.WithStdout( + vt10x.New(vt10x.WithWriter(tty)), + // Also write to the test log + // so that if this test fails, + // we can see what the user would have seen. + iotest.LogWriterPrefixed(t, "[stdout] "), + ), + expect.WithCloser(ptty, tty), + // Without this timeout, the test will hang forever + // if expectations don't match. + expect.WithDefaultTimeout(time.Second), + ) + require.NoError(t, err, "creating console") + defer func() { + assert.NoError(t, console.Close(), "close console") + }() + + expect := func(t *testing.T, s string) { + t.Helper() + + t.Logf("expect(%q)", s) + _, err := console.ExpectString(s) + require.NoError(t, err) + } + + sendLine := func(t *testing.T, s string) { + t.Helper() + + t.Logf("send(%q)", s) + _, err := console.SendLine(s) + require.NoError(t, err) + } + + donec := make(chan struct{}) + go func() { + defer close(donec) + + stacks := []tokens.Name{"foo", "bar", "baz"} + projects := make([]tokens.Name, len(stacks)) + + err := (&stateUpgradeProjectNameWidget{ + Stdin: console.Tty(), + Stdout: console.Tty(), + Stderr: iotest.LogWriterPrefixed(t, "[stderr] "), + }).Prompt(stacks, projects) + assert.NoError(t, err, "prompt failed") + assert.Equal(t, []tokens.Name{"foo-project", "", "baz-project"}, projects) + + // We need to close the TTY after we're done here + // so that ExpectEOF unblocks. + assert.NoError(t, console.Tty().Close(), "close tty") + }() + defer func() { + select { + case <-donec: + // Goroutine exited normally. + + case <-time.After(time.Second): + t.Error("timed out waiting for test to finish") + } + }() + + expect(t, "Found stacks without a project name") + + // Subtests must be run serially, in-order + // because they share the same console. + + t.Run("valid name", func(t *testing.T) { + expect(t, "Stack foo") + sendLine(t, "foo-project") + }) + + t.Run("bad name", func(t *testing.T) { + expect(t, "Stack bar") + sendLine(t, "not a valid project name") + expect(t, "project names may only contain alphanumerics") + }) + + t.Run("skip", func(t *testing.T) { + expect(t, "Stack bar") + sendLine(t, "") + }) + + t.Run("long name", func(t *testing.T) { + expect(t, "Stack baz") + sendLine(t, strings.Repeat("a", 101)) // max length is 100 + expect(t, "project names are limited to 100 characters") + }) + + t.Run("recovery after bad name", func(t *testing.T) { + expect(t, "Stack baz") + sendLine(t, "baz-project") + }) + + // ExpectEOF blocks until the console reaches EOF on its input. + // This will happen when the widget exits and closes the TTY. + _, err = console.ExpectEOF() + assert.NoError(t, err, "expect EOF") +} + +func TestStateUpgradeProjectNameWidget_noStacks(t *testing.T) { + t.Parallel() + + if runtime.GOOS == "windows" { + t.Skip("Skipping: Cannot create pseudo-terminal on Windows") + } + + ptty, tty, err := pty.Open() + require.NoError(t, err, "creating pseudo-terminal") + defer func() { + assert.NoError(t, ptty.Close()) + assert.NoError(t, tty.Close()) + }() + + err = (&stateUpgradeProjectNameWidget{ + Stdin: tty, + Stdout: tty, + Stderr: iotest.LogWriterPrefixed(t, "[stderr] "), + }).Prompt([]tokens.Name{}, []tokens.Name{}) + require.NoError(t, err) +} + +func TestStateUpgradeProjectNameWidget_notATerminal(t *testing.T) { + t.Parallel() + + stacks := []tokens.Name{"foo", "bar", "baz"} + projects := make([]tokens.Name, len(stacks)) + + err := (&stateUpgradeProjectNameWidget{ + Stdin: bytes.NewReader(nil), + Stdout: bytes.NewBuffer(nil), + Stderr: iotest.LogWriterPrefixed(t, "[stderr] "), + }).Prompt(stacks, projects) + require.NoError(t, err) + + // No change expected. + assert.Equal(t, []tokens.Name{"", "", ""}, projects) +} + type stubFileBackend struct { filestate.Backend diff --git a/pkg/go.mod b/pkg/go.mod index 41941aceac3e..9d365a5ba103 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -36,7 +36,7 @@ require ( github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d github.com/opentracing/opentracing-go v1.2.0 github.com/pgavlin/goldmark v1.1.33-0.20200616210433-b5eb04559386 - github.com/pulumi/pulumi/sdk/v3 v3.72.2 + github.com/pulumi/pulumi/sdk/v3 v3.73.0 github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 github.com/sergi/go-diff v1.2.0 github.com/spf13/cobra v1.7.0 @@ -63,14 +63,17 @@ require ( require ( github.com/AlecAivazis/survey/v2 v2.0.5 github.com/BurntSushi/toml v1.2.1 + github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 github.com/aws/aws-sdk-go-v2 v1.17.3 github.com/aws/aws-sdk-go-v2/config v1.15.15 github.com/aws/aws-sdk-go-v2/service/iam v1.19.0 github.com/aws/aws-sdk-go-v2/service/kms v1.18.1 github.com/aws/aws-sdk-go-v2/service/sts v1.16.10 + github.com/creack/pty v1.1.17 github.com/edsrzf/mmap-go v1.1.0 github.com/go-git/go-git/v5 v5.6.0 github.com/hexops/gotextdiff v1.0.3 + github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/muesli/cancelreader v0.2.2 @@ -112,7 +115,6 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 // indirect github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 // indirect github.com/acomagu/bufpipe v1.0.3 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect @@ -139,7 +141,6 @@ require ( github.com/cheggaaa/pb v1.0.29 // indirect github.com/cloudflare/circl v1.3.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/creack/pty v1.1.17 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/ettle/strcase v0.1.1 // indirect @@ -176,7 +177,6 @@ require ( github.com/hashicorp/vault/api v1.8.2 // indirect github.com/hashicorp/vault/sdk v0.6.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec // indirect github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect diff --git a/pkg/go.sum b/pkg/go.sum index c74540a7a130..0b36ec15e880 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -1023,8 +1023,8 @@ github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUq github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/valast v1.4.0 h1:sFzyxPDP0riFQUzSBXTCCrAbbIndHPWMndxuEjXdZlc= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= +github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= diff --git a/sdk/go/pulumi-language-go/go.mod b/sdk/go/pulumi-language-go/go.mod index 4247cef24b9f..e4d979068423 100644 --- a/sdk/go/pulumi-language-go/go.mod +++ b/sdk/go/pulumi-language-go/go.mod @@ -12,7 +12,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/opentracing/opentracing-go v1.2.0 github.com/pulumi/pulumi/pkg/v3 v3.55.0 - github.com/pulumi/pulumi/sdk/v3 v3.72.2 + github.com/pulumi/pulumi/sdk/v3 v3.73.0 github.com/stretchr/testify v1.8.3 golang.org/x/mod v0.10.0 google.golang.org/grpc v1.55.0 diff --git a/sdk/go/pulumi-language-go/go.sum b/sdk/go/pulumi-language-go/go.sum index 224fe029da20..608f9a8a1f6f 100644 --- a/sdk/go/pulumi-language-go/go.sum +++ b/sdk/go/pulumi-language-go/go.sum @@ -1005,7 +1005,7 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= github.com/hetznercloud/hcloud-go v1.35.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= diff --git a/sdk/nodejs/cmd/pulumi-language-nodejs/go.mod b/sdk/nodejs/cmd/pulumi-language-nodejs/go.mod index 8834a86284a8..31e639306d6e 100644 --- a/sdk/nodejs/cmd/pulumi-language-nodejs/go.mod +++ b/sdk/nodejs/cmd/pulumi-language-nodejs/go.mod @@ -15,7 +15,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/opentracing/opentracing-go v1.2.0 github.com/pulumi/pulumi/pkg/v3 v3.55.0 - github.com/pulumi/pulumi/sdk/v3 v3.72.2 + github.com/pulumi/pulumi/sdk/v3 v3.73.0 github.com/stretchr/testify v1.8.3 google.golang.org/grpc v1.55.0 ) diff --git a/sdk/nodejs/cmd/pulumi-language-nodejs/go.sum b/sdk/nodejs/cmd/pulumi-language-nodejs/go.sum index e08abae9df82..ab0ae8763bf3 100644 --- a/sdk/nodejs/cmd/pulumi-language-nodejs/go.sum +++ b/sdk/nodejs/cmd/pulumi-language-nodejs/go.sum @@ -1007,7 +1007,7 @@ github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbg github.com/hetznercloud/hcloud-go v1.33.1/go.mod h1:XX/TQub3ge0yWR2yHWmnDVIrB+MQbda1pHxkUmDlUME= github.com/hetznercloud/hcloud-go v1.35.0/go.mod h1:mepQwR6va27S3UQthaEPGS86jtzSY9xWL1e9dyxXpgA= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= diff --git a/tests/go.mod b/tests/go.mod index eaaa9d2f06a2..cf223ca000fa 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -13,7 +13,7 @@ require ( github.com/golang/protobuf v1.5.3 github.com/hexops/autogold v1.3.0 github.com/pulumi/pulumi/pkg/v3 v3.49.0 - github.com/pulumi/pulumi/sdk/v3 v3.72.2 + github.com/pulumi/pulumi/sdk/v3 v3.73.0 github.com/stretchr/testify v1.8.3 google.golang.org/grpc v1.55.0 sourcegraph.com/sourcegraph/appdash v0.0.0-20211028080628-e2786a622600 diff --git a/tests/go.sum b/tests/go.sum index 74ce790dcc79..40b9c113b213 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -1014,7 +1014,7 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/hexops/valast v1.4.0 h1:sFzyxPDP0riFQUzSBXTCCrAbbIndHPWMndxuEjXdZlc= github.com/hexops/valast v1.4.0/go.mod h1:uVjKZ0smVuYlgCSPz9NRi5A04sl7lp6GtFWsROKDgEs= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=