vendor: github.com/docker/docker 8941dcfcc5db4aefc351cd5b5bb4d524823035c0

- updated the default value for `--limit` on `docker search` as the const has been
  removed (added a todo to remove it)
- updated some fixtures to account for `KernelMemoryTCP` no longer being included
  in the output.

full diff: 83b51522df...8941dcfcc5

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2022-03-25 00:11:07 +01:00
parent 5a1e151c02
commit a1e67401d2
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
71 changed files with 803 additions and 934 deletions

View File

@ -3,12 +3,15 @@ package cli
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
pluginmanager "github.com/docker/cli/cli-plugins/manager" pluginmanager "github.com/docker/cli/cli-plugins/manager"
"github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config"
cliflags "github.com/docker/cli/cli/flags" cliflags "github.com/docker/cli/cli/flags"
"github.com/docker/docker/pkg/homedir"
"github.com/docker/docker/registry"
"github.com/moby/term" "github.com/moby/term"
"github.com/morikuni/aec" "github.com/morikuni/aec"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -52,6 +55,13 @@ func setupCommonRootCommand(rootCmd *cobra.Command) (*cliflags.ClientOptions, *p
rootCmd.Annotations = map[string]string{"additionalHelp": "To get more help with docker, check out our guides at https://docs.docker.com/go/guides/"} rootCmd.Annotations = map[string]string{"additionalHelp": "To get more help with docker, check out our guides at https://docs.docker.com/go/guides/"}
// Configure registry.CertsDir() when running in rootless-mode
if os.Getenv("ROOTLESSKIT_STATE_DIR") != "" {
if configHome, err := homedir.GetConfigHome(); err == nil {
registry.SetCertsDir(filepath.Join(configHome, "docker/certs.d"))
}
}
return opts, flags, helpCommand return opts, flags, helpCommand
} }

View File

@ -38,7 +38,8 @@ func NewSearchCommand(dockerCli command.Cli) *cobra.Command {
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output") flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided") flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
flags.IntVar(&options.limit, "limit", registry.DefaultSearchLimit, "Max number of search results") // TODO(thaJeztah) remove default from client as the daemon already has a default
flags.IntVar(&options.limit, "limit", 25, "Max number of search results")
flags.StringVar(&options.format, "format", "", "Pretty-print search using a Go template") flags.StringVar(&options.format, "format", "", "Pretty-print search using a Go template")
return cmd return cmd

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ServerErrors":["a server error occurred"],"ClientInfo":{"Debug":false,"Context":"","Plugins":[],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["foo="],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ServerErrors":["a server error occurred"],"ClientInfo":{"Debug":false,"Context":"","Plugins":[],"Warnings":null}}

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":["WARNING: No memory limit support","WARNING: No swap limit support","WARNING: No oom kill disable support","WARNING: No cpu cfs quota support","WARNING: No cpu cfs period support","WARNING: No cpu shares support","WARNING: No cpuset support","WARNING: IPv4 forwarding is disabled","WARNING: bridge-nf-call-iptables is disabled","WARNING: bridge-nf-call-ip6tables is disabled"],"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}}

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"KernelMemory":false,"KernelMemoryTCP":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"PidsLimit":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":false,"SwapLimit":false,"CpuCfsPeriod":false,"CpuCfsQuota":false,"CPUShares":false,"CPUSet":false,"PidsLimit":false,"IPv4Forwarding":false,"BridgeNfIptables":false,"BridgeNfIp6tables":false,"Debug":true,"NFd":33,"OomKillDisable":false,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}}

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":true,"Context":"default","Plugins":[],"Warnings":null}}

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","ShortDescription":"this plugin has no version","Name":"unversionedplugin","Path":"/path/to/docker-unversionedplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"","NodeAddr":"","LocalNodeState":"inactive","ControlAvailable":false,"Error":"","RemoteManagers":null},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","Version":"0.1.0","ShortDescription":"unit test is good","Name":"goodplugin","Path":"/path/to/docker-goodplugin"},{"SchemaVersion":"0.1.0","Vendor":"ACME Corp","ShortDescription":"this plugin has no version","Name":"unversionedplugin","Path":"/path/to/docker-unversionedplugin"},{"Name":"badplugin","Path":"/path/to/docker-badplugin","Err":"something wrong"}],"Warnings":null}}

View File

