Skip to content

Commit b37d84f

Browse files
committed
cli/command: move prompt utilities to separate package
Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent af85e1e commit b37d84f

File tree

18 files changed

+343
-276
lines changed

18 files changed

+343
-276
lines changed

cli/command/builder/prune.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/cli/cli"
1010
"github.com/docker/cli/cli/command"
1111
"github.com/docker/cli/cli/command/completion"
12+
"github.com/docker/cli/internal/prompt"
1213
"github.com/docker/cli/opts"
1314
"github.com/docker/docker/api/types"
1415
"github.com/docker/docker/errdefs"
@@ -69,7 +70,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
6970
warning = allCacheWarning
7071
}
7172
if !options.force {
72-
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning)
73+
r, err := prompt.Confirm(ctx, dockerCli.In(), dockerCli.Out(), warning)
7374
if err != nil {
7475
return 0, "", err
7576
}

cli/command/container/prune.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/docker/cli/cli"
88
"github.com/docker/cli/cli/command"
99
"github.com/docker/cli/cli/command/completion"
10+
"github.com/docker/cli/internal/prompt"
1011
"github.com/docker/cli/opts"
1112
"github.com/docker/docker/errdefs"
1213
units "github.com/docker/go-units"
@@ -56,7 +57,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
5657
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
5758

5859
if !options.force {
59-
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning)
60+
r, err := prompt.Confirm(ctx, dockerCli.In(), dockerCli.Out(), warning)
6061
if err != nil {
6162
return 0, "", err
6263
}

cli/command/image/prune.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/cli/cli"
1010
"github.com/docker/cli/cli/command"
1111
"github.com/docker/cli/cli/command/completion"
12+
"github.com/docker/cli/internal/prompt"
1213
"github.com/docker/cli/opts"
1314
"github.com/docker/docker/errdefs"
1415
units "github.com/docker/go-units"
@@ -70,7 +71,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
7071
warning = allImageWarning
7172
}
7273
if !options.force {
73-
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning)
74+
r, err := prompt.Confirm(ctx, dockerCli.In(), dockerCli.Out(), warning)
7475
if err != nil {
7576
return 0, "", err
7677
}

cli/command/network/prune.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/docker/cli/cli"
88
"github.com/docker/cli/cli/command"
9+
"github.com/docker/cli/internal/prompt"
910
"github.com/docker/cli/opts"
1011
"github.com/docker/docker/errdefs"
1112
"github.com/pkg/errors"
@@ -52,7 +53,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
5253
pruneFilters := command.PruneFilters(dockerCli, options.filter.Value())
5354

5455
if !options.force {
55-
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), warning)
56+
r, err := prompt.Confirm(ctx, dockerCli.In(), dockerCli.Out(), warning)
5657
if err != nil {
5758
return "", err
5859
}

cli/command/network/remove.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/docker/cli/cli"
99
"github.com/docker/cli/cli/command"
1010
"github.com/docker/cli/cli/command/completion"
11+
"github.com/docker/cli/internal/prompt"
1112
"github.com/docker/docker/api/types/network"
1213
"github.com/docker/docker/errdefs"
1314
"github.com/spf13/cobra"
@@ -49,7 +50,7 @@ func runRemove(ctx context.Context, dockerCLI command.Cli, networks []string, op
4950
for _, name := range networks {
5051
nw, _, err := apiClient.NetworkInspectWithRaw(ctx, name, network.InspectOptions{})
5152
if err == nil && nw.Ingress {
52-
r, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), ingressWarning)
53+
r, err := prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), ingressWarning)
5354
if err != nil {
5455
return err
5556
}

cli/command/plugin/install.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/docker/cli/cli/command"
1111
"github.com/docker/cli/cli/command/image"
1212
"github.com/docker/cli/cli/internal/jsonstream"
13+
"github.com/docker/cli/internal/prompt"
1314
"github.com/docker/docker/api/types"
1415
registrytypes "github.com/docker/docker/api/types/registry"
1516
"github.com/docker/docker/registry"
@@ -133,12 +134,12 @@ func runInstall(ctx context.Context, dockerCLI command.Cli, opts pluginOptions)
133134
return nil
134135
}
135136

