Skip to content

Commit

Permalink
fix: cli prompt termination exit code
Browse files Browse the repository at this point in the history
Signed-off-by: Alano Terblanche <18033717+Benehiko@users.noreply.github.com>
  • Loading branch information
Benehiko committed Feb 21, 2024
1 parent 1443014 commit 0bffacc
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 3 deletions.
22 changes: 19 additions & 3 deletions cli/command/utils.go
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions 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"
Expand Down Expand Up @@ -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]")
}
31 changes: 31 additions & 0 deletions cli/command/volume/prune_test.go
Expand Up @@ -5,7 +5,9 @@ import (
"io"
"runtime"
"strings"
"syscall"
"testing"
"time"

"github.com/docker/cli/cli/streams"
"github.com/docker/cli/internal/test"
Expand Down Expand Up @@ -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")
}
Empty file.

0 comments on commit 0bffacc

Please sign in to comment.