@ -1 +1 @@
{"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"KernelMemoryTCP":false,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[],"Warnings":null}} {"ID":"EKHL:QDUU:QZ7U:MKGD:VDXK:S27Q:GIPU:24B7:R7VT:DGN6:QCSF:2UBX","Containers":0,"ContainersRunning":0,"ContainersPaused":0,"ContainersStopped":0,"Images":0,"Driver":"aufs","DriverStatus":[["Root Dir","/var/lib/docker/aufs"],["Backing Filesystem","extfs"],["Dirs","0"],["Dirperm1 Supported","true"]],"Plugins":{"Volume":["local"],"Network":["bridge","host","macvlan","null","overlay"],"Authorization":null,"Log":["awslogs","fluentd","gcplogs","gelf","journald","json-file","logentries","splunk","syslog"]},"MemoryLimit":true,"SwapLimit":true,"KernelMemory":true,"CpuCfsPeriod":true,"CpuCfsQuota":true,"CPUShares":true,"CPUSet":true,"PidsLimit":false,"IPv4Forwarding":true,"BridgeNfIptables":true,"BridgeNfIp6tables":true,"Debug":true,"NFd":33,"OomKillDisable":true,"NGoroutines":135,"SystemTime":"2017-08-24T17:44:34.077811894Z","LoggingDriver":"json-file","CgroupDriver":"cgroupfs","NEventsListener":0,"KernelVersion":"4.4.0-87-generic","OperatingSystem":"Ubuntu 16.04.3 LTS","OSVersion":"","OSType":"linux","Architecture":"x86_64","IndexServerAddress":"https://index.docker.io/v1/","RegistryConfig":{"AllowNondistributableArtifactsCIDRs":null,"AllowNondistributableArtifactsHostnames":null,"InsecureRegistryCIDRs":["127.0.0.0/8"],"IndexConfigs":{"docker.io":{"Name":"docker.io","Mirrors":null,"Secure":true,"Official":true}},"Mirrors":null},"NCPU":2,"MemTotal":2097356800,"GenericResources":null,"DockerRootDir":"/var/lib/docker","HttpProxy":"","HttpsProxy":"","NoProxy":"","Name":"system-sample","Labels":["provider=digitalocean"],"ExperimentalBuild":false,"ServerVersion":"17.06.1-ce","Runtimes":{"runc":{"path":"docker-runc"}},"DefaultRuntime":"runc","Swarm":{"NodeID":"qo2dfdig9mmxqkawulggepdih","NodeAddr":"165.227.107.89","LocalNodeState":"active","ControlAvailable":true,"Error":"","RemoteManagers":[{"NodeID":"qo2dfdig9mmxqkawulggepdih","Addr":"165.227.107.89:2377"}],"Nodes":1,"Managers":1,"Cluster":{"ID":"9vs5ygs0gguyyec4iqf2314c0","Version":{"Index":11},"CreatedAt":"2017-08-24T17:34:19.278062352Z","UpdatedAt":"2017-08-24T17:34:42.398815481Z","Spec":{"Name":"default","Labels":null,"Orchestration":{"TaskHistoryRetentionLimit":5},"Raft":{"SnapshotInterval":10000,"KeepOldSnapshots":0,"LogEntriesForSlowFollowers":500,"ElectionTick":3,"HeartbeatTick":1},"Dispatcher":{"HeartbeatPeriod":5000000000},"CAConfig":{"NodeCertExpiry":7776000000000000},"TaskDefaults":{},"EncryptionConfig":{"AutoLockManagers":true}},"TLSInfo":{"TrustRoot":"\n-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUaFCW5xsq8eyiJ+Pmcv3MCflMLnMwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwODI0MTcyOTAwWhcNMzcwODE5MTcy\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABDy7NebyUJyUjWJDBUdnZoV6GBxEGKO4TZPNDwnxDxJcUdLVaB7WGa4/DLrW\nUfsVgh1JGik2VTiLuTMA1tLlNPOjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBQl16XFtaaXiUAwEuJptJlDjfKskDAKBggqhkjO\nPQQDAgNIADBFAiEAo9fTQNM5DP9bHVcTJYfl2Cay1bFu1E+lnpmN+EYJfeACIGKH\n1pCUkZ+D0IB6CiEZGWSHyLuXPM1rlP+I5KuS7sB8\n-----END CERTIFICATE-----\n","CertIssuerSubject":"MBMxETAPBgNVBAMTCHN3YXJtLWNh","CertIssuerPublicKey":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPLs15vJQnJSNYkMFR2dmhXoYHEQYo7hNk80PCfEPElxR0tVoHtYZrj8MutZR+xWCHUkaKTZVOIu5MwDW0uU08w=="},"RootRotationInProgress":false,"DefaultAddrPool":null,"SubnetSize":0,"DataPathPort":0}},"LiveRestoreEnabled":false,"Isolation":"","InitBinary":"docker-init","ContainerdCommit":{"ID":"6e23458c129b551d5c9871e5174f6b1b7f6d1170","Expected":"6e23458c129b551d5c9871e5174f6b1b7f6d1170"},"RuncCommit":{"ID":"810190ceaa507aa2727d7ae6f4790c76ec150bd2","Expected":"810190ceaa507aa2727d7ae6f4790c76ec150bd2"},"InitCommit":{"ID":"949e6fa","Expected":"949e6fa"},"SecurityOptions":["name=apparmor","name=seccomp,profile=default"],"DefaultAddressPools":[{"Base":"10.123.0.0/16","Size":24}],"Warnings":null,"ClientInfo":{"Debug":false,"Context":"default","Plugins":[],"Warnings":null}}

View File

@ -10,7 +10,7 @@ require (
github.com/containerd/containerd v1.6.2 github.com/containerd/containerd v1.6.2
github.com/creack/pty v1.1.11 github.com/creack/pty v1.1.11
github.com/docker/distribution v2.8.1+incompatible github.com/docker/distribution v2.8.1+incompatible
github.com/docker/docker v20.10.7+incompatible // see "replace" for the actual version github.com/docker/docker v20.10.14+incompatible // see "replace" for the actual version
github.com/docker/docker-credential-helpers v0.6.4 github.com/docker/docker-credential-helpers v0.6.4
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
@ -74,6 +74,6 @@ require (
) )
replace ( replace (
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible // master (v21.xx-dev) github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220326171151-8941dcfcc5db+incompatible // master (v21.xx-dev)
github.com/gogo/googleapis => github.com/gogo/googleapis v1.3.2 github.com/gogo/googleapis => github.com/gogo/googleapis v1.3.2
) )

View File

@ -105,8 +105,8 @@ github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xb
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible h1:bL4hLpxukr5Ls3bzYrn3LCYIwML+XXCktZHaGBIN3og= github.com/docker/docker v20.10.3-0.20220326171151-8941dcfcc5db+incompatible h1:5DYFLB020CbxyjsxBle60QaEUb4krFjr30O0eLXsNp0=
github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.3-0.20220326171151-8941dcfcc5db+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o=
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=

View File

@ -577,19 +577,13 @@ definitions:
type: "array" type: "array"
items: items:
$ref: "#/definitions/DeviceRequest" $ref: "#/definitions/DeviceRequest"
KernelMemory:
description: |
Kernel memory limit in bytes.
<p><br /></p>
> **Deprecated**: This field is deprecated as the kernel 5.4 deprecated
> `kmem.limit_in_bytes`.
type: "integer"
format: "int64"
example: 209715200
KernelMemoryTCP: KernelMemoryTCP:
description: "Hard limit for kernel TCP buffer memory (in bytes)." description: |
Hard limit for kernel TCP buffer memory (in bytes). Depending on the
OCI runtime in use, this option may be ignored. It is no longer supported
by the default (runc) runtime.
This field is omitted when empty.
type: "integer" type: "integer"
format: "int64" format: "int64"
MemoryReservation: MemoryReservation:
@ -1075,8 +1069,9 @@ definitions:
description: "Mount the container's root filesystem as read only." description: "Mount the container's root filesystem as read only."
SecurityOpt: SecurityOpt:
type: "array" type: "array"
description: "A list of string values to customize labels for MLS description: |
systems, such as SELinux." A list of string values to customize labels for MLS systems, such
as SELinux.
items: items:
type: "string" type: "string"
StorageOpt: StorageOpt:
@ -1869,18 +1864,22 @@ definitions:
type: "string" type: "string"
description: "Name of the volume." description: "Name of the volume."
x-nullable: false x-nullable: false
example: "tardis"
Driver: Driver:
type: "string" type: "string"
description: "Name of the volume driver used by the volume." description: "Name of the volume driver used by the volume."
x-nullable: false x-nullable: false
example: "custom"
Mountpoint: Mountpoint:
type: "string" type: "string"
description: "Mount path of the volume on the host." description: "Mount path of the volume on the host."
x-nullable: false x-nullable: false
example: "/var/lib/docker/volumes/tardis"
CreatedAt: CreatedAt:
type: "string" type: "string"
format: "dateTime" format: "dateTime"
description: "Date/Time the volume was created." description: "Date/Time the volume was created."
example: "2016-06-07T20:31:11.853781916Z"
Status: Status:
type: "object" type: "object"
description: | description: |
@ -1892,12 +1891,17 @@ definitions:
does not support this feature. does not support this feature.
additionalProperties: additionalProperties:
type: "object" type: "object"
example:
hello: "world"
Labels: Labels:
type: "object" type: "object"
description: "User-defined key/value metadata." description: "User-defined key/value metadata."
x-nullable: false x-nullable: false
additionalProperties: additionalProperties:
type: "string" type: "string"
example:
com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value"
Scope: Scope:
type: "string" type: "string"
description: | description: |
@ -1906,12 +1910,17 @@ definitions:
default: "local" default: "local"
x-nullable: false x-nullable: false
enum: ["local", "global"] enum: ["local", "global"]
example: "local"
Options: Options:
type: "object" type: "object"
description: | description: |
The driver specific options used when creating the volume. The driver specific options used when creating the volume.
additionalProperties: additionalProperties:
type: "string" type: "string"
example:
device: "tmpfs"
o: "size=100m,uid=1000"
type: "tmpfs"
UsageData: UsageData:
type: "object" type: "object"
x-nullable: true x-nullable: true
@ -1937,17 +1946,43 @@ definitions:
is set to `-1` if the reference-count is not available. is set to `-1` if the reference-count is not available.
x-nullable: false x-nullable: false
VolumeCreateOptions:
description: "Volume configuration"
type: "object"
title: "VolumeConfig"
x-go-name: "VolumeCreateBody"
properties:
Name:
description: |
The new volume's name. If not specified, Docker generates a name.
type: "string"
x-nullable: false
example: "tardis"
Driver:
description: "Name of the volume driver to use."
type: "string"
default: "local"
x-nullable: false
example: "custom"
DriverOpts:
description: |
A mapping of driver options and values. These options are
passed directly to the driver and are driver specific.
type: "object"
additionalProperties:
type: "string"
example: example:
Name: "tardis" device: "tmpfs"
Driver: "custom" o: "size=100m,uid=1000"
Mountpoint: "/var/lib/docker/volumes/tardis" type: "tmpfs"
Status:
hello: "world"
Labels: Labels:
description: "User-defined key/value metadata."
type: "object"
additionalProperties:
type: "string"
example:
com.example.some-label: "some-value" com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value" com.example.some-other-label: "some-other-value"
Scope: "local"
CreatedAt: "2016-06-07T20:31:11.853781916Z"
Network: Network:
type: "object" type: "object"
@ -2035,11 +2070,23 @@ definitions:
``` ```
type: "array" type: "array"
items: items:
$ref: "#/definitions/IPAMConfig"
Options:
description: "Driver-specific options, specified as a map."
type: "object" type: "object"
additionalProperties: additionalProperties:
type: "string" type: "string"
Options:
description: "Driver-specific options, specified as a map." IPAMConfig:
type: "object"
properties:
Subnet:
type: "string"
IPRange:
type: "string"
Gateway:
type: "string"
AuxiliaryAddresses:
type: "object" type: "object"
additionalProperties: additionalProperties:
type: "string" type: "string"
@ -3827,6 +3874,7 @@ definitions:
ServiceSpec: ServiceSpec:
description: "User modifiable configuration for a service." description: "User modifiable configuration for a service."
type: object
properties: properties:
Name: Name:
description: "Name of the service." description: "Name of the service."
@ -4464,6 +4512,29 @@ definitions:
Health: Health:
$ref: "#/definitions/Health" $ref: "#/definitions/Health"
ContainerWaitResponse:
description: "OK response to ContainerWait operation"
type: "object"
x-go-name: "ContainerWaitOKBody"
title: "ContainerWaitResponse"
required: [StatusCode, Error]
properties:
StatusCode:
description: "Exit code of the container"
type: "integer"
x-nullable: false
Error:
$ref: "#/definitions/ContainerWaitExitError"
ContainerWaitExitError:
description: "container waiting error, if any"
type: "object"
x-go-name: "ContainerWaitOKBodyError"
properties:
Message:
description: "Details of an error"
type: "string"
SystemVersion: SystemVersion:
type: "object" type: "object"
description: | description: |
@ -4644,19 +4715,10 @@ definitions:
description: "Indicates if the host has memory swap limit support enabled." description: "Indicates if the host has memory swap limit support enabled."
type: "boolean" type: "boolean"
example: true example: true
KernelMemory:
description: |
Indicates if the host has kernel memory limit support enabled.
<p><br /></p>
> **Deprecated**: This field is deprecated as the kernel 5.4 deprecated
> `kmem.limit_in_bytes`.
type: "boolean"
example: true
KernelMemoryTCP: KernelMemoryTCP:
description: | description: |
Indicates if the host has kernel memory TCP limit support enabled. Indicates if the host has kernel memory TCP limit support enabled. This
field is omitted if not supported.
Kernel memory TCP limits are not supported when using cgroups v2, which Kernel memory TCP limits are not supported when using cgroups v2, which
does not support the corresponding `memory.kmem.tcp.limit_in_bytes` cgroup. does not support the corresponding `memory.kmem.tcp.limit_in_bytes` cgroup.
@ -5369,6 +5431,7 @@ definitions:
PeerNode: PeerNode:
description: "Represents a peer-node in the swarm" description: "Represents a peer-node in the swarm"
type: "object"
properties: properties:
NodeID: NodeID:
description: "Unique identifier of for this node in the swarm." description: "Unique identifier of for this node in the swarm."
@ -5820,7 +5883,6 @@ paths:
Memory: 0 Memory: 0
MemorySwap: 0 MemorySwap: 0
MemoryReservation: 0 MemoryReservation: 0
KernelMemory: 0
NanoCpus: 500000 NanoCpus: 500000
CpuPercent: 80 CpuPercent: 80
CpuShares: 512 CpuShares: 512
@ -6112,7 +6174,6 @@ paths:
Memory: 0 Memory: 0
MemorySwap: 0 MemorySwap: 0
MemoryReservation: 0 MemoryReservation: 0
KernelMemory: 0
OomKillDisable: false OomKillDisable: false
OomScoreAdj: 500 OomScoreAdj: 500
NetworkMode: "bridge" NetworkMode: "bridge"
@ -6857,7 +6918,6 @@ paths:
Memory: 314572800 Memory: 314572800
MemorySwap: 514288000 MemorySwap: 514288000
MemoryReservation: 209715200 MemoryReservation: 209715200
KernelMemory: 52428800
RestartPolicy: RestartPolicy:
MaximumRetryCount: 4 MaximumRetryCount: 4
Name: "on-failure" Name: "on-failure"
@ -7196,22 +7256,7 @@ paths:
200: 200:
description: "The container has exit." description: "The container has exit."
schema: schema:
type: "object" $ref: "#/definitions/ContainerWaitResponse"
title: "ContainerWaitResponse"
description: "OK response to ContainerWait operation"
required: [StatusCode]
properties:
StatusCode:
description: "Exit code of the container"
type: "integer"
x-nullable: false
Error:
description: "container waiting error, if any"
type: "object"
properties:
Message:
description: "Details of an error"
type: "string"
400: 400:
description: "bad parameter" description: "bad parameter"
schema: schema:
@ -7319,17 +7364,7 @@ paths:
400: 400:
description: "Bad parameter" description: "Bad parameter"
schema: schema:
allOf: $ref: "#/definitions/ErrorResponse"
- $ref: "#/definitions/ErrorResponse"
- type: "object"
properties:
message:
description: |
The error message. Either "must specify path parameter"
(path cannot be empty) or "not a directory" (path was
asserted to be a directory but exists as a file).
type: "string"
x-nullable: false
404: 404:
description: "Container or path does not exist" description: "Container or path does not exist"
schema: schema:
@ -7364,17 +7399,7 @@ paths:
400: 400:
description: "Bad parameter" description: "Bad parameter"
schema: schema:
allOf: $ref: "#/definitions/ErrorResponse"
- $ref: "#/definitions/ErrorResponse"
- type: "object"
properties:
message:
description: |
The error message. Either "must specify path parameter"
(path cannot be empty) or "not a directory" (path was
asserted to be a directory but exists as a file).
type: "string"
x-nullable: false
404: 404:
description: "Container or path does not exist" description: "Container or path does not exist"
schema: schema:
@ -7400,7 +7425,10 @@ paths:
tags: ["Container"] tags: ["Container"]
put: put:
summary: "Extract an archive of files or folders to a directory in a container" summary: "Extract an archive of files or folders to a directory in a container"
description: "Upload a tar archive to be extracted to a path in the filesystem of container id." description: |
Upload a tar archive to be extracted to a path in the filesystem of container id.
`path` parameter is asserted to be a directory. If it exists as a file, 400 error
will be returned with message "not a directory".
operationId: "PutContainerArchive" operationId: "PutContainerArchive"
consumes: ["application/x-tar", "application/octet-stream"] consumes: ["application/x-tar", "application/octet-stream"]
responses: responses:
@ -7410,6 +7438,9 @@ paths:
description: "Bad parameter" description: "Bad parameter"
schema: schema:
$ref: "#/definitions/ErrorResponse" $ref: "#/definitions/ErrorResponse"
examples:
application/json:
message: "not a directory"
403: 403:
description: "Permission denied, the volume or container rootfs is marked as read-only." description: "Permission denied, the volume or container rootfs is marked as read-only."
schema: schema:
@ -8346,6 +8377,13 @@ paths:
Docker-Experimental: Docker-Experimental:
type: "boolean" type: "boolean"
description: "If the server is running with experimental mode enabled" description: "If the server is running with experimental mode enabled"
Swarm:
type: "string"
enum: ["inactive", "pending", "error", "locked", "active/worker", "active/manager"]
description: |
Contains information about Swarm status of the daemon,
and if the daemon is acting as a manager or worker node.
default: "inactive"
Cache-Control: Cache-Control:
type: "string" type: "string"
default: "no-cache, no-store, must-revalidate" default: "no-cache, no-store, must-revalidate"
@ -8385,6 +8423,13 @@ paths:
Docker-Experimental: Docker-Experimental:
type: "boolean" type: "boolean"
description: "If the server is running with experimental mode enabled" description: "If the server is running with experimental mode enabled"
Swarm:
type: "string"
enum: ["inactive", "pending", "error", "locked", "active/worker", "active/manager"]
description: |
Contains information about Swarm status of the daemon,
and if the daemon is acting as a manager or worker node.
default: "inactive"
Cache-Control: Cache-Control:
type: "string" type: "string"
default: "no-cache, no-store, must-revalidate" default: "no-cache, no-store, must-revalidate"
@ -9051,23 +9096,6 @@ paths:
Warnings that occurred when fetching the list of volumes. Warnings that occurred when fetching the list of volumes.
items: items:
type: "string" type: "string"
examples:
application/json:
Volumes:
- CreatedAt: "2017-07-19T12:00:26Z"
Name: "tardis"
Driver: "local"
Mountpoint: "/var/lib/docker/volumes/tardis"
Labels:
com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value"
Scope: "local"
Options:
device: "tmpfs"
o: "size=100m,uid=1000"
type: "tmpfs"
Warnings: []
500: 500:
description: "Server error" description: "Server error"
schema: schema:
@ -9112,38 +9140,7 @@ paths:
required: true required: true
description: "Volume configuration" description: "Volume configuration"
schema: schema:
type: "object" $ref: "#/definitions/VolumeCreateOptions"
description: "Volume configuration"
title: "VolumeConfig"
properties:
Name:
description: |
The new volume's name. If not specified, Docker generates a name.
type: "string"
x-nullable: false
Driver:
description: "Name of the volume driver to use."
type: "string"
default: "local"
x-nullable: false
DriverOpts:
description: |
A mapping of driver options and values. These options are
passed directly to the driver and are driver specific.
type: "object"
additionalProperties:
type: "string"
Labels:
description: "User-defined key/value metadata."
type: "object"
additionalProperties:
type: "string"
example:
Name: "tardis"
Labels:
com.example.some-label: "some-value"
com.example.some-other-label: "some-other-value"
Driver: "custom"
tags: ["Volume"] tags: ["Volume"]
/volumes/{name}: /volumes/{name}:

View File

@ -1,28 +0,0 @@
package container // import "github.com/docker/docker/api/types/container"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerWaitOKBodyError container waiting error, if any
// swagger:model ContainerWaitOKBodyError
type ContainerWaitOKBodyError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}
// ContainerWaitOKBody OK response to ContainerWait operation
// swagger:model ContainerWaitOKBody
type ContainerWaitOKBody struct {
// error
// Required: true
Error *ContainerWaitOKBodyError `json:"Error"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

View File

@ -0,0 +1,19 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ContainerWaitOKBody ContainerWaitResponse
//
// OK response to ContainerWait operation
// swagger:model ContainerWaitOKBody
type ContainerWaitOKBody struct {
// error
// Required: true
Error *ContainerWaitOKBodyError `json:"Error"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`
}

View File

@ -0,0 +1,12 @@
package container
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// ContainerWaitOKBodyError container waiting error, if any
// swagger:model ContainerWaitOKBodyError
type ContainerWaitOKBodyError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}

View File

@ -376,8 +376,11 @@ type Resources struct {
Devices []DeviceMapping // List of devices to map inside the container Devices []DeviceMapping // List of devices to map inside the container
DeviceCgroupRules []string // List of rule to be added to the device cgroup DeviceCgroupRules []string // List of rule to be added to the device cgroup
DeviceRequests []DeviceRequest // List of device requests for device drivers DeviceRequests []DeviceRequest // List of device requests for device drivers
KernelMemory int64 // Kernel memory limit (in bytes), Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP int64 // Hard limit for kernel TCP buffer memory (in bytes) // KernelMemory specifies the kernel memory limit (in bytes) for the container.
// Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes.
KernelMemory int64 `json:",omitempty"`
KernelMemoryTCP int64 `json:",omitempty"` // Hard limit for kernel TCP buffer memory (in bytes)
MemoryReservation int64 // Memory soft limit (in bytes) MemoryReservation int64 // Memory soft limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
MemorySwappiness *int64 // Tuning container memory swappiness behaviour MemorySwappiness *int64 // Tuning container memory swappiness behaviour

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/pkg/errors"
) )
// Args stores a mapping of keys to a set of multiple values. // Args stores a mapping of keys to a set of multiple values.
@ -97,7 +98,7 @@ func FromJSON(p string) (Args, error) {
// Fallback to parsing arguments in the legacy slice format // Fallback to parsing arguments in the legacy slice format
deprecated := map[string][]string{} deprecated := map[string][]string{}
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil { if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
return args, err return args, invalidFilter{errors.Wrap(err, "invalid filter")}
} }
args.fields = deprecatedArgs(deprecated) args.fields = deprecatedArgs(deprecated)
@ -247,10 +248,10 @@ func (args Args) Contains(field string) bool {
return ok return ok
} }
type invalidFilter string type invalidFilter struct{ error }
func (e invalidFilter) Error() string { func (e invalidFilter) Error() string {
return "Invalid filter '" + string(e) + "'" return e.error.Error()
} }
func (invalidFilter) InvalidParameter() {} func (invalidFilter) InvalidParameter() {}
@ -260,7 +261,7 @@ func (invalidFilter) InvalidParameter() {}
func (args Args) Validate(accepted map[string]bool) error { func (args Args) Validate(accepted map[string]bool) error {
for name := range args.fields { for name := range args.fields {
if !accepted[name] { if !accepted[name] {
return invalidFilter(name) return invalidFilter{errors.New("invalid filter '" + name + "'")}
} }
} }
return nil return nil

View File

@ -213,6 +213,16 @@ type Info struct {
Warnings []string `json:",omitempty"` Warnings []string `json:",omitempty"`
} }
// Status provides information about the current swarm status and role,
// obtained from the "Swarm" header in the API response.
type Status struct {
// NodeState represents the state of the node.
NodeState LocalNodeState
// ControlAvailable indicates if the node is a swarm manager.
ControlAvailable bool
}
// Peer represents a peer. // Peer represents a peer.
type Peer struct { type Peer struct {
NodeID string NodeID string

View File

@ -188,6 +188,15 @@ type Ping struct {
OSType string OSType string
Experimental bool Experimental bool
BuilderVersion BuilderVersion BuilderVersion BuilderVersion
// SwarmStatus provides information about the current swarm status of the
// engine, obtained from the "Swarm" header in the API response.
//
// It can be a nil struct if the API version does not provide this header
// in the ping response, or if an error occurred, in which case the client
// should use other ways to get the current swarm status, such as the /swarm
// endpoint.
SwarmStatus *swarm.Status
} }
// ComponentVersion describes the version information for a specific component. // ComponentVersion describes the version information for a specific component.
@ -239,8 +248,8 @@ type Info struct {
Plugins PluginsInfo Plugins PluginsInfo
MemoryLimit bool MemoryLimit bool
SwapLimit bool SwapLimit bool
KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes KernelMemory bool `json:",omitempty"` // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
KernelMemoryTCP bool KernelMemoryTCP bool `json:",omitempty"` // KernelMemoryTCP is not supported on cgroups v2.
CPUCfsPeriod bool `json:"CpuCfsPeriod"` CPUCfsPeriod bool `json:"CpuCfsPeriod"`
CPUCfsQuota bool `json:"CpuCfsQuota"` CPUCfsQuota bool `json:"CpuCfsQuota"`
CPUShares bool CPUShares bool

View File

@ -1,31 +0,0 @@
package volume // import "github.com/docker/docker/api/types/volume"
// ----------------------------------------------------------------------------
// Code generated by `swagger generate operation`. DO NOT EDIT.
//
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// VolumeCreateBody Volume configuration
// swagger:model VolumeCreateBody
type VolumeCreateBody struct {
// Name of the volume driver to use.
// Required: true
Driver string `json:"Driver"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
// Required: true
DriverOpts map[string]string `json:"DriverOpts"`
// User-defined key/value metadata.
// Required: true
Labels map[string]string `json:"Labels"`
// The new volume's name. If not specified, Docker generates a name.
//
// Required: true
Name string `json:"Name"`
}

View File

@ -0,0 +1,26 @@
package volume
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
// VolumeCreateBody VolumeConfig
//
// Volume configuration
// swagger:model VolumeCreateBody
type VolumeCreateBody struct {
// Name of the volume driver to use.
Driver string `json:"Driver,omitempty"`
// A mapping of driver options and values. These options are
// passed directly to the driver and are driver specific.
//
DriverOpts map[string]string `json:"DriverOpts,omitempty"`
// User-defined key/value metadata.
Labels map[string]string `json:"Labels,omitempty"`
// The new volume's name. If not specified, Docker generates a name.
//
Name string `json:"Name,omitempty"`
}

View File

@ -20,7 +20,7 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil) resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return checkpoints, wrapResponseError(err, resp, "container", container) return checkpoints, err
} }
err = json.NewDecoder(resp.body).Decode(&checkpoints) err = json.NewDecoder(resp.body).Decode(&checkpoints)

View File

