- The `DisableInputEcho` and `PromptForInput` utilities were added inc15ade0c64as part of a bug-fix, which was part of v28.x. [There are no (publicly visible) users][1] of either. - The `ErrPromptTerminated` was added in v26.x (originally added in10bf91a02d, later updated in commit7c722c08d0. [It is not used][2] - The `PromptForConfirmation` was added in [moby@280c872] (docker v1.13.0) as part of the `docker <object> prune` subcommands. It was meant for internal use but exported to allow re-using it in the `container`, `image` (etc.) packages. However, a breaking change to its signature was made in10bf91a02d. It currently does [not appear to have any (public) users][2]. This patch deprecates the `ErrPromptTerminated`, `DisableInputEcho`, `PromptForInput`, and `PromptForConfirmation` utilities from the `cli/command` package. The core functionality of these is still available in the `internal/prompt` package, which we may make public at some point, but still needs some refining / decoupling. [moby@280c872]:280c872366[1]: https://grep.app/search?f.lang=Go®exp=true&q=%5C.%28DisableInputEcho%7CPromptForInput%29%5C%28 [2]: https://grep.app/search?f.lang=Go&q=%5C.ErrPromptTerminated [3]: https://grep.app/search?f.lang=Go&q=.PromptForConfirmation%28 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
161 lines
5.3 KiB
Go
161 lines
5.3 KiB
Go
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
|
|
//go:build go1.23
|
|
|
|
package command
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/docker/cli/cli/config"
|
|
"github.com/docker/cli/cli/streams"
|
|
"github.com/docker/cli/internal/prompt"
|
|
"github.com/docker/docker/api/types/filters"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ErrPromptTerminated is returned if the user terminated the prompt.
|
|
//
|
|
// Deprecated: this error is for internal use and will be removed in the next release.
|
|
const ErrPromptTerminated = prompt.ErrTerminated
|
|
|
|
// DisableInputEcho disables input echo on the provided streams.In.
|
|
// This is useful when the user provides sensitive information like passwords.
|
|
// The function returns a restore function that should be called to restore the
|
|
// terminal state.
|
|
//
|
|
// Deprecated: this function is for internal use and will be removed in the next release.
|
|
func DisableInputEcho(ins *streams.In) (restore func() error, err error) {
|
|
return prompt.DisableInputEcho(ins)
|
|
}
|
|
|
|
// PromptForInput requests input from the user.
|
|
//
|
|
// If the user terminates the CLI with SIGINT or SIGTERM while the prompt is
|
|
// active, the prompt will return an empty string ("") with an ErrPromptTerminated error.
|
|
// When the prompt returns an error, the caller should propagate the error up
|
|
// the stack and close the io.Reader used for the prompt which will prevent the
|
|
// background goroutine from blocking indefinitely.
|
|
//
|
|
// Deprecated: this function is for internal use and will be removed in the next release.
|
|
func PromptForInput(ctx context.Context, in io.Reader, out io.Writer, message string) (string, error) {
|
|
return prompt.ReadInput(ctx, in, out, message)
|
|
}
|
|
|
|
// PromptForConfirmation requests and checks confirmation from the user.
|
|
// This will display the provided message followed by ' [y/N] '. If the user
|
|
// input 'y' or 'Y' it returns true otherwise false. If no message is provided,
|
|
// "Are you sure you want to proceed? [y/N] " will be used instead.
|
|
//
|
|
// If the user terminates the CLI with SIGINT or SIGTERM while the prompt is
|
|
// active, the prompt will return false with an ErrPromptTerminated error.
|
|
// When the prompt returns an error, the caller should propagate the error up
|
|
// the stack and close the io.Reader used for the prompt which will prevent the
|
|
// background goroutine from blocking indefinitely.
|
|
//
|
|
// Deprecated: this function is for internal use and will be removed in the next release.
|
|
func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, message string) (bool, error) {
|
|
return prompt.Confirm(ctx, ins, outs, message)
|
|
}
|
|
|
|
// PruneFilters merges prune filters specified in config.json with those specified
|
|
// as command-line flags.
|
|
//
|
|
// CLI label filters have precedence over those specified in config.json. If a
|
|
// label filter specified as flag conflicts with a label defined in config.json
|
|
// (i.e., "label=some-value" conflicts with "label!=some-value", and vice versa),
|
|
// then the filter defined in config.json is omitted.
|
|
func PruneFilters(dockerCLI config.Provider, pruneFilters filters.Args) filters.Args {
|
|
cfg := dockerCLI.ConfigFile()
|
|
if cfg == nil {
|
|
return pruneFilters
|
|
}
|
|
|
|
// Merge filters provided through the CLI with default filters defined
|
|
// in the CLI-configfile.
|
|
for _, f := range cfg.PruneFilters {
|
|
k, v, ok := strings.Cut(f, "=")
|
|
if !ok {
|
|
continue
|
|
}
|
|
switch k {
|
|
case "label":
|
|
// "label != some-value" conflicts with "label = some-value"
|
|
if pruneFilters.ExactMatch("label!", v) {
|
|
continue
|
|
}
|
|
pruneFilters.Add(k, v)
|
|
case "label!":
|
|
// "label != some-value" conflicts with "label = some-value"
|
|
if pruneFilters.ExactMatch("label", v) {
|
|
continue
|
|
}
|
|
pruneFilters.Add(k, v)
|
|
default:
|
|
pruneFilters.Add(k, v)
|
|
}
|
|
}
|
|
|
|
return pruneFilters
|
|
}
|
|
|
|
// ValidateOutputPath validates the output paths of the "docker cp" command.
|
|
func ValidateOutputPath(path string) error {
|
|
dir := filepath.Dir(filepath.Clean(path))
|
|
if dir != "" && dir != "." {
|
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
return errors.Errorf("invalid output path: directory %q does not exist", dir)
|
|
}
|
|
}
|
|
// check whether `path` points to a regular file
|
|
// (if the path exists and doesn't point to a directory)
|
|
if fileInfo, err := os.Stat(path); !os.IsNotExist(err) {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if fileInfo.Mode().IsDir() || fileInfo.Mode().IsRegular() {
|
|
return nil
|
|
}
|
|
|
|
if err := ValidateOutputPathFileMode(fileInfo.Mode()); err != nil {
|
|
return errors.Wrapf(err, "invalid output path: %q must be a directory or a regular file", path)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateOutputPathFileMode validates the output paths of the "docker cp" command
|
|
// and serves as a helper to [ValidateOutputPath]
|
|
func ValidateOutputPathFileMode(fileMode os.FileMode) error {
|
|
switch {
|
|
case fileMode&os.ModeDevice != 0:
|
|
return errors.New("got a device")
|
|
case fileMode&os.ModeIrregular != 0:
|
|
return errors.New("got an irregular file")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func invalidParameter(err error) error {
|
|
return invalidParameterErr{err}
|
|
}
|
|
|
|
type invalidParameterErr struct{ error }
|
|
|
|
func (invalidParameterErr) InvalidParameter() {}
|
|
|
|
func notFound(err error) error {
|
|
return notFoundErr{err}
|
|
}
|
|
|
|
type notFoundErr struct{ error }
|
|
|
|
func (notFoundErr) NotFound() {}
|
|
func (e notFoundErr) Unwrap() error {
|
|
return e.error
|
|
}
|