136-
func acceptPrivileges(dockerCLI command.Cli, name string) func(ctx context.Context, privileges types.PluginPrivileges) (bool, error) {
137+
func acceptPrivileges(dockerCLI command.Streams, name string) func(ctx context.Context, privileges types.PluginPrivileges) (bool, error) {
137138
return func(ctx context.Context, privileges types.PluginPrivileges) (bool, error) {
138139
_, _ = fmt.Fprintf(dockerCLI.Out(), "Plugin %q is requesting the following privileges:\n", name)
139140
for _, privilege := range privileges {
140141
_, _ = fmt.Fprintf(dockerCLI.Out(), " - %s: %v\n", privilege.Name, privilege.Value)
141142
}
142-
return command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), "Do you grant the above permissions?")
143+
return prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), "Do you grant the above permissions?")
143144
}
144145
}

cli/command/plugin/upgrade.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/cli/cli"
1010
"github.com/docker/cli/cli/command"
1111
"github.com/docker/cli/cli/internal/jsonstream"
12+
"github.com/docker/cli/internal/prompt"
1213
"github.com/docker/docker/errdefs"
1314
"github.com/pkg/errors"
1415
"github.com/spf13/cobra"
@@ -64,7 +65,7 @@ func runUpgrade(ctx context.Context, dockerCLI command.Cli, opts pluginOptions)
6465

6566
_, _ = fmt.Fprintf(dockerCLI.Out(), "Upgrading plugin %s from %s to %s\n", p.Name, reference.FamiliarString(old), reference.FamiliarString(remote))
6667
if !opts.skipRemoteCheck && remote.String() != old.String() {
67-
r, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), "Plugin images do not match, are you sure?")
68+
r, err := prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), "Plugin images do not match, are you sure?")
6869
if err != nil {
6970
return err
7071
}

cli/command/registry.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
configtypes "github.com/docker/cli/cli/config/types"
1414
"github.com/docker/cli/cli/hints"
1515
"github.com/docker/cli/cli/streams"
16+
"github.com/docker/cli/internal/prompt"
1617
"github.com/docker/cli/internal/tui"
1718
registrytypes "github.com/docker/docker/api/types/registry"
1819
"github.com/morikuni/aec"
@@ -148,16 +149,16 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
148149
}
149150
}
150151

151-
var prompt string
152+
var msg string
152153
defaultUsername = strings.TrimSpace(defaultUsername)
153154
if defaultUsername == "" {
154-
prompt = "Username: "
155+
msg = "Username: "
155156
} else {
156-
prompt = fmt.Sprintf("Username (%s): ", defaultUsername)
157+
msg = fmt.Sprintf("Username (%s): ", defaultUsername)
157158
}
158159

159160
var err error
160-
argUser, err = PromptForInput(ctx, cli.In(), cli.Out(), prompt)
161+
argUser, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), msg)
161162
if err != nil {
162163
return registrytypes.AuthConfig{}, err
163164
}
@@ -171,7 +172,7 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
171172