@ -43,7 +43,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"fmt"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
@ -93,15 +92,18 @@ type Client struct {
} }
// CheckRedirect specifies the policy for dealing with redirect responses: // CheckRedirect specifies the policy for dealing with redirect responses:
// If the request is non-GET return `ErrRedirect`. Otherwise use the last response. // If the request is non-GET return ErrRedirect, otherwise use the last response.
// //
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308) in the client . // Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308)
// The Docker client (and by extension docker API client) can be made to send a request // in the client. The Docker client (and by extension docker API client) can be
// like POST /containers//start where what would normally be in the name section of the URL is empty. // made to send a request like POST /containers//start where what would normally
// This triggers an HTTP 301 from the daemon. // be in the name section of the URL is empty. This triggers an HTTP 301 from
// In go 1.8 this 301 will be converted to a GET request, and ends up getting a 404 from the daemon. // the daemon.
// This behavior change manifests in the client in that before the 301 was not followed and //
// the client did not generate an error, but now results in a message like Error response from daemon: page not found. // In go 1.8 this 301 will be converted to a GET request, and ends up getting
// a 404 from the daemon. This behavior change manifests in the client in that
// before, the 301 was not followed and the client did not generate an error,
// but now results in a message like Error response from daemon: page not found.
func CheckRedirect(req *http.Request, via []*http.Request) error { func CheckRedirect(req *http.Request, via []*http.Request) error {
if via[0].Method == http.MethodGet { if via[0].Method == http.MethodGet {
return http.ErrUseLastResponse return http.ErrUseLastResponse
@ -109,13 +111,22 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
return ErrRedirect return ErrRedirect
} }
// NewClientWithOpts initializes a new API client with default values. It takes functors // NewClientWithOpts initializes a new API client with a default HTTPClient, and
// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))` // default API host and version. It also initializes the custom HTTP headers to
// It also initializes the custom http headers to add to each request. // add to each request.
//
// It takes an optional list of Opt functional arguments, which are applied in
// the order they're provided, which allows modifying the defaults when creating
// the client. For example, the following initializes a client that configures
// itself with values from environment variables (client.FromEnv), and has
// automatic API version negotiation enabled (client.WithAPIVersionNegotiation()).
//
//
// cli, err := client.NewClientWithOpts(
// client.FromEnv,
// client.WithAPIVersionNegotiation(),
// )
// //
// It won't send any version information if the version number is empty. It is
// highly recommended that you set a version or your client may break if the
// server is upgraded.
func NewClientWithOpts(ops ...Opt) (*Client, error) { func NewClientWithOpts(ops ...Opt) (*Client, error) {
client, err := defaultHTTPClient(DefaultDockerHost) client, err := defaultHTTPClient(DefaultDockerHost)
if err != nil { if err != nil {
@ -153,12 +164,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
} }
func defaultHTTPClient(host string) (*http.Client, error) { func defaultHTTPClient(host string) (*http.Client, error) {
url, err := ParseHostURL(host) hostURL, err := ParseHostURL(host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
transport := new(http.Transport) transport := &http.Transport{}
sockets.ConfigureTransport(transport, url.Scheme, url.Host) _ = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
return &http.Client{ return &http.Client{
Transport: transport, Transport: transport,
CheckRedirect: CheckRedirect, CheckRedirect: CheckRedirect,
@ -194,11 +205,21 @@ func (cli *Client) ClientVersion() string {
return cli.version return cli.version
} }
// NegotiateAPIVersion queries the API and updates the version to match the // NegotiateAPIVersion queries the API and updates the version to match the API
// API version. Any errors are silently ignored. If a manual override is in place, // version. NegotiateAPIVersion downgrades the client's API version to match the
// either through the `DOCKER_API_VERSION` environment variable, or if the client // APIVersion if the ping version is lower than the default version. If the API
// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation // version reported by the server is higher than the maximum version supported
// will be performed. // by the client, it uses the client's maximum version.
//
// If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, or if the
// client did not get a successful ping response, it assumes it is connected with
// an old daemon that does not support API version negotiation, in which case it
// downgrades to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersion(ctx context.Context) { func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
if !cli.manualOverride { if !cli.manualOverride {
ping, _ := cli.Ping(ctx) ping, _ := cli.Ping(ctx)
@ -206,23 +227,31 @@ func (cli *Client) NegotiateAPIVersion(ctx context.Context) {
} }
} }
// NegotiateAPIVersionPing updates the client version to match the Ping.APIVersion // NegotiateAPIVersionPing downgrades the client's API version to match the
// if the ping version is less than the default version. If a manual override is // APIVersion in the ping response. If the API version in pingResponse is higher
// in place, either through the `DOCKER_API_VERSION` environment variable, or if // than the maximum version supported by the client, it uses the client's maximum
// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no // version.
// negotiation is performed. //
func (cli *Client) NegotiateAPIVersionPing(p types.Ping) { // If a manual override is in place, either through the "DOCKER_API_VERSION"
// (EnvOverrideAPIVersion) environment variable, or if the client is initialized
// with a fixed version (WithVersion(xx)), no negotiation is performed.
//
// If the API server's ping response does not contain an API version, we assume
// we are connected with an old daemon without API version negotiation support,
// and downgrade to the latest version of the API before version negotiation was
// added (1.24).
func (cli *Client) NegotiateAPIVersionPing(pingResponse types.Ping) {
if !cli.manualOverride { if !cli.manualOverride {
cli.negotiateAPIVersionPing(p) cli.negotiateAPIVersionPing(pingResponse)
} }
} }
// negotiateAPIVersionPing queries the API and updates the version to match the // negotiateAPIVersionPing queries the API and updates the version to match the
// API version. Any errors are silently ignored. // API version from the ping response.
func (cli *Client) negotiateAPIVersionPing(p types.Ping) { func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) {
// try the latest version before versioning headers existed // default to the latest version before versioning headers existed
if p.APIVersion == "" { if pingResponse.APIVersion == "" {
p.APIVersion = "1.24" pingResponse.APIVersion = "1.24"
} }
// if the client is not initialized with a version, start with the latest supported version // if the client is not initialized with a version, start with the latest supported version
@ -231,8 +260,8 @@ func (cli *Client) negotiateAPIVersionPing(p types.Ping) {
} }
// if server version is lower than the client version, downgrade // if server version is lower than the client version, downgrade
if versions.LessThan(p.APIVersion, cli.version) { if versions.LessThan(pingResponse.APIVersion, cli.version) {
cli.version = p.APIVersion cli.version = pingResponse.APIVersion
} }
// Store the results, so that automatic API version negotiation (if enabled) // Store the results, so that automatic API version negotiation (if enabled)
@ -258,7 +287,7 @@ func (cli *Client) HTTPClient() *http.Client {
func ParseHostURL(host string) (*url.URL, error) { func ParseHostURL(host string) (*url.URL, error) {
protoAddrParts := strings.SplitN(host, "://", 2) protoAddrParts := strings.SplitN(host, "://", 2)
if len(protoAddrParts) == 1 { if len(protoAddrParts) == 1 {
return nil, fmt.Errorf("unable to parse docker host `%s`", host) return nil, errors.Errorf("unable to parse docker host `%s`", host)
} }
var basePath string var basePath string
@ -278,7 +307,9 @@ func ParseHostURL(host string) (*url.URL, error) {
}, nil }, nil
} }
// Dialer returns a dialer for a raw stream connection, with HTTP/1.1 header, that can be used for proxying the daemon connection. // Dialer returns a dialer for a raw stream connection, with an HTTP/1.1 header,
// that can be used for proxying the daemon connection.
//
// Used by `docker dial-stdio` (docker/cli#889). // Used by `docker dial-stdio` (docker/cli#889).
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) { func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {
return func(ctx context.Context) (net.Conn, error) { return func(ctx context.Context) (net.Conn, error) {

View File

@ -3,7 +3,8 @@
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset // DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "unix:///var/run/docker.sock" const DefaultDockerHost = "unix:///var/run/docker.sock"
const defaultProto = "unix" const defaultProto = "unix"

View File

@ -1,6 +1,7 @@
package client // import "github.com/docker/docker/client" package client // import "github.com/docker/docker/client"
// DefaultDockerHost defines os specific default if DOCKER_HOST is unset // DefaultDockerHost defines OS-specific default host if the DOCKER_HOST
// (EnvOverrideHost) environment variable is unset or empty.
const DefaultDockerHost = "npipe:////./pipe/docker_engine" const DefaultDockerHost = "npipe:////./pipe/docker_engine"
const defaultProto = "npipe" const defaultProto = "npipe"

View File

@ -20,7 +20,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
resp, err := cli.get(ctx, "/configs/"+id, nil, nil) resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id) return swarm.Config{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

View File

@ -9,5 +9,5 @@ func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
} }
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil) resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "config", id) return err
} }

View File

@ -23,7 +23,7 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
response, err := cli.head(ctx, urlStr, query, nil) response, err := cli.head(ctx, urlStr, query, nil)
defer ensureReaderClosed(response) defer ensureReaderClosed(response)
if err != nil { if err != nil {
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path) return types.ContainerPathStat{}, err
} }
return getContainerPathStatFromHeader(response.header) return getContainerPathStatFromHeader(response.header)
} }
@ -47,7 +47,7 @@ func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath str
response, err := cli.putRaw(ctx, apiPath, query, content, nil) response, err := cli.putRaw(ctx, apiPath, query, content, nil)
defer ensureReaderClosed(response) defer ensureReaderClosed(response)
if err != nil { if err != nil {
return wrapResponseError(err, response, "container:path", containerID+":"+dstPath) return err
} }
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior
@ -67,7 +67,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
apiPath := "/containers/" + containerID + "/archive" apiPath := "/containers/" + containerID + "/archive"
response, err := cli.get(ctx, apiPath, query, nil) response, err := cli.get(ctx, apiPath, query, nil)
if err != nil { if err != nil {
return nil, types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+srcPath) return nil, types.ContainerPathStat{}, err
} }
// TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior // TODO this code converts non-error status-codes (e.g., "204 No Content") into an error; verify if this is the desired behavior

View File

@ -18,7 +18,7 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, err
} }
var response types.ContainerJSON var response types.ContainerJSON
@ -38,7 +38,7 @@ func (cli *Client) ContainerInspectWithRaw(ctx context.Context, containerID stri
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil) serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID) return types.ContainerJSON{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

View File

@ -74,7 +74,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil) resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
if err != nil { if err != nil {
return nil, wrapResponseError(err, resp, "container", container) return nil, err
} }
return resp.body, nil return resp.body, nil
} }

View File

@ -23,5 +23,5 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil) resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "container", containerID) return err
} }

90
vendor/github.com/docker/docker/client/envvars.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package client // import "github.com/docker/docker/client"
const (
// EnvOverrideHost is the name of the environment variable that can be used
// to override the default host to connect to (DefaultDockerHost).
//
// This env-var is read by FromEnv and WithHostFromEnv and when set to a
// non-empty value, takes precedence over the default host (which is platform
// specific), or any host already set.
EnvOverrideHost = "DOCKER_HOST"
// EnvOverrideAPIVersion is the name of the environment variable that can
// be used to override the API version to use. Value should be
// formatted as MAJOR.MINOR, for example, "1.19".
//
// This env-var is read by FromEnv and WithVersionFromEnv and when set to a
// non-empty value, takes precedence over API version negotiation.
//
// This environment variable should be used for debugging purposes only, as
// it can set the client to use an incompatible (or invalid) API version.
EnvOverrideAPIVersion = "DOCKER_API_VERSION"
// EnvOverrideCertPath is the name of the environment variable that can be
// used to specify the directory from which to load the TLS certificates
// (ca.pem, cert.pem, key.pem) from. These certificates are used to configure
// the Client for a TCP connection protected by TLS client authentication.
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection. Refer to EnvTLSVerify below to learn how to
// disable verification for testing purposes.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// For local access to the API, it is recommended to connect with the daemon
// using the default local socket connection (on Linux), or the named pipe
// (on Windows).
//
// If you need to access the API of a remote daemon, consider using an SSH
// (ssh://) connection, which is easier to set up, and requires no additional
// configuration if the host is accessible using ssh.
//
// If you cannot use the alternatives above, and you must expose the API over
// a TCP connection, refer to https://docs.docker.com/engine/security/protect-access/
// to learn how to configure the daemon and client to use a TCP connection
// with TLS client authentication. Make sure you know the differences between
// a regular TLS connection and a TLS connection protected by TLS client
// authentication, and verify that the API cannot be accessed by other clients.
EnvOverrideCertPath = "DOCKER_CERT_PATH"
// EnvTLSVerify is the name of the environment variable that can be used to
// enable or disable TLS certificate verification. When set to a non-empty
// value, TLS certificate verification is enabled, and the client is configured
// to use a TLS connection, using certificates from the default directories
// (within `~/.docker`); refer to EnvOverrideCertPath above for additional
// details.
//
// WARNING: Access to the remote API is equivalent to root access to the
// host where the daemon runs. Do not expose the API without protection,
// and only if needed. Make sure you are familiar with the "daemon attack
// surface" (https://docs.docker.com/go/attack-surface/).
//
// Before setting up your client and daemon to use a TCP connection with TLS
// client authentication, consider using one of the alternatives mentioned
// in EnvOverrideCertPath above.
//
// Disabling TLS certificate verification (for testing purposes)
//
// TLS certificate verification is enabled by default if the Client is configured
// to use a TLS connection, and it is highly recommended to keep verification
// enabled to prevent machine-in-the-middle attacks. Refer to the documentation
// at https://docs.docker.com/engine/security/protect-access/ and pages linked
// from that page to learn how to configure the daemon and client to use a
// TCP connection with TLS client authentication enabled.
//
// Set the "DOCKER_TLS_VERIFY" environment to an empty string ("") to
// disable TLS certificate verification. Disabling verification is insecure,
// so should only be done for testing purposes. From the Go documentation
// (https://pkg.go.dev/crypto/tls#Config):
//
// InsecureSkipVerify controls whether a client verifies the server's
// certificate chain and host name. If InsecureSkipVerify is true, crypto/tls
// accepts any certificate presented by the server and any host name in that
// certificate. In this mode, TLS is susceptible to machine-in-the-middle
// attacks unless custom verification is used. This should be used only for
// testing or in combination with VerifyConnection or VerifyPeerCertificate.
EnvTLSVerify = "DOCKER_TLS_VERIFY"
)

View File

@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
import ( import (
"fmt" "fmt"
"net/http"
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
@ -59,19 +58,6 @@ func (e objectNotFoundError) Error() string {
return fmt.Sprintf("Error: No such %s: %s", e.object, e.id) return fmt.Sprintf("Error: No such %s: %s", e.object, e.id)
} }
func wrapResponseError(err error, resp serverResponse, object, id string) error {
switch {
case err == nil:
return nil
case resp.statusCode == http.StatusNotFound:
return objectNotFoundError{object: object, id: id}
case resp.statusCode == http.StatusNotImplemented:
return errdefs.NotImplemented(err)
default:
return err
}
}
// unauthorizedError represents an authorization error in a remote registry. // unauthorizedError represents an authorization error in a remote registry.
type unauthorizedError struct { type unauthorizedError struct {
cause error cause error

View File

@ -17,7 +17,7 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil) serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID) return types.ImageInspect{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

View File

@ -23,7 +23,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil) resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return dels, wrapResponseError(err, resp, "image", imageID) return dels, err
} }
err = json.NewDecoder(resp.body).Decode(&dels) err = json.NewDecoder(resp.body).Decode(&dels)

View File

@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"net/url" "net/url"
"strconv"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
@ -18,7 +18,9 @@ func (cli *Client) ImageSearch(ctx context.Context, term string, options types.I
var results []registry.SearchResult var results []registry.SearchResult
query := url.Values{} query := url.Values{}
query.Set("term", term) query.Set("term", term)
query.Set("limit", fmt.Sprintf("%d", options.Limit)) if options.Limit > 0 {
query.Set("limit", strconv.Itoa(options.Limit))
}
if options.Filters.Len() > 0 { if options.Filters.Len() > 0 {
filterJSON, err := filters.ToJSON(options.Filters) filterJSON, err := filters.ToJSON(options.Filters)

View File

@ -36,7 +36,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
resp, err = cli.get(ctx, "/networks/"+networkID, query, nil) resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return networkResource, nil, wrapResponseError(err, resp, "network", networkID) return networkResource, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

View File

@ -6,5 +6,5 @@ import "context"
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error { func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil) resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "network", networkID) return err
} }

View File

@ -17,7 +17,7 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil) serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID) return swarm.Node{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

View File

@ -16,5 +16,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil) resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "node", nodeID) return err
} }

View File

