diff --git a/cli/command/image/inspect.go b/cli/command/image/inspect.go
index ece81cae719a..684e4d782362 100644
--- a/cli/command/image/inspect.go
+++ b/cli/command/image/inspect.go
@@ -7,6 +7,7 @@ import (
"bytes"
"context"
+ "github.com/containerd/platforms"
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/command/completion"
@@ -14,12 +15,14 @@ import (
flagsHelper "github.com/docker/cli/cli/flags"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
)
type inspectOptions struct {
- format string
- refs []string
+ format string
+ refs []string
+ platform string
}
// newInspectCommand creates a new cobra.Command for `docker image inspect`
@@ -39,14 +42,37 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
flags := cmd.Flags()
flags.StringVarP(&opts.format, "format", "f", "", flagsHelper.InspectFormatHelp)
+
+ // Don't default to DOCKER_DEFAULT_PLATFORM env variable, always default to
+ // inspecting the image as-is. This also avoids forcing the platform selection
+ // on older APIs which don't support it.
+ flags.StringVar(&opts.platform, "platform", "", `Inspect a specific platform of the multi-platform image.
+If the image or the server is not multi-platform capable, the command will error out if the platform does not match.
+'os[/arch[/variant]]': Explicit platform (eg. linux/amd64)`)
+ flags.SetAnnotation("platform", "version", []string{"1.49"})
+
+ _ = cmd.RegisterFlagCompletionFunc("platform", completion.Platforms)
return cmd
}
func runInspect(ctx context.Context, dockerCLI command.Cli, opts inspectOptions) error {
+ var platform *ocispec.Platform
+ if opts.platform != "" {
+ p, err := platforms.Parse(opts.platform)
+ if err != nil {
+ return err
+ }
+ platform = &p
+ }
+
apiClient := dockerCLI.Client()
return inspect.Inspect(dockerCLI.Out(), opts.refs, opts.format, func(ref string) (any, []byte, error) {
var buf bytes.Buffer
- resp, err := apiClient.ImageInspect(ctx, ref, client.ImageInspectWithRawResponse(&buf))
+ _ = platform
+ resp, err := apiClient.ImageInspect(ctx, ref,
+ client.ImageInspectWithRawResponse(&buf),
+ client.ImageInspectWithPlatform(platform),
+ )
if err != nil {
return image.InspectResponse{}, nil, err
}
diff --git a/docs/reference/commandline/image_inspect.md b/docs/reference/commandline/image_inspect.md
index 1f59b0014d1b..74da0bee2c05 100644
--- a/docs/reference/commandline/image_inspect.md
+++ b/docs/reference/commandline/image_inspect.md
@@ -8,6 +8,7 @@ Display detailed information on one or more images
| Name | Type | Default | Description |
|:-----------------|:---------|:--------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `-f`, `--format` | `string` | | Format output using a custom template:
'json': Print in JSON format
'TEMPLATE': Print output using the given Go template.
Refer to https://docs.docker.com/go/formatting/ for more information about formatting output with templates |
+| `--platform` | `string` | | Inspect a specific platform of the multi-platform image.
If the image or the server is not multi-platform capable, the command will error out if the platform does not match.
'os[/arch[/variant]]': Explicit platform (eg. linux/amd64) |
diff --git a/vendor.mod b/vendor.mod
index c97dadb07900..8b2aecc0df8e 100644
--- a/vendor.mod
+++ b/vendor.mod
@@ -6,6 +6,8 @@ module github.com/docker/cli
go 1.23.0
+replace github.com/docker/docker => github.com/vvoland/moby v20.10.16-0.20250314163427-17840c8adc30+incompatible
+
require (
dario.cat/mergo v1.0.1
github.com/containerd/platforms v1.0.0-rc.1
diff --git a/vendor.sum b/vendor.sum
index 7dd39222aca2..05d939c6488d 100644
--- a/vendor.sum
+++ b/vendor.sum
@@ -51,8 +51,6 @@ github.com/docker/cli-docs-tool v0.9.0/go.mod h1:ClrwlNW+UioiRyH9GiAOe1o3J/TsY3T
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
-github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.2 h1:50JF7ADQiHdAVBRtg/vy883Y4U5+5GmPOBNtUU+X+6A=
github.com/docker/docker-credential-helpers v0.9.2/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
@@ -269,6 +267,8 @@ github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a h1:tlJ
github.com/theupdateframework/notary v0.7.1-0.20210315103452-bf96a202a09a/go.mod h1:Y94A6rPp2OwNfP/7vmf8O2xx2IykP8pPXQ1DLouGnEw=
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346 h1:TvtdmeYsYEij78hS4oxnwikoiLdIrgav3BA+CbhaDAI=
github.com/tonistiigi/go-rosetta v0.0.0-20220804170347-3f4430f2d346/go.mod h1:xKQhd7snlzKFuUi1taTGWjpRE8iFTA06DeacYi3CVFQ=
+github.com/vvoland/moby v20.10.16-0.20250314163427-17840c8adc30+incompatible h1:opFk89LW5aZllunsKB+iR0yjfwCVsGjKpBBmVcvhW3o=
+github.com/vvoland/moby v20.10.16-0.20250314163427-17840c8adc30+incompatible/go.mod h1:nLN96xVmxZq8CPEl0UgxMpO/G2e8MQtjhAdlRDUdBi0=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
diff --git a/vendor/github.com/docker/docker/api/common.go b/vendor/github.com/docker/docker/api/common.go
index 2c62cd4032e4..d75c43d7c16d 100644
--- a/vendor/github.com/docker/docker/api/common.go
+++ b/vendor/github.com/docker/docker/api/common.go
@@ -3,7 +3,7 @@ package api // import "github.com/docker/docker/api"
// Common constants for daemon and client.
const (
// DefaultVersion of the current REST API.
- DefaultVersion = "1.48"
+ DefaultVersion = "1.49"
// MinSupportedAPIVersion is the minimum API version that can be supported
// by the API server, specified as "major.minor". Note that the daemon
diff --git a/vendor/github.com/docker/docker/api/swagger.yaml b/vendor/github.com/docker/docker/api/swagger.yaml
index a4881f951e3a..646032d6e0ef 100644
--- a/vendor/github.com/docker/docker/api/swagger.yaml
+++ b/vendor/github.com/docker/docker/api/swagger.yaml
@@ -5508,8 +5508,11 @@ definitions:
com.example.some-other-label: "some-other-value"
Data:
description: |
- Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
- data to store as secret.
+ Data is the data to store as a secret, formatted as a Base64-url-safe-encoded
+ ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
+ It must be empty if the Driver field is set, in which case the data is
+ loaded from an external secret store. The maximum allowed size is 500KB,
+ as defined in [MaxSecretSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize).
This field is only used to _create_ a secret, and is not returned by
other endpoints.
@@ -5560,8 +5563,9 @@ definitions:
type: "string"
Data:
description: |
- Base64-url-safe-encoded ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5))
- config data.
+ Data is the data to store as a config, formatted as a Base64-url-safe-encoded
+ ([RFC 4648](https://tools.ietf.org/html/rfc4648#section-5)) string.
+ The maximum allowed size is 1000KB, as defined in [MaxConfigSize](https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/manager/controlapi#MaxConfigSize).
type: "string"
Templating:
description: |
diff --git a/vendor/github.com/docker/docker/api/types/image/image_inspect.go b/vendor/github.com/docker/docker/api/types/image/image_inspect.go
index 78e81f052c67..40d1f97a315d 100644
--- a/vendor/github.com/docker/docker/api/types/image/image_inspect.go
+++ b/vendor/github.com/docker/docker/api/types/image/image_inspect.go
@@ -128,11 +128,12 @@ type InspectResponse struct {
// compatibility.
Descriptor *ocispec.Descriptor `json:"Descriptor,omitempty"`
- // Manifests is a list of image manifests available in this image. It
+ // Manifests is a list of image manifests available in this image. It
// provides a more detailed view of the platform-specific image manifests or
// other image-attached data like build attestations.
//
- // Only available if the daemon provides a multi-platform image store.
+ // Only available if the daemon provides a multi-platform image store, the client
+ // requests manifests AND does not request a specific platform.
//
// WARNING: This is experimental and may change at any time without any backward
// compatibility.
diff --git a/vendor/github.com/docker/docker/api/types/image/opts.go b/vendor/github.com/docker/docker/api/types/image/opts.go
index 919510fe37b2..62e100dd6734 100644
--- a/vendor/github.com/docker/docker/api/types/image/opts.go
+++ b/vendor/github.com/docker/docker/api/types/image/opts.go
@@ -106,6 +106,15 @@ type LoadOptions struct {
type InspectOptions struct {
// Manifests returns the image manifests.
Manifests bool
+
+ // Platform selects the specific platform of a multi-platform image to inspect.
+ //
+ // If the server is not multi-platform capable, this option will make the
+ // server return an error if the actual image platform doesn't match this
+ // platform.
+ //
+ // This option is only available for API version 1.49 and up.
+ Platform *ocispec.Platform
}
// SaveOptions holds parameters to save images.
diff --git a/vendor/github.com/docker/docker/api/types/registry/registry.go b/vendor/github.com/docker/docker/api/types/registry/registry.go
index b0a4d604f5f8..8117cb09e7ee 100644
--- a/vendor/github.com/docker/docker/api/types/registry/registry.go
+++ b/vendor/github.com/docker/docker/api/types/registry/registry.go
@@ -49,15 +49,17 @@ func (ipnet *NetIPNet) MarshalJSON() ([]byte, error) {
}
// UnmarshalJSON sets the IPNet from a byte array of JSON
-func (ipnet *NetIPNet) UnmarshalJSON(b []byte) (err error) {
+func (ipnet *NetIPNet) UnmarshalJSON(b []byte) error {
var ipnetStr string
- if err = json.Unmarshal(b, &ipnetStr); err == nil {
- var cidr *net.IPNet
- if _, cidr, err = net.ParseCIDR(ipnetStr); err == nil {
- *ipnet = NetIPNet(*cidr)
- }
+ if err := json.Unmarshal(b, &ipnetStr); err != nil {
+ return err
}
- return
+ _, cidr, err := net.ParseCIDR(ipnetStr)
+ if err != nil {
+ return err
+ }
+ *ipnet = NetIPNet(*cidr)
+ return nil
}
// IndexInfo contains information about a registry
diff --git a/vendor/github.com/docker/docker/api/types/swarm/config.go b/vendor/github.com/docker/docker/api/types/swarm/config.go
index 16202ccce615..f9a65187ffaf 100644
--- a/vendor/github.com/docker/docker/api/types/swarm/config.go
+++ b/vendor/github.com/docker/docker/api/types/swarm/config.go
@@ -12,6 +12,12 @@ type Config struct {
// ConfigSpec represents a config specification from a config in swarm
type ConfigSpec struct {
Annotations
+
+ // Data is the data to store as a config.
+ //
+ // The maximum allowed size is 1000KB, as defined in [MaxConfigSize].
+ //
+ // [MaxConfigSize]: https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/manager/controlapi#MaxConfigSize
Data []byte `json:",omitempty"`
// Templating controls whether and how to evaluate the config payload as
diff --git a/vendor/github.com/docker/docker/api/types/swarm/secret.go b/vendor/github.com/docker/docker/api/types/swarm/secret.go
index d5213ec981c3..aeb5bb54ad1a 100644
--- a/vendor/github.com/docker/docker/api/types/swarm/secret.go
+++ b/vendor/github.com/docker/docker/api/types/swarm/secret.go
@@ -12,8 +12,22 @@ type Secret struct {
// SecretSpec represents a secret specification from a secret in swarm
type SecretSpec struct {
Annotations
- Data []byte `json:",omitempty"`
- Driver *Driver `json:",omitempty"` // name of the secrets driver used to fetch the secret's value from an external secret store
+
+ // Data is the data to store as a secret. It must be empty if a
+ // [Driver] is used, in which case the data is loaded from an external
+ // secret store. The maximum allowed size is 500KB, as defined in
+ // [MaxSecretSize].
+ //
+ // This field is only used to create the secret, and is not returned
+ // by other endpoints.
+ //
+ // [MaxSecretSize]: https://pkg.go.dev/github.com/moby/swarmkit/v2@v2.0.0-20250103191802-8c1959736554/api/validation#MaxSecretSize
+ Data []byte `json:",omitempty"`
+
+ // Driver is the name of the secrets driver used to fetch the secret's
+ // value from an external secret store. If not set, the default built-in
+ // store is used.
+ Driver *Driver `json:",omitempty"`
// Templating controls whether and how to evaluate the secret payload as
// a template. If it is not set, no templating is used.
diff --git a/vendor/github.com/docker/docker/client/container_create.go b/vendor/github.com/docker/docker/client/container_create.go
index 9b8616f7614e..9bb106f77637 100644
--- a/vendor/github.com/docker/docker/client/container_create.go
+++ b/vendor/github.com/docker/docker/client/container_create.go
@@ -3,6 +3,7 @@ package client // import "github.com/docker/docker/client"
import (
"context"
"encoding/json"
+ "errors"
"net/url"
"path"
"sort"
@@ -54,6 +55,19 @@ func (cli *Client) ContainerCreate(ctx context.Context, config *container.Config
// When using API under 1.42, the Linux daemon doesn't respect the ConsoleSize
hostConfig.ConsoleSize = [2]uint{0, 0}
}
+ if versions.LessThan(cli.ClientVersion(), "1.44") {
+ for _, m := range hostConfig.Mounts {
+ if m.BindOptions != nil {
+ // ReadOnlyNonRecursive can be safely ignored when API < 1.44
+ if m.BindOptions.ReadOnlyForceRecursive {
+ return response, errors.New("bind-recursive=readonly requires API v1.44 or later")
+ }
+ if m.BindOptions.NonRecursive && versions.LessThan(cli.ClientVersion(), "1.40") {
+ return response, errors.New("bind-recursive=disabled requires API v1.40 or later")
+ }
+ }
+ }
+ }
hostConfig.CapAdd = normalizeCapabilities(hostConfig.CapAdd)
hostConfig.CapDrop = normalizeCapabilities(hostConfig.CapDrop)
diff --git a/vendor/github.com/docker/docker/client/image_inspect.go b/vendor/github.com/docker/docker/client/image_inspect.go
index 11611954675e..644439906e22 100644
--- a/vendor/github.com/docker/docker/client/image_inspect.go
+++ b/vendor/github.com/docker/docker/client/image_inspect.go
@@ -24,6 +24,10 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
}
}
+ if opts.apiOptions.Manifests && opts.apiOptions.Platform != nil {
+ return image.InspectResponse{}, fmt.Errorf("manifests and platform options cannot both be set")
+ }
+
query := url.Values{}
if opts.apiOptions.Manifests {
if err := cli.NewVersionError(ctx, "1.48", "manifests"); err != nil {
@@ -32,6 +36,17 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
query.Set("manifests", "1")
}
+ if opts.apiOptions.Platform != nil {
+ if err := cli.NewVersionError(ctx, "1.48", "platform"); err != nil {
+ return image.InspectResponse{}, err
+ }
+ platform, err := encodePlatform(opts.apiOptions.Platform)
+ if err != nil {
+ return image.InspectResponse{}, err
+ }
+ query.Set("platform", platform)
+ }
+
resp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
diff --git a/vendor/github.com/docker/docker/client/image_inspect_opts.go b/vendor/github.com/docker/docker/client/image_inspect_opts.go
index 2607f36789c6..5589d95748f2 100644
--- a/vendor/github.com/docker/docker/client/image_inspect_opts.go
+++ b/vendor/github.com/docker/docker/client/image_inspect_opts.go
@@ -4,6 +4,7 @@ import (
"bytes"
"github.com/docker/docker/api/types/image"
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// ImageInspectOption is a type representing functional options for the image inspect operation.
@@ -36,6 +37,20 @@ func ImageInspectWithManifests(manifests bool) ImageInspectOption {
})
}
+// ImageInspectWithPlatform sets platform API option for the image inspect operation.
+// This option is only available for API version 1.49 and up.
+// With this option set, the image inspect operation will return information for the
+// specified platform variant of the multi-platform image.
+// If the server is not multi-platform capable, this option will make the
+// server return an error if the actual image platform doesn't match this
+// platform.
+func ImageInspectWithPlatform(platform *ocispec.Platform) ImageInspectOption {
+ return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
+ clientOpts.apiOptions.Platform = platform
+ return nil
+ })
+}
+
// ImageInspectWithAPIOpts sets the API options for the image inspect operation.
func ImageInspectWithAPIOpts(opts image.InspectOptions) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
diff --git a/vendor/github.com/docker/docker/client/service_create.go b/vendor/github.com/docker/docker/client/service_create.go
index fb12dd5b5959..54c03b138948 100644
--- a/vendor/github.com/docker/docker/client/service_create.go
+++ b/vendor/github.com/docker/docker/client/service_create.go
@@ -37,6 +37,11 @@ func (cli *Client) ServiceCreate(ctx context.Context, service swarm.ServiceSpec,
if err := validateServiceSpec(service); err != nil {
return response, err
}
+ if versions.LessThan(cli.version, "1.30") {
+ if err := validateAPIVersion(service, cli.version); err != nil {
+ return response, err
+ }
+ }
// ensure that the image is tagged
var resolveWarning string
@@ -191,3 +196,18 @@ func validateServiceSpec(s swarm.ServiceSpec) error {
}
return nil
}
+
+func validateAPIVersion(c swarm.ServiceSpec, apiVersion string) error {
+ for _, m := range c.TaskTemplate.ContainerSpec.Mounts {
+ if m.BindOptions != nil {
+ if m.BindOptions.NonRecursive && versions.LessThan(apiVersion, "1.40") {
+ return errors.Errorf("bind-recursive=disabled requires API v1.40 or later")
+ }
+ // ReadOnlyNonRecursive can be safely ignored when API < 1.44
+ if m.BindOptions.ReadOnlyForceRecursive && versions.LessThan(apiVersion, "1.44") {
+ return errors.Errorf("bind-recursive=readonly requires API v1.44 or later")
+ }
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive.go b/vendor/github.com/docker/docker/pkg/archive/archive.go
index b05780406b0c..9bbb11c197be 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive.go
@@ -236,11 +236,9 @@ func (r *readCloserWrapper) Close() error {
return nil
}
-var (
- bufioReader32KPool = &sync.Pool{
- New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
- }
-)
+var bufioReader32KPool = &sync.Pool{
+ New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
+}
type bufferedReader struct {
buf *bufio.Reader
@@ -252,17 +250,17 @@ func newBufferedReader(r io.Reader) *bufferedReader {
return &bufferedReader{buf}
}
-func (r *bufferedReader) Read(p []byte) (n int, err error) {
+func (r *bufferedReader) Read(p []byte) (int, error) {
if r.buf == nil {
return 0, io.EOF
}
- n, err = r.buf.Read(p)
+ n, err := r.buf.Read(p)
if err == io.EOF {
r.buf.Reset(nil)
bufioReader32KPool.Put(r.buf)
r.buf = nil
}
- return
+ return n, err
}
func (r *bufferedReader) Peek(n int) ([]byte, error) {
@@ -428,7 +426,7 @@ func ReplaceFileTarWrapper(inputTarStream io.ReadCloser, mods map[string]TarModi
pipeWriter.CloseWithError(err)
return
}
- if _, err := copyWithBuffer(tarWriter, tarReader); err != nil {
+ if err := copyWithBuffer(tarWriter, tarReader); err != nil {
pipeWriter.CloseWithError(err)
return
}
@@ -731,7 +729,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
return err
}
- _, err = copyWithBuffer(ta.TarWriter, file)
+ err = copyWithBuffer(ta.TarWriter, file)
file.Close()
if err != nil {
return err
@@ -778,11 +776,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, o
if err != nil {
return err
}
- if _, err := copyWithBuffer(file, reader); err != nil {
- file.Close()
+ if err := copyWithBuffer(file, reader); err != nil {
+ _ = file.Close()
return err
}
- file.Close()
+ _ = file.Close()
case tar.TypeBlock, tar.TypeChar:
if inUserns { // cannot create devices in a userns
@@ -1438,7 +1436,7 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
if err := tw.WriteHeader(hdr); err != nil {
return err
}
- if _, err := copyWithBuffer(tw, srcF); err != nil {
+ if err := copyWithBuffer(tw, srcF); err != nil {
return err
}
return nil
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
index 631d2e3c5b72..7b6c3e02b643 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_linux.go
@@ -20,7 +20,7 @@ func getWhiteoutConverter(format WhiteoutFormat) tarWhiteoutConverter {
type overlayWhiteoutConverter struct{}
-func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, err error) {
+func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os.FileInfo) (wo *tar.Header, _ error) {
// convert whiteouts to AUFS format
if fi.Mode()&os.ModeCharDevice != 0 && hdr.Devmajor == 0 && hdr.Devminor == 0 {
// we just rename the file and make it normal
@@ -31,38 +31,41 @@ func (overlayWhiteoutConverter) ConvertWrite(hdr *tar.Header, path string, fi os
hdr.Size = 0
}
- if fi.Mode()&os.ModeDir != 0 {
- opaqueXattrName := "trusted.overlay.opaque"
- if userns.RunningInUserNS() {
- opaqueXattrName = "user.overlay.opaque"
- }
+ if fi.Mode()&os.ModeDir == 0 {
+ // FIXME(thaJeztah): return a sentinel error instead of nil, nil
+ return nil, nil
+ }
- // convert opaque dirs to AUFS format by writing an empty file with the prefix
- opaque, err := lgetxattr(path, opaqueXattrName)
- if err != nil {
- return nil, err
- }
- if len(opaque) == 1 && opaque[0] == 'y' {
- delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
-
- // create a header for the whiteout file
- // it should inherit some properties from the parent, but be a regular file
- wo = &tar.Header{
- Typeflag: tar.TypeReg,
- Mode: hdr.Mode & int64(os.ModePerm),
- Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
- Size: 0,
- Uid: hdr.Uid,
- Uname: hdr.Uname,
- Gid: hdr.Gid,
- Gname: hdr.Gname,
- AccessTime: hdr.AccessTime,
- ChangeTime: hdr.ChangeTime,
- }
- }
+ opaqueXattrName := "trusted.overlay.opaque"
+ if userns.RunningInUserNS() {
+ opaqueXattrName = "user.overlay.opaque"
}
- return
+ // convert opaque dirs to AUFS format by writing an empty file with the prefix
+ opaque, err := lgetxattr(path, opaqueXattrName)
+ if err != nil {
+ return nil, err
+ }
+ if len(opaque) != 1 || opaque[0] != 'y' {
+ // FIXME(thaJeztah): return a sentinel error instead of nil, nil
+ return nil, nil
+ }
+ delete(hdr.PAXRecords, paxSchilyXattr+opaqueXattrName)
+
+ // create a header for the whiteout file
+ // it should inherit some properties from the parent, but be a regular file
+ return &tar.Header{
+ Typeflag: tar.TypeReg,
+ Mode: hdr.Mode & int64(os.ModePerm),
+ Name: filepath.Join(hdr.Name, WhiteoutOpaqueDir), // #nosec G305 -- An archive is being created, not extracted.
+ Size: 0,
+ Uid: hdr.Uid,
+ Uname: hdr.Uname,
+ Gid: hdr.Gid,
+ Gname: hdr.Gname,
+ AccessTime: hdr.AccessTime,
+ ChangeTime: hdr.ChangeTime,
+ }, nil
}
func (c overlayWhiteoutConverter) ConvertRead(hdr *tar.Header, path string) (bool, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
index 9c70d1789f12..bc6b25ae07ba 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_unix.go
@@ -73,14 +73,13 @@ func statUnix(fi os.FileInfo, hdr *tar.Header) error {
return nil
}
-func getInodeFromStat(stat interface{}) (inode uint64, err error) {
+func getInodeFromStat(stat interface{}) (uint64, error) {
s, ok := stat.(*syscall.Stat_t)
-
- if ok {
- inode = s.Ino
+ if !ok {
+ // FIXME(thaJeztah): this should likely return an error; see https://github.com/moby/moby/pull/49493#discussion_r1979152897
+ return 0, nil
}
-
- return
+ return s.Ino, nil
}
func getFileUIDGID(stat interface{}) (idtools.Identity, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
index 031608162f9f..fd2546eab73a 100644
--- a/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
+++ b/vendor/github.com/docker/docker/pkg/archive/archive_windows.go
@@ -48,9 +48,9 @@ func setHeaderForSpecialDevice(hdr *tar.Header, name string, stat interface{}) (
return
}
-func getInodeFromStat(stat interface{}) (inode uint64, err error) {
+func getInodeFromStat(stat interface{}) (uint64, error) {
// do nothing. no notion of Inode in stat on Windows
- return
+ return 0, nil
}
// handleTarTypeBlockCharFifo is an OS-specific helper function used by
diff --git a/vendor/github.com/docker/docker/pkg/archive/changes.go b/vendor/github.com/docker/docker/pkg/archive/changes.go
index 79c810a6819b..1c0509d039e2 100644
--- a/vendor/github.com/docker/docker/pkg/archive/changes.go
+++ b/vendor/github.com/docker/docker/pkg/archive/changes.go
@@ -83,7 +83,7 @@ func aufsMetadataSkip(path string) (skip bool, err error) {
if err != nil {
skip = true
}
- return
+ return skip, err
}
func aufsDeletedFile(root, path string, fi os.FileInfo) (string, error) {
diff --git a/vendor/github.com/docker/docker/pkg/archive/copy.go b/vendor/github.com/docker/docker/pkg/archive/copy.go
index cddf18ecdb8b..cae0173def2a 100644
--- a/vendor/github.com/docker/docker/pkg/archive/copy.go
+++ b/vendor/github.com/docker/docker/pkg/archive/copy.go
@@ -25,11 +25,11 @@ var copyPool = sync.Pool{
New: func() interface{} { s := make([]byte, 32*1024); return &s },
}
-func copyWithBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
+func copyWithBuffer(dst io.Writer, src io.Reader) error {
buf := copyPool.Get().(*[]byte)
- written, err = io.CopyBuffer(dst, src, *buf)
+ _, err := io.CopyBuffer(dst, src, *buf)
copyPool.Put(buf)
- return
+ return err
}
// PreserveTrailingDotOrSeparator returns the given cleaned path (after
@@ -105,13 +105,13 @@ func TarResource(sourceInfo CopyInfo) (content io.ReadCloser, err error) {
// TarResourceRebase is like TarResource but renames the first path element of
// items in the resulting tar archive to match the given rebaseName if not "".
-func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, err error) {
+func TarResourceRebase(sourcePath, rebaseName string) (content io.ReadCloser, _ error) {
sourcePath = normalizePath(sourcePath)
- if _, err = os.Lstat(sourcePath); err != nil {
+ if _, err := os.Lstat(sourcePath); err != nil {
// Catches the case where the source does not exist or is not a
// directory if asserted to be a directory, as this also causes an
// error.
- return
+ return nil, err
}
// Separate the source path between its directory and
@@ -442,11 +442,12 @@ func CopyTo(content io.Reader, srcInfo CopyInfo, dstPath string) error {
// whether to follow symbol link or not, if followLink is true, resolvedPath will return
// link target of any symbol link file, else it will only resolve symlink of directory
// but return symbol link file itself without resolving.
-func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, err error) {
+func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseName string, _ error) {
if followLink {
+ var err error
resolvedPath, err = filepath.EvalSymlinks(path)
if err != nil {
- return
+ return "", "", err
}
resolvedPath, rebaseName = GetRebaseName(path, resolvedPath)
@@ -454,10 +455,9 @@ func ResolveHostSourcePath(path string, followLink bool) (resolvedPath, rebaseNa
dirPath, basePath := filepath.Split(path)
// if not follow symbol link, then resolve symbol link of parent dir
- var resolvedDirPath string
- resolvedDirPath, err = filepath.EvalSymlinks(dirPath)
+ resolvedDirPath, err := filepath.EvalSymlinks(dirPath)
if err != nil {
- return
+ return "", "", err
}
// resolvedDirPath will have been cleaned (no trailing path separators) so
// we can manually join it with the base path element.
diff --git a/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go b/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
index 8ce83bd0b50e..5bfdfa2f17e8 100644
--- a/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
+++ b/vendor/github.com/docker/docker/pkg/archive/time_nonwindows.go
@@ -17,12 +17,13 @@ func chtimes(name string, atime time.Time, mtime time.Time) error {
return os.Chtimes(name, atime, mtime)
}
-func timeToTimespec(time time.Time) (ts unix.Timespec) {
+func timeToTimespec(time time.Time) unix.Timespec {
if time.IsZero() {
// Return UTIME_OMIT special value
- ts.Sec = 0
- ts.Nsec = (1 << 30) - 2
- return
+ return unix.Timespec{
+ Sec: 0,
+ Nsec: (1 << 30) - 2,
+ }
}
return unix.NsecToTimespec(time.UnixNano())
}
diff --git a/vendor/github.com/docker/docker/pkg/archive/wrap.go b/vendor/github.com/docker/docker/pkg/archive/wrap.go
index 903befd76301..f8a97254eede 100644
--- a/vendor/github.com/docker/docker/pkg/archive/wrap.go
+++ b/vendor/github.com/docker/docker/pkg/archive/wrap.go
@@ -45,8 +45,8 @@ func Generate(input ...string) (io.Reader, error) {
return buf, nil
}
-func parseStringPairs(input ...string) (output [][2]string) {
- output = make([][2]string, 0, len(input)/2+1)
+func parseStringPairs(input ...string) [][2]string {
+ output := make([][2]string, 0, len(input)/2+1)
for i := 0; i < len(input); i += 2 {
var pair [2]string
pair[0] = input[i]
@@ -55,5 +55,5 @@ func parseStringPairs(input ...string) (output [][2]string) {
}
output = append(output, pair)
}
- return
+ return output
}
diff --git a/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go b/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
index cbbe835bb128..abf46275318c 100644
--- a/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
+++ b/vendor/github.com/docker/docker/pkg/atomicwriter/atomicwriter.go
@@ -11,12 +11,12 @@ import (
// destination path. Writing and closing concurrently is not allowed.
// NOTE: umask is not considered for the file's permissions.
func New(filename string, perm os.FileMode) (io.WriteCloser, error) {
- f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
+ abspath, err := filepath.Abs(filename)
if err != nil {
return nil, err
}
- abspath, err := filepath.Abs(filename)
+ f, err := os.CreateTemp(filepath.Dir(abspath), ".tmp-"+filepath.Base(filename))
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go b/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go
index 8f6e0a737aa6..854e4c371814 100644
--- a/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go
+++ b/vendor/github.com/docker/docker/pkg/stdcopy/stdcopy.go
@@ -43,9 +43,9 @@ type stdWriter struct {
// It inserts the prefix header before the buffer,
// so stdcopy.StdCopy knows where to multiplex the output.
// It makes stdWriter to implement io.Writer.
-func (w *stdWriter) Write(p []byte) (n int, err error) {
+func (w *stdWriter) Write(p []byte) (int, error) {
if w == nil || w.Writer == nil {
- return 0, errors.New("Writer not instantiated")
+ return 0, errors.New("writer not instantiated")
}
if p == nil {
return 0, nil
@@ -57,7 +57,7 @@ func (w *stdWriter) Write(p []byte) (n int, err error) {
buf.Write(header[:])
buf.Write(p)
- n, err = w.Writer.Write(buf.Bytes())
+ n, err := w.Writer.Write(buf.Bytes())
n -= stdWriterPrefixLen
if n < 0 {
n = 0
@@ -65,7 +65,7 @@ func (w *stdWriter) Write(p []byte) (n int, err error) {
buf.Reset()
bufPool.Put(buf)
- return
+ return n, err
}
// NewStdWriter instantiates a new Writer.
diff --git a/vendor/github.com/docker/docker/registry/auth.go b/vendor/github.com/docker/docker/registry/auth.go
index 8c62b83c0759..8f35dfff9c4c 100644
--- a/vendor/github.com/docker/docker/registry/auth.go
+++ b/vendor/github.com/docker/docker/registry/auth.go
@@ -66,13 +66,13 @@ func (scs staticCredentialStore) SetRefreshToken(*url.URL, string, string) {
// loginV2 tries to login to the v2 registry server. The given registry
// endpoint will be pinged to get authorization challenges. These challenges
// will be used to authenticate against the registry to validate credentials.
-func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (status string, token string, _ error) {
+func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (token string, _ error) {
endpointStr := strings.TrimRight(endpoint.URL.String(), "/") + "/v2/"
log.G(context.TODO()).Debugf("attempting v2 login to registry endpoint %s", endpointStr)
req, err := http.NewRequest(http.MethodGet, endpointStr, nil)
if err != nil {
- return "", "", err
+ return "", err
}
var (
@@ -84,22 +84,22 @@ func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent st
loginClient, err := v2AuthHTTPClient(endpoint.URL, authTrans, modifiers, creds, nil)
if err != nil {
- return "", "", err
+ return "", err
}
resp, err := loginClient.Do(req)
if err != nil {
err = translateV2AuthError(err)
- return "", "", err
+ return "", err
}
defer resp.Body.Close()
- if resp.StatusCode == http.StatusOK {
- return "Login Succeeded", credentialAuthConfig.IdentityToken, nil
+ if resp.StatusCode != http.StatusOK {
+ // TODO(dmcgowan): Attempt to further interpret result, status code and error code string
+ return "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
}
- // TODO(dmcgowan): Attempt to further interpret result, status code and error code string
- return "", "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
+ return credentialAuthConfig.IdentityToken, nil
}
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) {
diff --git a/vendor/github.com/docker/docker/registry/config.go b/vendor/github.com/docker/docker/registry/config.go
index f8d94ce80636..433454624a1a 100644
--- a/vendor/github.com/docker/docker/registry/config.go
+++ b/vendor/github.com/docker/docker/registry/config.go
@@ -4,13 +4,17 @@ import (
"context"
"net"
"net/url"
+ "os"
+ "path/filepath"
"strconv"
"strings"
+ "sync"
"github.com/containerd/log"
"github.com/distribution/reference"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/internal/lazyregexp"
+ "github.com/docker/docker/pkg/homedir"
)
// ServiceOptions holds command line options.
@@ -56,26 +60,52 @@ var (
Host: DefaultRegistryHost,
}
- emptyServiceConfig, _ = newServiceConfig(ServiceOptions{})
- validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
+ validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`)
- // certsDir is used to override defaultCertsDir.
- certsDir string
+ // certsDir is used to override defaultCertsDir when running with rootlessKit.
+ //
+ // TODO(thaJeztah): change to a sync.OnceValue once we remove [SetCertsDir]
+ // TODO(thaJeztah): certsDir should not be a package variable, but stored in our config, and passed when needed.
+ setCertsDirOnce sync.Once
+ certsDir string
)
+func setCertsDir(dir string) string {
+ setCertsDirOnce.Do(func() {
+ if dir != "" {
+ certsDir = dir
+ return
+ }
+ if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
+ // Configure registry.CertsDir() when running in rootless-mode
+ // This is the equivalent of [rootless.RunningWithRootlessKit],
+ // but inlining it to prevent adding that as a dependency
+ // for docker/cli.
+ //
+ // [rootless.RunningWithRootlessKit]: https://github.com/moby/moby/blob/b4bdf12daec84caaf809a639f923f7370d4926ad/pkg/rootless/rootless.go#L5-L8
+ if configHome, _ := homedir.GetConfigHome(); configHome != "" {
+ certsDir = filepath.Join(configHome, "docker/certs.d")
+ return
+ }
+ }
+ certsDir = defaultCertsDir
+ })
+ return certsDir
+}
+
// SetCertsDir allows the default certs directory to be changed. This function
// is used at daemon startup to set the correct location when running in
// rootless mode.
+//
+// Deprecated: the cert-directory is now automatically selected when running with rootlessKit, and should no longer be set manually.
func SetCertsDir(path string) {
- certsDir = path
+ setCertsDir(path)
}
// CertsDir is the directory where certificates are stored.
func CertsDir() string {
- if certsDir != "" {
- return certsDir
- }
- return defaultCertsDir
+ // call setCertsDir with an empty path to synchronise with [SetCertsDir]
+ return setCertsDir("")
}
// newServiceConfig returns a new instance of ServiceConfig
@@ -181,7 +211,7 @@ skip:
// Assume `host:port` if not CIDR.
indexConfigs[r] = ®istry.IndexInfo{
Name: r,
- Mirrors: make([]string, 0),
+ Mirrors: []string{},
Secure: false,
Official: false,
}
@@ -288,16 +318,22 @@ func ValidateMirror(val string) (string, error) {
// ValidateIndexName validates an index name. It is used by the daemon to
// validate the daemon configuration.
func ValidateIndexName(val string) (string, error) {
- // TODO: upstream this to check to reference package
- if val == "index.docker.io" {
- val = "docker.io"
- }
+ val = normalizeIndexName(val)
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
return "", invalidParamf("invalid index name (%s). Cannot begin or end with a hyphen", val)
}
return val, nil
}
+func normalizeIndexName(val string) string {
+ // TODO(thaJeztah): consider normalizing other known options, such as "(https://)registry-1.docker.io", "https://index.docker.io/v1/".
+ // TODO: upstream this to check to reference package
+ if val == "index.docker.io" {
+ return "docker.io"
+ }
+ return val
+}
+
func hasScheme(reposName string) bool {
return strings.Contains(reposName, "://")
}
@@ -327,25 +363,20 @@ func validateHostPort(s string) error {
}
// newIndexInfo returns IndexInfo configuration from indexName
-func newIndexInfo(config *serviceConfig, indexName string) (*registry.IndexInfo, error) {
- var err error
- indexName, err = ValidateIndexName(indexName)
- if err != nil {
- return nil, err
- }
+func newIndexInfo(config *serviceConfig, indexName string) *registry.IndexInfo {
+ indexName = normalizeIndexName(indexName)
// Return any configured index info, first.
if index, ok := config.IndexConfigs[indexName]; ok {
- return index, nil
+ return index
}
// Construct a non-configured index info.
return ®istry.IndexInfo{
- Name: indexName,
- Mirrors: make([]string, 0),
- Secure: config.isSecureIndex(indexName),
- Official: false,
- }, nil
+ Name: indexName,
+ Mirrors: []string{},
+ Secure: config.isSecureIndex(indexName),
+ }
}
// GetAuthConfigKey special-cases using the full index address of the official
@@ -358,18 +389,22 @@ func GetAuthConfigKey(index *registry.IndexInfo) string {
}
// newRepositoryInfo validates and breaks down a repository name into a RepositoryInfo
-func newRepositoryInfo(config *serviceConfig, name reference.Named) (*RepositoryInfo, error) {
- index, err := newIndexInfo(config, reference.Domain(name))
- if err != nil {
- return nil, err
+func newRepositoryInfo(config *serviceConfig, name reference.Named) *RepositoryInfo {
+ index := newIndexInfo(config, reference.Domain(name))
+ var officialRepo bool
+ if index.Official {
+ // RepositoryInfo.Official indicates whether the image repository
+ // is an official (docker library official images) repository.
+ //
+ // We only need to check this if the image-repository is on Docker Hub.
+ officialRepo = !strings.ContainsRune(reference.FamiliarName(name), '/')
}
- official := !strings.ContainsRune(reference.FamiliarName(name), '/')
return &RepositoryInfo{
Name: reference.TrimNamed(name),
Index: index,
- Official: official,
- }, nil
+ Official: officialRepo,
+ }
}
// ParseRepositoryInfo performs the breakdown of a repository name into a
@@ -377,5 +412,70 @@ func newRepositoryInfo(config *serviceConfig, name reference.Named) (*Repository
//
// It is used by the Docker cli to interact with registry-related endpoints.
func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
- return newRepositoryInfo(emptyServiceConfig, reposName)
+ indexName := normalizeIndexName(reference.Domain(reposName))
+ if indexName == IndexName {
+ officialRepo := !strings.ContainsRune(reference.FamiliarName(reposName), '/')
+ return &RepositoryInfo{
+ Name: reference.TrimNamed(reposName),
+ Index: ®istry.IndexInfo{
+ Name: IndexName,
+ Mirrors: []string{},
+ Secure: true,
+ Official: true,
+ },
+ Official: officialRepo,
+ }, nil
+ }
+
+ insecure := false
+ if isInsecure(indexName) {
+ insecure = true
+ }
+
+ return &RepositoryInfo{
+ Name: reference.TrimNamed(reposName),
+ Index: ®istry.IndexInfo{
+ Name: indexName,
+ Mirrors: []string{},
+ Secure: !insecure,
+ },
+ }, nil
+}
+
+// isInsecure is used to detect whether a registry domain or IP-address is allowed
+// to use an insecure (non-TLS, or self-signed cert) connection according to the
+// defaults, which allows for insecure connections with registries running on a
+// loopback address ("localhost", "::1/128", "127.0.0.0/8").
+//
+// It is used in situations where we don't have access to the daemon's configuration,
+// for example, when used from the client / CLI.
+func isInsecure(hostNameOrIP string) bool {
+ // Attempt to strip port if present; this also strips brackets for
+ // IPv6 addresses with a port (e.g. "[::1]:5000").
+ //
+ // This is best-effort; we'll continue using the address as-is if it fails.
+ if host, _, err := net.SplitHostPort(hostNameOrIP); err == nil {
+ hostNameOrIP = host
+ }
+ if hostNameOrIP == "127.0.0.1" || hostNameOrIP == "::1" || strings.EqualFold(hostNameOrIP, "localhost") {
+ // Fast path; no need to resolve these, assuming nobody overrides
+ // "localhost" for anything else than a loopback address (sorry, not sorry).
+ return true
+ }
+
+ var addresses []net.IP
+ if ip := net.ParseIP(hostNameOrIP); ip != nil {
+ addresses = append(addresses, ip)
+ } else {
+ // Try to resolve the host's IP-addresses.
+ addrs, _ := lookupIP(hostNameOrIP)
+ addresses = append(addresses, addrs...)
+ }
+
+ for _, addr := range addresses {
+ if addr.IsLoopback() {
+ return true
+ }
+ }
+ return false
}
diff --git a/vendor/github.com/docker/docker/registry/registry.go b/vendor/github.com/docker/docker/registry/registry.go
index 6b079199ddc0..a26f976ceead 100644
--- a/vendor/github.com/docker/docker/registry/registry.go
+++ b/vendor/github.com/docker/docker/registry/registry.go
@@ -8,7 +8,6 @@ import (
"net/http"
"os"
"path/filepath"
- "strings"
"time"
"github.com/containerd/log"
@@ -18,7 +17,14 @@ import (
)
// HostCertsDir returns the config directory for a specific host.
+//
+// Deprecated: this function was only used internally, and will be removed in a future release.
func HostCertsDir(hostname string) string {
+ return hostCertsDir(hostname)
+}
+
+// hostCertsDir returns the config directory for a specific host.
+func hostCertsDir(hostname string) string {
return filepath.Join(CertsDir(), cleanPath(hostname))
}
@@ -26,11 +32,10 @@ func HostCertsDir(hostname string) string {
func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
// PreferredServerCipherSuites should have no effect
tlsConfig := tlsconfig.ServerDefault()
-
tlsConfig.InsecureSkipVerify = !isSecure
- if isSecure && CertsDir() != "" {
- hostDir := HostCertsDir(hostname)
+ if isSecure {
+ hostDir := hostCertsDir(hostname)
log.G(context.TODO()).Debugf("hostDir: %s", hostDir)
if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
return nil, err
@@ -59,7 +64,8 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
}
for _, f := range fs {
- if strings.HasSuffix(f.Name(), ".crt") {
+ switch filepath.Ext(f.Name()) {
+ case ".crt":
if tlsConfig.RootCAs == nil {
systemPool, err := tlsconfig.SystemCertPool()
if err != nil {
@@ -67,17 +73,17 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
}
tlsConfig.RootCAs = systemPool
}
- log.G(context.TODO()).Debugf("crt: %s", filepath.Join(directory, f.Name()))
- data, err := os.ReadFile(filepath.Join(directory, f.Name()))
+ fileName := filepath.Join(directory, f.Name())
+ log.G(context.TODO()).Debugf("crt: %s", fileName)
+ data, err := os.ReadFile(fileName)
if err != nil {
return err
}
tlsConfig.RootCAs.AppendCertsFromPEM(data)
- }
- if strings.HasSuffix(f.Name(), ".cert") {
+ case ".cert":
certName := f.Name()
keyName := certName[:len(certName)-5] + ".key"
- log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, f.Name()))
+ log.G(context.TODO()).Debugf("cert: %s", filepath.Join(directory, certName))
if !hasFile(fs, keyName) {
return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
}
@@ -86,11 +92,10 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
return err
}
tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
- }
- if strings.HasSuffix(f.Name(), ".key") {
+ case ".key":
keyName := f.Name()
certName := keyName[:len(keyName)-4] + ".cert"
- log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, f.Name()))
+ log.G(context.TODO()).Debugf("key: %s", filepath.Join(directory, keyName))
if !hasFile(fs, certName) {
return invalidParamf("missing client certificate %s for key %s", certName, keyName)
}
diff --git a/vendor/github.com/docker/docker/registry/search.go b/vendor/github.com/docker/docker/registry/search.go
index 4ce90f55d4d6..8f4739ac0e7d 100644
--- a/vendor/github.com/docker/docker/registry/search.go
+++ b/vendor/github.com/docker/docker/registry/search.go
@@ -93,12 +93,8 @@ func (s *Service) searchUnfiltered(ctx context.Context, term string, limit int,
// Search is a long-running operation, just lock s.config to avoid block others.
s.mu.RLock()
- index, err := newIndexInfo(s.config, indexName)
+ index := newIndexInfo(s.config, indexName)
s.mu.RUnlock()
-
- if err != nil {
- return nil, err
- }
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
@@ -158,5 +154,24 @@ func splitReposSearchTerm(reposName string) (string, string) {
// for that.
func ParseSearchIndexInfo(reposName string) (*registry.IndexInfo, error) {
indexName, _ := splitReposSearchTerm(reposName)
- return newIndexInfo(emptyServiceConfig, indexName)
+ indexName = normalizeIndexName(indexName)
+ if indexName == IndexName {
+ return ®istry.IndexInfo{
+ Name: IndexName,
+ Mirrors: []string{},
+ Secure: true,
+ Official: true,
+ }, nil
+ }
+
+ insecure := false
+ if isInsecure(indexName) {
+ insecure = true
+ }
+
+ return ®istry.IndexInfo{
+ Name: indexName,
+ Mirrors: []string{},
+ Secure: !insecure,
+ }, nil
}
diff --git a/vendor/github.com/docker/docker/registry/search_session.go b/vendor/github.com/docker/docker/registry/search_session.go
index a0d25c805e81..a3d2c894d2c3 100644
--- a/vendor/github.com/docker/docker/registry/search_session.go
+++ b/vendor/github.com/docker/docker/registry/search_session.go
@@ -83,12 +83,12 @@ type onEOFReader struct {
Fn func()
}
-func (r *onEOFReader) Read(p []byte) (n int, err error) {
- n, err = r.Rc.Read(p)
+func (r *onEOFReader) Read(p []byte) (int, error) {
+ n, err := r.Rc.Read(p)
if err == io.EOF {
r.runFunc()
}
- return
+ return n, err
}
// Close closes the file and run the function.
diff --git a/vendor/github.com/docker/docker/registry/service.go b/vendor/github.com/docker/docker/registry/service.go
index 4d66523c616a..bc67a434517f 100644
--- a/vendor/github.com/docker/docker/registry/service.go
+++ b/vendor/github.com/docker/docker/registry/service.go
@@ -52,7 +52,7 @@ func (s *Service) ReplaceConfig(options ServiceOptions) (commit func(), err erro
// Auth contacts the public registry with the provided credentials,
// and returns OK if authentication was successful.
// It can be used to verify the validity of a client's credentials.
-func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (status, token string, err error) {
+func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, userAgent string) (statusMessage, token string, _ error) {
// TODO Use ctx when searching for repositories
registryHostName := IndexHostname
@@ -77,19 +77,28 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
return "", "", invalidParam(err)
}
+ var lastErr error
for _, endpoint := range endpoints {
- status, token, err = loginV2(authConfig, endpoint, userAgent)
- if err == nil {
- return
- }
- if errdefs.IsUnauthorized(err) {
- // Failed to authenticate; don't continue with (non-TLS) endpoints.
- return status, token, err
+ authToken, err := loginV2(authConfig, endpoint, userAgent)
+ if err != nil {
+ if errdefs.IsUnauthorized(err) {
+ // Failed to authenticate; don't continue with (non-TLS) endpoints.
+ return "", "", err
+ }
+ // Try next endpoint
+ log.G(ctx).WithFields(log.Fields{
+ "error": err,
+ "endpoint": endpoint,
+ }).Infof("Error logging in to endpoint, trying next endpoint")
+ lastErr = err
+ continue
}
- log.G(ctx).WithError(err).Infof("Error logging in to endpoint, trying next endpoint")
+
+ // TODO(thaJeztah): move the statusMessage to the API endpoint; we don't need to produce that here?
+ return "Login Succeeded", authToken, nil
}
- return "", "", err
+ return "", "", lastErr
}
// ResolveRepository splits a repository name into its components
@@ -97,7 +106,8 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use
func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
s.mu.RLock()
defer s.mu.RUnlock()
- return newRepositoryInfo(s.config, name)
+ // TODO(thaJeztah): remove error return as it's no longer used.
+ return newRepositoryInfo(s.config, name), nil
}
// APIEndpoint represents a remote API endpoint
diff --git a/vendor/github.com/docker/docker/registry/types.go b/vendor/github.com/docker/docker/registry/types.go
index 02d7f4f383b7..63ace0fbadf9 100644
--- a/vendor/github.com/docker/docker/registry/types.go
+++ b/vendor/github.com/docker/docker/registry/types.go
@@ -13,6 +13,8 @@ type RepositoryInfo struct {
// Official indicates whether the repository is considered official.
// If the registry is official, and the normalized name does not
// contain a '/' (e.g. "foo"), then it is considered an official repo.
+ //
+ // Deprecated: this field is no longer used and will be removed in the next release. The information captured in this field can be obtained from the [Name] field instead.
Official bool
// Class represents the class of the repository, such as "plugin"
// or "image".
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f2c9398ab0e3..f7b810eace34 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -55,7 +55,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid
-# github.com/docker/docker v28.0.1+incompatible
+# github.com/docker/docker v28.0.1+incompatible => github.com/vvoland/moby v20.10.16-0.20250314163427-17840c8adc30+incompatible
## explicit
github.com/docker/docker/api
github.com/docker/docker/api/types
@@ -548,3 +548,4 @@ gotest.tools/v3/skip
# tags.cncf.io/container-device-interface v0.8.0
## explicit; go 1.20
tags.cncf.io/container-device-interface/pkg/parser
+# github.com/docker/docker => github.com/vvoland/moby v20.10.16-0.20250314163427-17840c8adc30+incompatible