diff --git a/Dockerfile b/Dockerfile index f48105ee..19a05cff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG GO_VERSION=1.21 ARG XX_VERSION=1.4.0 # for testing -ARG DOCKER_VERSION=25.0.2 +ARG DOCKER_VERSION=26.0.0 ARG GOTESTSUM_VERSION=v1.9.0 ARG REGISTRY_VERSION=2.8.0 ARG BUILDKIT_VERSION=v0.12.5 diff --git a/commands/util.go b/commands/util.go index d7e44f50..3bde9b71 100644 --- a/commands/util.go +++ b/commands/util.go @@ -1,17 +1,22 @@ package commands import ( + "bufio" "context" + "fmt" "io" + "os" + "runtime" + "strings" - "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/streams" ) func prompt(ctx context.Context, ins io.Reader, out io.Writer, msg string) (bool, error) { done := make(chan struct{}) var ok bool go func() { - ok = command.PromptForConfirmation(ins, out, msg) + ok = promptForConfirmation(ins, out, msg) close(done) }() select { @@ -21,3 +26,32 @@ func prompt(ctx context.Context, ins io.Reader, out io.Writer, msg string) (bool return ok, nil } } + +// promptForConfirmation requests and checks confirmation from user. +// This will display the provided message followed by ' [y/N] '. If +// the user input 'y' or 'Y' it returns true other false. If no +// message is provided "Are you sure you want to proceed? [y/N] " +// will be used instead. +// +// Copied from github.com/docker/cli since the upstream version changed +// recently with an incompatible change. +// +// See https://github.com/docker/buildx/pull/2359#discussion_r1544736494 +// for discussion on the issue. +func promptForConfirmation(ins io.Reader, outs io.Writer, message string) bool { + if message == "" { + message = "Are you sure you want to proceed?" + } + message += " [y/N] " + + _, _ = fmt.Fprint(outs, message) + + // On Windows, force the use of the regular OS stdin stream. + if runtime.GOOS == "windows" { + ins = streams.NewIn(os.Stdin) + } + + reader := bufio.NewReader(ins) + answer, _, _ := reader.ReadLine() + return strings.ToLower(string(answer)) == "y" +} diff --git a/go.mod b/go.mod index 540ada85..38ea139f 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/containerd/typeurl/v2 v2.1.1 github.com/creack/pty v1.1.18 github.com/distribution/reference v0.5.0 - github.com/docker/cli v26.0.0-rc1+incompatible + github.com/docker/cli v26.0.0+incompatible github.com/docker/cli-docs-tool v0.7.0 github.com/docker/docker v26.0.0-rc1+incompatible github.com/docker/go-units v0.5.0 diff --git a/go.sum b/go.sum index a95bbe55..3cdc1005 100644 --- a/go.sum +++ b/go.sum @@ -121,8 +121,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v26.0.0-rc1+incompatible h1:PVxv2ySd8iZHoNfoAoKcnWSC/hKP2qMb806PWM34v50= -github.com/docker/cli v26.0.0-rc1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUyzJVPxD30I= +github.com/docker/cli v26.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli-docs-tool v0.7.0 h1:M2Da98Unz2kz3A5d4yeSGbhyOge2mfYSNjAFt01Rw0M= github.com/docker/cli-docs-tool v0.7.0/go.mod h1:zMjqTFCU361PRh8apiXzeAZ1Q/xupbIwTusYpzCXS/o= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go b/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go index be3a0582..feff8a8f 100644 --- a/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go +++ b/vendor/github.com/docker/cli/cli-plugins/manager/cobra.go @@ -2,11 +2,14 @@ package manager import ( "fmt" + "net/url" "os" + "strings" "sync" "github.com/docker/cli/cli/command" "github.com/spf13/cobra" + "go.opentelemetry.io/otel/attribute" ) const ( @@ -30,6 +33,10 @@ const ( // is, one which failed it's candidate test) and contains the // reason for the failure. CommandAnnotationPluginInvalid = "com.docker.cli.plugin-invalid" + + // CommandAnnotationPluginCommandPath is added to overwrite the + // command path for a plugin invocation. + CommandAnnotationPluginCommandPath = "com.docker.cli.plugin.command_path" ) var pluginCommandStubsOnce sync.Once @@ -98,3 +105,44 @@ func AddPluginCommandStubs(dockerCli command.Cli, rootCmd *cobra.Command) (err e }) return err } + +const ( + dockerCliAttributePrefix = attribute.Key("docker.cli") + + cobraCommandPath = attribute.Key("cobra.command_path") +) + +func getPluginResourceAttributes(cmd *cobra.Command, plugin Plugin) attribute.Set { + commandPath := cmd.Annotations[CommandAnnotationPluginCommandPath] + if commandPath == "" { + commandPath = fmt.Sprintf("%s %s", cmd.CommandPath(), plugin.Name) + } + + attrSet := attribute.NewSet( + cobraCommandPath.String(commandPath), + ) + + kvs := make([]attribute.KeyValue, 0, attrSet.Len()) + for iter := attrSet.Iter(); iter.Next(); { + attr := iter.Attribute() + kvs = append(kvs, attribute.KeyValue{ + Key: dockerCliAttributePrefix + "." + attr.Key, + Value: attr.Value, + }) + } + return attribute.NewSet(kvs...) +} + +func appendPluginResourceAttributesEnvvar(env []string, cmd *cobra.Command, plugin Plugin) []string { + if attrs := getPluginResourceAttributes(cmd, plugin); attrs.Len() > 0 { + // values in environment variables need to be in baggage format + // otel/baggage package can be used after update to v1.22, currently it encodes incorrectly + attrsSlice := make([]string, attrs.Len()) + for iter := attrs.Iter(); iter.Next(); { + i, v := iter.IndexedAttribute() + attrsSlice[i] = string(v.Key) + "=" + url.PathEscape(v.Value.AsString()) + } + env = append(env, ResourceAttributesEnvvar+"="+strings.Join(attrsSlice, ",")) + } + return env +} diff --git a/vendor/github.com/docker/cli/cli-plugins/manager/manager.go b/vendor/github.com/docker/cli/cli-plugins/manager/manager.go index d2393494..d3b7b7b5 100644 --- a/vendor/github.com/docker/cli/cli-plugins/manager/manager.go +++ b/vendor/github.com/docker/cli/cli-plugins/manager/manager.go @@ -17,11 +17,17 @@ import ( "golang.org/x/sync/errgroup" ) -// ReexecEnvvar is the name of an ennvar which is set to the command -// used to originally invoke the docker CLI when executing a -// plugin. Assuming $PATH and $CWD remain unchanged this should allow -// the plugin to re-execute the original CLI. -const ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND" +const ( + // ReexecEnvvar is the name of an ennvar which is set to the command + // used to originally invoke the docker CLI when executing a + // plugin. Assuming $PATH and $CWD remain unchanged this should allow + // the plugin to re-execute the original CLI. + ReexecEnvvar = "DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND" + + // ResourceAttributesEnvvar is the name of the envvar that includes additional + // resource attributes for OTEL. + ResourceAttributesEnvvar = "OTEL_RESOURCE_ATTRIBUTES" +) // errPluginNotFound is the error returned when a plugin could not be found. type errPluginNotFound string @@ -236,6 +242,7 @@ func PluginRunCommand(dockerCli command.Cli, name string, rootcmd *cobra.Command cmd.Env = os.Environ() cmd.Env = append(cmd.Env, ReexecEnvvar+"="+os.Args[0]) + cmd.Env = appendPluginResourceAttributesEnvvar(cmd.Env, rootcmd, plugin) return cmd, nil } diff --git a/vendor/github.com/docker/cli/cli/cobra.go b/vendor/github.com/docker/cli/cli/cobra.go index 627096bb..d07a3b21 100644 --- a/vendor/github.com/docker/cli/cli/cobra.go +++ b/vendor/github.com/docker/cli/cli/cobra.go @@ -470,7 +470,7 @@ Common Commands: Management Commands: {{- range managementSubCommands . }} - {{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}} + {{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}} {{- end}} {{- end}} @@ -479,7 +479,7 @@ Management Commands: Swarm Commands: {{- range orchestratorSubCommands . }} - {{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}}{{ if isPlugin .}} {{vendorAndVersion .}}{{ end}} + {{rpad (decoratedName .) (add .NamePadding 1)}}{{.Short}} {{- end}} {{- end}} diff --git a/vendor/github.com/docker/cli/cli/command/events_utils.go b/vendor/github.com/docker/cli/cli/command/events_utils.go index 497886cd..bb656fbf 100644 --- a/vendor/github.com/docker/cli/cli/command/events_utils.go +++ b/vendor/github.com/docker/cli/cli/command/events_utils.go @@ -9,12 +9,16 @@ import ( // EventHandler is abstract interface for user to customize // own handle functions of each type of events +// +// Deprecated: EventHandler is no longer used, and will be removed in the next release. type EventHandler interface { Handle(action events.Action, h func(events.Message)) Watch(c <-chan events.Message) } // InitEventHandler initializes and returns an EventHandler +// +// Deprecated: InitEventHandler is no longer used, and will be removed in the next release. func InitEventHandler() EventHandler { return &eventHandler{handlers: make(map[events.Action]func(events.Message))} } diff --git a/vendor/github.com/docker/cli/cli/command/utils.go b/vendor/github.com/docker/cli/cli/command/utils.go index 5c83efc9..df1f876f 100644 --- a/vendor/github.com/docker/cli/cli/command/utils.go +++ b/vendor/github.com/docker/cli/cli/command/utils.go @@ -5,12 +5,15 @@ package command import ( "bufio" + "context" "fmt" "io" "os" + "os/signal" "path/filepath" "runtime" "strings" + "syscall" "github.com/docker/cli/cli/streams" "github.com/docker/docker/api/types/filters" @@ -72,12 +75,21 @@ func PrettyPrint(i any) string { } } -// PromptForConfirmation requests and checks confirmation from user. -// This will display the provided message followed by ' [y/N] '. If -// the user input 'y' or 'Y' it returns true other false. If no -// message is provided "Are you sure you want to proceed? [y/N] " -// will be used instead. -func PromptForConfirmation(ins io.Reader, outs io.Writer, message string) bool { +type PromptError error + +var ErrPromptTerminated = PromptError(errors.New("prompt terminated")) + +// 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. +func PromptForConfirmation(ctx context.Context, ins io.Reader, outs io.Writer, message string) (bool, error) { if message == "" { message = "Are you sure you want to proceed?" } @@ -90,9 +102,31 @@ 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) + + // Catch the termination signal and exit the prompt gracefully. + // The caller is responsible for properly handling the termination. + notifyCtx, notifyCancel := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM) + defer notifyCancel() + + go func() { + var res bool + scanner := bufio.NewScanner(ins) + if scanner.Scan() { + answer := strings.TrimSpace(scanner.Text()) + if strings.EqualFold(answer, "y") { + res = true + } + } + result <- res + }() + + select { + case <-notifyCtx.Done(): + return false, ErrPromptTerminated + case r := <-result: + return r, nil + } } // PruneFilters returns consolidated prune filters obtained from config.json and cli diff --git a/vendor/modules.txt b/vendor/modules.txt index fc7e3e18..45b5d0ad 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -215,7 +215,7 @@ github.com/davecgh/go-spew/spew # github.com/distribution/reference v0.5.0 ## explicit; go 1.20 github.com/distribution/reference -# github.com/docker/cli v26.0.0-rc1+incompatible +# github.com/docker/cli v26.0.0+incompatible ## explicit github.com/docker/cli/cli github.com/docker/cli/cli-plugins/manager