172173
argPassword = strings.TrimSpace(argPassword)
173174
if argPassword == "" {
174-
restoreInput, err := DisableInputEcho(cli.In())
175+
restoreInput, err := prompt.DisableInputEcho(cli.In())
175176
if err != nil {
176177
return registrytypes.AuthConfig{}, err
177178
}
@@ -188,7 +189,7 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
188189
out := tui.NewOutput(cli.Err())
189190
out.PrintNote("A Personal Access Token (PAT) can be used instead.\n" +
190191
"To create a PAT, visit " + aec.Underline.Apply("https://app.docker.com/settings") + "\n\n")
191-
argPassword, err = PromptForInput(ctx, cli.In(), cli.Out(), "Password: ")
192+
argPassword, err = prompt.ReadInput(ctx, cli.In(), cli.Out(), "Password: ")
192193
if err != nil {
193194
return registrytypes.AuthConfig{}, err
194195
}

cli/command/registry/login_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import (
99
"time"
1010

1111
"github.com/creack/pty"
12-
"github.com/docker/cli/cli/command"
1312
configtypes "github.com/docker/cli/cli/config/types"
1413
"github.com/docker/cli/cli/streams"
14+
"github.com/docker/cli/internal/prompt"
1515
"github.com/docker/cli/internal/test"
1616
registrytypes "github.com/docker/docker/api/types/registry"
1717
"github.com/docker/docker/api/types/system"
@@ -492,7 +492,7 @@ func TestLoginTermination(t *testing.T) {
492492
case <-time.After(1 * time.Second):
493493
t.Fatal("timed out after 1 second. `runLogin` did not return")
494494
case err := <-runErr:
495-
assert.ErrorIs(t, err, command.ErrPromptTerminated)
495+
assert.ErrorIs(t, err, prompt.ErrTerminated)
496496
}
497497
}
498498

cli/command/system/prune.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/docker/cli/cli/command/image"
1616
"github.com/docker/cli/cli/command/network"
1717
"github.com/docker/cli/cli/command/volume"
18+
"github.com/docker/cli/internal/prompt"
1819
"github.com/docker/cli/opts"
1920
"github.com/docker/docker/api/types/versions"
2021
"github.com/docker/docker/errdefs"
@@ -77,7 +78,7 @@ func runPrune(ctx context.Context, dockerCli command.Cli, options pruneOptions)
7778
return errors.New(`ERROR: The "until" filter is not supported with "--volumes"`)
7879
}
7980
if !options.force {
80-
r, err := command.PromptForConfirmation(ctx, dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options))
81+
r, err := prompt.Confirm(ctx, dockerCli.In(), dockerCli.Out(), confirmationMessage(dockerCli, options))
8182
if err != nil {
8283
return err
8384
}

cli/command/trust/revoke.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/docker/cli/cli/command"
99
"github.com/docker/cli/cli/command/image"
1010
"github.com/docker/cli/cli/trust"
11+
"github.com/docker/cli/internal/prompt"
1112
"github.com/docker/docker/errdefs"
1213
"github.com/pkg/errors"
1314
"github.com/spf13/cobra"
@@ -44,7 +45,7 @@ func revokeTrust(ctx context.Context, dockerCLI command.Cli, remote string, opti
4445
return errors.New("cannot use a digest reference for IMAGE:TAG")
4546
}
4647
if imgRefAndAuth.Tag() == "" && !options.forceYes {
47-
deleteRemote, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), fmt.Sprintf("Confirm you would like to delete all signature data for %s?", remote))
48+
deleteRemote, err := prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), fmt.Sprintf("Confirm you would like to delete all signature data for %s?", remote))
4849
if err != nil {
4950
return err
5051
}

cli/command/trust/signer_remove.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/docker/cli/cli/command"
1010
"github.com/docker/cli/cli/command/image"
1111
"github.com/docker/cli/cli/trust"
12+
"github.com/docker/cli/internal/prompt"
1213
"github.com/pkg/errors"
1314
"github.com/spf13/cobra"
1415
"github.com/theupdateframework/notary/client"
@@ -82,11 +83,7 @@ func maybePromptForSignerRemoval(ctx context.Context, dockerCLI command.Cli, rep
8283
"Are you sure you want to continue?",
8384
signerName, repoName, repoName,
8485
)
85-
removeSigner, err := command.PromptForConfirmation(ctx, dockerCLI.In(), dockerCLI.Out(), message)
86-
if err != nil {
87-
return false, err
88-
}
89-
return removeSigner, nil
86+
return prompt.Confirm(ctx, dockerCLI.In(), dockerCLI.Out(), message)
9087
}
9188
return false, nil
9289
}

cli/command/utils.go