@ -18,11 +18,18 @@ type Opt func(*Client) error
// FromEnv configures the client with values from environment variables. // FromEnv configures the client with values from environment variables.
// //
// Supported environment variables: // FromEnv uses the following environment variables:
// DOCKER_HOST to set the url to the docker server. //
// DOCKER_API_VERSION to set the version of the API to reach, leave empty for latest. // DOCKER_HOST (EnvOverrideHost) to set the URL to the docker server.
// DOCKER_CERT_PATH to load the TLS certificates from. //
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. // DOCKER_API_VERSION (EnvOverrideAPIVersion) to set the version of the API to
// use, leave empty for latest.
//
// 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).
func FromEnv(c *Client) error { func FromEnv(c *Client) error {
ops := []Opt{ ops := []Opt{
WithTLSClientConfigFromEnv(), WithTLSClientConfigFromEnv(),
@ -75,11 +82,11 @@ func WithHost(host string) Opt {
} }
// WithHostFromEnv overrides the client host with the host specified in the // WithHostFromEnv overrides the client host with the host specified in the
// DOCKER_HOST environment variable. If DOCKER_HOST is not set, the host is // DOCKER_HOST (EnvOverrideHost) environment variable. If DOCKER_HOST is not set,
// not modified. // or set to an empty value, the host is not modified.
func WithHostFromEnv() Opt { func WithHostFromEnv() Opt {
return func(c *Client) error { return func(c *Client) error {
if host := os.Getenv("DOCKER_HOST"); host != "" { if host := os.Getenv(EnvOverrideHost); host != "" {
return WithHost(host)(c) return WithHost(host)(c)
} }
return nil return nil
@ -145,12 +152,16 @@ func WithTLSClientConfig(cacertPath, certPath, keyPath string) Opt {
// settings in the DOCKER_CERT_PATH and DOCKER_TLS_VERIFY environment variables. // settings in the DOCKER_CERT_PATH and DOCKER_TLS_VERIFY environment variables.
// If DOCKER_CERT_PATH is not set or empty, TLS configuration is not modified. // If DOCKER_CERT_PATH is not set or empty, TLS configuration is not modified.
// //
// Supported environment variables: // WithTLSClientConfigFromEnv uses the following environment variables:
// DOCKER_CERT_PATH directory to load the TLS certificates (ca.pem, cert.pem, key.pem) from. //
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default. // 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).
func WithTLSClientConfigFromEnv() Opt { func WithTLSClientConfigFromEnv() Opt {
return func(c *Client) error { return func(c *Client) error {
dockerCertPath := os.Getenv("DOCKER_CERT_PATH") dockerCertPath := os.Getenv(EnvOverrideCertPath)
if dockerCertPath == "" { if dockerCertPath == "" {
return nil return nil
} }
@ -158,7 +169,7 @@ func WithTLSClientConfigFromEnv() Opt {
CAFile: filepath.Join(dockerCertPath, "ca.pem"), CAFile: filepath.Join(dockerCertPath, "ca.pem"),
CertFile: filepath.Join(dockerCertPath, "cert.pem"), CertFile: filepath.Join(dockerCertPath, "cert.pem"),
KeyFile: filepath.Join(dockerCertPath, "key.pem"), KeyFile: filepath.Join(dockerCertPath, "key.pem"),
InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "", InsecureSkipVerify: os.Getenv(EnvTLSVerify) == "",
} }
tlsc, err := tlsconfig.Client(options) tlsc, err := tlsconfig.Client(options)
if err != nil { if err != nil {
@ -190,10 +201,7 @@ func WithVersion(version string) Opt {
// the version is not modified. // the version is not modified.
func WithVersionFromEnv() Opt { func WithVersionFromEnv() Opt {
return func(c *Client) error { return func(c *Client) error {
if version := os.Getenv("DOCKER_API_VERSION"); version != "" { return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c)
return WithVersion(version)(c)
}
return nil
} }
} }

View File

@ -4,8 +4,10 @@ import (
"context" "context"
"net/http" "net/http"
"path" "path"
"strings"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
) )
@ -61,6 +63,13 @@ func parsePingResponse(cli *Client, resp serverResponse) (types.Ping, error) {
if bv := resp.header.Get("Builder-Version"); bv != "" { if bv := resp.header.Get("Builder-Version"); bv != "" {
ping.BuilderVersion = types.BuilderVersion(bv) ping.BuilderVersion = types.BuilderVersion(bv)
} }
if si := resp.header.Get("Swarm"); si != "" {
parts := strings.SplitN(si, "/", 2)
ping.SwarmStatus = &swarm.Status{
NodeState: swarm.LocalNodeState(parts[0]),
ControlAvailable: len(parts) == 2 && parts[1] == "manager",
}
}
err := cli.checkResponseErr(resp) err := cli.checkResponseErr(resp)
return ping, errdefs.FromStatusCode(err, resp.statusCode) return ping, errdefs.FromStatusCode(err, resp.statusCode)
} }

View File

@ -17,7 +17,7 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil) resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return nil, nil, wrapResponseError(err, resp, "plugin", name) return nil, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

View File

@ -25,7 +25,7 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P
resp, err := cli.get(ctx, "/plugins", query, nil) resp, err := cli.get(ctx, "/plugins", query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return plugins, wrapResponseError(err, resp, "plugin", "") return plugins, err
} }
err = json.NewDecoder(resp.body).Decode(&plugins) err = json.NewDecoder(resp.body).Decode(&plugins)

View File

@ -16,5 +16,5 @@ func (cli *Client) PluginRemove(ctx context.Context, name string, options types.
resp, err := cli.delete(ctx, "/plugins/"+name, query, nil) resp, err := cli.delete(ctx, "/plugins/"+name, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "plugin", name) return err
} }

View File

@ -20,7 +20,7 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil) resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id) return swarm.Secret{}, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

View File

@ -9,5 +9,5 @@ func (cli *Client) SecretRemove(ctx context.Context, id string) error {
} }
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil) resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "secret", id) return err
} }

View File

@ -22,7 +22,7 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string,
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil) serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID) return swarm.Service{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

View File

@ -6,5 +6,5 @@ import "context"
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error { func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil) resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "service", serviceID) return err
} }

View File

@ -17,7 +17,7 @@ func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil) serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
defer ensureReaderClosed(serverResp) defer ensureReaderClosed(serverResp)
if err != nil { if err != nil {
return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID) return swarm.Task{}, nil, err
} }
body, err := io.ReadAll(serverResp.body) body, err := io.ReadAll(serverResp.body)

View File

@ -25,7 +25,7 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (t
resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil) resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
if err != nil { if err != nil {
return volume, nil, wrapResponseError(err, resp, "volume", volumeID) return volume, nil, err
} }
body, err := io.ReadAll(resp.body) body, err := io.ReadAll(resp.body)

View File

@ -17,5 +17,5 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool
} }
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil) resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
defer ensureReaderClosed(resp) defer ensureReaderClosed(resp)
return wrapResponseError(err, resp, "volume", volumeID) return err
} }

View File

@ -1,78 +1,11 @@
package errdefs // import "github.com/docker/docker/errdefs" package errdefs // import "github.com/docker/docker/errdefs"
import ( import (
"fmt"
"net/http" "net/http"
containerderrors "github.com/containerd/containerd/errdefs"
"github.com/docker/distribution/registry/api/errcode"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
// GetHTTPErrorStatusCode retrieves status code from error message.
func GetHTTPErrorStatusCode(err error) int {
if err == nil {
logrus.WithFields(logrus.Fields{"error": err}).Error("unexpected HTTP error handling")
return http.StatusInternalServerError
}
var statusCode int
// Stop right there
// Are you sure you should be adding a new error class here? Do one of the existing ones work?
// Note that the below functions are already checking the error causal chain for matches.
switch {
case IsNotFound(err):
statusCode = http.StatusNotFound
case IsInvalidParameter(err):
statusCode = http.StatusBadRequest
case IsConflict(err):
statusCode = http.StatusConflict
case IsUnauthorized(err):
statusCode = http.StatusUnauthorized
case IsUnavailable(err):
statusCode = http.StatusServiceUnavailable
case IsForbidden(err):
statusCode = http.StatusForbidden
case IsNotModified(err):
statusCode = http.StatusNotModified
case IsNotImplemented(err):
statusCode = http.StatusNotImplemented
case IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) || IsCancelled(err):
statusCode = http.StatusInternalServerError
default:
statusCode = statusCodeFromGRPCError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
}
statusCode = statusCodeFromContainerdError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
}
statusCode = statusCodeFromDistributionError(err)
if statusCode != http.StatusInternalServerError {
return statusCode
}
if e, ok := err.(causer); ok {
return GetHTTPErrorStatusCode(e.Cause())
}
logrus.WithFields(logrus.Fields{
"module": "api",
"error_type": fmt.Sprintf("%T", err),
}).Debugf("FIXME: Got an API for which error does not match any expected type!!!: %+v", err)
}
if statusCode == 0 {
statusCode = http.StatusInternalServerError
}
return statusCode
}
// FromStatusCode creates an errdef error, based on the provided HTTP status-code // FromStatusCode creates an errdef error, based on the provided HTTP status-code
func FromStatusCode(err error, statusCode int) error { func FromStatusCode(err error, statusCode int) error {
if err == nil { if err == nil {
@ -118,74 +51,3 @@ func FromStatusCode(err error, statusCode int) error {
} }
return err return err
} }
// statusCodeFromGRPCError returns status code according to gRPC error
func statusCodeFromGRPCError(err error) int {
switch status.Code(err) {
case codes.InvalidArgument: // code 3
return http.StatusBadRequest
case codes.NotFound: // code 5
return http.StatusNotFound
case codes.AlreadyExists: // code 6
return http.StatusConflict
case codes.PermissionDenied: // code 7
return http.StatusForbidden
case codes.FailedPrecondition: // code 9
return http.StatusBadRequest
case codes.Unauthenticated: // code 16
return http.StatusUnauthorized
case codes.OutOfRange: // code 11
return http.StatusBadRequest
case codes.Unimplemented: // code 12
return http.StatusNotImplemented
case codes.Unavailable: // code 14
return http.StatusServiceUnavailable
default:
// codes.Canceled(1)
// codes.Unknown(2)
// codes.DeadlineExceeded(4)
// codes.ResourceExhausted(8)
// codes.Aborted(10)
// codes.Internal(13)
// codes.DataLoss(15)
return http.StatusInternalServerError
}
}
// statusCodeFromDistributionError returns status code according to registry errcode
// code is loosely based on errcode.ServeJSON() in docker/distribution
func statusCodeFromDistributionError(err error) int {
switch errs := err.(type) {
case errcode.Errors:
if len(errs) < 1 {
return http.StatusInternalServerError
}
if _, ok := errs[0].(errcode.ErrorCoder); ok {
return statusCodeFromDistributionError(errs[0])
}
case errcode.ErrorCoder:
return errs.ErrorCode().Descriptor().HTTPStatusCode
}
return http.StatusInternalServerError
}
// statusCodeFromContainerdError returns status code for containerd errors when
// consumed directly (not through gRPC)
func statusCodeFromContainerdError(err error) int {
switch {
case containerderrors.IsInvalidArgument(err):
return http.StatusBadRequest
case containerderrors.IsNotFound(err):
return http.StatusNotFound
case containerderrors.IsAlreadyExists(err):
return http.StatusConflict
case containerderrors.IsFailedPrecondition(err):
return http.StatusPreconditionFailed
case containerderrors.IsUnavailable(err):
return http.StatusServiceUnavailable
case containerderrors.IsNotImplemented(err):
return http.StatusNotImplemented
default:
return http.StatusInternalServerError
}
}

View File

@ -40,8 +40,7 @@ type (
ExcludePatterns []string ExcludePatterns []string
Compression Compression Compression Compression
NoLchown bool NoLchown bool
UIDMaps []idtools.IDMap IDMap idtools.IdentityMapping
GIDMaps []idtools.IDMap
ChownOpts *idtools.Identity ChownOpts *idtools.Identity
IncludeSourceDir bool IncludeSourceDir bool
// WhiteoutFormat is the expected on disk format for whiteout files. // WhiteoutFormat is the expected on disk format for whiteout files.
@ -63,12 +62,12 @@ type (
// mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations. // mappings for untar, an Archiver can be created with maps which will then be passed to Untar operations.
type Archiver struct { type Archiver struct {
Untar func(io.Reader, string, *TarOptions) error Untar func(io.Reader, string, *TarOptions) error
IDMapping *idtools.IdentityMapping IDMapping idtools.IdentityMapping
} }
// NewDefaultArchiver returns a new Archiver without any IdentityMapping // NewDefaultArchiver returns a new Archiver without any IdentityMapping
func NewDefaultArchiver() *Archiver { func NewDefaultArchiver() *Archiver {
return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}} return &Archiver{Untar: Untar}
} }
// breakoutError is used to differentiate errors related to breaking out // breakoutError is used to differentiate errors related to breaking out
@ -534,7 +533,7 @@ type tarAppender struct {
// for hardlink mapping // for hardlink mapping
SeenFiles map[uint64]string SeenFiles map[uint64]string
IdentityMapping *idtools.IdentityMapping IdentityMapping idtools.IdentityMapping
ChownOpts *idtools.Identity ChownOpts *idtools.Identity
// For packing and unpacking whiteout files in the // For packing and unpacking whiteout files in the
@ -544,7 +543,7 @@ type tarAppender struct {
WhiteoutConverter tarWhiteoutConverter WhiteoutConverter tarWhiteoutConverter
} }
func newTarAppender(idMapping *idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender { func newTarAppender(idMapping idtools.IdentityMapping, writer io.Writer, chownOpts *idtools.Identity) *tarAppender {
return &tarAppender{ return &tarAppender{
SeenFiles: make(map[uint64]string), SeenFiles: make(map[uint64]string),
TarWriter: tar.NewWriter(writer), TarWriter: tar.NewWriter(writer),
@ -860,7 +859,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
go func() { go func() {
ta := newTarAppender( ta := newTarAppender(
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps), options.IDMap,
compressWriter, compressWriter,
options.ChownOpts, options.ChownOpts,
) )
@ -1044,8 +1043,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
defer pools.BufioReader32KPool.Put(trBuf) defer pools.BufioReader32KPool.Put(trBuf)
var dirs []*tar.Header var dirs []*tar.Header
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps) rootIDs := options.IDMap.RootPair()
rootIDs := idMapping.RootPair()
whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS) whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
if err != nil { if err != nil {
return err return err
@ -1134,7 +1132,7 @@ loop:
} }
trBuf.Reset(tr) trBuf.Reset(tr)
if err := remapIDs(idMapping, hdr); err != nil { if err := remapIDs(options.IDMap, hdr); err != nil {
return err return err
} }
@ -1221,8 +1219,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
} }
defer archive.Close() defer archive.Close()
options := &TarOptions{ options := &TarOptions{
UIDMaps: archiver.IDMapping.UIDs(), IDMap: archiver.IDMapping,
GIDMaps: archiver.IDMapping.GIDs(),
} }
return archiver.Untar(archive, dst, options) return archiver.Untar(archive, dst, options)
} }
@ -1235,8 +1232,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
} }
defer archive.Close() defer archive.Close()
options := &TarOptions{ options := &TarOptions{
UIDMaps: archiver.IDMapping.UIDs(), IDMap: archiver.IDMapping,
GIDMaps: archiver.IDMapping.GIDs(),
} }
return archiver.Untar(archive, dst, options) return archiver.Untar(archive, dst, options)
} }
@ -1343,11 +1339,11 @@ func (archiver *Archiver) CopyFileWithTar(src, dst string) (err error) {
} }
// IdentityMapping returns the IdentityMapping of the archiver. // IdentityMapping returns the IdentityMapping of the archiver.
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping { func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping {
return archiver.IDMapping return archiver.IDMapping
} }
func remapIDs(idMapping *idtools.IdentityMapping, hdr *tar.Header) error { func remapIDs(idMapping idtools.IdentityMapping, hdr *tar.Header) error {
ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid}) ids, err := idMapping.ToHost(idtools.Identity{UID: hdr.Uid, GID: hdr.Gid})
hdr.Uid, hdr.Gid = ids.UID, ids.GID hdr.Uid, hdr.Gid = ids.UID, ids.GID
return err return err

View File

