From 0bffacc05d9a74aca56133a79cb04c7fc9c18122 Mon Sep 17 00:00:00 2001 From: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> Date: Wed, 21 Feb 2024 16:36:17 +0100 Subject: [PATCH] fix: cli prompt termination exit code Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com> --- cli/command/utils.go | 22 +++++++++++-- cli/command/utils_test.go | 26 ++++++++++++++++ cli/command/volume/prune_test.go | 31 +++++++++++++++++++ .../testdata/volume-prune-terminate.golden | 0 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 cli/command/volume/testdata/volume-prune-terminate.golden diff --git a/cli/command/utils.go b/cli/command/utils.go index 5c83efc98674..86405fc2e1c1 100644 --- a/cli/command/utils.go +++ b/cli/command/utils.go @@ -8,9 +8,11 @@ import ( "fmt" "io" "os" + "os/signal" "path/filepath" "runtime" "strings" + "syscall" "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/filters" @@ -90,9 +92,23 @@ func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool { ins = streams.NewIn(os.Stdin) } - reader := bufio.NewReader(ins) - answer, _, _ := reader.ReadLine() - return strings.ToLower(string(answer)) == "y" + result := make(chan bool, 1) + go func() { + reader := bufio.NewReader(ins) + answer, _, _ := reader.ReadLine() + result <- strings.ToLower(string(answer)) == "y" + }() + + go func() { + // We catch the signal to avoid the prompt + // from exiting as an error + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) + <-c + result <- false + }() + + return <-result } // PruneFilters returns consolidated prune filters obtained from config.json and cli diff --git a/cli/command/utils_test.go b/cli/command/utils_test.go index 5ca5d0b3bbd9..05e60dccdd9e 100644 --- a/cli/command/utils_test.go +++ b/cli/command/utils_test.go @@ -1,9 +1,14 @@ package command import ( + "bufio" + "bytes" "os" "path/filepath" + "strings" + "syscall" "testing" + "time" "github.com/pkg/errors" "gotest.tools/v3/assert" @@ -68,3 +73,24 @@ func TestValidateOutputPath(t *testing.T) { }) } } + +func TestPromptForConfirmation(t *testing.T) { + buf := new(bytes.Buffer) + w := bufio.NewWriter(buf) + result := make(chan bool) + go func() { + result <- PromptForConfirmation(os.Stdin, w, "") + }() + + time.Sleep(100 * time.Millisecond) + + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + select { + case r := <-result: + assert.Check(t, !r) + case <-time.After(100 * time.Millisecond): + t.Fatal("PromptForConfirmation did not return after SIGINT") + } + assert.NilError(t, w.Flush()) + assert.Equal(t, strings.TrimSpace(buf.String()), "Are you sure you want to proceed? [y/N]") +} diff --git a/cli/command/volume/prune_test.go b/cli/command/volume/prune_test.go index ec0b188894e1..0c347ab10ac6 100644 --- a/cli/command/volume/prune_test.go +++ b/cli/command/volume/prune_test.go @@ -5,7 +5,9 @@ import ( "io" "runtime" "strings" + "syscall" "testing" + "time" "github.com/docker/cli/cli/streams" "github.com/docker/cli/internal/test" @@ -183,3 +185,32 @@ func simplePruneFunc(filters.Args) (types.VolumesPruneReport, error) { SpaceReclaimed: 2000, }, nil } + +func TestVolumePrunePromptTerminate(t *testing.T) { + cli := test.NewFakeCli(&fakeClient{ + volumePruneFunc: func(filter filters.Args) (types.VolumesPruneReport, error) { + return types.VolumesPruneReport{}, errors.New("should not be called") + }, + }) + + cmd := NewPruneCommand(cli) + + errChan := make(chan error) + go func() { + errChan <- cmd.Execute() + }() + + // wait for the prompt to be displayed + time.Sleep(100 * time.Millisecond) + + syscall.Kill(syscall.Getpid(), syscall.SIGINT) + // sigint and sigterm are caught by the prompt + // this allows us to gracefully exit the prompt with a 0 exit code + select { + case err := <-errChan: + assert.NilError(t, err) + case <-time.After(100 * time.Millisecond): + t.Fatal("Prune Volume did not return after SIGINT") + } + golden.Assert(t, cli.OutBuffer().String(), "volume-prune-terminate.golden") +} diff --git a/cli/command/volume/testdata/volume-prune-terminate.golden b/cli/command/volume/testdata/volume-prune-terminate.golden new file mode 100644 index 000000000000..e69de29bb2d1