Lines changed: 5 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,17 @@
44
package command
55

66
import (
7-
"bufio"
87
"context"
9-
"fmt"
108
"io"
119
"os"
1210
"path/filepath"
13-
"runtime"
1411
"strings"
1512

1613
"github.com/docker/cli/cli/config"
1714
"github.com/docker/cli/cli/streams"
15+
"github.com/docker/cli/internal/prompt"
1816
"github.com/docker/docker/api/types/filters"
1917
"github.com/moby/sys/atomicwriter"
20-
"github.com/moby/term"
2118
"github.com/pkg/errors"
2219
"github.com/spf13/pflag"
2320
)
@@ -35,29 +32,14 @@ func CopyToFile(outfile string, r io.Reader) error {
3532
return err
3633
}
3734

38-
const ErrPromptTerminated cancelledErr = "prompt terminated"
39-
40-
type cancelledErr string
41-
42-
func (e cancelledErr) Error() string {
43-
return string(e)
44-
}
45-
46-
func (cancelledErr) Cancelled() {}
35+
const ErrPromptTerminated = prompt.ErrTerminated
4736

4837
// DisableInputEcho disables input echo on the provided streams.In.
4938
// This is useful when the user provides sensitive information like passwords.
5039
// The function returns a restore function that should be called to restore the
5140
// terminal state.
5241
func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
53-
oldState, err := term.SaveState(ins.FD())
54-
if err != nil {
55-
return nil, err
56-
}
57-
restore = func() error {
58-
return term.RestoreTerminal(ins.FD(), oldState)
59-
}
60-
return restore, term.DisableEcho(ins.FD(), oldState)
42+
return prompt.DisableInputEcho(ins)
6143
}
6244

6345
// PromptForInput requests input from the user.
@@ -68,23 +50,7 @@ func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
6850
// the stack and close the io.Reader used for the prompt which will prevent the
6951
// background goroutine from blocking indefinitely.
7052
func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message string) (string, error) {
71-
_, _ = fmt.Fprint(out, message)
72-
73-
result := make(chan string)
74-
go func() {
75-
scanner := bufio.NewScanner(in)
76-
if scanner.Scan() {
77-
result <- strings.TrimSpace(scanner.Text())
78-
}
79-
}()
80-
81-
select {
82-
case <-ctx.Done():
83-
_, _ = fmt.Fprintln(out, "")
84-
return "", ErrPromptTerminated
85-
case r := <-result:
86-
return r, nil
87-
}
53+
return prompt.ReadInput(ctx, in, out, message)
8854
}
8955

9056
// PromptForConfirmation requests and checks confirmation from the user.
@@ -98,39 +64,7 @@ func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message st
9864
// the stack and close the io.Reader used for the prompt which will prevent the
9965
// background goroutine from blocking indefinitely.
10066
func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, message string) (bool, error) {
101-
if message == "" {
102-
message = "Are you sure you want to proceed?"
103-
}
104-
message += " [y/N] "
105-
106-
_, _ = fmt.Fprint(outs, message)
107-
108-
// On Windows, force the use of the regular OS stdin stream.
109-
if runtime.GOOS == "windows" {
110-
ins = streams.NewIn(os.Stdin)
111-
}
112-
113-
result := make(chan bool)
114-
115-
go func() {
116-
var res bool
117-
scanner := bufio.NewScanner(ins)
118-
if scanner.Scan() {
119-
answer := strings.TrimSpace(scanner.Text())
120-
if strings.EqualFold(answer, "y") {
121-
res = true
122-
}
123-
}
124-
result <- res
125-
}()
126-
127-
select {
128-
case <-ctx.Done():
129-
_, _ = fmt.Fprintln(outs, "")
130-
return false, ErrPromptTerminated
131-
case r := <-result:
132-
return r, nil
133-
}
67+
return prompt.Confirm(ctx, ins, outs, message)
13468
}
13569

13670
// PruneFilters merges prune filters specified in config.json with those specified

0 commit comments

Comments
 (0)