@ -394,10 +394,10 @@ func ChangesSize(newDir string, changes []Change) int64 {
} }
// ExportChanges produces an Archive from the provided changes, relative to dir. // ExportChanges produces an Archive from the provided changes, relative to dir.
func ExportChanges(dir string, changes []Change, uidMaps, gidMaps []idtools.IDMap) (io.ReadCloser, error) { func ExportChanges(dir string, changes []Change, idMap idtools.IdentityMapping) (io.ReadCloser, error) {
reader, writer := io.Pipe() reader, writer := io.Pipe()
go func() { go func() {
ta := newTarAppender(idtools.NewIDMappingsFromMaps(uidMaps, gidMaps), writer, nil) ta := newTarAppender(idMap, writer, nil)
// this buffer is needed for the duration of this piped stream // this buffer is needed for the duration of this piped stream
defer pools.BufioWriter32KPool.Put(ta.Buffer) defer pools.BufioWriter32KPool.Put(ta.Buffer)

View File

@ -9,7 +9,6 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/pools"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -32,7 +31,6 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
if options.ExcludePatterns == nil { if options.ExcludePatterns == nil {
options.ExcludePatterns = []string{} options.ExcludePatterns = []string{}
} }
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
aufsTempdir := "" aufsTempdir := ""
aufsHardlinks := make(map[string]*tar.Header) aufsHardlinks := make(map[string]*tar.Header)
@ -192,7 +190,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
srcData = tmpFile srcData = tmpFile
} }
if err := remapIDs(idMapping, srcHdr); err != nil { if err := remapIDs(options.IDMap, srcHdr); err != nil {
return 0, err return 0, err
} }

View File

@ -108,70 +108,72 @@ type Identity struct {
SID string SID string
} }
// IdentityMapping contains a mappings of UIDs and GIDs // Chown changes the numeric uid and gid of the named file to id.UID and id.GID.
type IdentityMapping struct { func (id Identity) Chown(name string) error {
uids []IDMap return os.Chown(name, id.UID, id.GID)
gids []IDMap
} }
// NewIDMappingsFromMaps creates a new mapping from two slices // IdentityMapping contains a mappings of UIDs and GIDs.
// Deprecated: this is a temporary shim while transitioning to IDMapping // The zero value represents an empty mapping.
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping { type IdentityMapping struct {
return &IdentityMapping{uids: uids, gids: gids} UIDMaps []IDMap `json:"UIDMaps"`
GIDMaps []IDMap `json:"GIDMaps"`
} }
// RootPair returns a uid and gid pair for the root user. The error is ignored // RootPair returns a uid and gid pair for the root user. The error is ignored
// because a root user always exists, and the defaults are correct when the uid // because a root user always exists, and the defaults are correct when the uid
// and gid maps are empty. // and gid maps are empty.
func (i *IdentityMapping) RootPair() Identity { func (i IdentityMapping) RootPair() Identity {
uid, gid, _ := GetRootUIDGID(i.uids, i.gids) uid, gid, _ := GetRootUIDGID(i.UIDMaps, i.GIDMaps)
return Identity{UID: uid, GID: gid} return Identity{UID: uid, GID: gid}
} }
// ToHost returns the host UID and GID for the container uid, gid. // ToHost returns the host UID and GID for the container uid, gid.
// Remapping is only performed if the ids aren't already the remapped root ids // Remapping is only performed if the ids aren't already the remapped root ids
func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) { func (i IdentityMapping) ToHost(pair Identity) (Identity, error) {
var err error var err error
target := i.RootPair() target := i.RootPair()
if pair.UID != target.UID { if pair.UID != target.UID {
target.UID, err = toHost(pair.UID, i.uids) target.UID, err = toHost(pair.UID, i.UIDMaps)
if err != nil { if err != nil {
return target, err return target, err
} }
} }
if pair.GID != target.GID { if pair.GID != target.GID {
target.GID, err = toHost(pair.GID, i.gids) target.GID, err = toHost(pair.GID, i.GIDMaps)
} }
return target, err return target, err
} }
// ToContainer returns the container UID and GID for the host uid and gid // ToContainer returns the container UID and GID for the host uid and gid
func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) { func (i IdentityMapping) ToContainer(pair Identity) (int, int, error) {
uid, err := toContainer(pair.UID, i.uids) uid, err := toContainer(pair.UID, i.UIDMaps)
if err != nil { if err != nil {
return -1, -1, err return -1, -1, err
} }
gid, err := toContainer(pair.GID, i.gids) gid, err := toContainer(pair.GID, i.GIDMaps)
return uid, gid, err return uid, gid, err
} }
// Empty returns true if there are no id mappings // Empty returns true if there are no id mappings
func (i *IdentityMapping) Empty() bool { func (i IdentityMapping) Empty() bool {
return len(i.uids) == 0 && len(i.gids) == 0 return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
} }
// UIDs return the UID mapping // UIDs returns the mapping for UID.
// TODO: remove this once everything has been refactored to use pairs //
func (i *IdentityMapping) UIDs() []IDMap { // Deprecated: reference the UIDMaps field directly.
return i.uids func (i IdentityMapping) UIDs() []IDMap {
return i.UIDMaps
} }
// GIDs return the UID mapping // GIDs returns the mapping for GID.
// TODO: remove this once everything has been refactored to use pairs //
func (i *IdentityMapping) GIDs() []IDMap { // Deprecated: reference the GIDMaps field directly.
return i.gids func (i IdentityMapping) GIDs() []IDMap {
return i.GIDMaps
} }
func createIDMap(subidRanges ranges) []IDMap { func createIDMap(subidRanges ranges) []IDMap {

View File

@ -240,24 +240,37 @@ func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT
// NewIdentityMapping takes a requested username and // NewIdentityMapping takes a requested username and
// using the data from /etc/sub{uid,gid} ranges, creates the // using the data from /etc/sub{uid,gid} ranges, creates the
// proper uid and gid remapping ranges for that user/group pair // proper uid and gid remapping ranges for that user/group pair
//
// Deprecated: Use LoadIdentityMapping.
func NewIdentityMapping(name string) (*IdentityMapping, error) { func NewIdentityMapping(name string) (*IdentityMapping, error) {
m, err := LoadIdentityMapping(name)
if err != nil {
return nil, err
}
return &m, err
}
// LoadIdentityMapping takes a requested username and
// using the data from /etc/sub{uid,gid} ranges, creates the
// proper uid and gid remapping ranges for that user/group pair
func LoadIdentityMapping(name string) (IdentityMapping, error) {
usr, err := LookupUser(name) usr, err := LookupUser(name)
if err != nil { if err != nil {
return nil, fmt.Errorf("Could not get user for username %s: %v", name, err) return IdentityMapping{}, fmt.Errorf("Could not get user for username %s: %v", name, err)
} }
subuidRanges, err := lookupSubUIDRanges(usr) subuidRanges, err := lookupSubUIDRanges(usr)
if err != nil { if err != nil {
return nil, err return IdentityMapping{}, err
} }
subgidRanges, err := lookupSubGIDRanges(usr) subgidRanges, err := lookupSubGIDRanges(usr)
if err != nil { if err != nil {
return nil, err return IdentityMapping{}, err
} }
return &IdentityMapping{ return IdentityMapping{
uids: subuidRanges, UIDMaps: subuidRanges,
gids: subgidRanges, GIDMaps: subgidRanges,
}, nil }, nil
} }

View File

@ -1,23 +0,0 @@
package system // import "github.com/docker/docker/pkg/system"
import "golang.org/x/sys/windows"
const (
// Deprecated: use github.com/docker/pkg/idtools.SeTakeOwnershipPrivilege
SeTakeOwnershipPrivilege = "SeTakeOwnershipPrivilege"
// Deprecated: use github.com/docker/pkg/idtools.ContainerAdministratorSidString
ContainerAdministratorSidString = "S-1-5-93-2-1"
// Deprecated: use github.com/docker/pkg/idtools.ContainerUserSidString
ContainerUserSidString = "S-1-5-93-2-2"
)
// VER_NT_WORKSTATION, see https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
const verNTWorkstation = 0x00000001 // VER_NT_WORKSTATION
// IsWindowsClient returns true if the SKU is client. It returns false on
// Windows server, or if an error occurred when making the GetVersionExW
// syscall.
func IsWindowsClient() bool {
ver := windows.RtlGetVersion()
return ver != nil && ver.ProductType == verNTWorkstation
}

View File

@ -10,15 +10,13 @@ import (
"github.com/docker/distribution/registry/client/auth/challenge" "github.com/docker/distribution/registry/client/auth/challenge"
"github.com/docker/distribution/registry/client/transport" "github.com/docker/distribution/registry/client/transport"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const ( // AuthClientID is used the ClientID used for the token server
// AuthClientID is used the ClientID used for the token server const AuthClientID = "docker"
AuthClientID = "docker"
)
type loginCredentialStore struct { type loginCredentialStore struct {
authConfig *types.AuthConfig authConfig *types.AuthConfig
@ -65,14 +63,6 @@ func (scs staticCredentialStore) RefreshToken(*url.URL, string) string {
func (scs staticCredentialStore) SetRefreshToken(*url.URL, string, string) { func (scs staticCredentialStore) SetRefreshToken(*url.URL, string, string) {
} }
type fallbackError struct {
err error
}
func (err fallbackError) Error() string {
return err.err.Error()
}
// loginV2 tries to login to the v2 registry server. The given registry // loginV2 tries to login to the v2 registry server. The given registry
// endpoint will be pinged to get authorization challenges. These challenges // endpoint will be pinged to get authorization challenges. These challenges
// will be used to authenticate against the registry to validate credentials. // will be used to authenticate against the registry to validate credentials.
@ -80,7 +70,7 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin
var ( var (
endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/" endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/"
modifiers = Headers(userAgent, nil) modifiers = Headers(userAgent, nil)
authTransport = transport.NewTransport(NewTransport(endpoint.TLSConfig), modifiers...) authTransport = transport.NewTransport(newTransport(endpoint.TLSConfig), modifiers...)
credentialAuthConfig = *authConfig credentialAuthConfig = *authConfig
creds = loginCredentialStore{authConfig: &credentialAuthConfig} creds = loginCredentialStore{authConfig: &credentialAuthConfig}
) )
@ -109,8 +99,7 @@ func loginV2(authConfig *types.AuthConfig, endpoint APIEndpoint, userAgent strin
} }
// TODO(dmcgowan): Attempt to further interpret result, status code and error code string // TODO(dmcgowan): Attempt to further interpret result, status code and error code string
err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode)) return "", "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
return "", "", err
} }
func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) { func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifiers []transport.RequestModifier, creds auth.CredentialStore, scopes []auth.Scope) (*http.Client, error) {
@ -129,10 +118,9 @@ func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifi
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions) tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
basicHandler := auth.NewBasicHandler(creds) basicHandler := auth.NewBasicHandler(creds)
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)) modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
tr := transport.NewTransport(authTransport, modifiers...)
return &http.Client{ return &http.Client{
Transport: tr, Transport: transport.NewTransport(authTransport, modifiers...),
Timeout: 15 * time.Second, Timeout: 15 * time.Second,
}, nil }, nil
} }
@ -146,14 +134,11 @@ func ConvertToHostname(url string) string {
} else if strings.HasPrefix(url, "https://") { } else if strings.HasPrefix(url, "https://") {
stripped = strings.TrimPrefix(url, "https://") stripped = strings.TrimPrefix(url, "https://")
} }
return strings.SplitN(stripped, "/", 2)[0]
nameParts := strings.SplitN(stripped, "/", 2)
return nameParts[0]
} }
// ResolveAuthConfig matches an auth configuration to a server address or a URL // ResolveAuthConfig matches an auth configuration to a server address or a URL
func ResolveAuthConfig(authConfigs map[string]types.AuthConfig, index *registrytypes.IndexInfo) types.AuthConfig { func ResolveAuthConfig(authConfigs map[string]types.AuthConfig, index *registry.IndexInfo) types.AuthConfig {
configKey := GetAuthConfigKey(index) configKey := GetAuthConfigKey(index)
// First try the happy case // First try the happy case
if c, found := authConfigs[configKey]; found || index.Official { if c, found := authConfigs[configKey]; found || index.Official {

View File

@ -1,7 +1,6 @@
package registry // import "github.com/docker/docker/registry" package registry // import "github.com/docker/docker/registry"
import ( import (
"fmt"
"net" "net"
"net/url" "net/url"
"regexp" "regexp"
@ -9,8 +8,7 @@ import (
"strings" "strings"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -22,9 +20,7 @@ type ServiceOptions struct {
} }
// serviceConfig holds daemon configuration for the registry service. // serviceConfig holds daemon configuration for the registry service.
type serviceConfig struct { type serviceConfig registry.ServiceConfig
registrytypes.ServiceConfig
}
// TODO(thaJeztah) both the "index.docker.io" and "registry-1.docker.io" domains // TODO(thaJeztah) both the "index.docker.io" and "registry-1.docker.io" domains
// are here for historic reasons and backward-compatibility. These domains // are here for historic reasons and backward-compatibility. These domains
@ -58,70 +54,92 @@ var (
Host: DefaultRegistryHost, Host: DefaultRegistryHost,
} }
// ErrInvalidRepositoryName is an error returned if the repository name did
// not have the correct form
ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
emptyServiceConfig, _ = newServiceConfig(ServiceOptions{}) emptyServiceConfig, _ = newServiceConfig(ServiceOptions{})
validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`) validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`)
// for mocking in unit tests // for mocking in unit tests
lookupIP = net.LookupIP lookupIP = net.LookupIP
// certsDir is used to override defaultCertsDir.
certsDir string
) )
// 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.
func SetCertsDir(path string) {
certsDir = path
}
// CertsDir is the directory where certificates are stored.
func CertsDir() string {
if certsDir != "" {
return certsDir
}
return defaultCertsDir
}
// newServiceConfig returns a new instance of ServiceConfig // newServiceConfig returns a new instance of ServiceConfig
func newServiceConfig(options ServiceOptions) (*serviceConfig, error) { func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
config := &serviceConfig{ config := &serviceConfig{}
ServiceConfig: registrytypes.ServiceConfig{ if err := config.loadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
InsecureRegistryCIDRs: make([]*registrytypes.NetIPNet, 0),
IndexConfigs: make(map[string]*registrytypes.IndexInfo),
// Hack: Bypass setting the mirrors to IndexConfigs since they are going away
// and Mirrors are only for the official registry anyways.
},
}
if err := config.LoadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
return nil, err return nil, err
} }
if err := config.LoadMirrors(options.Mirrors); err != nil { if err := config.loadMirrors(options.Mirrors); err != nil {
return nil, err return nil, err
} }
if err := config.LoadInsecureRegistries(options.InsecureRegistries); err != nil { if err := config.loadInsecureRegistries(options.InsecureRegistries); err != nil {
return nil, err return nil, err
} }
return config, nil return config, nil
} }
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config. // copy constructs a new ServiceConfig with a copy of the configuration in config.
func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []string) error { func (config *serviceConfig) copy() *registry.ServiceConfig {
cidrs := map[string]*registrytypes.NetIPNet{} ic := make(map[string]*registry.IndexInfo)
for key, value := range config.IndexConfigs {
ic[key] = value
}
return &registry.ServiceConfig{
AllowNondistributableArtifactsCIDRs: append([]*registry.NetIPNet(nil), config.AllowNondistributableArtifactsCIDRs...),
AllowNondistributableArtifactsHostnames: append([]string(nil), config.AllowNondistributableArtifactsHostnames...),
InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...),
IndexConfigs: ic,
Mirrors: append([]string(nil), config.Mirrors...),
}
}
// loadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
func (config *serviceConfig) loadAllowNondistributableArtifacts(registries []string) error {
cidrs := map[string]*registry.NetIPNet{}
hostnames := map[string]bool{} hostnames := map[string]bool{}
for _, r := range registries { for _, r := range registries {
if _, err := ValidateIndexName(r); err != nil { if _, err := ValidateIndexName(r); err != nil {
return err return err
} }
if validateNoScheme(r) != nil { if hasScheme(r) {
return fmt.Errorf("allow-nondistributable-artifacts registry %s should not contain '://'", r) return invalidParamf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
} }
if _, ipnet, err := net.ParseCIDR(r); err == nil { if _, ipnet, err := net.ParseCIDR(r); err == nil {
// Valid CIDR. // Valid CIDR.
cidrs[ipnet.String()] = (*registrytypes.NetIPNet)(ipnet) cidrs[ipnet.String()] = (*registry.NetIPNet)(ipnet)
} else if err := validateHostPort(r); err == nil { } else if err = validateHostPort(r); err == nil {
// Must be `host:port` if not CIDR. // Must be `host:port` if not CIDR.
hostnames[r] = true hostnames[r] = true
} else { } else {
return fmt.Errorf("allow-nondistributable-artifacts registry %s is not valid: %v", r, err) return invalidParamWrapf(err, "allow-nondistributable-artifacts registry %s is not valid", r)
} }
} }
config.AllowNondistributableArtifactsCIDRs = make([]*(registrytypes.NetIPNet), 0) config.AllowNondistributableArtifactsCIDRs = make([]*registry.NetIPNet, 0, len(cidrs))
for _, c := range cidrs { for _, c := range cidrs {
config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c) config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
} }
config.AllowNondistributableArtifactsHostnames = make([]string, 0) config.AllowNondistributableArtifactsHostnames = make([]string, 0, len(hostnames))
for h := range hostnames { for h := range hostnames {
config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h) config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
} }
@ -129,9 +147,9 @@ func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []str
return nil return nil
} }
// LoadMirrors loads mirrors to config, after removing duplicates. // loadMirrors loads mirrors to config, after removing duplicates.
// Returns an error if mirrors contains an invalid mirror. // Returns an error if mirrors contains an invalid mirror.
func (config *serviceConfig) LoadMirrors(mirrors []string) error { func (config *serviceConfig) loadMirrors(mirrors []string) error {
mMap := map[string]struct{}{} mMap := map[string]struct{}{}
unique := []string{} unique := []string{}
@ -149,40 +167,33 @@ func (config *serviceConfig) LoadMirrors(mirrors []string) error {
config.Mirrors = unique config.Mirrors = unique
// Configure public registry since mirrors may have changed. // Configure public registry since mirrors may have changed.
config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{ config.IndexConfigs = map[string]*registry.IndexInfo{
IndexName: {
Name: IndexName, Name: IndexName,
Mirrors: config.Mirrors, Mirrors: unique,
Secure: true, Secure: true,
Official: true, Official: true,
},
} }
return nil return nil
} }
// LoadInsecureRegistries loads insecure registries to config // loadInsecureRegistries loads insecure registries to config
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error { func (config *serviceConfig) loadInsecureRegistries(registries []string) error {
// Localhost is by default considered as an insecure registry // Localhost is by default considered as an insecure registry. This is a
// This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker). // stop-gap for people who are running a private registry on localhost.
//
// TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
// daemon flags on boot2docker?
registries = append(registries, "127.0.0.0/8") registries = append(registries, "127.0.0.0/8")
// Store original InsecureRegistryCIDRs and IndexConfigs var (
// Clean InsecureRegistryCIDRs and IndexConfigs in config, as passed registries has all insecure registry info. insecureRegistryCIDRs = make([]*registry.NetIPNet, 0)
originalCIDRs := config.ServiceConfig.InsecureRegistryCIDRs indexConfigs = make(map[string]*registry.IndexInfo)
originalIndexInfos := config.ServiceConfig.IndexConfigs )
config.ServiceConfig.InsecureRegistryCIDRs = make([]*registrytypes.NetIPNet, 0)
config.ServiceConfig.IndexConfigs = make(map[string]*registrytypes.IndexInfo)
skip: skip:
for _, r := range registries { for _, r := range registries {
// validate insecure registry // validate insecure registry
if _, err := ValidateIndexName(r); err != nil { if _, err := ValidateIndexName(r); err != nil {
// before returning err, roll back to original data
config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
config.ServiceConfig.IndexConfigs = originalIndexInfos
return err return err
} }
if strings.HasPrefix(strings.ToLower(r), "http://") { if strings.HasPrefix(strings.ToLower(r), "http://") {
@ -191,35 +202,27 @@ skip:
} else if strings.HasPrefix(strings.ToLower(r), "https://") { } else if strings.HasPrefix(strings.ToLower(r), "https://") {
logrus.Warnf("insecure registry %s should not contain 'https://' and 'https://' has been removed from the insecure registry config", r) logrus.Warnf("insecure registry %s should not contain 'https://' and 'https://' has been removed from the insecure registry config", r)
r = r[8:] r = r[8:]
} else if validateNoScheme(r) != nil { } else if hasScheme(r) {
// Insecure registry should not contain '://' return invalidParamf("insecure registry %s should not contain '://'", r)
// before returning err, roll back to original data
config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
config.ServiceConfig.IndexConfigs = originalIndexInfos
return fmt.Errorf("insecure registry %s should not contain '://'", r)
} }
// Check if CIDR was passed to --insecure-registry // Check if CIDR was passed to --insecure-registry
_, ipnet, err := net.ParseCIDR(r) _, ipnet, err := net.ParseCIDR(r)
if err == nil { if err == nil {
// Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip. // Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip.
data := (*registrytypes.NetIPNet)(ipnet) data := (*registry.NetIPNet)(ipnet)
for _, value := range config.InsecureRegistryCIDRs { for _, value := range insecureRegistryCIDRs {
if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() { if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() {
continue skip continue skip
} }
} }
// ipnet is not found, add it in config.InsecureRegistryCIDRs // ipnet is not found, add it in config.InsecureRegistryCIDRs
config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, data) insecureRegistryCIDRs = append(insecureRegistryCIDRs, data)
} else { } else {
if err := validateHostPort(r); err != nil { if err := validateHostPort(r); err != nil {
config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs return invalidParamWrapf(err, "insecure registry %s is not valid", r)
config.ServiceConfig.IndexConfigs = originalIndexInfos
return fmt.Errorf("insecure registry %s is not valid: %v", r, err)
} }
// Assume `host:port` if not CIDR. // Assume `host:port` if not CIDR.
config.IndexConfigs[r] = &registrytypes.IndexInfo{ indexConfigs[r] = &registry.IndexInfo{
Name: r, Name: r,
Mirrors: make([]string, 0), Mirrors: make([]string, 0),
Secure: false, Secure: false,
@ -229,12 +232,14 @@ skip:
} }
// Configure public registry. // Configure public registry.
config.IndexConfigs[IndexName] = &registrytypes.IndexInfo{ indexConfigs[IndexName] = &registry.IndexInfo{
Name: IndexName, Name: IndexName,
Mirrors: config.Mirrors, Mirrors: config.Mirrors,
Secure: true, Secure: true,
Official: true, Official: true,
} }
config.InsecureRegistryCIDRs = insecureRegistryCIDRs
config.IndexConfigs = indexConfigs
return nil return nil
} }
@ -248,7 +253,7 @@ skip:
// hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name // hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name
// or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If // or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If
// resolution fails, CIDR matching is not performed. // resolution fails, CIDR matching is not performed.
func allowNondistributableArtifacts(config *serviceConfig, hostname string) bool { func (config *serviceConfig) allowNondistributableArtifacts(hostname string) bool {
for _, h := range config.AllowNondistributableArtifactsHostnames { for _, h := range config.AllowNondistributableArtifactsHostnames {
if h == hostname { if h == hostname {
return true return true
@ -269,7 +274,7 @@ func allowNondistributableArtifacts(config *serviceConfig, hostname string) bool
// or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained // or an IP address. If it is a domain name, then it will be resolved in order to check if the IP is contained
// in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element // in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
// of insecureRegistries. // of insecureRegistries.
func isSecureIndex(config *serviceConfig, indexName string) bool { func (config *serviceConfig) isSecureIndex(indexName string) bool {
// Check for configured index, first. This is needed in case isSecureIndex // Check for configured index, first. This is needed in case isSecureIndex
// is called from anything besides newIndexInfo, in order to honor per-index configurations. // is called from anything besides newIndexInfo, in order to honor per-index configurations.
if index, ok := config.IndexConfigs[indexName]; ok { if index, ok := config.IndexConfigs[indexName]; ok {
@ -282,7 +287,7 @@ func isSecureIndex(config *serviceConfig, indexName string) bool {
// isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is a URL.Host (`host:port` or `host`) // isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is a URL.Host (`host:port` or `host`)
// where the `host` part can be either a domain name or an IP address. If it is a domain name, then it will be // where the `host` part can be either a domain name or an IP address. If it is a domain name, then it will be
// resolved to IP addresses for matching. If resolution fails, false is returned. // resolved to IP addresses for matching. If resolution fails, false is returned.
func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool { func isCIDRMatch(cidrs []*registry.NetIPNet, URLHost string) bool {
host, _, err := net.SplitHostPort(URLHost) host, _, err := net.SplitHostPort(URLHost)
if err != nil { if err != nil {
// Assume URLHost is of the form `host` without the port and go on. // Assume URLHost is of the form `host` without the port and go on.
@ -318,18 +323,18 @@ func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool {
func ValidateMirror(val string) (string, error) { func ValidateMirror(val string) (string, error) {
uri, err := url.Parse(val) uri, err := url.Parse(val)
if err != nil { if err != nil {
return "", fmt.Errorf("invalid mirror: %q is not a valid URI", val) return "", invalidParamWrapf(err, "invalid mirror: %q is not a valid URI", val)
} }
if uri.Scheme != "http" && uri.Scheme != "https" { if uri.Scheme != "http" && uri.Scheme != "https" {
return "", fmt.Errorf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri) return "", invalidParamf("invalid mirror: unsupported scheme %q in %q", uri.Scheme, uri)
} }
if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" { if (uri.Path != "" && uri.Path != "/") || uri.RawQuery != "" || uri.Fragment != "" {
return "", fmt.Errorf("invalid mirror: path, query, or fragment at end of the URI %q", uri) return "", invalidParamf("invalid mirror: path, query, or fragment at end of the URI %q", uri)
} }
if uri.User != nil { if uri.User != nil {
// strip password from output // strip password from output
uri.User = url.UserPassword(uri.User.Username(), "xxxxx") uri.User = url.UserPassword(uri.User.Username(), "xxxxx")
return "", fmt.Errorf("invalid mirror: username/password not allowed in URI %q", uri) return "", invalidParamf("invalid mirror: username/password not allowed in URI %q", uri)
} }
return strings.TrimSuffix(val, "/") + "/", nil return strings.TrimSuffix(val, "/") + "/", nil
} }
@ -341,17 +346,13 @@ func ValidateIndexName(val string) (string, error) {
val = "docker.io" val = "docker.io"
} }
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") { if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") {
return "", fmt.Errorf("invalid index name (%s). Cannot begin or end with a hyphen", val) return "", invalidParamf("invalid index name (%s). Cannot begin or end with a hyphen", val)
} }
return val, nil return val, nil
} }
func validateNoScheme(reposName string) error { func hasScheme(reposName string) bool {
if strings.Contains(reposName, "://") { return strings.Contains(reposName, "://")
// It cannot contain a scheme!
return ErrInvalidRepositoryName
}
return nil
} }
func validateHostPort(s string) error { func validateHostPort(s string) error {
@ -364,7 +365,7 @@ func validateHostPort(s string) error {
// If match against the `host:port` pattern fails, // If match against the `host:port` pattern fails,
// it might be `IPv6:port`, which will be captured by net.ParseIP(host) // it might be `IPv6:port`, which will be captured by net.ParseIP(host)
if !validHostPortRegex.MatchString(s) && net.ParseIP(host) == nil { if !validHostPortRegex.MatchString(s) && net.ParseIP(host) == nil {
return fmt.Errorf("invalid host %q", host) return invalidParamf("invalid host %q", host)
} }
if port != "" { if port != "" {
v, err := strconv.Atoi(port) v, err := strconv.Atoi(port)
@ -372,14 +373,14 @@ func validateHostPort(s string) error {
return err return err
} }
if v < 0 || v > 65535 { if v < 0 || v > 65535 {
return fmt.Errorf("invalid port %q", port) return invalidParamf("invalid port %q", port)
} }
} }
return nil return nil
} }
// newIndexInfo returns IndexInfo configuration from indexName // newIndexInfo returns IndexInfo configuration from indexName
func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.IndexInfo, error) { func newIndexInfo(config *serviceConfig, indexName string) (*registry.IndexInfo, error) {
var err error var err error
indexName, err = ValidateIndexName(indexName) indexName, err = ValidateIndexName(indexName)
if err != nil { if err != nil {
@ -392,18 +393,17 @@ func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.Index
} }
// Construct a non-configured index info. // Construct a non-configured index info.
index := &registrytypes.IndexInfo{ return &registry.IndexInfo{
Name: indexName, Name: indexName,
Mirrors: make([]string, 0), Mirrors: make([]string, 0),
Secure: config.isSecureIndex(indexName),
Official: false, Official: false,
} }, nil
index.Secure = isSecureIndex(config, indexName)
return index, nil
} }
// GetAuthConfigKey special-cases using the full index address of the official // GetAuthConfigKey special-cases using the full index address of the official
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes. // index as the AuthConfig key, and uses the (host)name[:port] for private indexes.
func GetAuthConfigKey(index *registrytypes.IndexInfo) string { func GetAuthConfigKey(index *registry.IndexInfo) string {
if index.Official { if index.Official {
return IndexServer return IndexServer
} }
@ -432,7 +432,12 @@ func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
} }
// ParseSearchIndexInfo will use repository name to get back an indexInfo. // ParseSearchIndexInfo will use repository name to get back an indexInfo.
func ParseSearchIndexInfo(reposName string) (*registrytypes.IndexInfo, error) { //
// TODO(thaJeztah) this function is only used by the CLI, and used to get
// information of the registry (to provide credentials if needed). We should
// move this function (or equivalent) to the CLI, as it's doing too much just
// for that.
func ParseSearchIndexInfo(reposName string) (*registry.IndexInfo, error) {
indexName, _ := splitReposSearchTerm(reposName) indexName, _ := splitReposSearchTerm(reposName)
indexInfo, err := newIndexInfo(emptyServiceConfig, indexName) indexInfo, err := newIndexInfo(emptyServiceConfig, indexName)

View File

@ -3,25 +3,10 @@
package registry // import "github.com/docker/docker/registry" package registry // import "github.com/docker/docker/registry"
import ( // defaultCertsDir is the platform-specific default directory where certificates
"path/filepath" // are stored. On Linux, it may be overridden through certsDir, for example, when
// running in rootless mode.
"github.com/docker/docker/pkg/homedir" const defaultCertsDir = "/etc/docker/certs.d"
"github.com/docker/docker/rootless"
)
// CertsDir is the directory where certificates are stored
func CertsDir() string {
d := "/etc/docker/certs.d"
if rootless.RunningWithRootlessKit() {
configHome, err := homedir.GetConfigHome()
if err == nil {
d = filepath.Join(configHome, "docker/certs.d")
}
}
return d
}
// cleanPath is used to ensure that a directory name is valid on the target // cleanPath is used to ensure that a directory name is valid on the target
// platform. It will be passed in something *similar* to a URL such as // platform. It will be passed in something *similar* to a URL such as

View File

@ -6,10 +6,10 @@ import (
"strings" "strings"
) )
// CertsDir is the directory where certificates are stored // defaultCertsDir is the platform-specific default directory where certificates
func CertsDir() string { // are stored. On Linux, it may be overridden through certsDir, for example, when
return os.Getenv("programdata") + `\docker\certs.d` // running in rootless mode.
} var defaultCertsDir = os.Getenv("programdata") + `\docker\certs.d`
// cleanPath is used to ensure that a directory name is valid on the target // cleanPath is used to ensure that a directory name is valid on the target
// platform. It will be passed in something *similar* to a URL such as // platform. It will be passed in something *similar* to a URL such as

View File

@ -3,27 +3,39 @@ package registry // import "github.com/docker/docker/registry"
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"github.com/docker/distribution/registry/client/transport" "github.com/docker/distribution/registry/client/transport"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// V1Endpoint stores basic information about a V1 registry endpoint. // v1PingResult contains the information returned when pinging a registry. It
type V1Endpoint struct { // indicates the registry's version and whether the registry claims to be a
// standalone registry.
type v1PingResult struct {
// Version is the registry version supplied by the registry in an HTTP
// header
Version string `json:"version"`
// Standalone is set to true if the registry indicates it is a
// standalone registry in the X-Docker-Registry-Standalone
// header
Standalone bool `json:"standalone"`
}
// v1Endpoint stores basic information about a V1 registry endpoint.
type v1Endpoint struct {
client *http.Client client *http.Client
URL *url.URL URL *url.URL
IsSecure bool IsSecure bool
} }
// NewV1Endpoint parses the given address to return a registry endpoint. // newV1Endpoint parses the given address to return a registry endpoint.
// TODO: remove. This is only used by search. // TODO: remove. This is only used by search.
func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders http.Header) (*V1Endpoint, error) { func newV1Endpoint(index *registry.IndexInfo, userAgent string, metaHeaders http.Header) (*v1Endpoint, error) {
tlsConfig, err := newTLSConfig(index.Name, index.Secure) tlsConfig, err := newTLSConfig(index.Name, index.Secure)
if err != nil { if err != nil {
return nil, err return nil, err
@ -42,28 +54,28 @@ func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders
return endpoint, nil return endpoint, nil
} }
func validateEndpoint(endpoint *V1Endpoint) error { func validateEndpoint(endpoint *v1Endpoint) error {
logrus.Debugf("pinging registry endpoint %s", endpoint) logrus.Debugf("pinging registry endpoint %s", endpoint)
// Try HTTPS ping to registry // Try HTTPS ping to registry
endpoint.URL.Scheme = "https" endpoint.URL.Scheme = "https"
if _, err := endpoint.Ping(); err != nil { if _, err := endpoint.ping(); err != nil {
if endpoint.IsSecure { if endpoint.IsSecure {
// If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry` // If registry is secure and HTTPS failed, show user the error and tell them about `--insecure-registry`
// in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP. // in case that's what they need. DO NOT accept unknown CA certificates, and DO NOT fallback to HTTP.
return fmt.Errorf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host) return invalidParamf("invalid registry endpoint %s: %v. If this private registry supports only HTTP or HTTPS with an unknown CA certificate, please add `--insecure-registry %s` to the daemon's arguments. In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag; simply place the CA certificate at /etc/docker/certs.d/%s/ca.crt", endpoint, err, endpoint.URL.Host, endpoint.URL.Host)
} }
// If registry is insecure and HTTPS failed, fallback to HTTP. // If registry is insecure and HTTPS failed, fallback to HTTP.
logrus.Debugf("Error from registry %q marked as insecure: %v. Insecurely falling back to HTTP", endpoint, err) logrus.WithError(err).Debugf("error from registry %q marked as insecure - insecurely falling back to HTTP", endpoint)
endpoint.URL.Scheme = "http" endpoint.URL.Scheme = "http"
var err2 error var err2 error
if _, err2 = endpoint.Ping(); err2 == nil { if _, err2 = endpoint.ping(); err2 == nil {
return nil return nil
} }
return fmt.Errorf("invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2) return invalidParamf("invalid registry endpoint %q. HTTPS attempt: %v. HTTP attempt: %v", endpoint, err, err2)
} }
return nil return nil
@ -72,28 +84,23 @@ func validateEndpoint(endpoint *V1Endpoint) error {
// trimV1Address trims the version off the address and returns the // trimV1Address trims the version off the address and returns the
// trimmed address or an error if there is a non-V1 version. // trimmed address or an error if there is a non-V1 version.
func trimV1Address(address string) (string, error) { func trimV1Address(address string) (string, error) {
var (
chunks []string
apiVersionStr string
)
address = strings.TrimSuffix(address, "/") address = strings.TrimSuffix(address, "/")
chunks = strings.Split(address, "/") chunks := strings.Split(address, "/")
apiVersionStr = chunks[len(chunks)-1] apiVersionStr := chunks[len(chunks)-1]
if apiVersionStr == "v1" { if apiVersionStr == "v1" {
return strings.Join(chunks[:len(chunks)-1], "/"), nil return strings.Join(chunks[:len(chunks)-1], "/"), nil
} }
for k, v := range apiVersions { for k, v := range apiVersions {
if k != APIVersion1 && apiVersionStr == v { if k != APIVersion1 && apiVersionStr == v {
return "", fmt.Errorf("unsupported V1 version path %s", apiVersionStr) return "", invalidParamf("unsupported V1 version path %s", apiVersionStr)
} }
} }
return address, nil return address, nil
} }
func newV1EndpointFromStr(address string, tlsConfig *tls.Config, userAgent string, metaHeaders http.Header) (*V1Endpoint, error) { func newV1EndpointFromStr(address string, tlsConfig *tls.Config, userAgent string, metaHeaders http.Header) (*v1Endpoint, error) {
if !strings.HasPrefix(address, "http://") && !strings.HasPrefix(address, "https://") { if !strings.HasPrefix(address, "http://") && !strings.HasPrefix(address, "https://") {
address = "https://" + address address = "https://" + address
} }
@ -105,69 +112,64 @@ func newV1EndpointFromStr(address string, tlsConfig *tls.Config, userAgent strin
uri, err := url.Parse(address) uri, err := url.Parse(address)
if err != nil { if err != nil {
return nil, err return nil, invalidParam(err)
} }
// TODO(tiborvass): make sure a ConnectTimeout transport is used // TODO(tiborvass): make sure a ConnectTimeout transport is used
tr := NewTransport(tlsConfig) tr := newTransport(tlsConfig)
return &V1Endpoint{ return &v1Endpoint{
IsSecure: tlsConfig == nil || !tlsConfig.InsecureSkipVerify, IsSecure: tlsConfig == nil || !tlsConfig.InsecureSkipVerify,
URL: uri, URL: uri,
client: HTTPClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)), client: httpClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)),
}, nil }, nil
} }
// Get the formatted URL for the root of this registry Endpoint // Get the formatted URL for the root of this registry Endpoint
func (e *V1Endpoint) String() string { func (e *v1Endpoint) String() string {
return e.URL.String() + "/v1/" return e.URL.String() + "/v1/"
} }
// Path returns a formatted string for the URL // ping returns a v1PingResult which indicates whether the registry is standalone or not.
// of this endpoint with the given path appended. func (e *v1Endpoint) ping() (v1PingResult, error) {
func (e *V1Endpoint) Path(path string) string {
return e.URL.String() + "/v1/" + path
}
// Ping returns a PingResult which indicates whether the registry is standalone or not.
func (e *V1Endpoint) Ping() (PingResult, error) {
if e.String() == IndexServer { if e.String() == IndexServer {
// Skip the check, we know this one is valid // Skip the check, we know this one is valid
// (and we never want to fallback to http in case of error) // (and we never want to fallback to http in case of error)
return PingResult{}, nil return v1PingResult{}, nil
} }
logrus.Debugf("attempting v1 ping for registry endpoint %s", e) logrus.Debugf("attempting v1 ping for registry endpoint %s", e)
req, err := http.NewRequest(http.MethodGet, e.Path("_ping"), nil) pingURL := e.String() + "_ping"
req, err := http.NewRequest(http.MethodGet, pingURL, nil)
if err != nil { if err != nil {
return PingResult{}, err return v1PingResult{}, invalidParam(err)
} }
resp, err := e.client.Do(req) resp, err := e.client.Do(req)
if err != nil { if err != nil {
return PingResult{}, err return v1PingResult{}, invalidParam(err)
} }
defer resp.Body.Close() defer resp.Body.Close()
jsonString, err := io.ReadAll(resp.Body) jsonString, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return PingResult{}, fmt.Errorf("error while reading the http response: %s", err) return v1PingResult{}, invalidParamWrapf(err, "error while reading response from %s", pingURL)
} }
// If the header is absent, we assume true for compatibility with earlier // If the header is absent, we assume true for compatibility with earlier
// versions of the registry. default to true // versions of the registry. default to true
info := PingResult{ info := v1PingResult{
Standalone: true, Standalone: true,
} }
if err := json.Unmarshal(jsonString, &info); err != nil { if err := json.Unmarshal(jsonString, &info); err != nil {
logrus.Debugf("Error unmarshaling the _ping PingResult: %s", err) logrus.WithError(err).Debug("error unmarshaling _ping response")
// don't stop here. Just assume sane defaults // don't stop here. Just assume sane defaults
} }
if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" { if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" {
info.Version = hdr info.Version = hdr
} }
logrus.Debugf("PingResult.Version: %q", info.Version) logrus.Debugf("v1PingResult.Version: %q", info.Version)
standalone := resp.Header.Get("X-Docker-Registry-Standalone") standalone := resp.Header.Get("X-Docker-Registry-Standalone")
@ -178,6 +180,6 @@ func (e *V1Endpoint) Ping() (PingResult, error) {
// there is a header set, and it is not "true" or "1", so assume fails // there is a header set, and it is not "true" or "1", so assume fails
info.Standalone = false info.Standalone = false
} }
logrus.Debugf("PingResult.Standalone: %t", info.Standalone) logrus.Debugf("v1PingResult.Standalone: %t", info.Standalone)
return info, nil return info, nil
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/docker/distribution/registry/api/errcode" "github.com/docker/distribution/registry/api/errcode"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/pkg/errors"
) )
func translateV2AuthError(err error) error { func translateV2AuthError(err error) error {
@ -21,3 +22,15 @@ func translateV2AuthError(err error) error {
return err return err
} }
func invalidParam(err error) error {
return errdefs.InvalidParameter(err)
}
func invalidParamf(format string, args ...interface{}) error {
return errdefs.InvalidParameter(errors.Errorf(format, args...))
}
func invalidParamWrapf(err error, format string, args ...interface{}) error {
return errdefs.InvalidParameter(errors.Wrapf(err, format, args...))
}

View File

@ -3,7 +3,6 @@ package registry // import "github.com/docker/docker/registry"
import ( import (
"crypto/tls" "crypto/tls"
"fmt"
"net" "net"
"net/http" "net/http"
"os" "os"
@ -16,15 +15,12 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// HostCertsDir returns the config directory for a specific host // HostCertsDir returns the config directory for a specific host.
func HostCertsDir(hostname string) (string, error) { func HostCertsDir(hostname string) string {
certsDir := CertsDir() return filepath.Join(CertsDir(), cleanPath(hostname))
hostDir := filepath.Join(certsDir, cleanPath(hostname))
return hostDir, nil
} }
// newTLSConfig constructs a client TLS configuration based on server defaults
func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) { func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
// PreferredServerCipherSuites should have no effect // PreferredServerCipherSuites should have no effect
tlsConfig := tlsconfig.ServerDefault() tlsConfig := tlsconfig.ServerDefault()
@ -32,11 +28,7 @@ func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
tlsConfig.InsecureSkipVerify = !isSecure tlsConfig.InsecureSkipVerify = !isSecure
if isSecure && CertsDir() != "" { if isSecure && CertsDir() != "" {
hostDir, err := HostCertsDir(hostname) hostDir := HostCertsDir(hostname)
if err != nil {
return nil, err
}
logrus.Debugf("hostDir: %s", hostDir) logrus.Debugf("hostDir: %s", hostDir)
if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil { if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
return nil, err return nil, err
@ -61,7 +53,7 @@ func hasFile(files []os.DirEntry, name string) bool {
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error { func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
fs, err := os.ReadDir(directory) fs, err := os.ReadDir(directory)
if err != nil && !os.IsNotExist(err) { if err != nil && !os.IsNotExist(err) {
return err return invalidParam(err)
} }
for _, f := range fs { for _, f := range fs {
@ -69,7 +61,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
if tlsConfig.RootCAs == nil { if tlsConfig.RootCAs == nil {
systemPool, err := tlsconfig.SystemCertPool() systemPool, err := tlsconfig.SystemCertPool()
if err != nil { if err != nil {
return fmt.Errorf("unable to get system cert pool: %v", err) return invalidParamWrapf(err, "unable to get system cert pool")
} }
tlsConfig.RootCAs = systemPool tlsConfig.RootCAs = systemPool
} }
@ -85,7 +77,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
keyName := certName[:len(certName)-5] + ".key" keyName := certName[:len(certName)-5] + ".key"
logrus.Debugf("cert: %s", filepath.Join(directory, f.Name())) logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
if !hasFile(fs, keyName) { if !hasFile(fs, keyName) {
return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName) return invalidParamf("missing key %s for client certificate %s. CA certificates must use the extension .crt", keyName, certName)
} }
cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName)) cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
if err != nil { if err != nil {
@ -98,7 +90,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
certName := keyName[:len(keyName)-4] + ".cert" certName := keyName[:len(keyName)-4] + ".cert"
logrus.Debugf("key: %s", filepath.Join(directory, f.Name())) logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
if !hasFile(fs, certName) { if !hasFile(fs, certName) {
return fmt.Errorf("Missing client certificate %s for key %s", certName, keyName) return invalidParamf("missing client certificate %s for key %s", certName, keyName)
} }
} }
} }
@ -120,9 +112,9 @@ func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModif
return modifiers return modifiers
} }
// HTTPClient returns an HTTP client structure which uses the given transport // httpClient returns an HTTP client structure which uses the given transport
// and contains the necessary headers for redirected requests // and contains the necessary headers for redirected requests
func HTTPClient(transport http.RoundTripper) *http.Client { func httpClient(transport http.RoundTripper) *http.Client {
return &http.Client{ return &http.Client{
Transport: transport, Transport: transport,
CheckRedirect: addRequiredHeadersToRedirectedRequests, CheckRedirect: addRequiredHeadersToRedirectedRequests,
@ -165,9 +157,9 @@ func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Reque
return nil return nil
} }
// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the // newTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
// default TLS configuration. // default TLS configuration.
func NewTransport(tlsConfig *tls.Config) *http.Transport { func newTransport(tlsConfig *tls.Config) *http.Transport {
if tlsConfig == nil { if tlsConfig == nil {
tlsConfig = tlsconfig.ServerDefault() tlsConfig = tlsconfig.ServerDefault()
} }
@ -177,7 +169,7 @@ func NewTransport(tlsConfig *tls.Config) *http.Transport {
KeepAlive: 30 * time.Second, KeepAlive: 30 * time.Second,
} }
base := &http.Transport{ return &http.Transport{
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
DialContext: direct.DialContext, DialContext: direct.DialContext,
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
@ -185,6 +177,4 @@ func NewTransport(tlsConfig *tls.Config) *http.Transport {
// TODO(dmcgowan): Call close idle connections when complete and use keep alive // TODO(dmcgowan): Call close idle connections when complete and use keep alive
DisableKeepAlives: true, DisableKeepAlives: true,
} }
return base
} }

View File

@ -11,102 +11,74 @@ import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/distribution/registry/client/auth" "github.com/docker/distribution/registry/client/auth"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
// DefaultSearchLimit is the default value for maximum number of returned search results.
DefaultSearchLimit = 25
)
// Service is the interface defining what a registry service should implement. // Service is the interface defining what a registry service should implement.
type Service interface { type Service interface {
Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error) Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error)
LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error)
LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error)
ResolveRepository(name reference.Named) (*RepositoryInfo, error) ResolveRepository(name reference.Named) (*RepositoryInfo, error)
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error)
ServiceConfig() *registrytypes.ServiceConfig ServiceConfig() *registry.ServiceConfig
TLSConfig(hostname string) (*tls.Config, error)
LoadAllowNondistributableArtifacts([]string) error LoadAllowNondistributableArtifacts([]string) error
LoadMirrors([]string) error LoadMirrors([]string) error
LoadInsecureRegistries([]string) error LoadInsecureRegistries([]string) error
} }
// DefaultService is a registry service. It tracks configuration data such as a list // defaultService is a registry service. It tracks configuration data such as a list
// of mirrors. // of mirrors.
type DefaultService struct { type defaultService struct {
config *serviceConfig config *serviceConfig
mu sync.Mutex mu sync.RWMutex
} }
// NewService returns a new instance of DefaultService ready to be // NewService returns a new instance of defaultService ready to be
// installed into an engine. // installed into an engine.
func NewService(options ServiceOptions) (*DefaultService, error) { func NewService(options ServiceOptions) (Service, error) {
config, err := newServiceConfig(options) config, err := newServiceConfig(options)
return &DefaultService{config: config}, err return &defaultService{config: config}, err
} }
// ServiceConfig returns the public registry service configuration. // ServiceConfig returns a copy of the public registry service's configuration.
func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig { func (s *defaultService) ServiceConfig() *registry.ServiceConfig {
s.mu.Lock() s.mu.RLock()
defer s.mu.Unlock() defer s.mu.RUnlock()
return s.config.copy()
servConfig := registrytypes.ServiceConfig{
AllowNondistributableArtifactsCIDRs: make([]*(registrytypes.NetIPNet), 0),
AllowNondistributableArtifactsHostnames: make([]string, 0),
InsecureRegistryCIDRs: make([]*(registrytypes.NetIPNet), 0),
IndexConfigs: make(map[string]*(registrytypes.IndexInfo)),
Mirrors: make([]string, 0),
}
// construct a new ServiceConfig which will not retrieve s.Config directly,
// and look up items in s.config with mu locked
servConfig.AllowNondistributableArtifactsCIDRs = append(servConfig.AllowNondistributableArtifactsCIDRs, s.config.ServiceConfig.AllowNondistributableArtifactsCIDRs...)
servConfig.AllowNondistributableArtifactsHostnames = append(servConfig.AllowNondistributableArtifactsHostnames, s.config.ServiceConfig.AllowNondistributableArtifactsHostnames...)
servConfig.InsecureRegistryCIDRs = append(servConfig.InsecureRegistryCIDRs, s.config.ServiceConfig.InsecureRegistryCIDRs...)
for key, value := range s.config.ServiceConfig.IndexConfigs {
servConfig.IndexConfigs[key] = value
}
servConfig.Mirrors = append(servConfig.Mirrors, s.config.ServiceConfig.Mirrors...)
return &servConfig
} }
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries for Service. // LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries for Service.
func (s *DefaultService) LoadAllowNondistributableArtifacts(registries []string) error { func (s *defaultService) LoadAllowNondistributableArtifacts(registries []string) error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.config.LoadAllowNondistributableArtifacts(registries) return s.config.loadAllowNondistributableArtifacts(registries)
} }
// LoadMirrors loads registry mirrors for Service // LoadMirrors loads registry mirrors for Service
func (s *DefaultService) LoadMirrors(mirrors []string) error { func (s *defaultService) LoadMirrors(mirrors []string) error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.config.LoadMirrors(mirrors) return s.config.loadMirrors(mirrors)
} }
// LoadInsecureRegistries loads insecure registries for Service // LoadInsecureRegistries loads insecure registries for Service
func (s *DefaultService) LoadInsecureRegistries(registries []string) error { func (s *defaultService) LoadInsecureRegistries(registries []string) error {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
return s.config.LoadInsecureRegistries(registries) return s.config.loadInsecureRegistries(registries)
} }
// Auth contacts the public registry with the provided credentials, // Auth contacts the public registry with the provided credentials,
// and returns OK if authentication was successful. // and returns OK if authentication was successful.
// It can be used to verify the validity of a client's credentials. // It can be used to verify the validity of a client's credentials.
func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error) { func (s *defaultService) Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error) {
// TODO Use ctx when searching for repositories // TODO Use ctx when searching for repositories
var registryHostName = IndexHostname var registryHostName = IndexHostname
@ -117,7 +89,7 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
} }
u, err := url.Parse(serverAddress) u, err := url.Parse(serverAddress)
if err != nil { if err != nil {
return "", "", errdefs.InvalidParameter(errors.Errorf("unable to parse server address: %v", err)) return "", "", invalidParamWrapf(err, "unable to parse server address")
} }
registryHostName = u.Host registryHostName = u.Host
} }
@ -127,7 +99,7 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
// to a mirror. // to a mirror.
endpoints, err := s.LookupPushEndpoints(registryHostName) endpoints, err := s.LookupPushEndpoints(registryHostName)
if err != nil { if err != nil {
return "", "", errdefs.InvalidParameter(err) return "", "", invalidParam(err)
} }
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
@ -159,25 +131,28 @@ func splitReposSearchTerm(reposName string) (string, string) {
// Search queries the public registry for images matching the specified // Search queries the public registry for images matching the specified
// search terms, and returns the results. // search terms, and returns the results.
func (s *DefaultService) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registrytypes.SearchResults, error) { func (s *defaultService) Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error) {
// TODO Use ctx when searching for repositories // TODO Use ctx when searching for repositories
if err := validateNoScheme(term); err != nil { if hasScheme(term) {
return nil, err return nil, invalidParamf("invalid repository name: repository name (%s) should not have a scheme", term)
} }
indexName, remoteName := splitReposSearchTerm(term) indexName, remoteName := splitReposSearchTerm(term)
// Search is a long-running operation, just lock s.config to avoid block others. // Search is a long-running operation, just lock s.config to avoid block others.
s.mu.Lock() s.mu.RLock()
index, err := newIndexInfo(s.config, indexName) index, err := newIndexInfo(s.config, indexName)
s.mu.Unlock() s.mu.RUnlock()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
}
// *TODO: Search multiple indexes. endpoint, err := newV1Endpoint(index, userAgent, headers)
endpoint, err := NewV1Endpoint(index, userAgent, headers)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -195,12 +170,8 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut
modifiers := Headers(userAgent, nil) modifiers := Headers(userAgent, nil)
v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes) v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
if err != nil { if err != nil {
if fErr, ok := err.(fallbackError); ok {
logrus.Errorf("Cannot use identity token for search, v2 auth not supported: %v", fErr.err)
} else {
return nil, err return nil, err
} }
} else {
// Copy non transport http client features // Copy non transport http client features
v2Client.Timeout = endpoint.client.Timeout v2Client.Timeout = endpoint.client.Timeout
v2Client.CheckRedirect = endpoint.client.CheckRedirect v2Client.CheckRedirect = endpoint.client.CheckRedirect
@ -208,30 +179,21 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut
logrus.Debugf("using v2 client for search to %s", endpoint.URL) logrus.Debugf("using v2 client for search to %s", endpoint.URL)
client = v2Client client = v2Client
} } else {
}
if client == nil {
client = endpoint.client client = endpoint.client
if err := authorizeClient(client, authConfig, endpoint); err != nil { if err := authorizeClient(client, authConfig, endpoint); err != nil {
return nil, err return nil, err
} }
} }
r := newSession(client, authConfig, endpoint) return newSession(client, endpoint).searchRepositories(remoteName, limit)
if index.Official {
// If pull "library/foo", it's stored locally under "foo"
remoteName = strings.TrimPrefix(remoteName, "library/")
}
return r.SearchRepositories(remoteName, limit)
} }
// ResolveRepository splits a repository name into its components // ResolveRepository splits a repository name into its components
// and configuration of the associated registry. // and configuration of the associated registry.
func (s *DefaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) { func (s *defaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
s.mu.Lock() s.mu.RLock()
defer s.mu.Unlock() defer s.mu.RUnlock()
return newRepositoryInfo(s.config, name) return newRepositoryInfo(s.config, name)
} }
@ -246,33 +208,20 @@ type APIEndpoint struct {
TLSConfig *tls.Config TLSConfig *tls.Config
} }
// TLSConfig constructs a client TLS configuration based on server defaults
func (s *DefaultService) TLSConfig(hostname string) (*tls.Config, error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.tlsConfig(hostname)
}
// tlsConfig constructs a client TLS configuration based on server defaults
func (s *DefaultService) tlsConfig(hostname string) (*tls.Config, error) {
return newTLSConfig(hostname, isSecureIndex(s.config, hostname))
}
// LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference. // LookupPullEndpoints creates a list of v2 endpoints to try to pull from, in order of preference.
// It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP. // It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP.
func (s *DefaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) { func (s *defaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
s.mu.Lock() s.mu.RLock()
defer s.mu.Unlock() defer s.mu.RUnlock()
return s.lookupV2Endpoints(hostname) return s.lookupV2Endpoints(hostname)
} }
// LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference. // LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference.
// It gives preference to HTTPS over plain HTTP. Mirrors are not included. // It gives preference to HTTPS over plain HTTP. Mirrors are not included.
func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) { func (s *defaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
s.mu.Lock() s.mu.RLock()
defer s.mu.Unlock() defer s.mu.RUnlock()
allEndpoints, err := s.lookupV2Endpoints(hostname) allEndpoints, err := s.lookupV2Endpoints(hostname)
if err == nil { if err == nil {

View File

@ -7,8 +7,7 @@ import (
"github.com/docker/go-connections/tlsconfig" "github.com/docker/go-connections/tlsconfig"
) )
func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) { func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
tlsConfig := tlsconfig.ServerDefault()
if hostname == DefaultNamespace || hostname == IndexHostname { if hostname == DefaultNamespace || hostname == IndexHostname {
for _, mirror := range s.config.Mirrors { for _, mirror := range s.config.Mirrors {
if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") { if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") {
@ -16,9 +15,9 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
} }
mirrorURL, err := url.Parse(mirror) mirrorURL, err := url.Parse(mirror)
if err != nil { if err != nil {
return nil, err return nil, invalidParam(err)
} }
mirrorTLSConfig, err := s.tlsConfig(mirrorURL.Host) mirrorTLSConfig, err := newTLSConfig(mirrorURL.Host, s.config.isSecureIndex(mirrorURL.Host))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -35,19 +34,18 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
Version: APIVersion2, Version: APIVersion2,
Official: true, Official: true,
TrimHostname: true, TrimHostname: true,
TLSConfig: tlsConfig, TLSConfig: tlsconfig.ServerDefault(),
}) })
return endpoints, nil return endpoints, nil
} }
ana := allowNondistributableArtifacts(s.config, hostname) tlsConfig, err := newTLSConfig(hostname, s.config.isSecureIndex(hostname))
tlsConfig, err = s.tlsConfig(hostname)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ana := s.config.allowNondistributableArtifacts(hostname)
endpoints = []APIEndpoint{ endpoints = []APIEndpoint{
{ {
URL: &url.URL{ URL: &url.URL{

View File

@ -12,7 +12,7 @@ import (
"sync" "sync"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/errdefs" "github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
@ -21,12 +21,10 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// A Session is used to communicate with a V1 registry // A session is used to communicate with a V1 registry
type Session struct { type session struct {
indexEndpoint *V1Endpoint indexEndpoint *v1Endpoint
client *http.Client client *http.Client
// TODO(tiborvass): remove authConfig
authConfig *types.AuthConfig
id string id string
} }
@ -41,7 +39,7 @@ type authTransport struct {
modReq map[*http.Request]*http.Request // original -> modified modReq map[*http.Request]*http.Request // original -> modified
} }
// AuthTransport handles the auth layer when communicating with a v1 registry (private or official) // newAuthTransport handles the auth layer when communicating with a v1 registry (private or official)
// //
// For private v1 registries, set alwaysSetBasicAuth to true. // For private v1 registries, set alwaysSetBasicAuth to true.
// //
@ -54,7 +52,7 @@ type authTransport struct {
// If the server sends a token without the client having requested it, it is ignored. // If the server sends a token without the client having requested it, it is ignored.
// //
// This RoundTripper also has a CancelRequest method important for correct timeout handling. // This RoundTripper also has a CancelRequest method important for correct timeout handling.
func AuthTransport(base http.RoundTripper, authConfig *types.AuthConfig, alwaysSetBasicAuth bool) http.RoundTripper { func newAuthTransport(base http.RoundTripper, authConfig *types.AuthConfig, alwaysSetBasicAuth bool) *authTransport {
if base == nil { if base == nil {
base = http.DefaultTransport base = http.DefaultTransport
} }
@ -149,13 +147,13 @@ func (tr *authTransport) CancelRequest(req *http.Request) {
} }
} }
func authorizeClient(client *http.Client, authConfig *types.AuthConfig, endpoint *V1Endpoint) error { func authorizeClient(client *http.Client, authConfig *types.AuthConfig, endpoint *v1Endpoint) error {
var alwaysSetBasicAuth bool var alwaysSetBasicAuth bool
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers // If we're working with a standalone private registry over HTTPS, send Basic Auth headers
// alongside all our requests. // alongside all our requests.
if endpoint.String() != IndexServer && endpoint.URL.Scheme == "https" { if endpoint.String() != IndexServer && endpoint.URL.Scheme == "https" {
info, err := endpoint.Ping() info, err := endpoint.ping()
if err != nil { if err != nil {
return err return err
} }
@ -167,47 +165,42 @@ func authorizeClient(client *http.Client, authConfig *types.AuthConfig, endpoint
// Annotate the transport unconditionally so that v2 can // Annotate the transport unconditionally so that v2 can
// properly fallback on v1 when an image is not found. // properly fallback on v1 when an image is not found.
client.Transport = AuthTransport(client.Transport, authConfig, alwaysSetBasicAuth) client.Transport = newAuthTransport(client.Transport, authConfig, alwaysSetBasicAuth)
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
if err != nil { if err != nil {
return errors.New("cookiejar.New is not supposed to return an error") return errdefs.System(errors.New("cookiejar.New is not supposed to return an error"))
} }
client.Jar = jar client.Jar = jar
return nil return nil
} }
func newSession(client *http.Client, authConfig *types.AuthConfig, endpoint *V1Endpoint) *Session { func newSession(client *http.Client, endpoint *v1Endpoint) *session {
return &Session{ return &session{
authConfig: authConfig,
client: client, client: client,
indexEndpoint: endpoint, indexEndpoint: endpoint,
id: stringid.GenerateRandomID(), id: stringid.GenerateRandomID(),
} }
} }
// NewSession creates a new session // defaultSearchLimit is the default value for maximum number of returned search results.
// TODO(tiborvass): remove authConfig param once registry client v2 is vendored const defaultSearchLimit = 25
func NewSession(client *http.Client, authConfig *types.AuthConfig, endpoint *V1Endpoint) (*Session, error) {
if err := authorizeClient(client, authConfig, endpoint); err != nil { // searchRepositories performs a search against the remote repository
return nil, err func (r *session) searchRepositories(term string, limit int) (*registry.SearchResults, error) {
if limit == 0 {
limit = defaultSearchLimit
} }
return newSession(client, authConfig, endpoint), nil
}
// SearchRepositories performs a search against the remote repository
func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.SearchResults, error) {
if limit < 1 || limit > 100 { if limit < 1 || limit > 100 {
return nil, errdefs.InvalidParameter(errors.Errorf("Limit %d is outside the range of [1, 100]", limit)) return nil, invalidParamf("limit %d is outside the range of [1, 100]", limit)
} }
logrus.Debugf("Index server: %s", r.indexEndpoint) logrus.Debugf("Index server: %s", r.indexEndpoint)
u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit)) u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit))
req, err := http.NewRequest(http.MethodGet, u, nil) req, err := http.NewRequest(http.MethodGet, u, nil)
if err != nil { if err != nil {
return nil, errors.Wrap(errdefs.InvalidParameter(err), "Error building request") return nil, invalidParamWrapf(err, "error building request")
} }
// Have the AuthTransport send authentication, when logged in. // Have the AuthTransport send authentication, when logged in.
req.Header.Set("X-Docker-Token", "true") req.Header.Set("X-Docker-Token", "true")
@ -222,6 +215,6 @@ func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.Sea
Code: res.StatusCode, Code: res.StatusCode,
} }
} }
result := new(registrytypes.SearchResults) result := new(registry.SearchResults)
return result, errors.Wrap(json.NewDecoder(res.Body).Decode(result), "error decoding registry search results") return result, errors.Wrap(json.NewDecoder(res.Body).Decode(result), "error decoding registry search results")
} }

