Don't print error prefix when a confirmation prompt is declined

Use `result.Result` in more places, so when a confirmation prompt is
declined, we just return `result.Bail()` after printing a message
without the `error: ` prefix.

Fixes #2070
This commit is contained in:
Matt Ellis 2019-03-25 13:45:12 -07:00
parent f34aef2f4d
commit ccd958777c
7 changed files with 64 additions and 51 deletions

View file

@ -1,9 +1,10 @@
## 0.17.4 (Unreleased)
## 0.17.3 (Released March 26, 2019)
## Improvements
- Add support for serializing JavaScript function that capture [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
- Support serializing arrow-functions with deconstructed parameters.
- Don't print the `error:` prefix when Pulumi exists because of a declined confirmation prompt (fixes [pulumi/pulumi#458](https://github.com/pulumi/pulumi/issues/2070))
## 0.17.3 (Released March 26, 2019)
### Improvements
@ -12,7 +13,8 @@
- A bug in the previous version of the Pulumi CLI occasionally caused the Pulumi Engine to load the incorrect resource
plugin when processing an update. This bug has been fixed in 0.17.3 by performing a deterministic selection of the
best set of plugins available to the engine before starting up. See
[pulumi/pulumi#2579](https://github.com/pulumi/pulumi/issues/2579) for discussion on this issue.
- Add support for serializing JavaScript function that capture [BigInts](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
- Support serializing arrow-functions with deconstructed parameters.
## 0.17.2 (Released March 15, 2019)

View file

@ -17,7 +17,8 @@ package cmd
import (
"fmt"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/result"
"github.com/spf13/cobra"
"github.com/pulumi/pulumi/pkg/backend/display"
@ -41,11 +42,11 @@ func newCancelCmd() *cobra.Command {
"\n" +
"After this command completes successfully, the stack will be ready for further\n" +
"updates.",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
Run: cmdutil.RunResultFunc(func(cmd *cobra.Command, args []string) result.Result {
// Use the stack provided or, if missing, default to the current one.
if len(args) > 0 {
if stack != "" {
return errors.New("only one of --stack or argument stack name may be specified, not both")
return result.Error("only one of --stack or argument stack name may be specified, not both")
}
stack = args[0]
@ -57,25 +58,26 @@ func newCancelCmd() *cobra.Command {
s, err := requireStack(stack, false, opts, true /*setCurrent*/)
if err != nil {
return err
return result.FromError(err)
}
// Ensure that we are targeting the Pulumi cloud.
backend, ok := s.Backend().(httpstate.Backend)
if !ok {
return errors.New("the `cancel` command is not supported for local stacks")
return result.Error("the `cancel` command is not supported for local stacks")
}
// Ensure the user really wants to do this.
stackName := string(s.Ref().Name())
prompt := fmt.Sprintf("This will irreversibly cancel the currently running update for '%s'!", stackName)
if !yes && !confirmPrompt(prompt, stackName, opts) {
return errors.New("confirmation declined")
fmt.Println("confirmation declined")
return result.Bail()
}
// Cancel the update.
if err := backend.CancelCurrentUpdate(commandContext(), s.Ref()); err != nil {
return err
return result.FromError(err)
}
msg := fmt.Sprintf(

View file

@ -18,7 +18,8 @@ import (
"fmt"
"os"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/util/result"
"github.com/spf13/cobra"
"github.com/pulumi/pulumi/pkg/backend/display"
@ -44,11 +45,11 @@ func newStackRmCmd() *cobra.Command {
"`destroy` command for removing a resources, as this is a distinct operation.\n" +
"\n" +
"After this command completes, the stack will no longer be available for updates.",
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
Run: cmdutil.RunResultFunc(func(cmd *cobra.Command, args []string) result.Result {
// Use the stack provided or, if missing, default to the current one.
if len(args) > 0 {
if stack != "" {
return errors.New("only one of --stack or argument stack name may be specified, not both")
return result.Error("only one of --stack or argument stack name may be specified, not both")
}
stack = args[0]
}
@ -59,29 +60,30 @@ func newStackRmCmd() *cobra.Command {
s, err := requireStack(stack, false, opts, true /*setCurrent*/)
if err != nil {
return err
return result.FromError(err)
}
// Ensure the user really wants to do this.
prompt := fmt.Sprintf("This will permanently remove the '%s' stack!", s.Ref())
if !yes && !confirmPrompt(prompt, s.Ref().String(), opts) {
return errors.New("confirmation declined")
fmt.Println("confirmation declined")
return result.Bail()
}
hasResources, err := s.Remove(commandContext(), force)
if err != nil {
if hasResources {
return errors.Errorf(
return result.Errorf(
"'%s' still has resources; removal rejected; pass --force to override", s.Ref())
}
return err
return result.FromError(err)
}
if !preserveConfig {
// Blow away stack specific settings if they exist. If we get an ENOENT error, ignore it.
if path, err := workspace.DetectProjectStackPath(s.Ref().Name()); err == nil {
if err = os.Remove(path); err != nil && !os.IsNotExist(err) {
return err
return result.FromError(err)
}
}
}

View file

@ -18,6 +18,8 @@ import (
"encoding/json"
"fmt"
"github.com/pulumi/pulumi/pkg/util/result"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pkg/errors"
@ -109,7 +111,7 @@ func locateStackResource(opts display.Options, snap *deploy.Snapshot, urn resour
}
// runStateEdit runs the given state edit function on a resource with the given URN in a given stack.
func runStateEdit(stackName string, urn resource.URN, operation edit.OperationFunc) error {
func runStateEdit(stackName string, urn resource.URN, operation edit.OperationFunc) result.Result {
return runTotalStateEdit(stackName, func(opts display.Options, snap *deploy.Snapshot) error {
res, err := locateStackResource(opts, snap, urn)
if err != nil {
@ -122,17 +124,18 @@ func runStateEdit(stackName string, urn resource.URN, operation edit.OperationFu
// runTotalStateEdit runs a snapshot-mutating function on the entirity of the given stack's snapshot. Before mutating
// the snapshot, the user is prompted for confirmation if the current session is interactive.
func runTotalStateEdit(stackName string, operation func(opts display.Options, snap *deploy.Snapshot) error) error {
func runTotalStateEdit(stackName string,
operation func(opts display.Options, snap *deploy.Snapshot) error) result.Result {
opts := display.Options{
Color: cmdutil.GetGlobalColorization(),
}
s, err := requireStack(stackName, true, opts, true /*setCurrent*/)
if err != nil {
return err
return result.FromError(err)
}
snap, err := s.Snapshot(commandContext())
if err != nil {
return err
return result.FromError(err)
}
if cmdutil.Interactive() {
@ -145,7 +148,8 @@ func runTotalStateEdit(stackName string, operation func(opts display.Options, sn
if err = survey.AskOne(&survey.Confirm{
Message: prompt,
}, &confirm, nil); err != nil || !confirm {
return errors.New("confirmation declined")
fmt.Println("confirmation declined")
return result.Bail()
}
}
@ -154,7 +158,7 @@ func runTotalStateEdit(stackName string, operation func(opts display.Options, sn
// before we mutated it, we'll assert that we didn't make it invalid by mutating it.
stackIsAlreadyHosed := snap.VerifyIntegrity() != nil
if err = operation(opts, snap); err != nil {
return err
return result.FromError(err)
}
// If the stack is already broken, don't bother verifying the integrity here.
@ -165,11 +169,11 @@ func runTotalStateEdit(stackName string, operation func(opts display.Options, sn
// Once we've mutated the snapshot, import it back into the backend so that it can be persisted.
bytes, err := json.Marshal(stack.SerializeDeployment(snap))
if err != nil {
return err
return result.FromError(err)
}
dep := apitype.UntypedDeployment{
Version: apitype.DeploymentSchemaVersionCurrent,
Deployment: bytes,
}
return s.ImportDeployment(commandContext(), &dep)
return result.WrapIfNonNil(s.ImportDeployment(commandContext(), &dep))
}

View file

@ -17,13 +17,14 @@ package cmd
import (
"fmt"
"github.com/pulumi/pulumi/pkg/util/result"
"github.com/pulumi/pulumi/pkg/diag"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/deploy"
"github.com/pulumi/pulumi/pkg/resource/edit"
"github.com/pulumi/pulumi/pkg/util/cmdutil"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -42,9 +43,9 @@ by its Pulumi URN (use 'pulumi stack --show-urns' to get it).
Resources can't be deleted if there exist other resources that depend on it or are parented to it. Protected resources
will not be deleted unless it is specifically requested using the --force flag.`,
Args: cmdutil.ExactArgs(1),
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
Run: cmdutil.RunResultFunc(func(cmd *cobra.Command, args []string) result.Result {
urn := resource.URN(args[0])
err := runStateEdit(stack, urn, func(snap *deploy.Snapshot, res *resource.State) error {
res := runStateEdit(stack, urn, func(snap *deploy.Snapshot, res *resource.State) error {
if !force {
return edit.DeleteResource(snap, res)
}
@ -56,8 +57,8 @@ will not be deleted unless it is specifically requested using the --force flag.`
return edit.DeleteResource(snap, res)
})
if err != nil {
switch e := err.(type) {
if res != nil {
switch e := res.Error().(type) {
case edit.ResourceHasDependenciesError:
message := "This resource can't be safely deleted because the following resources depend on it:\n"
for _, dependentResource := range e.Dependencies {
@ -66,13 +67,13 @@ will not be deleted unless it is specifically requested using the --force flag.`
}
message += "\nDelete those resources first before deleting this one."
return errors.New(message)
return result.Error(message)
case edit.ResourceProtectedError:
return errors.New(
return result.Error(
"This resource can't be safely deleted because it is protected. " +
"Re-run this command with --force to force deletion")
default:
return err
return res
}
}
fmt.Println("Resource deleted successfully")

View file

@ -17,9 +17,10 @@ package cmd
import (
"fmt"
"github.com/pulumi/pulumi/pkg/util/result"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/pkg/backend/display"
"github.com/pulumi/pulumi/pkg/resource/deploy"
@ -41,13 +42,13 @@ func newStateUnprotectCommand() *cobra.Command {
This command clears the 'protect' bit on one or more resources, allowing those resources to be deleted.`,
Args: cmdutil.MaximumNArgs(1),
Run: cmdutil.RunFunc(func(cmd *cobra.Command, args []string) error {
Run: cmdutil.RunResultFunc(func(cmd *cobra.Command, args []string) result.Result {
if unprotectAll {
return unprotectAllResources(stack)
}
if len(args) != 1 {
return errors.New("must provide a URN corresponding to a resource")
return result.Error("must provide a URN corresponding to a resource")
}
urn := resource.URN(args[0])
@ -62,8 +63,8 @@ This command clears the 'protect' bit on one or more resources, allowing those r
return cmd
}
func unprotectAllResources(stackName string) error {
err := runTotalStateEdit(stackName, func(_ display.Options, snap *deploy.Snapshot) error {
func unprotectAllResources(stackName string) result.Result {
res := runTotalStateEdit(stackName, func(_ display.Options, snap *deploy.Snapshot) error {
for _, res := range snap.Resources {
err := edit.UnprotectResource(snap, res)
contract.AssertNoError(err)
@ -72,17 +73,17 @@ func unprotectAllResources(stackName string) error {
return nil
})
if err != nil {
return err
if res != nil {
return res
}
fmt.Println("All resources successfully unprotected")
return nil
}
func unprotectResource(stackName string, urn resource.URN) error {
err := runStateEdit(stackName, urn, edit.UnprotectResource)
if err != nil {
return err
func unprotectResource(stackName string, urn resource.URN) result.Result {
res := runStateEdit(stackName, urn, edit.UnprotectResource)
if res != nil {
return res
}
fmt.Println("Resource successfully unprotected")
return nil

View file

@ -123,14 +123,14 @@ func PreviewThenPrompt(ctx context.Context, kind apitype.UpdateKind, stack Stack
}
// Otherwise, ensure the user wants to proceed.
err := confirmBeforeUpdating(kind, stack, events, op.Opts)
res = confirmBeforeUpdating(kind, stack, events, op.Opts)
close(eventsChannel)
return changes, result.WrapIfNonNil(err)
return changes, res
}
// confirmBeforeUpdating asks the user whether to proceed. A nil error means yes.
func confirmBeforeUpdating(kind apitype.UpdateKind, stack Stack,
events []engine.Event, opts UpdateOptions) error {
events []engine.Event, opts UpdateOptions) result.Result {
for {
var response string
@ -167,11 +167,12 @@ func confirmBeforeUpdating(kind apitype.UpdateKind, stack Stack,
Options: choices,
Default: string(no),
}, &response, nil); err != nil {
return errors.Wrapf(err, "confirmation cancelled, not proceeding with the %s", kind)
return result.FromError(errors.Wrapf(err, "confirmation cancelled, not proceeding with the %s", kind))
}
if response == string(no) {
return errors.Errorf("confirmation declined, not proceeding with the %s", kind)
fmt.Printf("confirmation declined, not proceeding with the %s\n", kind)
return result.Bail()
}
if response == string(yes) {