Merge pull request #3684 from thaJeztah/bump_moby

vendor: docker/cli v29.3.0, moby/api v1.54.0, moby/client v0.3.0
This commit is contained in:
CrazyMax
2026-03-06 11:23:00 +01:00
committed by GitHub
27 changed files with 175 additions and 82 deletions
+3 -3
View File
@@ -16,7 +16,7 @@ require (
github.com/creack/pty v1.1.24 github.com/creack/pty v1.1.24
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/distribution/reference v0.6.0 github.com/distribution/reference v0.6.0
github.com/docker/cli v29.2.1+incompatible github.com/docker/cli v29.3.0+incompatible
github.com/docker/cli-docs-tool v0.11.0 github.com/docker/cli-docs-tool v0.11.0
github.com/docker/docker v28.5.2+incompatible github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-units v0.5.0 github.com/docker/go-units v0.5.0
@@ -31,8 +31,8 @@ require (
github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/moby/buildkit v0.28.0 github.com/moby/buildkit v0.28.0
github.com/moby/go-archive v0.2.0 github.com/moby/go-archive v0.2.0
github.com/moby/moby/api v1.53.0 github.com/moby/moby/api v1.54.0
github.com/moby/moby/client v0.2.2 github.com/moby/moby/client v0.3.0
github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c
github.com/moby/sys/atomicwriter v0.1.0 github.com/moby/sys/atomicwriter v0.1.0
github.com/moby/sys/mountinfo v0.7.2 github.com/moby/sys/mountinfo v0.7.2
+6 -6
View File
@@ -177,8 +177,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v29.2.1+incompatible h1:n3Jt0QVCN65eiVBoUTZQM9mcQICCJt3akW4pKAbKdJg= github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=
github.com/docker/cli v29.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE=
github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -430,10 +430,10 @@ github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w= github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=
github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=
github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM= github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=
github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ= github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c h1:hRUo0Ir9PEaa0PQCgg8WvGku0sgmTo/NgnCzMb83iII= github.com/moby/policy-helpers v0.0.0-20260211190020-824747bfdd3c h1:hRUo0Ir9PEaa0PQCgg8WvGku0sgmTo/NgnCzMb83iII=
+1 -1
View File
@@ -93,7 +93,7 @@ func (pl *PluginServer) Addr() net.Addr {
// Close ensures that the server is no longer accepting new connections and // Close ensures that the server is no longer accepting new connections and
// closes all existing connections. Existing connections will receive [io.EOF]. // closes all existing connections. Existing connections will receive [io.EOF].
// //
// The error value is that of the underlying [net.Listner.Close] call. // The error value is that of the underlying [net.Listener.Close] call.
func (pl *PluginServer) Close() error { func (pl *PluginServer) Close() error {
if pl == nil { if pl == nil {
return nil return nil
+2 -3
View File
@@ -6,6 +6,7 @@ package command
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"maps"
"github.com/docker/cli/cli/context/store" "github.com/docker/cli/cli/context/store"
) )
@@ -23,9 +24,7 @@ func (dc DockerContext) MarshalJSON() ([]byte, error) {
s["Description"] = dc.Description s["Description"] = dc.Description
} }
if dc.AdditionalFields != nil { if dc.AdditionalFields != nil {
for k, v := range dc.AdditionalFields { maps.Copy(s, dc.AdditionalFields)
s[k] = v
}
} }
return json.Marshal(s) return json.Marshal(s)
} }
+1 -1
View File
@@ -350,7 +350,7 @@ func DisplayablePorts(ports []container.PortSummary) string {
last uint16 last uint16
} }
groupMap := make(map[string]*portGroup) groupMap := make(map[string]*portGroup)
var result []string //nolint:prealloc var result []string
var hostMappings []string var hostMappings []string
var groupMapKeys []string var groupMapKeys []string
sort.Slice(ports, func(i, j int) bool { sort.Slice(ports, func(i, j int) bool {
+2 -2
View File
@@ -144,8 +144,8 @@ func PromptUserForCredentials(ctx context.Context, cli Cli, argUser, argPassword
} }
} }
argPassword = strings.TrimSpace(argPassword) isEmpty := strings.TrimSpace(argPassword) == ""
if argPassword == "" { if isEmpty {
restoreInput, err := prompt.DisableInputEcho(cli.In()) restoreInput, err := prompt.DisableInputEcho(cli.In())
if err != nil { if err != nil {
return registrytypes.AuthConfig{}, err return registrytypes.AuthConfig{}, err
+3 -3
View File
@@ -155,11 +155,11 @@ func (e statusError) Error() string {
// Note: The root command's name is excluded. If cmd is the root cmd, return "" // Note: The root command's name is excluded. If cmd is the root cmd, return ""
func getCommandName(cmd *cobra.Command) string { func getCommandName(cmd *cobra.Command) string {
fullCmdName := getFullCommandName(cmd) fullCmdName := getFullCommandName(cmd)
i := strings.Index(fullCmdName, " ") _, after, ok := strings.Cut(fullCmdName, " ")
if i == -1 { if !ok {
return "" return ""
} }
return fullCmdName[i+1:] return after
} }
// getFullCommandName gets the full cobra command name in the format // getFullCommandName gets the full cobra command name in the format
+5 -3
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package configfile package configfile
import ( import (
@@ -6,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"maps"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -374,9 +378,7 @@ func getConfiguredCredentialStore(c *ConfigFile, registryHostname string) string
func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) { func (configFile *ConfigFile) GetAllCredentials() (map[string]types.AuthConfig, error) {
auths := make(map[string]types.AuthConfig) auths := make(map[string]types.AuthConfig)
addAll := func(from map[string]types.AuthConfig) { addAll := func(from map[string]types.AuthConfig) {
for reg, ac := range from { maps.Copy(auths, from)
auths[reg] = ac
}
} }
defaultStore := configFile.GetCredentialsStore("") defaultStore := configFile.GetCredentialsStore("")
+6 -4
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
// Package connhelper provides helpers for connecting to a remote daemon host with custom logic. // Package connhelper provides helpers for connecting to a remote daemon host with custom logic.
package connhelper package connhelper
@@ -6,6 +9,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"slices"
"strings" "strings"
"github.com/docker/cli/cli/connhelper/commandconn" "github.com/docker/cli/cli/connhelper/commandconn"
@@ -89,10 +93,8 @@ func addSSHTimeout(sshFlags []string) []string {
// disablePseudoTerminalAllocation disables pseudo-terminal allocation to // disablePseudoTerminalAllocation disables pseudo-terminal allocation to
// prevent SSH from executing as a login shell // prevent SSH from executing as a login shell
func disablePseudoTerminalAllocation(sshFlags []string) []string { func disablePseudoTerminalAllocation(sshFlags []string) []string {
for _, flag := range sshFlags { if slices.Contains(sshFlags, "-T") {
if flag == "-T" { return sshFlags
return sshFlags
}
} }
return append(sshFlags, "-T") return append(sshFlags, "-T")
} }
+1 -1
View File
@@ -175,7 +175,7 @@ func quoteCommand(commandAndArgs ...string) (string, error) {
quotedCmd = a quotedCmd = a
continue continue
} }
quotedCmd += " " + a //nolint:perfsprint // ignore "concat-loop"; no need to use a string-builder for this. quotedCmd += " " + a
} }
// each part is quoted appropriately, so now we'll have a full // each part is quoted appropriately, so now we'll have a full
// shell command to pass off to "ssh" // shell command to pass off to "ssh"
+1 -1
View File
@@ -105,7 +105,7 @@ func (*GpuOpts) Type() string {
// String returns a string repr of this option // String returns a string repr of this option
func (o *GpuOpts) String() string { func (o *GpuOpts) String() string {
gpus := []string{} gpus := make([]string, 0, len(o.values))
for _, gpu := range o.values { for _, gpu := range o.values {
gpus = append(gpus, fmt.Sprintf("%v", gpu)) gpus = append(gpus, fmt.Sprintf("%v", gpu))
} }
+7 -2
View File
@@ -57,7 +57,7 @@ func (m *MountOpt) Set(value string) error {
if !hasValue { if !hasValue {
switch key { switch key {
case "readonly", "ro", "volume-nocopy", "bind-nonrecursive": case "readonly", "ro", "volume-nocopy", "bind-nonrecursive", "bind-create-src":
// boolean values // boolean values
default: default:
return fmt.Errorf("invalid field '%s' must be a key=value pair", field) return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
@@ -102,6 +102,11 @@ func (m *MountOpt) Set(value string) error {
default: default:
return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val) return fmt.Errorf(`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")`, key, val)
} }
case "bind-create-src":
ensureBindOptions(&mount).CreateMountpoint, err = parseBoolValue(key, val, hasValue)
if err != nil {
return err
}
case "volume-subpath": case "volume-subpath":
ensureVolumeOptions(&mount).Subpath = val ensureVolumeOptions(&mount).Subpath = val
case "volume-nocopy": case "volume-nocopy":
@@ -151,7 +156,7 @@ func (*MountOpt) Type() string {
// String returns a string repr of this option // String returns a string repr of this option
func (m *MountOpt) String() string { func (m *MountOpt) String() string {
mounts := []string{} mounts := make([]string, 0, len(m.values))
for _, mount := range m.values { for _, mount := range m.values {
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target) repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
mounts = append(mounts, repr) mounts = append(mounts, repr)
+5 -6
View File
@@ -1,3 +1,6 @@
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
//go:build go1.24
package opts package opts
import ( import (
@@ -7,6 +10,7 @@ import (
"math/big" "math/big"
"net" "net"
"path" "path"
"slices"
"strings" "strings"
"github.com/docker/cli/internal/lazyregexp" "github.com/docker/cli/internal/lazyregexp"
@@ -104,12 +108,7 @@ func (opts *ListOpts) GetAllOrEmpty() []string {
// Get checks the existence of the specified key. // Get checks the existence of the specified key.
func (opts *ListOpts) Get(key string) bool { func (opts *ListOpts) Get(key string) bool {
for _, k := range *opts.values { return slices.Contains(*opts.values, key)
if k == key {
return true
}
}
return false
} }
// Len returns the amount of element in the slice. // Len returns the amount of element in the slice.
+5
View File
@@ -73,6 +73,11 @@ type ImageProperties struct {
// Required: true // Required: true
Platform ocispec.Platform `json:"Platform"` Platform ocispec.Platform `json:"Platform"`
// Identity holds information about the identity and origin of the image.
// For image list responses, this can duplicate Build/Pull fields across
// image manifests, because those parts of identity are image-level metadata.
Identity *Identity `json:"Identity,omitempty"`
Size struct { Size struct {
// Unpacked is the size (in bytes) of the locally unpacked // Unpacked is the size (in bytes) of the locally unpacked
// (uncompressed) image content that's directly usable by the containers // (uncompressed) image content that's directly usable by the containers
+3
View File
@@ -8,5 +8,8 @@ type Error struct {
} }
func (e *Error) Error() string { func (e *Error) Error() string {
if e == nil {
return "<nil>"
}
return e.Message return e.Message
} }
+22
View File
@@ -1,6 +1,7 @@
package swarm package swarm
import ( import (
"cmp"
"net/netip" "net/netip"
"github.com/moby/moby/api/types/network" "github.com/moby/moby/api/types/network"
@@ -41,6 +42,27 @@ type PortConfig struct {
PublishMode PortConfigPublishMode `json:",omitempty"` PublishMode PortConfigPublishMode `json:",omitempty"`
} }
// Compare returns the lexical ordering of p and other, and can be used
// with [slices.SortFunc].
//
// The comparison is performed in the following priority order:
// 1. PublishedPort (host port)
// 2. TargetPort (container port)
// 3. Protocol
// 4. PublishMode
func (p PortConfig) Compare(other PortConfig) int {
if n := cmp.Compare(p.PublishedPort, other.PublishedPort); n != 0 {
return n
}
if n := cmp.Compare(p.TargetPort, other.TargetPort); n != 0 {
return n
}
if n := cmp.Compare(p.Protocol, other.Protocol); n != 0 {
return n
}
return cmp.Compare(p.PublishMode, other.PublishMode)
}
// PortConfigPublishMode represents the mode in which the port is to // PortConfigPublishMode represents the mode in which the port is to
// be published. // be published.
type PortConfigPublishMode string type PortConfigPublishMode string
+24 -12
View File
@@ -1,21 +1,33 @@
package types package types
// MediaType represents an HTTP media type (MIME type) used in API
// Content-Type and Accept headers.
//
// In addition to standard media types (for example, "application/json"),
// this package defines vendor-specific vendor media types for streaming
// endpoints, such as raw TTY streams and multiplexed stdout/stderr streams.
type MediaType = string
const ( const (
// MediaTypeRawStream is vendor specific MIME-Type set for raw TTY streams. // MediaTypeRawStream is a vendor-specific media type for raw TTY streams.
MediaTypeRawStream = "application/vnd.docker.raw-stream" MediaTypeRawStream MediaType = "application/vnd.docker.raw-stream"
// MediaTypeMultiplexedStream is vendor specific MIME-Type set for stdin/stdout/stderr multiplexed streams. // MediaTypeMultiplexedStream is a vendor-specific media type for streams
MediaTypeMultiplexedStream = "application/vnd.docker.multiplexed-stream" // where stdin, stdout, and stderr are multiplexed into a single byte stream.
//
// Use stdcopy.StdCopy (https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy)
// to demultiplex the stream.
MediaTypeMultiplexedStream MediaType = "application/vnd.docker.multiplexed-stream"
// MediaTypeJSON is the MIME-Type for JSON objects. // MediaTypeJSON is the media type for JSON objects.
MediaTypeJSON = "application/json" MediaTypeJSON MediaType = "application/json"
// MediaTypeNDJSON is the MIME-Type for Newline Delimited JSON objects streams (https://github.com/ndjson/ndjson-spec). // MediaTypeNDJSON is the media type for newline-delimited JSON streams (https://github.com/ndjson/ndjson-spec).
MediaTypeNDJSON = "application/x-ndjson" MediaTypeNDJSON MediaType = "application/x-ndjson"
// MediaTypeJSONLines is the MIME-Type for JSONLines objects streams (https://jsonlines.org/). // MediaTypeJSONLines is the media type for JSON Lines streams (https://jsonlines.org/).
MediaTypeJSONLines = "application/jsonl" MediaTypeJSONLines MediaType = "application/jsonl"
// MediaTypeJSONSequence is the MIME-Type for JSON Text Sequences (RFC7464). // MediaTypeJSONSequence is the media type for JSON text sequences (RFC 7464).
MediaTypeJSONSequence = "application/json-seq" MediaTypeJSONSequence MediaType = "application/json-seq"
) )
+3 -6
View File
@@ -107,11 +107,11 @@ const DummyHost = "api.moby.localhost"
// overriding the version and disable API-version negotiation. // overriding the version and disable API-version negotiation.
// //
// This version may be lower than the version of the api library module used. // This version may be lower than the version of the api library module used.
const MaxAPIVersion = "1.53" const MaxAPIVersion = "1.54"
// MinAPIVersion is the minimum API version supported by the client. API versions // MinAPIVersion is the minimum API version supported by the client. API versions
// below this version are not considered when performing API-version negotiation. // below this version are not considered when performing API-version negotiation.
const MinAPIVersion = "1.44" const MinAPIVersion = "1.40"
// Ensure that Client always implements APIClient. // Ensure that Client always implements APIClient.
var _ APIClient = &Client{} var _ APIClient = &Client{}
@@ -179,10 +179,7 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
// [WithAPIVersionFromEnv] to configure the client with a fixed API version // [WithAPIVersionFromEnv] to configure the client with a fixed API version
// and disable API version negotiation. // and disable API version negotiation.
// //
// cli, err := client.New( // cli, err := client.New(client.FromEnv)
// client.FromEnv,
// client.WithAPIVersionNegotiation(),
// )
func New(ops ...Opt) (*Client, error) { func New(ops ...Opt) (*Client, error) {
hostURL, err := ParseHostURL(DefaultDockerHost) hostURL, err := ParseHostURL(DefaultDockerHost)
if err != nil { if err != nil {
+17 -1
View File
@@ -97,7 +97,23 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option
if options.Follow { if options.Follow {
query.Set("follow", "1") query.Set("follow", "1")
} }
query.Set("tail", options.Tail)
switch options.Tail {
case "", "all":
// don't send option; default is to show all logs.
//
// The default on the daemon-side is to show all logs; account for
// some special values. The CLI may set a magic "all" value that's
// used as default.
//
// Given that the default is to show all logs, we can ignore these
// values, and don't send "tail".
//
// see https://github.com/moby/moby/blob/0df791cb72b568eeadba2267fe9a5040d12b0487/daemon/logs.go#L75-L78
// see https://github.com/moby/moby/blob/4d20b6fe56dfb2b06f4a5dd1f32913215a9c317b/daemon/cluster/services.go#L425-L449
default:
query.Set("tail", options.Tail)
}
resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil) resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil)
if err != nil { if err != nil {
+8
View File
@@ -41,6 +41,14 @@ func (cli *Client) ImageList(ctx context.Context, options ImageListOptions) (Ima
query.Set("manifests", "1") query.Set("manifests", "1")
} }
} }
if options.Identity {
if err := cli.requiresVersion(ctx, "1.54", "identity"); err != nil {
return ImageListResult{}, err
}
// Identity data in image list is scoped to manifests.
query.Set("manifests", "1")
query.Set("identity", "1")
}
resp, err := cli.get(ctx, "/images/json", query, nil) resp, err := cli.get(ctx, "/images/json", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
+3
View File
@@ -16,6 +16,9 @@ type ImageListOptions struct {
// Manifests indicates whether the image manifests should be returned. // Manifests indicates whether the image manifests should be returned.
Manifests bool Manifests bool
// Identity indicates whether image identity information should be returned.
Identity bool
} }
// ImageListResult holds the result from ImageList. // ImageListResult holds the result from ImageList.
+9 -7
View File
@@ -11,23 +11,23 @@ import (
"github.com/moby/moby/api/types/jsonstream" "github.com/moby/moby/api/types/jsonstream"
) )
func NewJSONMessageStream(rc io.ReadCloser) stream { func NewJSONMessageStream(rc io.ReadCloser) Stream {
if rc == nil { if rc == nil {
panic("nil io.ReadCloser") panic("nil io.ReadCloser")
} }
return stream{ return Stream{
rc: rc, rc: rc,
close: sync.OnceValue(rc.Close), close: sync.OnceValue(rc.Close),
} }
} }
type stream struct { type Stream struct {
rc io.ReadCloser rc io.ReadCloser
close func() error close func() error
} }
// Read implements io.ReadCloser // Read implements io.ReadCloser
func (r stream) Read(p []byte) (n int, err error) { func (r Stream) Read(p []byte) (n int, err error) {
if r.rc == nil { if r.rc == nil {
return 0, io.EOF return 0, io.EOF
} }
@@ -35,16 +35,18 @@ func (r stream) Read(p []byte) (n int, err error) {
} }
// Close implements io.ReadCloser // Close implements io.ReadCloser
func (r stream) Close() error { func (r Stream) Close() error {
if r.close == nil { if r.close == nil {
return nil return nil
} }
return r.close() return r.close()
} }
var _ io.ReadCloser = Stream{}
// JSONMessages decodes the response stream as a sequence of JSONMessages. // JSONMessages decodes the response stream as a sequence of JSONMessages.
// if stream ends or context is cancelled, the underlying [io.Reader] is closed. // if stream ends or context is cancelled, the underlying [io.Reader] is closed.
func (r stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] { func (r Stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] {
stop := context.AfterFunc(ctx, func() { stop := context.AfterFunc(ctx, func() {
_ = r.Close() _ = r.Close()
}) })
@@ -72,7 +74,7 @@ func (r stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message,
} }
// Wait waits for operation to complete and detects errors reported as JSONMessage // Wait waits for operation to complete and detects errors reported as JSONMessage
func (r stream) Wait(ctx context.Context) error { func (r Stream) Wait(ctx context.Context) error {
for _, err := range r.JSONMessages(ctx) { for _, err := range r.JSONMessages(ctx) {
if err != nil { if err != nil {
return err return err
+4 -4
View File
@@ -17,7 +17,7 @@ import (
var timeNow = time.Now // For overriding in tests. var timeNow = time.Now // For overriding in tests.
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to // RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time isalways the same number of characters. // ensure the formatted time is always the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00" const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
func RenderTUIProgress(p jsonstream.Progress, width uint16) string { func RenderTUIProgress(p jsonstream.Progress, width uint16) string {
@@ -144,14 +144,14 @@ func Display(jm jsonstream.Message, out io.Writer, isTerminal bool, width uint16
return nil return nil
} }
type JSONMessagesStream iter.Seq2[jsonstream.Message, error] type JSONMessagesStream = iter.Seq2[jsonstream.Message, error]
// DisplayJSONMessagesStream reads a JSON message stream from in, and writes // DisplayJSONMessagesStream reads a JSON message stream from in, and writes
// each [JSONMessage] to out. // each [JSONMessage] to out.
// see DisplayJSONMessages for details // see DisplayJSONMessages for details
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error { func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error {
dec := json.NewDecoder(in) dec := json.NewDecoder(in)
var f JSONMessagesStream = func(yield func(jsonstream.Message, error) bool) { f := func(yield func(jsonstream.Message, error) bool) {
for { for {
var jm jsonstream.Message var jm jsonstream.Message
err := dec.Decode(&jm) err := dec.Decode(&jm)
@@ -183,7 +183,7 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
// - auxCallback allows handling the [JSONMessage.Aux] field. It is // - auxCallback allows handling the [JSONMessage.Aux] field. It is
// called if a JSONMessage contains an Aux field, in which case // called if a JSONMessage contains an Aux field, in which case
// DisplayJSONMessagesStream does not present the JSONMessage. // DisplayJSONMessagesStream does not present the JSONMessage.
func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error { func DisplayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error {
ids := make(map[string]uint) ids := make(map[string]uint)
var width uint16 = 200 var width uint16 = 200
if isTerminal { if isTerminal {
+12 -9
View File
@@ -166,12 +166,12 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
} }
if errors.Is(err, os.ErrPermission) { if errors.Is(err, os.ErrPermission) {
// Don't include request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"), // Don't include request errors (Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
// which are irrelevant if we weren't able to connect. // which are irrelevant if we weren't able to connect.
return nil, errConnectionFailed{fmt.Errorf("permission denied while trying to connect to the docker API at %v", cli.host)} return nil, errConnectionFailed{fmt.Errorf("permission denied while trying to connect to the docker API at %v", cli.host)}
} }
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
// Unwrap the error to remove request errors ("Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"), // Unwrap the error to remove request errors (Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/version"),
// which are irrelevant if we weren't able to connect. // which are irrelevant if we weren't able to connect.
err = errors.Unwrap(err) err = errors.Unwrap(err)
return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v; check if the path is correct and if the daemon is running: %w", cli.host, err)} return nil, errConnectionFailed{fmt.Errorf("failed to connect to the docker API at %v; check if the path is correct and if the daemon is running: %w", cli.host, err)}
@@ -195,13 +195,16 @@ func (cli *Client) doRequest(req *http.Request) (*http.Response, error) {
// Although there's not a strongly typed error for this in go-winio, // Although there's not a strongly typed error for this in go-winio,
// lots of people are using the default configuration for the docker // lots of people are using the default configuration for the docker
// daemon on Windows where the daemon is listening on a named pipe // daemon on Windows where the daemon is listening on a named pipe
// `//./pipe/docker_engine, and the client must be running elevated. // ("//./pipe/docker_engine"), and the client must be running elevated.
// Give users a clue rather than the not-overly useful message //
// such as `error during connect: Get http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.26/info: // Give users a clue rather than the not-overly useful message such as;
// open //./pipe/docker_engine: The system cannot find the file specified.`. //
// open //./pipe/docker_engine: The system cannot find the file specified.
//
// Note we can't string compare "The system cannot find the file specified" as // Note we can't string compare "The system cannot find the file specified" as
// this is localised - for example in French the error would be // this is localized; for example. in French the error would be;
// `open //./pipe/docker_engine: Le fichier spécifié est introuvable.` //
// open //./pipe/docker_engine: Le fichier spécifié est introuvable.
if strings.Contains(err.Error(), `open //./pipe/docker_engine`) { if strings.Contains(err.Error(), `open //./pipe/docker_engine`) {
// Checks if client is running with elevated privileges // Checks if client is running with elevated privileges
if f, elevatedErr := os.Open(`\\.\PHYSICALDRIVE0`); elevatedErr != nil { if f, elevatedErr := os.Open(`\\.\PHYSICALDRIVE0`); elevatedErr != nil {
@@ -297,7 +300,7 @@ func checkResponseErr(serverResp *http.Response) (retErr error) {
} else { } else {
// Fall back to returning the response as-is for situations where a // Fall back to returning the response as-is for situations where a
// plain text error is returned. This branch may also catch // plain text error is returned. This branch may also catch
// situations where a proxy is involved, returning a HTML response. // situations where a proxy is involved, returning an HTML response.
daemonErr = errors.New(strings.TrimSpace(string(body))) daemonErr = errors.New(strings.TrimSpace(string(body)))
} }
return fmt.Errorf("Error response from daemon: %w", daemonErr) return fmt.Errorf("Error response from daemon: %w", daemonErr)
+16 -1
View File
@@ -70,7 +70,22 @@ func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options Se
if options.Follow { if options.Follow {
query.Set("follow", "1") query.Set("follow", "1")
} }
query.Set("tail", options.Tail) switch options.Tail {
case "", "all":
// don't send option; default is to show all logs.
//
// The default on the daemon-side is to show all logs; account for
// some special values. The CLI may set a magic "all" value that's
// used as default.
//
// Given that the default is to show all logs, we can ignore these
// values, and don't send "tail".
//
// see https://github.com/moby/moby/blob/0df791cb72b568eeadba2267fe9a5040d12b0487/daemon/logs.go#L75-L78
// see https://github.com/moby/moby/blob/4d20b6fe56dfb2b06f4a5dd1f32913215a9c317b/daemon/cluster/services.go#L425-L449
default:
query.Set("tail", options.Tail)
}
resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil) resp, err := cli.get(ctx, "/services/"+serviceID+"/logs", query, nil)
if err != nil { if err != nil {
+3 -3
View File
@@ -45,9 +45,9 @@ func (cli *Client) Events(ctx context.Context, options EventsListOptions) Events
} }
headers := http.Header{} headers := http.Header{}
headers.Add("Accept", types.MediaTypeJSONSequence) headers.Add("Accept", types.MediaTypeJSONLines) // Implicit q=1.0; in case server doesn't parse correctly.
headers.Add("Accept", types.MediaTypeJSONLines) headers.Add("Accept", types.MediaTypeNDJSON+";q=0.9")
headers.Add("Accept", types.MediaTypeNDJSON) headers.Add("Accept", types.MediaTypeJSONSequence+";q=0.5")
resp, err := cli.get(ctx, "/events", query, headers) resp, err := cli.get(ctx, "/events", query, headers)
if err != nil { if err != nil {
close(started) close(started)
+3 -3
View File
@@ -291,7 +291,7 @@ github.com/digitorus/timestamp
# github.com/distribution/reference v0.6.0 # github.com/distribution/reference v0.6.0
## explicit; go 1.20 ## explicit; go 1.20
github.com/distribution/reference github.com/distribution/reference
# github.com/docker/cli v29.2.1+incompatible # github.com/docker/cli v29.3.0+incompatible
## explicit ## explicit
github.com/docker/cli/cli github.com/docker/cli/cli
github.com/docker/cli/cli-plugins/metadata github.com/docker/cli/cli-plugins/metadata
@@ -760,7 +760,7 @@ github.com/moby/go-archive/tarheader
# github.com/moby/locker v1.0.1 # github.com/moby/locker v1.0.1
## explicit; go 1.13 ## explicit; go 1.13
github.com/moby/locker github.com/moby/locker
# github.com/moby/moby/api v1.53.0 # github.com/moby/moby/api v1.54.0
## explicit; go 1.24.0 ## explicit; go 1.24.0
github.com/moby/moby/api/pkg/authconfig github.com/moby/moby/api/pkg/authconfig
github.com/moby/moby/api/pkg/stdcopy github.com/moby/moby/api/pkg/stdcopy
@@ -781,7 +781,7 @@ github.com/moby/moby/api/types/storage
github.com/moby/moby/api/types/swarm github.com/moby/moby/api/types/swarm
github.com/moby/moby/api/types/system github.com/moby/moby/api/types/system
github.com/moby/moby/api/types/volume github.com/moby/moby/api/types/volume
# github.com/moby/moby/client v0.2.2 # github.com/moby/moby/client v0.3.0
## explicit; go 1.24.0 ## explicit; go 1.24.0
github.com/moby/moby/client github.com/moby/moby/client
github.com/moby/moby/client/internal github.com/moby/moby/client/internal