View File

@ -2,39 +2,9 @@ package registry // import "github.com/docker/docker/registry"
import ( import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/registry"
) )
// RepositoryData tracks the image list, list of endpoints for a repository
type RepositoryData struct {
// ImgList is a list of images in the repository
ImgList map[string]*ImgData
// Endpoints is a list of endpoints returned in X-Docker-Endpoints
Endpoints []string
}
// ImgData is used to transfer image checksums to and from the registry
type ImgData struct {
// ID is an opaque string that identifies the image
ID string `json:"id"`
Checksum string `json:"checksum,omitempty"`
ChecksumPayload string `json:"-"`
Tag string `json:",omitempty"`
}
// PingResult contains the information returned when pinging a registry. It
// indicates the registry's version and whether the registry claims to be a
// standalone registry.
type PingResult struct {
// Version is the registry version supplied by the registry in an HTTP
// header
Version string `json:"version"`
// Standalone is set to true if the registry indicates it is a
// standalone registry in the X-Docker-Registry-Standalone
// header
Standalone bool `json:"standalone"`
}
// APIVersion is an integral representation of an API version (presently // APIVersion is an integral representation of an API version (presently
// either 1 or 2) // either 1 or 2)
type APIVersion int type APIVersion int
@ -58,7 +28,7 @@ var apiVersions = map[APIVersion]string{
type RepositoryInfo struct { type RepositoryInfo struct {
Name reference.Named Name reference.Named
// Index points to registry information // Index points to registry information
Index *registrytypes.IndexInfo Index *registry.IndexInfo
// Official indicates whether the repository is considered official. // Official indicates whether the repository is considered official.
// If the registry is official, and the normalized name does not // If the registry is official, and the normalized name does not
// contain a '/' (e.g. "foo"), then it is considered an official repo. // contain a '/' (e.g. "foo"), then it is considered an official repo.

View File

@ -1,25 +0,0 @@
package rootless // import "github.com/docker/docker/rootless"
import (
"os"
"sync"
)
const (
// RootlessKitDockerProxyBinary is the binary name of rootlesskit-docker-proxy
RootlessKitDockerProxyBinary = "rootlesskit-docker-proxy"
)
var (
runningWithRootlessKit bool
runningWithRootlessKitOnce sync.Once
)
// RunningWithRootlessKit returns true if running under RootlessKit namespaces.
func RunningWithRootlessKit() bool {
runningWithRootlessKitOnce.Do(func() {
u := os.Getenv("ROOTLESSKIT_STATE_DIR")
runningWithRootlessKit = u != ""
})
return runningWithRootlessKit
}

5
vendor/modules.txt vendored
View File

@ -39,7 +39,7 @@ github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory github.com/docker/distribution/registry/storage/cache/memory
github.com/docker/distribution/uuid github.com/docker/distribution/uuid
# github.com/docker/docker v20.10.7+incompatible => github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible # github.com/docker/docker v20.10.14+incompatible => github.com/docker/docker v20.10.3-0.20220326171151-8941dcfcc5db+incompatible
## explicit ## explicit
github.com/docker/docker/api github.com/docker/docker/api
github.com/docker/docker/api/types github.com/docker/docker/api/types
@ -75,7 +75,6 @@ github.com/docker/docker/pkg/stringid
github.com/docker/docker/pkg/system github.com/docker/docker/pkg/system
github.com/docker/docker/pkg/urlutil github.com/docker/docker/pkg/urlutil
github.com/docker/docker/registry github.com/docker/docker/registry
github.com/docker/docker/rootless
# github.com/docker/docker-credential-helpers v0.6.4 # github.com/docker/docker-credential-helpers v0.6.4
## explicit; go 1.13 ## explicit; go 1.13
github.com/docker/docker-credential-helpers/client github.com/docker/docker-credential-helpers/client
@ -385,5 +384,5 @@ gotest.tools/v3/internal/format
gotest.tools/v3/internal/source gotest.tools/v3/internal/source
gotest.tools/v3/poll gotest.tools/v3/poll
gotest.tools/v3/skip gotest.tools/v3/skip
# github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220309172631-83b51522df43+incompatible # github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220326171151-8941dcfcc5db+incompatible
# github.com/gogo/googleapis => github.com/gogo/googleapis v1.3.2 # github.com/gogo/googleapis => github.com/gogo/googleapis v1.3.2