From 62329f6740eb05c666386fdaf090724a3027894c Mon Sep 17 00:00:00 2001 From: "red-hat-konflux[bot]" <126015336+red-hat-konflux[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 10:48:51 +0000 Subject: [PATCH] fix(deps): update module github.com/moby/moby/client to v0.4.1 Signed-off-by: red-hat-konflux <126015336+red-hat-konflux[bot]@users.noreply.github.com> --- go.mod | 4 +- go.sum | 8 +- .../go-connections/sockets/inmem_socket.go | 54 ++--- .../docker/go-connections/sockets/proxy.go | 31 --- .../docker/go-connections/sockets/sockets.go | 24 +- .../go-connections/sockets/sockets_unix.go | 13 +- .../go-connections/sockets/sockets_windows.go | 7 +- .../sockets/unix_socket_windows.go | 124 +++++++++- .../go-connections/tlsconfig/certpool.go | 18 +- .../docker/go-connections/tlsconfig/config.go | 28 ++- vendor/github.com/moby/moby/client/README.md | 17 +- .../moby/moby/client/build_cancel.go | 2 + vendor/github.com/moby/moby/client/client.go | 24 +- .../moby/moby/client/client_options.go | 102 ++++++-- .../moby/moby/client/config_remove.go | 2 + .../moby/moby/client/config_update.go | 1 + .../moby/moby/client/container_copy.go | 5 + vendor/github.com/moby/moby/client/filters.go | 5 +- .../moby/moby/client/image_build.go | 2 +- .../moby/moby/client/image_import.go | 2 +- .../github.com/moby/moby/client/image_tag.go | 2 + .../moby/moby/client/internal/json-stream.go | 44 ++-- .../moby/moby/client/internal/jsonmessages.go | 77 +++++- .../moby/moby/client/internal/mod/mod.go | 226 ++++++++++++++++++ .../moby/moby/client/node_inspect.go | 1 + .../github.com/moby/moby/client/node_list.go | 1 + .../moby/moby/client/node_remove.go | 2 + .../moby/moby/client/node_update.go | 1 + .../moby/moby/client/pkg/versions/compare.go | 7 +- vendor/github.com/moby/moby/client/request.go | 19 +- .../moby/moby/client/secret_remove.go | 2 + .../moby/moby/client/secret_update.go | 1 + .../moby/moby/client/service_logs.go | 2 +- .../moby/moby/client/system_disk_usage.go | 20 +- .../moby/moby/client/system_info.go | 2 + .../github.com/moby/moby/client/task_logs.go | 2 +- vendor/github.com/moby/moby/client/utils.go | 2 +- vendor/modules.txt | 9 +- 38 files changed, 682 insertions(+), 211 deletions(-) delete mode 100644 vendor/github.com/docker/go-connections/sockets/proxy.go create mode 100644 vendor/github.com/moby/moby/client/internal/mod/mod.go diff --git a/go.mod b/go.mod index 6541c95a7..f0bf9b762 100644 --- a/go.mod +++ b/go.mod @@ -6,12 +6,12 @@ require ( github.com/containerd/errdefs v1.0.0 github.com/distribution/reference v0.6.0 github.com/docker/docker v28.5.2+incompatible - github.com/docker/go-connections v0.6.0 + github.com/docker/go-connections v0.7.0 github.com/go-imports-organizer/goio v1.5.0 github.com/moby/buildkit v0.28.1 github.com/moby/docker-image-spec v1.3.1 github.com/moby/moby/api v1.54.2 - github.com/moby/moby/client v0.3.0 + github.com/moby/moby/client v0.4.1 github.com/opencontainers/image-spec v1.1.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/go.sum b/go.sum index 9e55c8b59..a4936e251 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaft github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c= +github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -90,8 +90,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= -github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs= -github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ= +github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY= +github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ= github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= diff --git a/vendor/github.com/docker/go-connections/sockets/inmem_socket.go b/vendor/github.com/docker/go-connections/sockets/inmem_socket.go index 99846ffdd..06fcf747a 100644 --- a/vendor/github.com/docker/go-connections/sockets/inmem_socket.go +++ b/vendor/github.com/docker/go-connections/sockets/inmem_socket.go @@ -1,48 +1,57 @@ package sockets import ( - "errors" "net" "sync" ) -var errClosed = errors.New("use of closed network connection") +// dummyAddr is used to satisfy net.Addr for the in-mem socket +// it is just stored as a string and returns the string for all calls +type dummyAddr string + +// Network returns the addr string, satisfies net.Addr +func (a dummyAddr) Network() string { + return string(a) +} -// InmemSocket implements net.Listener using in-memory only connections. +// String returns the string form +func (a dummyAddr) String() string { + return string(a) +} + +// InmemSocket implements [net.Listener] using in-memory only connections. type InmemSocket struct { chConn chan net.Conn chClose chan struct{} - addr string + addr dummyAddr mu sync.Mutex } -// dummyAddr is used to satisfy net.Addr for the in-mem socket -// it is just stored as a string and returns the string for all calls -type dummyAddr string - -// NewInmemSocket creates an in-memory only net.Listener -// The addr argument can be any string, but is used to satisfy the `Addr()` part -// of the net.Listener interface +// NewInmemSocket creates an in-memory only [net.Listener]. The addr argument +// can be any string, but is used to satisfy the [net.Listener.Addr] part +// of the [net.Listener] interface func NewInmemSocket(addr string, bufSize int) *InmemSocket { return &InmemSocket{ chConn: make(chan net.Conn, bufSize), chClose: make(chan struct{}), - addr: addr, + addr: dummyAddr(addr), } } // Addr returns the socket's addr string to satisfy net.Listener func (s *InmemSocket) Addr() net.Addr { - return dummyAddr(s.addr) + return s.addr } -// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn. +// Accept implements the Accept method in the Listener interface; it waits +// for the next call and returns a generic Conn. It returns a [net.ErrClosed] +// if the connection is already closed. func (s *InmemSocket) Accept() (net.Conn, error) { select { case conn := <-s.chConn: return conn, nil case <-s.chClose: - return nil, errClosed + return nil, net.ErrClosed } } @@ -58,24 +67,15 @@ func (s *InmemSocket) Close() error { return nil } -// Dial is used to establish a connection with the in-mem server +// Dial is used to establish a connection with the in-mem server. +// It returns a [net.ErrClosed] if the connection is already closed. func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) { srvConn, clientConn := net.Pipe() select { case s.chConn <- srvConn: case <-s.chClose: - return nil, errClosed + return nil, net.ErrClosed } return clientConn, nil } - -// Network returns the addr string, satisfies net.Addr -func (a dummyAddr) Network() string { - return string(a) -} - -// String returns the string form -func (a dummyAddr) String() string { - return string(a) -} diff --git a/vendor/github.com/docker/go-connections/sockets/proxy.go b/vendor/github.com/docker/go-connections/sockets/proxy.go deleted file mode 100644 index f04980e40..000000000 --- a/vendor/github.com/docker/go-connections/sockets/proxy.go +++ /dev/null @@ -1,31 +0,0 @@ -package sockets - -import ( - "net" - "os" - "strings" -) - -// GetProxyEnv allows access to the uppercase and the lowercase forms of -// proxy-related variables. See the Go specification for details on these -// variables. https://golang.org/pkg/net/http/ -// -// Deprecated: this function was used as helper for [DialerFromEnvironment] and is no longer used. It will be removed in the next release. -func GetProxyEnv(key string) string { - proxyValue := os.Getenv(strings.ToUpper(key)) - if proxyValue == "" { - return os.Getenv(strings.ToLower(key)) - } - return proxyValue -} - -// DialerFromEnvironment was previously used to configure a net.Dialer to route -// connections through a SOCKS proxy. -// -// Deprecated: SOCKS proxies are now supported by configuring only -// http.Transport.Proxy, and no longer require changing http.Transport.Dial. -// Therefore, only [sockets.ConfigureTransport] needs to be called, and any -// [sockets.DialerFromEnvironment] calls can be dropped. -func DialerFromEnvironment(direct *net.Dialer) (*net.Dialer, error) { - return direct, nil -} diff --git a/vendor/github.com/docker/go-connections/sockets/sockets.go b/vendor/github.com/docker/go-connections/sockets/sockets.go index 611729786..0d7789bbd 100644 --- a/vendor/github.com/docker/go-connections/sockets/sockets.go +++ b/vendor/github.com/docker/go-connections/sockets/sockets.go @@ -27,11 +27,19 @@ var ErrProtocolNotAvailable = errors.New("protocol not available") // make sure you do it _after_ any subsequent calls to ConfigureTransport is made against the same // [http.Transport]. func ConfigureTransport(tr *http.Transport, proto, addr string) error { + if tr.MaxIdleConns == 0 { + // prevent long-lived processes from leaking connections + // due to idle connections not being released. + // + // TODO: see if we can also address this from the server side; see: https://github.com/moby/moby/issues/45539 + tr.MaxIdleConns = 6 + tr.IdleConnTimeout = 30 * time.Second + } switch proto { case "unix": - return configureUnixTransport(tr, proto, addr) + return configureUnixTransport(tr, addr) case "npipe": - return configureNpipeTransport(tr, proto, addr) + return configureNpipeTransport(tr, addr) default: tr.Proxy = http.ProxyFromEnvironment tr.DisableCompression = false @@ -42,15 +50,7 @@ func ConfigureTransport(tr *http.Transport, proto, addr string) error { return nil } -// DialPipe connects to a Windows named pipe. It is not supported on -// non-Windows platforms. -// -// Deprecated: use [github.com/Microsoft/go-winio.DialPipe] or [github.com/Microsoft/go-winio.DialPipeContext]. -func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { - return dialPipe(addr, timeout) -} - -func configureUnixTransport(tr *http.Transport, proto, addr string) error { +func configureUnixTransport(tr *http.Transport, addr string) error { if len(addr) > maxUnixSocketPathSize { return fmt.Errorf("unix socket path %q is too long", addr) } @@ -60,7 +60,7 @@ func configureUnixTransport(tr *http.Transport, proto, addr string) error { Timeout: defaultTimeout, } tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { - return dialer.DialContext(ctx, proto, addr) + return dialer.DialContext(ctx, "unix", addr) } return nil } diff --git a/vendor/github.com/docker/go-connections/sockets/sockets_unix.go b/vendor/github.com/docker/go-connections/sockets/sockets_unix.go index 913d2f00d..b37c39eab 100644 --- a/vendor/github.com/docker/go-connections/sockets/sockets_unix.go +++ b/vendor/github.com/docker/go-connections/sockets/sockets_unix.go @@ -2,17 +2,6 @@ package sockets -import ( - "net" - "net/http" - "syscall" - "time" -) - -func configureNpipeTransport(tr *http.Transport, proto, addr string) error { +func configureNpipeTransport(any, string) error { return ErrProtocolNotAvailable } - -func dialPipe(_ string, _ time.Duration) (net.Conn, error) { - return nil, syscall.EAFNOSUPPORT -} diff --git a/vendor/github.com/docker/go-connections/sockets/sockets_windows.go b/vendor/github.com/docker/go-connections/sockets/sockets_windows.go index 6d6beb385..0863fc36a 100644 --- a/vendor/github.com/docker/go-connections/sockets/sockets_windows.go +++ b/vendor/github.com/docker/go-connections/sockets/sockets_windows.go @@ -4,12 +4,11 @@ import ( "context" "net" "net/http" - "time" "github.com/Microsoft/go-winio" ) -func configureNpipeTransport(tr *http.Transport, proto, addr string) error { +func configureNpipeTransport(tr *http.Transport, addr string) error { // No need for compression in local communications. tr.DisableCompression = true tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { @@ -17,7 +16,3 @@ func configureNpipeTransport(tr *http.Transport, proto, addr string) error { } return nil } - -func dialPipe(addr string, timeout time.Duration) (net.Conn, error) { - return winio.DialPipe(addr, &timeout) -} diff --git a/vendor/github.com/docker/go-connections/sockets/unix_socket_windows.go b/vendor/github.com/docker/go-connections/sockets/unix_socket_windows.go index 5ec29e059..01aee5f11 100644 --- a/vendor/github.com/docker/go-connections/sockets/unix_socket_windows.go +++ b/vendor/github.com/docker/go-connections/sockets/unix_socket_windows.go @@ -1,6 +1,128 @@ package sockets -import "net" +import ( + "errors" + "fmt" + "net" + "strings" + + "github.com/Microsoft/go-winio" + "golang.org/x/sys/windows" +) + +// BasePermissions defines the default DACL, which allows Administrators +// and LocalSystem full access (similar to defaults used in [moby]); +// +// - D:P: DACL without inheritance (protected, (P)). +// - (A;;GA;;;BA): Allow full access (GA) for built-in Administrators (BA). +// - (A;;GA;;;SY); Allow full access (GA) for LocalSystem (SY). +// - Any other user is denied access. +// +// [moby]: https://github.com/moby/moby/blob/6b45c76a233b1b8b56465f76c21c09fd7920e82d/daemon/listeners/listeners_windows.go#L53-L59 +const BasePermissions = "D:P(A;;GA;;;BA)(A;;GA;;;SY)" + +// WithBasePermissions sets a default DACL, which allows Administrators +// and LocalSystem full access (similar to defaults used in [moby]); +// +// - D:P: DACL without inheritance (protected, (P)). +// - (A;;GA;;;BA): Allow full access (GA) for built-in Administrators (BA). +// - (A;;GA;;;SY); Allow full access (GA) for LocalSystem (SY). +// - Any other user is denied access. +// +// [moby]: https://github.com/moby/moby/blob/6b45c76a233b1b8b56465f76c21c09fd7920e82d/daemon/listeners/listeners_windows.go#L53-L59 +func WithBasePermissions() SockOption { + return withSDDL(BasePermissions) +} + +// WithAdditionalUsersAndGroups modifies the socket file's DACL to grant +// access to additional users and groups. +// +// It sets [BasePermissions] on the socket path and grants the given additional +// users and groups to generic read (GR) and write (GW) access. It returns +// an error if no groups were given, when failing to resolve any of the +// additional users and groups, or when failing to apply the ACL. +func WithAdditionalUsersAndGroups(additionalUsersAndGroups []string) SockOption { + return func(path string) error { + if len(additionalUsersAndGroups) == 0 { + return errors.New("no additional users specified") + } + sd, err := getSecurityDescriptor(additionalUsersAndGroups...) + if err != nil { + return fmt.Errorf("looking up SID: %w", err) + } + return withSDDL(sd)(path) + } +} + +// withSDDL applies the given SDDL to the socket. It returns an error +// when failing parse the SDDL, or if the DACL was defaulted. +// +// TODO(thaJeztah); this is not exported yet, as some of the checks may need review if they're not too opinionated. +func withSDDL(sddl string) SockOption { + return func(path string) error { + sd, err := windows.SecurityDescriptorFromString(sddl) + if err != nil { + return fmt.Errorf("parsing SDDL: %w", err) + } + dacl, defaulted, err := sd.DACL() + if err != nil { + return fmt.Errorf("extracting DACL: %w", err) + } + if dacl == nil || defaulted { + // should never be hit with our [DefaultPermissions], + // as it contains "D:" and "P" (protected, don't inherit). + return errors.New("no DACL found in security descriptor or defaulted") + } + return windows.SetNamedSecurityInfo( + path, + windows.SE_FILE_OBJECT, + windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION, + nil, // do not change the owner + nil, // do not change the owner + dacl, + nil, + ) + } +} + +// NewUnixSocket creates a new unix socket. +// +// It sets [BasePermissions] on the socket path and grants the given additional +// users and groups to generic read (GR) and write (GW) access. It returns +// an error when failing to resolve any of the additional users and groups, +// or when failing to apply the ACL. +func NewUnixSocket(path string, additionalUsersAndGroups []string) (net.Listener, error) { + var opts []SockOption + if len(additionalUsersAndGroups) > 0 { + opts = append(opts, WithAdditionalUsersAndGroups(additionalUsersAndGroups)) + } else { + opts = append(opts, WithBasePermissions()) + } + return NewUnixSocketWithOpts(path, opts...) +} + +// getSecurityDescriptor returns the DACL for the Unix socket. +// +// By default, it grants [BasePermissions], but allows for additional +// users and groups to get generic read (GR) and write (GW) access. It +// returns an error when failing to resolve any of the additional users +// and groups. +func getSecurityDescriptor(additionalUsersAndGroups ...string) (string, error) { + sddl := BasePermissions + + // Grant generic read (GR) and write (GW) access to whatever + // additional users or groups were specified. + // + // TODO(thaJeztah): should we fail on, or remove duplicates? + for _, g := range additionalUsersAndGroups { + sid, err := winio.LookupSidByName(strings.TrimSpace(g)) + if err != nil { + return "", fmt.Errorf("looking up SID: %w", err) + } + sddl += fmt.Sprintf("(A;;GRGW;;;%s)", sid) + } + return sddl, nil +} func listenUnix(path string) (net.Listener, error) { return net.Listen("unix", path) diff --git a/vendor/github.com/docker/go-connections/tlsconfig/certpool.go b/vendor/github.com/docker/go-connections/tlsconfig/certpool.go index f84c624ba..803f1e122 100644 --- a/vendor/github.com/docker/go-connections/tlsconfig/certpool.go +++ b/vendor/github.com/docker/go-connections/tlsconfig/certpool.go @@ -1,16 +1,12 @@ package tlsconfig -import ( - "crypto/x509" - "runtime" -) +import "crypto/x509" -// SystemCertPool returns a copy of the system cert pool, -// returns an error if failed to load or empty pool on windows. +// SystemCertPool returns a copy of the system cert pool. +// +// Deprecated: use [x509.SystemCertPool] instead. +// +//go:fix inline func SystemCertPool() (*x509.CertPool, error) { - certpool, err := x509.SystemCertPool() - if err != nil && runtime.GOOS == "windows" { - return x509.NewCertPool(), nil - } - return certpool, err + return x509.SystemCertPool() } diff --git a/vendor/github.com/docker/go-connections/tlsconfig/config.go b/vendor/github.com/docker/go-connections/tlsconfig/config.go index 8b0264f68..761b36bb8 100644 --- a/vendor/github.com/docker/go-connections/tlsconfig/config.go +++ b/vendor/github.com/docker/go-connections/tlsconfig/config.go @@ -34,6 +34,9 @@ type Options struct { // the system pool will be used. ExclusiveRootPools bool MinVersion uint16 + + // systemCertPool allows mocking the system cert-pool for testing. + systemCertPool func() (*x509.CertPool, error) } // DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls @@ -47,6 +50,8 @@ var defaultCipherSuites = []uint16{ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, } // ServerDefault returns a secure-enough TLS configuration for the server TLS configuration. @@ -75,26 +80,33 @@ func defaultConfig(ops ...func(*tls.Config)) *tls.Config { } // certPool returns an X.509 certificate pool from `caFile`, the certificate file. -func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) { +func certPool(opts Options) (*x509.CertPool, error) { // If we should verify the server, we need to load a trusted ca var ( pool *x509.CertPool err error ) - if exclusivePool { + if opts.ExclusiveRootPools { pool = x509.NewCertPool() } else { - pool, err = SystemCertPool() + if opts.systemCertPool != nil { + pool, err = opts.systemCertPool() + } else { + pool, err = x509.SystemCertPool() + } if err != nil { return nil, fmt.Errorf("failed to read system certificates: %v", err) } } - pemData, err := os.ReadFile(caFile) + if opts.CAFile == "" { + return pool, nil + } + pemData, err := os.ReadFile(opts.CAFile) if err != nil { - return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err) + return nil, fmt.Errorf("could not read CA certificate %q: %v", opts.CAFile, err) } if !pool.AppendCertsFromPEM(pemData) { - return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) + return nil, fmt.Errorf("failed to append certificates from PEM file: %q", opts.CAFile) } return pool, nil } @@ -197,7 +209,7 @@ func Client(options Options) (*tls.Config, error) { tlsConfig := defaultConfig() tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify if !options.InsecureSkipVerify && options.CAFile != "" { - CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) + CAs, err := certPool(options) if err != nil { return nil, err } @@ -230,7 +242,7 @@ func Server(options Options) (*tls.Config, error) { } tlsConfig.Certificates = []tls.Certificate{tlsCert} if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" { - CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) + CAs, err := certPool(options) if err != nil { return nil, err } diff --git a/vendor/github.com/moby/moby/client/README.md b/vendor/github.com/moby/moby/client/README.md index 115e604db..aed3e641d 100644 --- a/vendor/github.com/moby/moby/client/README.md +++ b/vendor/github.com/moby/moby/client/README.md @@ -23,11 +23,16 @@ import ( ) func main() { - // Create a new client that handles common environment variables - // for configuration (DOCKER_HOST, DOCKER_API_VERSION), and does - // API-version negotiation to allow downgrading the API version - // when connecting with an older daemon version. - apiClient, err := client.New(client.FromEnv) + // Create a new client with "client.FromEnv" (configuring the client + // from commonly used environment variables such as DOCKER_HOST and + // DOCKER_API_VERSION) and set a custom User-Agent. + // + // API-version negotiation is enabled by default to allow downgrading + // the API version when connecting with an older daemon version. + apiClient, err := client.New( + client.FromEnv, + client.WithUserAgent("my-application/1.0.0"), + ) if err != nil { panic(err) } @@ -49,4 +54,4 @@ func main() { } ``` -[Full documentation is available on pkg.go.dev.](https://pkg.go.dev/github.com/moby/moby/client) +Full documentation is available on [pkg.go.dev](https://pkg.go.dev/github.com/moby/moby/client). diff --git a/vendor/github.com/moby/moby/client/build_cancel.go b/vendor/github.com/moby/moby/client/build_cancel.go index f6cfc6bc9..a31dced97 100644 --- a/vendor/github.com/moby/moby/client/build_cancel.go +++ b/vendor/github.com/moby/moby/client/build_cancel.go @@ -5,8 +5,10 @@ import ( "net/url" ) +// BuildCancelOptions holds options for [Client.BuildCancel]. type BuildCancelOptions struct{} +// BuildCancelResult holds the result of [Client.BuildCancel]. type BuildCancelResult struct{} // BuildCancel requests the daemon to cancel the ongoing build request diff --git a/vendor/github.com/moby/moby/client/client.go b/vendor/github.com/moby/moby/client/client.go index 2d1e0db79..89ba88ee5 100644 --- a/vendor/github.com/moby/moby/client/client.go +++ b/vendor/github.com/moby/moby/client/client.go @@ -59,6 +59,7 @@ import ( "net/http" "net/url" "path" + "runtime" "slices" "strings" "sync" @@ -67,6 +68,7 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/docker/go-connections/sockets" + "github.com/moby/moby/client/internal/mod" "github.com/moby/moby/client/pkg/versions" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) @@ -113,6 +115,10 @@ const MaxAPIVersion = "1.54" // below this version are not considered when performing API-version negotiation. const MinAPIVersion = "1.40" +// defaultUserAgent returns the default User-Agent to use if none is set. +// It defaults to "moby-client/ os/arch" +var defaultUserAgent = sync.OnceValue(userAgent) + // Ensure that Client always implements APIClient. var _ APIClient = &Client{} @@ -159,7 +165,9 @@ func CheckRedirect(_ *http.Request, via []*http.Request) error { // NewClientWithOpts initializes a new API client. // -// Deprecated: use New. This function will be removed in the next release. +// Deprecated: use [New]. This function will be removed in the next release. +// +//go:fix inline func NewClientWithOpts(ops ...Opt) (*Client, error) { return New(ops...) } @@ -207,6 +215,9 @@ func New(ops ...Opt) (*Client, error) { cfg := &c.clientConfig for _, op := range ops { + if op == nil { + continue + } if err := op(cfg); err != nil { return nil, err } @@ -431,3 +442,14 @@ func (cli *Client) dialer() func(context.Context) (net.Conn, error) { } } } + +func userAgent() string { + const defaultVersion = "v0.0.0+unknown" + const moduleName = "github.com/moby/moby/client" + + version := defaultVersion + if v := mod.Version(moduleName); v != "" { + version = v + } + return "moby-client/" + version + " " + runtime.GOOS + "/" + runtime.GOARCH +} diff --git a/vendor/github.com/moby/moby/client/client_options.go b/vendor/github.com/moby/moby/client/client_options.go index d92a16a45..399255723 100644 --- a/vendor/github.com/moby/moby/client/client_options.go +++ b/vendor/github.com/moby/moby/client/client_options.go @@ -2,6 +2,7 @@ package client import ( "context" + "crypto/tls" "errors" "fmt" "net" @@ -11,6 +12,7 @@ import ( "strings" "time" + cerrdefs "github.com/containerd/errdefs" "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" @@ -193,11 +195,23 @@ func WithUserAgent(ua string) Opt { } // WithHTTPHeaders appends custom HTTP headers to the client's default headers. -// It does not allow for built-in headers (such as "User-Agent", if set) to -// be overridden. Also see [WithUserAgent]. +// It does not allow overriding built-in headers (such as "User-Agent"). +// Also see [WithUserAgent]. +// +// It replaces any existing custom headers. Keys are case-insensitive and +// canonicalized using [http.CanonicalHeaderKey]. If multiple entries map +// to the same canonical key, a [cerrdefs.ErrInvalidArgument] is returned. func WithHTTPHeaders(headers map[string]string) Opt { return func(c *clientConfig) error { - c.customHTTPHeaders = headers + c.customHTTPHeaders = make(map[string]string) + for k, v := range headers { + k = http.CanonicalHeaderKey(k) + _, ok := c.customHTTPHeaders[k] + if ok { + return cerrdefs.ErrInvalidArgument.WithMessage(fmt.Sprintf("duplicate custom HTTP header (%s)", k)) + } + c.customHTTPHeaders[k] = v + } return nil } } @@ -210,56 +224,84 @@ func WithScheme(scheme string) Opt { } } -// WithTLSClientConfig applies a TLS config to the client transport. -func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt { +// WithTLSClientConfig configures the client's existing HTTP transport to use TLS. +// The minimum TLS version is TLS 1.2. +// +// If caFile is non-empty, it specifies the CA certificate file to use for +// server verification, and replaces the system root pool for that verification. +// If certFile is empty, the system root pool is used. +// +// If either certFile or keyFile is set, both must point to readable files +// containing a valid client certificate and unencrypted private key, or this +// option returns an error. +// +// If both certPath and keyPath are empty, no client certificate is configured. +// The connection will use TLS without client authentication (i.e., not mTLS). +func WithTLSClientConfig(caFile, certFile, keyFile string) Opt { return func(c *clientConfig) error { transport, ok := c.client.Transport.(*http.Transport) if !ok { - return fmt.Errorf("cannot apply tls config to transport: %T", c.client.Transport) + return fmt.Errorf("cannot configure TLS: unsupported HTTP transport %T", c.client.Transport) } config, err := tlsconfig.Client(tlsconfig.Options{ - CAFile: cacertPath, - CertFile: certPath, - KeyFile: keyPath, + CAFile: caFile, + CertFile: certFile, + KeyFile: keyFile, ExclusiveRootPools: true, + MinVersion: tls.VersionTLS12, }) if err != nil { - return fmt.Errorf("failed to create tls config: %w", err) + return fmt.Errorf("configure TLS: %w", err) } transport.TLSClientConfig = config return nil } } -// WithTLSClientConfigFromEnv configures the client's TLS settings with the -// settings in the DOCKER_CERT_PATH ([EnvOverrideCertPath]) and DOCKER_TLS_VERIFY -// ([EnvTLSVerify]) environment variables. If DOCKER_CERT_PATH is not set or empty, -// TLS configuration is not modified. +// WithTLSClientConfigFromEnv configures the client for TLS using the +// DOCKER_CERT_PATH ([EnvOverrideCertPath]) and DOCKER_TLS_VERIFY +// ([EnvTLSVerify]) environment variables. The minimum TLS version is TLS 1.2. // -// WithTLSClientConfigFromEnv uses the following environment variables: +// If DOCKER_CERT_PATH is unset or empty, this option leaves the client +// unchanged. // -// - DOCKER_CERT_PATH ([EnvOverrideCertPath]) to specify the directory from -// which to load the TLS certificates ("ca.pem", "cert.pem", "key.pem"). -// - DOCKER_TLS_VERIFY ([EnvTLSVerify]) to enable or disable TLS verification -// (off by default). +// When DOCKER_CERT_PATH is set, the following files are loaded from that +// directory: +// +// - "ca.pem" as the CA certificate +// - "cert.pem" as the client certificate +// - "key.pem" as the client private key +// +// These files must exist, be readable, and contain valid TLS material, or this +// option returns an error. A client certificate is always loaded from "cert.pem" +// and "key.pem" (mTLS is expected). +// +// If DOCKER_TLS_VERIFY is set to a non-empty value, server certificate +// verification is enabled. In that case, "ca.pem" is added to the system root +// pool used for verification. +// +// If DOCKER_TLS_VERIFY is unset or empty, server certificate verification is +// disabled. func WithTLSClientConfigFromEnv() Opt { return func(c *clientConfig) error { dockerCertPath := os.Getenv(EnvOverrideCertPath) if dockerCertPath == "" { return nil } - tlsc, err := tlsconfig.Client(tlsconfig.Options{ + tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ CAFile: filepath.Join(dockerCertPath, "ca.pem"), CertFile: filepath.Join(dockerCertPath, "cert.pem"), KeyFile: filepath.Join(dockerCertPath, "key.pem"), InsecureSkipVerify: os.Getenv(EnvTLSVerify) == "", + MinVersion: tls.VersionTLS12, }) if err != nil { - return err + return fmt.Errorf("configure TLS from %q: %w", EnvOverrideCertPath+"="+dockerCertPath, err) } + // FIXME(thaJeztah): unlike WithTLSClientConfig, this option replaces the client's http.Client and transport; consider updating just the transport. c.client = &http.Client{ - Transport: &http.Transport{TLSClientConfig: tlsc}, + Transport: &http.Transport{TLSClientConfig: tlsConfig}, CheckRedirect: CheckRedirect, } return nil @@ -295,6 +337,8 @@ func WithAPIVersion(version string) Opt { // WithVersion overrides the client version with the specified one. // // Deprecated: use [WithAPIVersion] instead. +// +//go:fix inline func WithVersion(version string) Opt { return WithAPIVersion(version) } @@ -328,6 +372,8 @@ func WithAPIVersionFromEnv() Opt { // the DOCKER_API_VERSION ([EnvOverrideAPIVersion]) environment variable. // // Deprecated: use [WithAPIVersionFromEnv] instead. +// +//go:fix inline func WithVersionFromEnv() Opt { return WithAPIVersionFromEnv() } @@ -337,8 +383,11 @@ func WithVersionFromEnv() Opt { // to use when making requests. API version negotiation is performed on the first // request; subsequent requests do not re-negotiate. // -// Deprecated: API-version negotiation is now enabled by default. Use [WithAPIVersion] -// or [WithAPIVersionFromEnv] to disable API version negotiation. +// Deprecated: API-version negotiation is now enabled by default and this options +// is now a no-op. +// +// Use [WithAPIVersion] or [WithAPIVersionFromEnv] to set a fixed API version +// instead of using automatic negotiation. func WithAPIVersionNegotiation() Opt { return func(c *clientConfig) error { return nil @@ -348,7 +397,10 @@ func WithAPIVersionNegotiation() Opt { // WithTraceProvider sets the trace provider for the client. // If this is not set then the global trace provider is used. func WithTraceProvider(provider trace.TracerProvider) Opt { - return WithTraceOptions(otelhttp.WithTracerProvider(provider)) + return func(c *clientConfig) error { + c.traceOpts = append(c.traceOpts, otelhttp.WithTracerProvider(provider)) + return nil + } } // WithTraceOptions sets tracing span options for the client. diff --git a/vendor/github.com/moby/moby/client/config_remove.go b/vendor/github.com/moby/moby/client/config_remove.go index c77a4c378..5cde5e143 100644 --- a/vendor/github.com/moby/moby/client/config_remove.go +++ b/vendor/github.com/moby/moby/client/config_remove.go @@ -2,10 +2,12 @@ package client import "context" +// ConfigRemoveOptions holds options for [Client.ConfigRemove]. type ConfigRemoveOptions struct { // Add future optional parameters here } +// ConfigRemoveResult holds the result of [Client.ConfigRemove]. type ConfigRemoveResult struct { // Add future fields here } diff --git a/vendor/github.com/moby/moby/client/config_update.go b/vendor/github.com/moby/moby/client/config_update.go index 2651f4b2f..31bdd7956 100644 --- a/vendor/github.com/moby/moby/client/config_update.go +++ b/vendor/github.com/moby/moby/client/config_update.go @@ -13,6 +13,7 @@ type ConfigUpdateOptions struct { Spec swarm.ConfigSpec } +// ConfigUpdateResult holds the result of [Client.ConfigUpdate]. type ConfigUpdateResult struct{} // ConfigUpdate attempts to update a config diff --git a/vendor/github.com/moby/moby/client/container_copy.go b/vendor/github.com/moby/moby/client/container_copy.go index f76511246..b37d1765f 100644 --- a/vendor/github.com/moby/moby/client/container_copy.go +++ b/vendor/github.com/moby/moby/client/container_copy.go @@ -14,10 +14,12 @@ import ( "github.com/moby/moby/api/types/container" ) +// ContainerStatPathOptions holds options for [Client.ContainerStatPath]. type ContainerStatPathOptions struct { Path string } +// ContainerStatPathResult holds the result of [Client.ContainerStatPath]. type ContainerStatPathResult struct { Stat container.PathStat } @@ -53,6 +55,7 @@ type CopyToContainerOptions struct { CopyUIDGID bool } +// CopyToContainerResult holds the result of [Client.CopyToContainer]. type CopyToContainerResult struct{} // CopyToContainer copies content into the container filesystem. @@ -83,10 +86,12 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID string, opti return CopyToContainerResult{}, nil } +// CopyFromContainerOptions holds options for [Client.CopyFromContainer]. type CopyFromContainerOptions struct { SourcePath string } +// CopyFromContainerResult holds the result of [Client.CopyFromContainer]. type CopyFromContainerResult struct { Content io.ReadCloser Stat container.PathStat diff --git a/vendor/github.com/moby/moby/client/filters.go b/vendor/github.com/moby/moby/client/filters.go index 347ad5c68..3669ae0d4 100644 --- a/vendor/github.com/moby/moby/client/filters.go +++ b/vendor/github.com/moby/moby/client/filters.go @@ -2,6 +2,7 @@ package client import ( "encoding/json" + "maps" "net/url" ) @@ -35,9 +36,7 @@ func (f Filters) Clone() Filters { out := make(Filters, len(f)) for term, values := range f { inner := make(map[string]bool, len(values)) - for v, ok := range values { - inner[v] = ok - } + maps.Copy(inner, values) out[term] = inner } return out diff --git a/vendor/github.com/moby/moby/client/image_build.go b/vendor/github.com/moby/moby/client/image_build.go index 5062ec5de..67ac204aa 100644 --- a/vendor/github.com/moby/moby/client/image_build.go +++ b/vendor/github.com/moby/moby/client/image_build.go @@ -23,7 +23,7 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio return ImageBuildResult{}, err } - buf, err := json.Marshal(options.AuthConfigs) + buf, err := json.Marshal(options.AuthConfigs) // #nosec G117 -- ignore "Marshaled struct field "Password" (JSON key "password") matches secret pattern" if err != nil { return ImageBuildResult{}, err } diff --git a/vendor/github.com/moby/moby/client/image_import.go b/vendor/github.com/moby/moby/client/image_import.go index f383f76d4..6c9f22866 100644 --- a/vendor/github.com/moby/moby/client/image_import.go +++ b/vendor/github.com/moby/moby/client/image_import.go @@ -39,7 +39,7 @@ func (cli *Client) ImageImport(ctx context.Context, source ImageImportSource, re query.Set("message", options.Message) } if p := formatPlatform(options.Platform); p != "unknown" { - // TODO(thaJeztah): would we ever support mutiple platforms here? (would require multiple rootfs tars as well?) + // TODO(thaJeztah): would we ever support multiple platforms here? (would require multiple rootfs tars as well?) query.Set("platform", p) } for _, change := range options.Changes { diff --git a/vendor/github.com/moby/moby/client/image_tag.go b/vendor/github.com/moby/moby/client/image_tag.go index 5566f4624..37272914f 100644 --- a/vendor/github.com/moby/moby/client/image_tag.go +++ b/vendor/github.com/moby/moby/client/image_tag.go @@ -9,11 +9,13 @@ import ( "github.com/distribution/reference" ) +// ImageTagOptions holds options for [Client.ImageTag]. type ImageTagOptions struct { Source string Target string } +// ImageTagResult holds the result of [Client.ImageTag]. type ImageTagResult struct{} // ImageTag tags an image in the docker host diff --git a/vendor/github.com/moby/moby/client/internal/json-stream.go b/vendor/github.com/moby/moby/client/internal/json-stream.go index 07d07bd7e..d86b99da2 100644 --- a/vendor/github.com/moby/moby/client/internal/json-stream.go +++ b/vendor/github.com/moby/moby/client/internal/json-stream.go @@ -12,8 +12,9 @@ const rs = 0x1E type DecoderFn func(v any) error -// NewJSONStreamDecoder builds adequate DecoderFn to read json records formatted with specified content-type -func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn { +// NewJSONStreamDecoder builds a DecoderFn to read a stream of JSON records +// formatted with the specified content-type. +func NewJSONStreamDecoder(r io.Reader, contentType types.MediaType) DecoderFn { switch contentType { case types.MediaTypeJSONSequence: return json.NewDecoder(NewRSFilterReader(r)).Decode @@ -24,27 +25,38 @@ func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn { } } -// RSFilterReader wraps an io.Reader and filters out ASCII RS characters -type RSFilterReader struct { +type rsFilterReader struct { reader io.Reader - buffer []byte } -// NewRSFilterReader creates a new RSFilterReader that filters out RS characters -func NewRSFilterReader(r io.Reader) *RSFilterReader { - return &RSFilterReader{ - reader: r, - buffer: make([]byte, 4096), // Internal buffer for reading chunks - } +// NewRSFilterReader creates an [io.Reader] that filters out ASCII Record Separators (RS). +func NewRSFilterReader(r io.Reader) io.Reader { + return &rsFilterReader{reader: r} } -// Read implements the io.Reader interface, filtering out RS characters -func (r *RSFilterReader) Read(p []byte) (n int, err error) { +func (r *rsFilterReader) Read(p []byte) (int, error) { if len(p) == 0 { return 0, nil } - n, err = r.reader.Read(p) - filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs }) - return len(filtered), err + for { + n, err := r.reader.Read(p) + if n == 0 { + return 0, err + } + + filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs }) + n = len(filtered) + if err != nil { + if err == io.EOF && n > 0 { + return n, nil + } + return n, err + } + if n == 0 { + // Avoid returning (0, nil) after consuming input; keep reading until data or an error (e.g., EOF). + continue + } + return n, nil + } } diff --git a/vendor/github.com/moby/moby/client/internal/jsonmessages.go b/vendor/github.com/moby/moby/client/internal/jsonmessages.go index 31262fd8e..05210d0dc 100644 --- a/vendor/github.com/moby/moby/client/internal/jsonmessages.go +++ b/vendor/github.com/moby/moby/client/internal/jsonmessages.go @@ -8,6 +8,8 @@ import ( "iter" "sync" + "github.com/containerd/errdefs/pkg/errhttp" + "github.com/moby/moby/api/types/jsonstream" ) @@ -44,41 +46,90 @@ func (r Stream) Close() error { var _ io.ReadCloser = Stream{} -// JSONMessages decodes the response stream as a sequence of JSONMessages. -// if stream ends or context is cancelled, the underlying [io.Reader] is closed. +// JSONMessages decodes the response stream as a sequence of [jsonstream.Message]. +// The underlying [io.Reader] is closed when the stream ends or if the context +// is cancelled. func (r Stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] { stop := context.AfterFunc(ctx, func() { _ = r.Close() }) - dec := json.NewDecoder(r) return func(yield func(jsonstream.Message, error) bool) { defer func() { stop() // unregister AfterFunc - r.Close() + _ = r.Close() }() + + dec := json.NewDecoder(r) for { var jm jsonstream.Message - err := dec.Decode(&jm) - if errors.Is(err, io.EOF) { - break - } - if ctx.Err() != nil { - yield(jm, ctx.Err()) + if err := dec.Decode(&jm); err != nil { + if errors.Is(err, io.EOF) { + return + } + if err := ctx.Err(); err != nil { + // Do not return decoding errors if the context was + // cancelled, because the decoding errors may be due + // to the context being cancelled. + yield(jsonstream.Message{}, err) + return + } + yield(jsonstream.Message{}, err) return } - if !yield(jm, err) { + if !yield(jm, nil) { return } } } } -// Wait waits for operation to complete and detects errors reported as JSONMessage +// Wait consumes the stream until completion. +// +// It returns nil if the operation completes successfully. Errors are +// returned if the context is canceled, a decoding/transport failure +// occurs, or a JSON message reports an error ([jsonstream.Message.Error]). func (r Stream) Wait(ctx context.Context) error { - for _, err := range r.JSONMessages(ctx) { + for jm, err := range r.JSONMessages(ctx) { if err != nil { + // decode, transport and context cancellation errors. return err } + if jm.Error != nil { + // push/pull failures. + return httpErrorFromStatusCode(jm.Error, jm.Error.Code) + } } return nil } + +type httpError struct { + err error + errdef error +} + +func (e *httpError) Error() string { + return e.err.Error() +} + +func (e *httpError) Unwrap() error { + return e.err +} + +func (e *httpError) Is(target error) bool { + return errors.Is(e.errdef, target) +} + +// httpErrorFromStatusCode creates an errdef error, based on the provided HTTP status-code +// +// TODO(thaJeztah): unify with the implementation in client and move to an internal package +// see https://github.com/moby/moby/blob/client/v0.4.0/client/errors.go#L76-L114 +func httpErrorFromStatusCode(err error, statusCode int) error { + if err == nil { + return nil + } + + return &httpError{ + err: err, + errdef: errhttp.ToNative(statusCode), + } +} diff --git a/vendor/github.com/moby/moby/client/internal/mod/mod.go b/vendor/github.com/moby/moby/client/internal/mod/mod.go new file mode 100644 index 000000000..355eb9532 --- /dev/null +++ b/vendor/github.com/moby/moby/client/internal/mod/mod.go @@ -0,0 +1,226 @@ +// Package mod provides a small helper to extract a module's version +// from [debug.BuildInfo] without depending on [golang.org/x/mod]. +// +// [golang.org/x/mod]: https://pkg.go.dev/golang.org/x/mod +package mod + +import ( + "fmt" + "runtime/debug" + "strconv" + "strings" + "sync" +) + +var readBuildInfo = sync.OnceValues(debug.ReadBuildInfo) + +// Version returns a best-effort version string for the given module path, +// similar to [mod.Version] in the daemon. +// +// If the module is present in [debug.BuildInfo] dependencies, its version +// is returned. Tagged versions are returned as-is (with "+incompatible" +// stripped). [Pseudo-versions] are normalized to: +// +// +[+meta...][+dirty] +// +// Where "" matches the behavior of [module.PseudoVersionBase] (i.e., +// downgrade to the previous tag for non-prerelease Pseudo-versions). +// +// If the module is replaced (for example via go.work or replace directives), +// or no usable version information is available, Version returns an empty string. +// +// The returned value is intended for display purposes (e.g., in a default +// User-Agent), not for version comparison. +// +// [mod.Version]: https://pkg.go.dev/github.com/moby/moby/v2@v2.0.0-beta.7/daemon/internal/builder-next/worker/mod#Version +// [module.PseudoVersionBase]: https://pkg.go.dev/golang.org/x/mod@v0.34.0/module#PseudoVersionBase +// [Pseudo-versions]: https://cs.opensource.google/go/x/mod/+/refs/tags/v0.34.0:module/pseudo.go;l=5-33 +func Version(name string) string { + bi, ok := readBuildInfo() + if !ok || bi == nil { + return "" + } + return moduleVersion(name, bi) +} + +func moduleVersion(name string, bi *debug.BuildInfo) (modVersion string) { + if bi == nil { + return "" + } + + // Check if we're the main module. + if v, ok := getVersion(name, &bi.Main); ok { + return v + } + + // iterate over all dependencies and find name + for _, dep := range bi.Deps { + if v, ok := getVersion(name, dep); ok { + return v + } + } + + return "" +} + +func getVersion(name string, dep *debug.Module) (string, bool) { + if dep == nil || dep.Path != name { + return "", false + } + + v := dep.Version + if dep.Replace != nil && dep.Replace.Version != "" { + v = dep.Replace.Version + } + if v == "" || v == "(devel)" { + return "", true + } + + return normalize(v), true +} + +// normalize converts a Go module version into a display-friendly form: +// +// - strips "+incompatible" unconditionally +// - if pseudo: vX.Y.Z[-pre][+rev][+meta...][+dirty] +// - if tagged: vX.Y.Z[-pre][+meta...][+dirty] +func normalize(v string) string { + base, metas, dirty := splitMetadata(v) + + out := base + if base2, rev, undoPatch, ok := splitPseudo(base); ok { + if undoPatch { + // Downgrade the patch version that was raised by pseudo-versions: + // + // (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 + if major, minor, patch, ok := parseSemVer(base2); ok && patch > 0 { + patch-- + base2 = fmt.Sprintf("v%d.%d.%d", major, minor, patch) + } + } + // Go pseudo rev is typically 12, but be defensive. + if len(rev) > 12 { + rev = rev[:12] + } + out = base2 + "+" + rev + } + + // Preserve other metadata (except for "+incompatible"). + for _, m := range metas { + out += m + } + if dirty { + // +dirty goes last + out += "+dirty" + } + return out +} + +func splitMetadata(v string) (base string, metas []string, dirty bool) { + base, meta, ok := strings.Cut(v, "+") + if !ok || meta == "" { + return base, nil, false + } + for m := range strings.SplitSeq(meta, "+") { + // drop incompatible, extract dirty, preserve everything else. + switch m { + case "incompatible", "": + // drop "+incompatible" and empty strings + case "dirty": + dirty = true + default: + metas = append(metas, "+"+m) + } + } + + return base, metas, dirty +} + +// splitPseudo splits a pseudo-version into base + revision, and reports whether +// it is a (Z+1) pseudo that needs patch undo. +// +// Supported (after stripping +incompatible/+dirty metadata): +// +// (1) vX.0.0-yyyymmddhhmmss-abcdef123456 +// (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 +// (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 +func splitPseudo(v string) (base, rev string, undoPatch bool, ok bool) { + // Split off revision at the last '-'. + last := strings.LastIndexByte(v, '-') + if last < 0 || last+1 >= len(v) { + return "", "", false, false + } + rev = v[last+1:] + left := v[:last] + + // First try the dot-joined timestamp forms: + // ...-0. (release pseudo; undoPatch) + // ....0. (prerelease pseudo; preserve prerelease) + if dot := strings.LastIndexByte(left, '.'); dot > 0 && dot+1 < len(left) { + ts := left[dot+1:] + if isTimestamp(ts) { + prefix := left[:dot] // ends with "-0" or ".0" for forms (2)/(4) + switch { + case strings.HasSuffix(prefix, "-0"): + // (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 + return prefix[:len(prefix)-2], rev, true, true + case strings.HasSuffix(prefix, ".0"): + // (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 + return prefix[:len(prefix)-2], rev, false, true + } + } + } + + // Fall back to form (1): ...-- + // + // (1) vX.0.0-yyyymmddhhmmss-abcdef123456 + if dash := strings.LastIndexByte(left, '-'); dash > 0 && dash+1 < len(left) { + ts := left[dash+1:] + if isTimestamp(ts) { + return left[:dash], rev, false, true + } + } + + return "", "", false, false +} + +// isTimestamp checks whether s is a timestamp ("yyyymmddhhmmss") +// component in a module version (vX.0.0-yyyymmddhhmmss-abcdef123456). +func isTimestamp(s string) bool { + if len(s) != 14 { + return false + } + for i := range len(s) { + c := s[i] + if c < '0' || c > '9' { + return false + } + } + return true +} + +// parseSemVer parses "vX.Y.Z" into numeric components. +// It intentionally handles only the strict three-segment core form. +func parseSemVer(v string) (major, minor, patch int, ok bool) { + if len(v) < 2 || v[0] != 'v' { + return 0, 0, 0, false + } + parts := strings.Split(v[1:], ".") + if len(parts) != 3 { + return 0, 0, 0, false + } + var err error + major, err = strconv.Atoi(parts[0]) + if err != nil { + return 0, 0, 0, false + } + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return 0, 0, 0, false + } + patch, err = strconv.Atoi(parts[2]) + if err != nil { + return 0, 0, 0, false + } + return major, minor, patch, true +} diff --git a/vendor/github.com/moby/moby/client/node_inspect.go b/vendor/github.com/moby/moby/client/node_inspect.go index cd4ce0119..ed482152c 100644 --- a/vendor/github.com/moby/moby/client/node_inspect.go +++ b/vendor/github.com/moby/moby/client/node_inspect.go @@ -12,6 +12,7 @@ import ( // NodeInspectOptions holds parameters to inspect nodes with. type NodeInspectOptions struct{} +// NodeInspectResult holds the result of [Client.NodeInspect]. type NodeInspectResult struct { Node swarm.Node Raw json.RawMessage diff --git a/vendor/github.com/moby/moby/client/node_list.go b/vendor/github.com/moby/moby/client/node_list.go index 1a1b57922..aec3355e4 100644 --- a/vendor/github.com/moby/moby/client/node_list.go +++ b/vendor/github.com/moby/moby/client/node_list.go @@ -13,6 +13,7 @@ type NodeListOptions struct { Filters Filters } +// NodeListResult holds the result of [Client.NodeList]. type NodeListResult struct { Items []swarm.Node } diff --git a/vendor/github.com/moby/moby/client/node_remove.go b/vendor/github.com/moby/moby/client/node_remove.go index 56c39d67a..2a88cf80e 100644 --- a/vendor/github.com/moby/moby/client/node_remove.go +++ b/vendor/github.com/moby/moby/client/node_remove.go @@ -9,6 +9,8 @@ import ( type NodeRemoveOptions struct { Force bool } + +// NodeRemoveResult holds the result of [Client.NodeRemove]. type NodeRemoveResult struct{} // NodeRemove removes a Node. diff --git a/vendor/github.com/moby/moby/client/node_update.go b/vendor/github.com/moby/moby/client/node_update.go index 4bc7c3b69..24f87a4df 100644 --- a/vendor/github.com/moby/moby/client/node_update.go +++ b/vendor/github.com/moby/moby/client/node_update.go @@ -13,6 +13,7 @@ type NodeUpdateOptions struct { Spec swarm.NodeSpec } +// NodeUpdateResult holds the result of [Client.NodeUpdate]. type NodeUpdateResult struct{} // NodeUpdate updates a Node. diff --git a/vendor/github.com/moby/moby/client/pkg/versions/compare.go b/vendor/github.com/moby/moby/client/pkg/versions/compare.go index 1a0325c7e..fa0ad9b5a 100644 --- a/vendor/github.com/moby/moby/client/pkg/versions/compare.go +++ b/vendor/github.com/moby/moby/client/pkg/versions/compare.go @@ -16,11 +16,8 @@ func compare(v1, v2 string) int { otherTab = strings.Split(v2, ".") ) - maxVer := len(currTab) - if len(otherTab) > maxVer { - maxVer = len(otherTab) - } - for i := 0; i < maxVer; i++ { + maxVer := max(len(otherTab), len(currTab)) + for i := range maxVer { var currInt, otherInt int if len(currTab) > i { diff --git a/vendor/github.com/moby/moby/client/request.go b/vendor/github.com/moby/moby/client/request.go index 7b1ff743d..10ed36dc6 100644 --- a/vendor/github.com/moby/moby/client/request.go +++ b/vendor/github.com/moby/moby/client/request.go @@ -128,7 +128,7 @@ func (cli *Client) sendRequest(ctx context.Context, method, path string, query u // when failing to make a connection, On error, any Response can be ignored. // A non-2xx status code doesn't cause an error. func (cli *Client) doRequest(req *http.Request) (*http.Response, error) { - resp, err := cli.client.Do(req) + resp, err := cli.client.Do(req) // #nosec G704 -- ignore "SSRF via taint analysis"; API client intentionally sends caller-provided requests/URLs. if err == nil { return resp, nil } @@ -317,12 +317,17 @@ func (cli *Client) addHeaders(req *http.Request, headers http.Header) *http.Requ req.Header[http.CanonicalHeaderKey(k)] = v } - if cli.userAgent != nil { - if *cli.userAgent == "" { - req.Header.Del("User-Agent") - } else { - req.Header.Set("User-Agent", *cli.userAgent) + if cli.userAgent == nil { + // No custom User-Agent set: use the default. + if req.Header.Get("User-Agent") == "" { + req.Header.Set("User-Agent", defaultUserAgent()) } + } else if *cli.userAgent == "" { + // User-Agent set to empty value; remove User-Agent. + req.Header.Del("User-Agent") + } else { + // Custom User-Agent set. + req.Header.Set("User-Agent", *cli.userAgent) } return req } @@ -344,7 +349,7 @@ func jsonEncode(data any) (io.Reader, error) { // encoding/json encodes a nil pointer as the JSON document `null`, // irrespective of whether the type implements json.Marshaler or encoding.TextMarshaler. // That is almost certainly not what the caller intended as the request body. - if v := reflect.ValueOf(data); v.Kind() == reflect.Ptr && v.IsNil() { + if v := reflect.ValueOf(data); v.Kind() == reflect.Pointer && v.IsNil() { return http.NoBody, nil } diff --git a/vendor/github.com/moby/moby/client/secret_remove.go b/vendor/github.com/moby/moby/client/secret_remove.go index 8554f3f21..42cbfec9e 100644 --- a/vendor/github.com/moby/moby/client/secret_remove.go +++ b/vendor/github.com/moby/moby/client/secret_remove.go @@ -2,10 +2,12 @@ package client import "context" +// SecretRemoveOptions holds options for [Client.SecretRemove]. type SecretRemoveOptions struct { // Add future optional parameters here } +// SecretRemoveResult holds the result of [Client.SecretRemove]. type SecretRemoveResult struct { // Add future fields here } diff --git a/vendor/github.com/moby/moby/client/secret_update.go b/vendor/github.com/moby/moby/client/secret_update.go index c88ad1106..d50fba4d4 100644 --- a/vendor/github.com/moby/moby/client/secret_update.go +++ b/vendor/github.com/moby/moby/client/secret_update.go @@ -13,6 +13,7 @@ type SecretUpdateOptions struct { Spec swarm.SecretSpec } +// SecretUpdateResult holds the result of [Client.SecretUpdate]. type SecretUpdateResult struct{} // SecretUpdate attempts to update a secret. diff --git a/vendor/github.com/moby/moby/client/service_logs.go b/vendor/github.com/moby/moby/client/service_logs.go index 57099ffe4..911b63cb7 100644 --- a/vendor/github.com/moby/moby/client/service_logs.go +++ b/vendor/github.com/moby/moby/client/service_logs.go @@ -34,7 +34,7 @@ type ServiceLogsResult interface { // // The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) ServiceLogs(ctx context.Context, serviceID string, options ServiceLogsOptions) (ServiceLogsResult, error) { - // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): this function needs documentation about the format of the stream (similar to for container logs) // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 serviceID, err := trimID("service", serviceID) diff --git a/vendor/github.com/moby/moby/client/system_disk_usage.go b/vendor/github.com/moby/moby/client/system_disk_usage.go index 1bb2d0d7e..3f54bfd34 100644 --- a/vendor/github.com/moby/moby/client/system_disk_usage.go +++ b/vendor/github.com/moby/moby/client/system_disk_usage.go @@ -244,22 +244,18 @@ func imageDiskUsageFromLegacyAPI(du *legacyDiskUsage) ImagesDiskUsage { Items: du.Images, } - var used int64 - for _, i := range idu.Items { - if i.Containers > 0 { + for _, img := range idu.Items { + switch { + case img.Containers < 0: + // No container-count information available; skip (assume it's in use). + case img.Containers > 0: idu.ActiveCount++ - - if i.Size == -1 || i.SharedSize == -1 { - continue - } - used += (i.Size - i.SharedSize) + case img.Containers == 0 && img.Size != -1 && img.SharedSize != -1: + reclaimable := img.Size - img.SharedSize + idu.Reclaimable += reclaimable } } - if idu.TotalCount > 0 { - idu.Reclaimable = idu.TotalSize - used - } - return idu } diff --git a/vendor/github.com/moby/moby/client/system_info.go b/vendor/github.com/moby/moby/client/system_info.go index 4c0a2238e..b4241742d 100644 --- a/vendor/github.com/moby/moby/client/system_info.go +++ b/vendor/github.com/moby/moby/client/system_info.go @@ -9,10 +9,12 @@ import ( "github.com/moby/moby/api/types/system" ) +// InfoOptions holds options for [Client.Info]. type InfoOptions struct { // No options currently; placeholder for future use } +// SystemInfoResult holds the result of [Client.Info]. type SystemInfoResult struct { Info system.Info } diff --git a/vendor/github.com/moby/moby/client/task_logs.go b/vendor/github.com/moby/moby/client/task_logs.go index e4de019f3..0174ad465 100644 --- a/vendor/github.com/moby/moby/client/task_logs.go +++ b/vendor/github.com/moby/moby/client/task_logs.go @@ -32,7 +32,7 @@ type TaskLogsResult interface { // // The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) { - // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): this function needs documentation about the format of the stream (similar to for container logs) // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 query := url.Values{} diff --git a/vendor/github.com/moby/moby/client/utils.go b/vendor/github.com/moby/moby/client/utils.go index 4415e0dc5..1c0d09dfa 100644 --- a/vendor/github.com/moby/moby/client/utils.go +++ b/vendor/github.com/moby/moby/client/utils.go @@ -136,7 +136,7 @@ func newCancelReadCloser(ctx context.Context, rc io.ReadCloser) io.ReadCloser { rc: rc, close: sync.OnceValue(rc.Close), } - crc.stop = context.AfterFunc(ctx, func() { _ = crc.Close() }) + crc.stop = context.AfterFunc(ctx, func() { _ = crc.close() }) return crc } diff --git a/vendor/modules.txt b/vendor/modules.txt index 079650e69..621738a41 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -78,8 +78,8 @@ github.com/docker/docker/pkg/stdcopy ## explicit; go 1.21 github.com/docker/docker-credential-helpers/client github.com/docker/docker-credential-helpers/credentials -# github.com/docker/go-connections v0.6.0 -## explicit; go 1.18 +# github.com/docker/go-connections v0.7.0 +## explicit; go 1.23 github.com/docker/go-connections/sockets github.com/docker/go-connections/tlsconfig # github.com/docker/go-units v0.5.0 @@ -173,10 +173,11 @@ github.com/moby/moby/api/types/storage github.com/moby/moby/api/types/swarm github.com/moby/moby/api/types/system github.com/moby/moby/api/types/volume -# github.com/moby/moby/client v0.3.0 -## explicit; go 1.24.0 +# github.com/moby/moby/client v0.4.1 +## explicit; go 1.24 github.com/moby/moby/client github.com/moby/moby/client/internal +github.com/moby/moby/client/internal/mod github.com/moby/moby/client/internal/timestamp github.com/moby/moby/client/pkg/versions # github.com/moby/sys/capability v0.4.0