mirror of https://github.com/docker/cli.git
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:
parent
5a1e151c02
commit
a1e67401d2
10
cli/cobra.go
10
cli/cobra.go
|
@ -3,12 +3,15 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
pluginmanager "github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/config"
|
||||
cliflags "github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/docker/docker/registry"
|
||||
"github.com/moby/term"
|
||||
"github.com/morikuni/aec"
|
||||
"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/"}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ func NewSearchCommand(dockerCli command.Cli) *cobra.Command {
|
|||
|
||||
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Don't truncate output")
|
||||
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")
|
||||
|
||||
return cmd
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -10,7 +10,7 @@ require (
|
|||
github.com/containerd/containerd v1.6.2
|
||||
github.com/creack/pty v1.1.11
|
||||
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/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0
|
||||
|
@ -74,6 +74,6 @@ require (
|
|||
)
|
||||
|
||||
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
|
||||
)
|
||||
|
|
|
@ -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.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/docker v20.10.3-0.20220309172631-83b51522df43+incompatible h1:bL4hLpxukr5Ls3bzYrn3LCYIwML+XXCktZHaGBIN3og=
|
||||
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 h1:5DYFLB020CbxyjsxBle60QaEUb4krFjr30O0eLXsNp0=
|
||||
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/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
|
|
|
@ -577,19 +577,13 @@ definitions:
|
|||
type: "array"
|
||||
items:
|
||||
$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:
|
||||
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"
|
||||
format: "int64"
|
||||
MemoryReservation:
|
||||
|
@ -1075,8 +1069,9 @@ definitions:
|
|||
description: "Mount the container's root filesystem as read only."
|
||||
SecurityOpt:
|
||||
type: "array"
|
||||
description: "A list of string values to customize labels for MLS
|
||||
systems, such as SELinux."
|
||||
description: |
|
||||
A list of string values to customize labels for MLS systems, such
|
||||
as SELinux.
|
||||
items:
|
||||
type: "string"
|
||||
StorageOpt:
|
||||
|
@ -1869,18 +1864,22 @@ definitions:
|
|||
type: "string"
|
||||
description: "Name of the volume."
|
||||
x-nullable: false
|
||||
example: "tardis"
|
||||
Driver:
|
||||
type: "string"
|
||||
description: "Name of the volume driver used by the volume."
|
||||
x-nullable: false
|
||||
example: "custom"
|
||||
Mountpoint:
|
||||
type: "string"
|
||||
description: "Mount path of the volume on the host."
|
||||
x-nullable: false
|
||||
example: "/var/lib/docker/volumes/tardis"
|
||||
CreatedAt:
|
||||
type: "string"
|
||||
format: "dateTime"
|
||||
description: "Date/Time the volume was created."
|
||||
example: "2016-06-07T20:31:11.853781916Z"
|
||||
Status:
|
||||
type: "object"
|
||||
description: |
|
||||
|
@ -1892,12 +1891,17 @@ definitions:
|
|||
does not support this feature.
|
||||
additionalProperties:
|
||||
type: "object"
|
||||
example:
|
||||
hello: "world"
|
||||
Labels:
|
||||
type: "object"
|
||||
description: "User-defined key/value metadata."
|
||||
x-nullable: false
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
example:
|
||||
com.example.some-label: "some-value"
|
||||
com.example.some-other-label: "some-other-value"
|
||||
Scope:
|
||||
type: "string"
|
||||
description: |
|
||||
|
@ -1906,12 +1910,17 @@ definitions:
|
|||
default: "local"
|
||||
x-nullable: false
|
||||
enum: ["local", "global"]
|
||||
example: "local"
|
||||
Options:
|
||||
type: "object"
|
||||
description: |
|
||||
The driver specific options used when creating the volume.
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
example:
|
||||
device: "tmpfs"
|
||||
o: "size=100m,uid=1000"
|
||||
type: "tmpfs"
|
||||
UsageData:
|
||||
type: "object"
|
||||
x-nullable: true
|
||||
|
@ -1937,17 +1946,43 @@ definitions:
|
|||
is set to `-1` if the reference-count is not available.
|
||||
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:
|
||||
Name: "tardis"
|
||||
Driver: "custom"
|
||||
Mountpoint: "/var/lib/docker/volumes/tardis"
|
||||
Status:
|
||||
hello: "world"
|
||||
device: "tmpfs"
|
||||
o: "size=100m,uid=1000"
|
||||
type: "tmpfs"
|
||||
Labels:
|
||||
description: "User-defined key/value metadata."
|
||||
type: "object"
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
example:
|
||||
com.example.some-label: "some-value"
|
||||
com.example.some-other-label: "some-other-value"
|
||||
Scope: "local"
|
||||
CreatedAt: "2016-06-07T20:31:11.853781916Z"
|
||||
|
||||
Network:
|
||||
type: "object"
|
||||
|
@ -2035,11 +2070,23 @@ definitions:
|
|||
```
|
||||
type: "array"
|
||||
items:
|
||||
$ref: "#/definitions/IPAMConfig"
|
||||
Options:
|
||||
description: "Driver-specific options, specified as a map."
|
||||
type: "object"
|
||||
additionalProperties:
|
||||
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"
|
||||
additionalProperties:
|
||||
type: "string"
|
||||
|
@ -3827,6 +3874,7 @@ definitions:
|
|||
|
||||
ServiceSpec:
|
||||
description: "User modifiable configuration for a service."
|
||||
type: object
|
||||
properties:
|
||||
Name:
|
||||
description: "Name of the service."
|
||||
|
@ -4464,6 +4512,29 @@ 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:
|
||||
type: "object"
|
||||
description: |
|
||||
|
@ -4644,19 +4715,10 @@ definitions:
|
|||
description: "Indicates if the host has memory swap limit support enabled."
|
||||
type: "boolean"
|
||||
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:
|
||||
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
|
||||
does not support the corresponding `memory.kmem.tcp.limit_in_bytes` cgroup.
|
||||
|
@ -5369,6 +5431,7 @@ definitions:
|
|||
|
||||
PeerNode:
|
||||
description: "Represents a peer-node in the swarm"
|
||||
type: "object"
|
||||
properties:
|
||||
NodeID:
|
||||
description: "Unique identifier of for this node in the swarm."
|
||||
|
@ -5820,7 +5883,6 @@ paths:
|
|||
Memory: 0
|
||||
MemorySwap: 0
|
||||
MemoryReservation: 0
|
||||
KernelMemory: 0
|
||||
NanoCpus: 500000
|
||||
CpuPercent: 80
|
||||
CpuShares: 512
|
||||
|
@ -6112,7 +6174,6 @@ paths:
|
|||
Memory: 0
|
||||
MemorySwap: 0
|
||||
MemoryReservation: 0
|
||||
KernelMemory: 0
|
||||
OomKillDisable: false
|
||||
OomScoreAdj: 500
|
||||
NetworkMode: "bridge"
|
||||
|
@ -6857,7 +6918,6 @@ paths:
|
|||
Memory: 314572800
|
||||
MemorySwap: 514288000
|
||||
MemoryReservation: 209715200
|
||||
KernelMemory: 52428800
|
||||
RestartPolicy:
|
||||
MaximumRetryCount: 4
|
||||
Name: "on-failure"
|
||||
|
@ -7196,22 +7256,7 @@ paths:
|
|||
200:
|
||||
description: "The container has exit."
|
||||
schema:
|
||||
type: "object"
|
||||
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"
|
||||
$ref: "#/definitions/ContainerWaitResponse"
|
||||
400:
|
||||
description: "bad parameter"
|
||||
schema:
|
||||
|
@ -7319,17 +7364,7 @@ paths:
|
|||
400:
|
||||
description: "Bad parameter"
|
||||
schema:
|
||||
allOf:
|
||||
- $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
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
description: "Container or path does not exist"
|
||||
schema:
|
||||
|
@ -7364,17 +7399,7 @@ paths:
|
|||
400:
|
||||
description: "Bad parameter"
|
||||
schema:
|
||||
allOf:
|
||||
- $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
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
404:
|
||||
description: "Container or path does not exist"
|
||||
schema:
|
||||
|
@ -7400,7 +7425,10 @@ paths:
|
|||
tags: ["Container"]
|
||||
put:
|
||||
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"
|
||||
consumes: ["application/x-tar", "application/octet-stream"]
|
||||
responses:
|
||||
|
@ -7410,6 +7438,9 @@ paths:
|
|||
description: "Bad parameter"
|
||||
schema:
|
||||
$ref: "#/definitions/ErrorResponse"
|
||||
examples:
|
||||
application/json:
|
||||
message: "not a directory"
|
||||
403:
|
||||
description: "Permission denied, the volume or container rootfs is marked as read-only."
|
||||
schema:
|
||||
|
@ -8346,6 +8377,13 @@ paths:
|
|||
Docker-Experimental:
|
||||
type: "boolean"
|
||||
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:
|
||||
type: "string"
|
||||
default: "no-cache, no-store, must-revalidate"
|
||||
|
@ -8385,6 +8423,13 @@ paths:
|
|||
Docker-Experimental:
|
||||
type: "boolean"
|
||||
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:
|
||||
type: "string"
|
||||
default: "no-cache, no-store, must-revalidate"
|
||||
|
@ -9051,23 +9096,6 @@ paths:
|
|||
Warnings that occurred when fetching the list of volumes.
|
||||
items:
|
||||
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:
|
||||
description: "Server error"
|
||||
schema:
|
||||
|
@ -9112,38 +9140,7 @@ paths:
|
|||
required: true
|
||||
description: "Volume configuration"
|
||||
schema:
|
||||
type: "object"
|
||||
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"
|
||||
$ref: "#/definitions/VolumeCreateOptions"
|
||||
tags: ["Volume"]
|
||||
|
||||
/volumes/{name}:
|
||||
|
|
|
@ -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"`
|
||||
}
|
19
vendor/github.com/docker/docker/api/types/container/container_wait_o_k_body.go
generated
vendored
Normal file
19
vendor/github.com/docker/docker/api/types/container/container_wait_o_k_body.go
generated
vendored
Normal 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"`
|
||||
}
|
12
vendor/github.com/docker/docker/api/types/container/container_wait_o_k_body_error.go
generated
vendored
Normal file
12
vendor/github.com/docker/docker/api/types/container/container_wait_o_k_body_error.go
generated
vendored
Normal 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"`
|
||||
}
|
|
@ -376,8 +376,11 @@ type Resources struct {
|
|||
Devices []DeviceMapping // List of devices to map inside the container
|
||||
DeviceCgroupRules []string // List of rule to be added to the device cgroup
|
||||
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)
|
||||
MemorySwap int64 // Total memory usage (memory + swap); set `-1` to enable unlimited swap
|
||||
MemorySwappiness *int64 // Tuning container memory swappiness behaviour
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// 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
|
||||
deprecated := map[string][]string{}
|
||||
if legacyErr := json.Unmarshal(raw, &deprecated); legacyErr != nil {
|
||||
return args, err
|
||||
return args, invalidFilter{errors.Wrap(err, "invalid filter")}
|
||||
}
|
||||
|
||||
args.fields = deprecatedArgs(deprecated)
|
||||
|
@ -247,10 +248,10 @@ func (args Args) Contains(field string) bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
type invalidFilter string
|
||||
type invalidFilter struct{ error }
|
||||
|
||||
func (e invalidFilter) Error() string {
|
||||
return "Invalid filter '" + string(e) + "'"
|
||||
return e.error.Error()
|
||||
}
|
||||
|
||||
func (invalidFilter) InvalidParameter() {}
|
||||
|
@ -260,7 +261,7 @@ func (invalidFilter) InvalidParameter() {}
|
|||
func (args Args) Validate(accepted map[string]bool) error {
|
||||
for name := range args.fields {
|
||||
if !accepted[name] {
|
||||
return invalidFilter(name)
|
||||
return invalidFilter{errors.New("invalid filter '" + name + "'")}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -213,6 +213,16 @@ type Info struct {
|
|||
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.
|
||||
type Peer struct {
|
||||
NodeID string
|
||||
|
|
|
@ -188,6 +188,15 @@ type Ping struct {
|
|||
OSType string
|
||||
Experimental bool
|
||||
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.
|
||||
|
@ -239,8 +248,8 @@ type Info struct {
|
|||
Plugins PluginsInfo
|
||||
MemoryLimit bool
|
||||
SwapLimit bool
|
||||
KernelMemory bool // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
|
||||
KernelMemoryTCP bool
|
||||
KernelMemory bool `json:",omitempty"` // Deprecated: kernel 5.4 deprecated kmem.limit_in_bytes
|
||||
KernelMemoryTCP bool `json:",omitempty"` // KernelMemoryTCP is not supported on cgroups v2.
|
||||
CPUCfsPeriod bool `json:"CpuCfsPeriod"`
|
||||
CPUCfsQuota bool `json:"CpuCfsQuota"`
|
||||
CPUShares bool
|
||||
|
|
|
@ -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"`
|
||||
}
|
26
vendor/github.com/docker/docker/api/types/volume/volume_create_body.go
generated
vendored
Normal file
26
vendor/github.com/docker/docker/api/types/volume/volume_create_body.go
generated
vendored
Normal 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"`
|
||||
}
|
|
@ -20,7 +20,7 @@ func (cli *Client) CheckpointList(ctx context.Context, container string, options
|
|||
resp, err := cli.get(ctx, "/containers/"+container+"/checkpoints", query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return checkpoints, wrapResponseError(err, resp, "container", container)
|
||||
return checkpoints, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.body).Decode(&checkpoints)
|
||||
|
|
|
@ -43,7 +43,6 @@ package client // import "github.com/docker/docker/client"
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -93,15 +92,18 @@ type Client struct {
|
|||
}
|
||||
|
||||
// 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 .
|
||||
// The Docker client (and by extension docker API client) can be made to send a request
|
||||
// like POST /containers//start where what would normally be in the name section of the URL is empty.
|
||||
// This triggers an HTTP 301 from the daemon.
|
||||
// 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.
|
||||
// Go 1.8 changes behavior for HTTP redirects (specifically 301, 307, and 308)
|
||||
// in the client. The Docker client (and by extension docker API client) can be
|
||||
// made to send a request like POST /containers//start where what would normally
|
||||
// be in the name section of the URL is empty. This triggers an HTTP 301 from
|
||||
// the daemon.
|
||||
//
|
||||
// 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 {
|
||||
if via[0].Method == http.MethodGet {
|
||||
return http.ErrUseLastResponse
|
||||
|
@ -109,13 +111,22 @@ func CheckRedirect(req *http.Request, via []*http.Request) error {
|
|||
return ErrRedirect
|
||||
}
|
||||
|
||||
// NewClientWithOpts initializes a new API client with default values. It takes functors
|
||||
// to modify values when creating it, like `NewClientWithOpts(WithVersion(…))`
|
||||
// It also initializes the custom http headers to add to each request.
|
||||
// NewClientWithOpts initializes a new API client with a default HTTPClient, and
|
||||
// default API host and version. It also initializes the custom HTTP headers to
|
||||
// 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) {
|
||||
client, err := defaultHTTPClient(DefaultDockerHost)
|
||||
if err != nil {
|
||||
|
@ -153,12 +164,12 @@ func NewClientWithOpts(ops ...Opt) (*Client, error) {
|
|||
}
|
||||
|
||||
func defaultHTTPClient(host string) (*http.Client, error) {
|
||||
url, err := ParseHostURL(host)
|
||||
hostURL, err := ParseHostURL(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport := new(http.Transport)
|
||||
sockets.ConfigureTransport(transport, url.Scheme, url.Host)
|
||||
transport := &http.Transport{}
|
||||
_ = sockets.ConfigureTransport(transport, hostURL.Scheme, hostURL.Host)
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: CheckRedirect,
|
||||
|
@ -194,11 +205,21 @@ func (cli *Client) ClientVersion() string {
|
|||
return cli.version
|
||||
}
|
||||
|
||||
// NegotiateAPIVersion queries the API and updates the version to match the
|
||||
// API version. Any errors are silently ignored. If a manual override is in place,
|
||||
// either through the `DOCKER_API_VERSION` environment variable, or if the client
|
||||
// was initialized with a fixed version (`opts.WithVersion(xx)`), no negotiation
|
||||
// will be performed.
|
||||
// NegotiateAPIVersion queries the API and updates the version to match the API
|
||||
// version. NegotiateAPIVersion downgrades the client's API version to match the
|
||||
// APIVersion if the ping version is lower than the default version. If the API
|
||||
// version reported by the server is higher than the maximum version supported
|
||||
// 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) {
|
||||
if !cli.manualOverride {
|
||||
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
|
||||
// if the ping version is less than the default version. If a manual override is
|
||||
// in place, either through the `DOCKER_API_VERSION` environment variable, or if
|
||||
// the client was initialized with a fixed version (`opts.WithVersion(xx)`), no
|
||||
// negotiation is performed.
|
||||
func (cli *Client) NegotiateAPIVersionPing(p types.Ping) {
|
||||
// NegotiateAPIVersionPing downgrades the client's API version to match the
|
||||
// APIVersion in the ping response. If the API version in pingResponse is higher
|
||||
// than the maximum version supported 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, 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 {
|
||||
cli.negotiateAPIVersionPing(p)
|
||||
cli.negotiateAPIVersionPing(pingResponse)
|
||||
}
|
||||
}
|
||||
|
||||
// negotiateAPIVersionPing queries the API and updates the version to match the
|
||||
// API version. Any errors are silently ignored.
|
||||
func (cli *Client) negotiateAPIVersionPing(p types.Ping) {
|
||||
// try the latest version before versioning headers existed
|
||||
if p.APIVersion == "" {
|
||||
p.APIVersion = "1.24"
|
||||
// API version from the ping response.
|
||||
func (cli *Client) negotiateAPIVersionPing(pingResponse types.Ping) {
|
||||
// default to the latest version before versioning headers existed
|
||||
if pingResponse.APIVersion == "" {
|
||||
pingResponse.APIVersion = "1.24"
|
||||
}
|
||||
|
||||
// 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 versions.LessThan(p.APIVersion, cli.version) {
|
||||
cli.version = p.APIVersion
|
||||
if versions.LessThan(pingResponse.APIVersion, cli.version) {
|
||||
cli.version = pingResponse.APIVersion
|
||||
}
|
||||
|
||||
// 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) {
|
||||
protoAddrParts := strings.SplitN(host, "://", 2)
|
||||
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
|
||||
|
@ -278,7 +307,9 @@ func ParseHostURL(host string) (*url.URL, error) {
|
|||
}, 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).
|
||||
func (cli *Client) Dialer() func(context.Context) (net.Conn, error) {
|
||||
return func(ctx context.Context) (net.Conn, error) {
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
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 defaultProto = "unix"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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 defaultProto = "npipe"
|
||||
|
|
|
@ -20,7 +20,7 @@ func (cli *Client) ConfigInspectWithRaw(ctx context.Context, id string) (swarm.C
|
|||
resp, err := cli.get(ctx, "/configs/"+id, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.Config{}, nil, wrapResponseError(err, resp, "config", id)
|
||||
return swarm.Config{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.body)
|
||||
|
|
|
@ -9,5 +9,5 @@ func (cli *Client) ConfigRemove(ctx context.Context, id string) error {
|
|||
}
|
||||
resp, err := cli.delete(ctx, "/configs/"+id, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "config", id)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ func (cli *Client) ContainerStatPath(ctx context.Context, containerID, path stri
|
|||
response, err := cli.head(ctx, urlStr, query, nil)
|
||||
defer ensureReaderClosed(response)
|
||||
if err != nil {
|
||||
return types.ContainerPathStat{}, wrapResponseError(err, response, "container:path", containerID+":"+path)
|
||||
return types.ContainerPathStat{}, err
|
||||
}
|
||||
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)
|
||||
defer ensureReaderClosed(response)
|
||||
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
|
||||
|
@ -67,7 +67,7 @@ func (cli *Client) CopyFromContainer(ctx context.Context, containerID, srcPath s
|
|||
apiPath := "/containers/" + containerID + "/archive"
|
||||
response, err := cli.get(ctx, apiPath, query, 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
|
||||
|
|
|
@ -18,7 +18,7 @@ func (cli *Client) ContainerInspect(ctx context.Context, containerID string) (ty
|
|||
serverResp, err := cli.get(ctx, "/containers/"+containerID+"/json", nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return types.ContainerJSON{}, wrapResponseError(err, serverResp, "container", containerID)
|
||||
return types.ContainerJSON{}, err
|
||||
}
|
||||
|
||||
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)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return types.ContainerJSON{}, nil, wrapResponseError(err, serverResp, "container", containerID)
|
||||
return types.ContainerJSON{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(serverResp.body)
|
||||
|
|
|
@ -74,7 +74,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, container string, options
|
|||
|
||||
resp, err := cli.get(ctx, "/containers/"+container+"/logs", query, nil)
|
||||
if err != nil {
|
||||
return nil, wrapResponseError(err, resp, "container", container)
|
||||
return nil, err
|
||||
}
|
||||
return resp.body, nil
|
||||
}
|
||||
|
|
|
@ -23,5 +23,5 @@ func (cli *Client) ContainerRemove(ctx context.Context, containerID string, opti
|
|||
|
||||
resp, err := cli.delete(ctx, "/containers/"+containerID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "container", containerID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
|
@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"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)
|
||||
}
|
||||
|
||||
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.
|
||||
type unauthorizedError struct {
|
||||
cause error
|
||||
|
|
|
@ -17,7 +17,7 @@ func (cli *Client) ImageInspectWithRaw(ctx context.Context, imageID string) (typ
|
|||
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return types.ImageInspect{}, nil, wrapResponseError(err, serverResp, "image", imageID)
|
||||
return types.ImageInspect{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(serverResp.body)
|
||||
|
|
|
@ -23,7 +23,7 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options type
|
|||
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return dels, wrapResponseError(err, resp, "image", imageID)
|
||||
return dels, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.body).Decode(&dels)
|
||||
|
|
|
@ -3,8 +3,8 @@ package client // import "github.com/docker/docker/client"
|
|||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"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
|
||||
query := url.Values{}
|
||||
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 {
|
||||
filterJSON, err := filters.ToJSON(options.Filters)
|
||||
|
|
|
@ -36,7 +36,7 @@ func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string,
|
|||
resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return networkResource, nil, wrapResponseError(err, resp, "network", networkID)
|
||||
return networkResource, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.body)
|
||||
|
|
|
@ -6,5 +6,5 @@ import "context"
|
|||
func (cli *Client) NetworkRemove(ctx context.Context, networkID string) error {
|
||||
resp, err := cli.delete(ctx, "/networks/"+networkID, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "network", networkID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func (cli *Client) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarm
|
|||
serverResp, err := cli.get(ctx, "/nodes/"+nodeID, nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return swarm.Node{}, nil, wrapResponseError(err, serverResp, "node", nodeID)
|
||||
return swarm.Node{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(serverResp.body)
|
||||
|
|
|
@ -16,5 +16,5 @@ func (cli *Client) NodeRemove(ctx context.Context, nodeID string, options types.
|
|||
|
||||
resp, err := cli.delete(ctx, "/nodes/"+nodeID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "node", nodeID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -18,11 +18,18 @@ type Opt func(*Client) error
|
|||
|
||||
// FromEnv configures the client with values from environment variables.
|
||||
//
|
||||
// Supported 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_CERT_PATH to load the TLS certificates from.
|
||||
// DOCKER_TLS_VERIFY to enable or disable TLS verification, off by default.
|
||||
// FromEnv uses the following environment variables:
|
||||
//
|
||||
// DOCKER_HOST (EnvOverrideHost) to set the URL to the docker server.
|
||||
//
|
||||
// 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 {
|
||||
ops := []Opt{
|
||||
WithTLSClientConfigFromEnv(),
|
||||
|
@ -75,11 +82,11 @@ func WithHost(host string) Opt {
|
|||
}
|
||||
|
||||
// WithHostFromEnv overrides the client host with the host specified in the
|
||||
// DOCKER_HOST environment variable. If DOCKER_HOST is not set, the host is
|
||||
// not modified.
|
||||
// DOCKER_HOST (EnvOverrideHost) environment variable. If DOCKER_HOST is not set,
|
||||
// or set to an empty value, the host is not modified.
|
||||
func WithHostFromEnv() Opt {
|
||||
return func(c *Client) error {
|
||||
if host := os.Getenv("DOCKER_HOST"); host != "" {
|
||||
if host := os.Getenv(EnvOverrideHost); host != "" {
|
||||
return WithHost(host)(c)
|
||||
}
|
||||
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.
|
||||
// If DOCKER_CERT_PATH is not set or empty, TLS configuration is not modified.
|
||||
//
|
||||
// Supported 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.
|
||||
// WithTLSClientConfigFromEnv uses the following environment variables:
|
||||
//
|
||||
// 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 {
|
||||
return func(c *Client) error {
|
||||
dockerCertPath := os.Getenv("DOCKER_CERT_PATH")
|
||||
dockerCertPath := os.Getenv(EnvOverrideCertPath)
|
||||
if dockerCertPath == "" {
|
||||
return nil
|
||||
}
|
||||
|
@ -158,7 +169,7 @@ func WithTLSClientConfigFromEnv() Opt {
|
|||
CAFile: filepath.Join(dockerCertPath, "ca.pem"),
|
||||
CertFile: filepath.Join(dockerCertPath, "cert.pem"),
|
||||
KeyFile: filepath.Join(dockerCertPath, "key.pem"),
|
||||
InsecureSkipVerify: os.Getenv("DOCKER_TLS_VERIFY") == "",
|
||||
InsecureSkipVerify: os.Getenv(EnvTLSVerify) == "",
|
||||
}
|
||||
tlsc, err := tlsconfig.Client(options)
|
||||
if err != nil {
|
||||
|
@ -190,10 +201,7 @@ func WithVersion(version string) Opt {
|
|||
// the version is not modified.
|
||||
func WithVersionFromEnv() Opt {
|
||||
return func(c *Client) error {
|
||||
if version := os.Getenv("DOCKER_API_VERSION"); version != "" {
|
||||
return WithVersion(version)(c)
|
||||
}
|
||||
return nil
|
||||
return WithVersion(os.Getenv(EnvOverrideAPIVersion))(c)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ import (
|
|||
"context"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"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 != "" {
|
||||
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)
|
||||
return ping, errdefs.FromStatusCode(err, resp.statusCode)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*type
|
|||
resp, err := cli.get(ctx, "/plugins/"+name+"/json", nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return nil, nil, wrapResponseError(err, resp, "plugin", name)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.body)
|
||||
|
|
|
@ -25,7 +25,7 @@ func (cli *Client) PluginList(ctx context.Context, filter filters.Args) (types.P
|
|||
resp, err := cli.get(ctx, "/plugins", query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return plugins, wrapResponseError(err, resp, "plugin", "")
|
||||
return plugins, err
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.body).Decode(&plugins)
|
||||
|
|
|
@ -16,5 +16,5 @@ func (cli *Client) PluginRemove(ctx context.Context, name string, options types.
|
|||
|
||||
resp, err := cli.delete(ctx, "/plugins/"+name, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "plugin", name)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ func (cli *Client) SecretInspectWithRaw(ctx context.Context, id string) (swarm.S
|
|||
resp, err := cli.get(ctx, "/secrets/"+id, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return swarm.Secret{}, nil, wrapResponseError(err, resp, "secret", id)
|
||||
return swarm.Secret{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.body)
|
||||
|
|
|
@ -9,5 +9,5 @@ func (cli *Client) SecretRemove(ctx context.Context, id string) error {
|
|||
}
|
||||
resp, err := cli.delete(ctx, "/secrets/"+id, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "secret", id)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func (cli *Client) ServiceInspectWithRaw(ctx context.Context, serviceID string,
|
|||
serverResp, err := cli.get(ctx, "/services/"+serviceID, query, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return swarm.Service{}, nil, wrapResponseError(err, serverResp, "service", serviceID)
|
||||
return swarm.Service{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(serverResp.body)
|
||||
|
|
|
@ -6,5 +6,5 @@ import "context"
|
|||
func (cli *Client) ServiceRemove(ctx context.Context, serviceID string) error {
|
||||
resp, err := cli.delete(ctx, "/services/"+serviceID, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "service", serviceID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func (cli *Client) TaskInspectWithRaw(ctx context.Context, taskID string) (swarm
|
|||
serverResp, err := cli.get(ctx, "/tasks/"+taskID, nil, nil)
|
||||
defer ensureReaderClosed(serverResp)
|
||||
if err != nil {
|
||||
return swarm.Task{}, nil, wrapResponseError(err, serverResp, "task", taskID)
|
||||
return swarm.Task{}, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(serverResp.body)
|
||||
|
|
|
@ -25,7 +25,7 @@ func (cli *Client) VolumeInspectWithRaw(ctx context.Context, volumeID string) (t
|
|||
resp, err := cli.get(ctx, "/volumes/"+volumeID, nil, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
if err != nil {
|
||||
return volume, nil, wrapResponseError(err, resp, "volume", volumeID)
|
||||
return volume, nil, err
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.body)
|
||||
|
|
|
@ -17,5 +17,5 @@ func (cli *Client) VolumeRemove(ctx context.Context, volumeID string, force bool
|
|||
}
|
||||
resp, err := cli.delete(ctx, "/volumes/"+volumeID, query, nil)
|
||||
defer ensureReaderClosed(resp)
|
||||
return wrapResponseError(err, resp, "volume", volumeID)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,78 +1,11 @@
|
|||
package errdefs // import "github.com/docker/docker/errdefs"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
containerderrors "github.com/containerd/containerd/errdefs"
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"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
|
||||
func FromStatusCode(err error, statusCode int) error {
|
||||
if err == nil {
|
||||
|
@ -118,74 +51,3 @@ func FromStatusCode(err error, statusCode int) error {
|
|||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ type (
|
|||
ExcludePatterns []string
|
||||
Compression Compression
|
||||
NoLchown bool
|
||||
UIDMaps []idtools.IDMap
|
||||
GIDMaps []idtools.IDMap
|
||||
IDMap idtools.IdentityMapping
|
||||
ChownOpts *idtools.Identity
|
||||
IncludeSourceDir bool
|
||||
// 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.
|
||||
type Archiver struct {
|
||||
Untar func(io.Reader, string, *TarOptions) error
|
||||
IDMapping *idtools.IdentityMapping
|
||||
IDMapping idtools.IdentityMapping
|
||||
}
|
||||
|
||||
// NewDefaultArchiver returns a new Archiver without any IdentityMapping
|
||||
func NewDefaultArchiver() *Archiver {
|
||||
return &Archiver{Untar: Untar, IDMapping: &idtools.IdentityMapping{}}
|
||||
return &Archiver{Untar: Untar}
|
||||
}
|
||||
|
||||
// breakoutError is used to differentiate errors related to breaking out
|
||||
|
@ -534,7 +533,7 @@ type tarAppender struct {
|
|||
|
||||
// for hardlink mapping
|
||||
SeenFiles map[uint64]string
|
||||
IdentityMapping *idtools.IdentityMapping
|
||||
IdentityMapping idtools.IdentityMapping
|
||||
ChownOpts *idtools.Identity
|
||||
|
||||
// For packing and unpacking whiteout files in the
|
||||
|
@ -544,7 +543,7 @@ type tarAppender struct {
|
|||
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{
|
||||
SeenFiles: make(map[uint64]string),
|
||||
TarWriter: tar.NewWriter(writer),
|
||||
|
@ -860,7 +859,7 @@ func TarWithOptions(srcPath string, options *TarOptions) (io.ReadCloser, error)
|
|||
|
||||
go func() {
|
||||
ta := newTarAppender(
|
||||
idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps),
|
||||
options.IDMap,
|
||||
compressWriter,
|
||||
options.ChownOpts,
|
||||
)
|
||||
|
@ -1044,8 +1043,7 @@ func Unpack(decompressedArchive io.Reader, dest string, options *TarOptions) err
|
|||
defer pools.BufioReader32KPool.Put(trBuf)
|
||||
|
||||
var dirs []*tar.Header
|
||||
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||
rootIDs := idMapping.RootPair()
|
||||
rootIDs := options.IDMap.RootPair()
|
||||
whiteoutConverter, err := getWhiteoutConverter(options.WhiteoutFormat, options.InUserNS)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1134,7 +1132,7 @@ loop:
|
|||
}
|
||||
trBuf.Reset(tr)
|
||||
|
||||
if err := remapIDs(idMapping, hdr); err != nil {
|
||||
if err := remapIDs(options.IDMap, hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1221,8 +1219,7 @@ func (archiver *Archiver) TarUntar(src, dst string) error {
|
|||
}
|
||||
defer archive.Close()
|
||||
options := &TarOptions{
|
||||
UIDMaps: archiver.IDMapping.UIDs(),
|
||||
GIDMaps: archiver.IDMapping.GIDs(),
|
||||
IDMap: archiver.IDMapping,
|
||||
}
|
||||
return archiver.Untar(archive, dst, options)
|
||||
}
|
||||
|
@ -1235,8 +1232,7 @@ func (archiver *Archiver) UntarPath(src, dst string) error {
|
|||
}
|
||||
defer archive.Close()
|
||||
options := &TarOptions{
|
||||
UIDMaps: archiver.IDMapping.UIDs(),
|
||||
GIDMaps: archiver.IDMapping.GIDs(),
|
||||
IDMap: archiver.IDMapping,
|
||||
}
|
||||
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.
|
||||
func (archiver *Archiver) IdentityMapping() *idtools.IdentityMapping {
|
||||
func (archiver *Archiver) IdentityMapping() idtools.IdentityMapping {
|
||||
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})
|
||||
hdr.Uid, hdr.Gid = ids.UID, ids.GID
|
||||
return err
|
||||
|
|
|
@ -394,10 +394,10 @@ func ChangesSize(newDir string, changes []Change) int64 {
|
|||
}
|
||||
|
||||
// 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()
|
||||
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
|
||||
defer pools.BufioWriter32KPool.Put(ta.Buffer)
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/pools"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -32,7 +31,6 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
|||
if options.ExcludePatterns == nil {
|
||||
options.ExcludePatterns = []string{}
|
||||
}
|
||||
idMapping := idtools.NewIDMappingsFromMaps(options.UIDMaps, options.GIDMaps)
|
||||
|
||||
aufsTempdir := ""
|
||||
aufsHardlinks := make(map[string]*tar.Header)
|
||||
|
@ -192,7 +190,7 @@ func UnpackLayer(dest string, layer io.Reader, options *TarOptions) (size int64,
|
|||
srcData = tmpFile
|
||||
}
|
||||
|
||||
if err := remapIDs(idMapping, srcHdr); err != nil {
|
||||
if err := remapIDs(options.IDMap, srcHdr); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
|
|
@ -108,70 +108,72 @@ type Identity struct {
|
|||
SID string
|
||||
}
|
||||
|
||||
// IdentityMapping contains a mappings of UIDs and GIDs
|
||||
type IdentityMapping struct {
|
||||
uids []IDMap
|
||||
gids []IDMap
|
||||
// Chown changes the numeric uid and gid of the named file to id.UID and id.GID.
|
||||
func (id Identity) Chown(name string) error {
|
||||
return os.Chown(name, id.UID, id.GID)
|
||||
}
|
||||
|
||||
// NewIDMappingsFromMaps creates a new mapping from two slices
|
||||
// Deprecated: this is a temporary shim while transitioning to IDMapping
|
||||
func NewIDMappingsFromMaps(uids []IDMap, gids []IDMap) *IdentityMapping {
|
||||
return &IdentityMapping{uids: uids, gids: gids}
|
||||
// IdentityMapping contains a mappings of UIDs and GIDs.
|
||||
// The zero value represents an empty mapping.
|
||||
type IdentityMapping struct {
|
||||
UIDMaps []IDMap `json:"UIDMaps"`
|
||||
GIDMaps []IDMap `json:"GIDMaps"`
|
||||
}
|
||||
|
||||
// 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
|
||||
// and gid maps are empty.
|
||||
func (i *IdentityMapping) RootPair() Identity {
|
||||
uid, gid, _ := GetRootUIDGID(i.uids, i.gids)
|
||||
func (i IdentityMapping) RootPair() Identity {
|
||||
uid, gid, _ := GetRootUIDGID(i.UIDMaps, i.GIDMaps)
|
||||
return Identity{UID: uid, GID: 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
|
||||
func (i *IdentityMapping) ToHost(pair Identity) (Identity, error) {
|
||||
func (i IdentityMapping) ToHost(pair Identity) (Identity, error) {
|
||||
var err error
|
||||
target := i.RootPair()
|
||||
|
||||
if pair.UID != target.UID {
|
||||
target.UID, err = toHost(pair.UID, i.uids)
|
||||
target.UID, err = toHost(pair.UID, i.UIDMaps)
|
||||
if err != nil {
|
||||
return target, err
|
||||
}
|
||||
}
|
||||
|
||||
if pair.GID != target.GID {
|
||||
target.GID, err = toHost(pair.GID, i.gids)
|
||||
target.GID, err = toHost(pair.GID, i.GIDMaps)
|
||||
}
|
||||
return target, err
|
||||
}
|
||||
|
||||
// ToContainer returns the container UID and GID for the host uid and gid
|
||||
func (i *IdentityMapping) ToContainer(pair Identity) (int, int, error) {
|
||||
uid, err := toContainer(pair.UID, i.uids)
|
||||
func (i IdentityMapping) ToContainer(pair Identity) (int, int, error) {
|
||||
uid, err := toContainer(pair.UID, i.UIDMaps)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
gid, err := toContainer(pair.GID, i.gids)
|
||||
gid, err := toContainer(pair.GID, i.GIDMaps)
|
||||
return uid, gid, err
|
||||
}
|
||||
|
||||
// Empty returns true if there are no id mappings
|
||||
func (i *IdentityMapping) Empty() bool {
|
||||
return len(i.uids) == 0 && len(i.gids) == 0
|
||||
func (i IdentityMapping) Empty() bool {
|
||||
return len(i.UIDMaps) == 0 && len(i.GIDMaps) == 0
|
||||
}
|
||||
|
||||
// UIDs return the UID mapping
|
||||
// TODO: remove this once everything has been refactored to use pairs
|
||||
func (i *IdentityMapping) UIDs() []IDMap {
|
||||
return i.uids
|
||||
// UIDs returns the mapping for UID.
|
||||
//
|
||||
// Deprecated: reference the UIDMaps field directly.
|
||||
func (i IdentityMapping) UIDs() []IDMap {
|
||||
return i.UIDMaps
|
||||
}
|
||||
|
||||
// GIDs return the UID mapping
|
||||
// TODO: remove this once everything has been refactored to use pairs
|
||||
func (i *IdentityMapping) GIDs() []IDMap {
|
||||
return i.gids
|
||||
// GIDs returns the mapping for GID.
|
||||
//
|
||||
// Deprecated: reference the GIDMaps field directly.
|
||||
func (i IdentityMapping) GIDs() []IDMap {
|
||||
return i.GIDMaps
|
||||
}
|
||||
|
||||
func createIDMap(subidRanges ranges) []IDMap {
|
||||
|
|
|
@ -240,24 +240,37 @@ func setPermissions(p string, mode os.FileMode, uid, gid int, stat *system.StatT
|
|||
// NewIdentityMapping 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
|
||||
//
|
||||
// Deprecated: Use LoadIdentityMapping.
|
||||
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)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return IdentityMapping{}, err
|
||||
}
|
||||
subgidRanges, err := lookupSubGIDRanges(usr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return IdentityMapping{}, err
|
||||
}
|
||||
|
||||
return &IdentityMapping{
|
||||
uids: subuidRanges,
|
||||
gids: subgidRanges,
|
||||
return IdentityMapping{
|
||||
UIDMaps: subuidRanges,
|
||||
GIDMaps: subgidRanges,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -10,15 +10,13 @@ import (
|
|||
"github.com/docker/distribution/registry/client/auth/challenge"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"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/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// AuthClientID is used the ClientID used for the token server
|
||||
AuthClientID = "docker"
|
||||
)
|
||||
// AuthClientID is used the ClientID used for the token server
|
||||
const AuthClientID = "docker"
|
||||
|
||||
type loginCredentialStore struct {
|
||||
authConfig *types.AuthConfig
|
||||
|
@ -65,14 +63,6 @@ func (scs staticCredentialStore) RefreshToken(*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
|
||||
// endpoint will be pinged to get authorization challenges. These challenges
|
||||
// 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 (
|
||||
endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/"
|
||||
modifiers = Headers(userAgent, nil)
|
||||
authTransport = transport.NewTransport(NewTransport(endpoint.TLSConfig), modifiers...)
|
||||
authTransport = transport.NewTransport(newTransport(endpoint.TLSConfig), modifiers...)
|
||||
credentialAuthConfig = *authConfig
|
||||
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
|
||||
err = errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
return "", "", err
|
||||
return "", "", errors.Errorf("login attempt to %s failed with status: %d %s", endpointStr, resp.StatusCode, http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
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)
|
||||
basicHandler := auth.NewBasicHandler(creds)
|
||||
modifiers = append(modifiers, auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))
|
||||
tr := transport.NewTransport(authTransport, modifiers...)
|
||||
|
||||
return &http.Client{
|
||||
Transport: tr,
|
||||
Transport: transport.NewTransport(authTransport, modifiers...),
|
||||
Timeout: 15 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
@ -146,14 +134,11 @@ func ConvertToHostname(url string) string {
|
|||
} else if strings.HasPrefix(url, "https://") {
|
||||
stripped = strings.TrimPrefix(url, "https://")
|
||||
}
|
||||
|
||||
nameParts := strings.SplitN(stripped, "/", 2)
|
||||
|
||||
return nameParts[0]
|
||||
return strings.SplitN(stripped, "/", 2)[0]
|
||||
}
|
||||
|
||||
// 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)
|
||||
// First try the happy case
|
||||
if c, found := authConfigs[configKey]; found || index.Official {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package registry // import "github.com/docker/docker/registry"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
@ -9,8 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
registrytypes "github.com/docker/docker/api/types/registry"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
@ -22,9 +20,7 @@ type ServiceOptions struct {
|
|||
}
|
||||
|
||||
// serviceConfig holds daemon configuration for the registry service.
|
||||
type serviceConfig struct {
|
||||
registrytypes.ServiceConfig
|
||||
}
|
||||
type serviceConfig registry.ServiceConfig
|
||||
|
||||
// TODO(thaJeztah) both the "index.docker.io" and "registry-1.docker.io" domains
|
||||
// are here for historic reasons and backward-compatibility. These domains
|
||||
|
@ -58,70 +54,92 @@ var (
|
|||
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{})
|
||||
validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`)
|
||||
|
||||
// for mocking in unit tests
|
||||
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
|
||||
func newServiceConfig(options ServiceOptions) (*serviceConfig, error) {
|
||||
config := &serviceConfig{
|
||||
ServiceConfig: registrytypes.ServiceConfig{
|
||||
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 {
|
||||
config := &serviceConfig{}
|
||||
if err := config.loadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.LoadMirrors(options.Mirrors); err != nil {
|
||||
if err := config.loadMirrors(options.Mirrors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.LoadInsecureRegistries(options.InsecureRegistries); err != nil {
|
||||
if err := config.loadInsecureRegistries(options.InsecureRegistries); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// LoadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config.
|
||||
func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []string) error {
|
||||
cidrs := map[string]*registrytypes.NetIPNet{}
|
||||
// copy constructs a new ServiceConfig with a copy of the configuration in config.
|
||||
func (config *serviceConfig) copy() *registry.ServiceConfig {
|
||||
ic := make(map[string]*registry.IndexInfo)
|
||||
for key, value := range config.IndexConfigs {
|
||||
ic[key] = value
|
||||
}
|
||||
return ®istry.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{}
|
||||
|
||||
for _, r := range registries {
|
||||
if _, err := ValidateIndexName(r); err != nil {
|
||||
return err
|
||||
}
|
||||
if validateNoScheme(r) != nil {
|
||||
return fmt.Errorf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
|
||||
if hasScheme(r) {
|
||||
return invalidParamf("allow-nondistributable-artifacts registry %s should not contain '://'", r)
|
||||
}
|
||||
|
||||
if _, ipnet, err := net.ParseCIDR(r); err == nil {
|
||||
// Valid CIDR.
|
||||
cidrs[ipnet.String()] = (*registrytypes.NetIPNet)(ipnet)
|
||||
} else if err := validateHostPort(r); err == nil {
|
||||
cidrs[ipnet.String()] = (*registry.NetIPNet)(ipnet)
|
||||
} else if err = validateHostPort(r); err == nil {
|
||||
// Must be `host:port` if not CIDR.
|
||||
hostnames[r] = true
|
||||
} 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 {
|
||||
config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c)
|
||||
}
|
||||
|
||||
config.AllowNondistributableArtifactsHostnames = make([]string, 0)
|
||||
config.AllowNondistributableArtifactsHostnames = make([]string, 0, len(hostnames))
|
||||
for h := range hostnames {
|
||||
config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h)
|
||||
}
|
||||
|
@ -129,9 +147,9 @@ func (config *serviceConfig) LoadAllowNondistributableArtifacts(registries []str
|
|||
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.
|
||||
func (config *serviceConfig) LoadMirrors(mirrors []string) error {
|
||||
func (config *serviceConfig) loadMirrors(mirrors []string) error {
|
||||
mMap := map[string]struct{}{}
|
||||
unique := []string{}
|
||||
|
||||
|
@ -149,40 +167,33 @@ func (config *serviceConfig) LoadMirrors(mirrors []string) error {
|
|||
config.Mirrors = unique
|
||||
|
||||
// Configure public registry since mirrors may have changed.
|
||||
config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{
|
||||
config.IndexConfigs = map[string]*registry.IndexInfo{
|
||||
IndexName: {
|
||||
Name: IndexName,
|
||||
Mirrors: config.Mirrors,
|
||||
Mirrors: unique,
|
||||
Secure: true,
|
||||
Official: true,
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadInsecureRegistries loads insecure registries to config
|
||||
func (config *serviceConfig) LoadInsecureRegistries(registries []string) error {
|
||||
// Localhost is by default considered as an insecure registry
|
||||
// This is a stop-gap for people who are running a private registry on localhost (especially on Boot2docker).
|
||||
//
|
||||
// TODO: should we deprecate this once it is easier for people to set up a TLS registry or change
|
||||
// daemon flags on boot2docker?
|
||||
// loadInsecureRegistries loads insecure registries to config
|
||||
func (config *serviceConfig) loadInsecureRegistries(registries []string) error {
|
||||
// Localhost is by default considered as an insecure registry. This is a
|
||||
// stop-gap for people who are running a private registry on localhost.
|
||||
registries = append(registries, "127.0.0.0/8")
|
||||
|
||||
// Store original InsecureRegistryCIDRs and IndexConfigs
|
||||
// Clean InsecureRegistryCIDRs and IndexConfigs in config, as passed registries has all insecure registry info.
|
||||
originalCIDRs := config.ServiceConfig.InsecureRegistryCIDRs
|
||||
originalIndexInfos := config.ServiceConfig.IndexConfigs
|
||||
|
||||
config.ServiceConfig.InsecureRegistryCIDRs = make([]*registrytypes.NetIPNet, 0)
|
||||
config.ServiceConfig.IndexConfigs = make(map[string]*registrytypes.IndexInfo)
|
||||
var (
|
||||
insecureRegistryCIDRs = make([]*registry.NetIPNet, 0)
|
||||
indexConfigs = make(map[string]*registry.IndexInfo)
|
||||
)
|
||||
|
||||
skip:
|
||||
for _, r := range registries {
|
||||
// validate insecure registry
|
||||
if _, err := ValidateIndexName(r); err != nil {
|
||||
// before returning err, roll back to original data
|
||||
config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
|
||||
config.ServiceConfig.IndexConfigs = originalIndexInfos
|
||||
return err
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(r), "http://") {
|
||||
|
@ -191,35 +202,27 @@ skip:
|
|||
} 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)
|
||||
r = r[8:]
|
||||
} else if validateNoScheme(r) != nil {
|
||||
// Insecure registry should not contain '://'
|
||||
// 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)
|
||||
} else if hasScheme(r) {
|
||||
return invalidParamf("insecure registry %s should not contain '://'", r)
|
||||
}
|
||||
// Check if CIDR was passed to --insecure-registry
|
||||
_, ipnet, err := net.ParseCIDR(r)
|
||||
if err == nil {
|
||||
// Valid CIDR. If ipnet is already in config.InsecureRegistryCIDRs, skip.
|
||||
data := (*registrytypes.NetIPNet)(ipnet)
|
||||
for _, value := range config.InsecureRegistryCIDRs {
|
||||
data := (*registry.NetIPNet)(ipnet)
|
||||
for _, value := range insecureRegistryCIDRs {
|
||||
if value.IP.String() == data.IP.String() && value.Mask.String() == data.Mask.String() {
|
||||
continue skip
|
||||
}
|
||||
}
|
||||
// ipnet is not found, add it in config.InsecureRegistryCIDRs
|
||||
config.InsecureRegistryCIDRs = append(config.InsecureRegistryCIDRs, data)
|
||||
|
||||
insecureRegistryCIDRs = append(insecureRegistryCIDRs, data)
|
||||
} else {
|
||||
if err := validateHostPort(r); err != nil {
|
||||
config.ServiceConfig.InsecureRegistryCIDRs = originalCIDRs
|
||||
config.ServiceConfig.IndexConfigs = originalIndexInfos
|
||||
return fmt.Errorf("insecure registry %s is not valid: %v", r, err)
|
||||
|
||||
return invalidParamWrapf(err, "insecure registry %s is not valid", r)
|
||||
}
|
||||
// Assume `host:port` if not CIDR.
|
||||
config.IndexConfigs[r] = ®istrytypes.IndexInfo{
|
||||
indexConfigs[r] = ®istry.IndexInfo{
|
||||
Name: r,
|
||||
Mirrors: make([]string, 0),
|
||||
Secure: false,
|
||||
|
@ -229,12 +232,14 @@ skip:
|
|||
}
|
||||
|
||||
// Configure public registry.
|
||||
config.IndexConfigs[IndexName] = ®istrytypes.IndexInfo{
|
||||
indexConfigs[IndexName] = ®istry.IndexInfo{
|
||||
Name: IndexName,
|
||||
Mirrors: config.Mirrors,
|
||||
Secure: true,
|
||||
Official: true,
|
||||
}
|
||||
config.InsecureRegistryCIDRs = insecureRegistryCIDRs
|
||||
config.IndexConfigs = indexConfigs
|
||||
|
||||
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
|
||||
// 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.
|
||||
func allowNondistributableArtifacts(config *serviceConfig, hostname string) bool {
|
||||
func (config *serviceConfig) allowNondistributableArtifacts(hostname string) bool {
|
||||
for _, h := range config.AllowNondistributableArtifactsHostnames {
|
||||
if h == hostname {
|
||||
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
|
||||
// in a subnet. If the resolving is not successful, isSecureIndex will only try to match hostname to any element
|
||||
// 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
|
||||
// is called from anything besides newIndexInfo, in order to honor per-index configurations.
|
||||
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`)
|
||||
// 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.
|
||||
func isCIDRMatch(cidrs []*registrytypes.NetIPNet, URLHost string) bool {
|
||||
func isCIDRMatch(cidrs []*registry.NetIPNet, URLHost string) bool {
|
||||
host, _, err := net.SplitHostPort(URLHost)
|
||||
if err != nil {
|
||||
// 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) {
|
||||
uri, err := url.Parse(val)
|
||||
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" {
|
||||
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 != "" {
|
||||
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 {
|
||||
// strip password from output
|
||||
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
|
||||
}
|
||||
|
@ -341,17 +346,13 @@ func ValidateIndexName(val string) (string, error) {
|
|||
val = "docker.io"
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
func validateNoScheme(reposName string) error {
|
||||
if strings.Contains(reposName, "://") {
|
||||
// It cannot contain a scheme!
|
||||
return ErrInvalidRepositoryName
|
||||
}
|
||||
return nil
|
||||
func hasScheme(reposName string) bool {
|
||||
return strings.Contains(reposName, "://")
|
||||
}
|
||||
|
||||
func validateHostPort(s string) error {
|
||||
|
@ -364,7 +365,7 @@ func validateHostPort(s string) error {
|
|||
// If match against the `host:port` pattern fails,
|
||||
// it might be `IPv6:port`, which will be captured by net.ParseIP(host)
|
||||
if !validHostPortRegex.MatchString(s) && net.ParseIP(host) == nil {
|
||||
return fmt.Errorf("invalid host %q", host)
|
||||
return invalidParamf("invalid host %q", host)
|
||||
}
|
||||
if port != "" {
|
||||
v, err := strconv.Atoi(port)
|
||||
|
@ -372,14 +373,14 @@ func validateHostPort(s string) error {
|
|||
return err
|
||||
}
|
||||
if v < 0 || v > 65535 {
|
||||
return fmt.Errorf("invalid port %q", port)
|
||||
return invalidParamf("invalid port %q", port)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
indexName, err = ValidateIndexName(indexName)
|
||||
if err != nil {
|
||||
|
@ -392,18 +393,17 @@ func newIndexInfo(config *serviceConfig, indexName string) (*registrytypes.Index
|
|||
}
|
||||
|
||||
// Construct a non-configured index info.
|
||||
index := ®istrytypes.IndexInfo{
|
||||
return ®istry.IndexInfo{
|
||||
Name: indexName,
|
||||
Mirrors: make([]string, 0),
|
||||
Secure: config.isSecureIndex(indexName),
|
||||
Official: false,
|
||||
}
|
||||
index.Secure = isSecureIndex(config, indexName)
|
||||
return index, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func GetAuthConfigKey(index *registrytypes.IndexInfo) string {
|
||||
func GetAuthConfigKey(index *registry.IndexInfo) string {
|
||||
if index.Official {
|
||||
return IndexServer
|
||||
}
|
||||
|
@ -432,7 +432,12 @@ func ParseRepositoryInfo(reposName reference.Named) (*RepositoryInfo, error) {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
indexInfo, err := newIndexInfo(emptyServiceConfig, indexName)
|
||||
|
|
|
@ -3,25 +3,10 @@
|
|||
|
||||
package registry // import "github.com/docker/docker/registry"
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"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
|
||||
}
|
||||
// defaultCertsDir is the platform-specific default directory where certificates
|
||||
// are stored. On Linux, it may be overridden through certsDir, for example, when
|
||||
// running in rootless mode.
|
||||
const defaultCertsDir = "/etc/docker/certs.d"
|
||||
|
||||
// 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
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// CertsDir is the directory where certificates are stored
|
||||
func CertsDir() string {
|
||||
return os.Getenv("programdata") + `\docker\certs.d`
|
||||
}
|
||||
// defaultCertsDir is the platform-specific default directory where certificates
|
||||
// are stored. On Linux, it may be overridden through certsDir, for example, when
|
||||
// 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
|
||||
// platform. It will be passed in something *similar* to a URL such as
|
||||
|
|
|
@ -3,27 +3,39 @@ package registry // import "github.com/docker/docker/registry"
|
|||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// V1Endpoint stores basic information about a V1 registry endpoint.
|
||||
type V1Endpoint struct {
|
||||
// v1PingResult 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 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
|
||||
URL *url.URL
|
||||
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.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -42,28 +54,28 @@ func NewV1Endpoint(index *registrytypes.IndexInfo, userAgent string, metaHeaders
|
|||
return endpoint, nil
|
||||
}
|
||||
|
||||
func validateEndpoint(endpoint *V1Endpoint) error {
|
||||
func validateEndpoint(endpoint *v1Endpoint) error {
|
||||
logrus.Debugf("pinging registry endpoint %s", endpoint)
|
||||
|
||||
// Try HTTPS ping to registry
|
||||
endpoint.URL.Scheme = "https"
|
||||
if _, err := endpoint.Ping(); err != nil {
|
||||
if _, err := endpoint.ping(); err != nil {
|
||||
if endpoint.IsSecure {
|
||||
// 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.
|
||||
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.
|
||||
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"
|
||||
|
||||
var err2 error
|
||||
if _, err2 = endpoint.Ping(); err2 == nil {
|
||||
if _, err2 = endpoint.ping(); err2 == 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
|
||||
|
@ -72,28 +84,23 @@ func validateEndpoint(endpoint *V1Endpoint) error {
|
|||
// trimV1Address trims the version off the address and returns the
|
||||
// trimmed address or an error if there is a non-V1 version.
|
||||
func trimV1Address(address string) (string, error) {
|
||||
var (
|
||||
chunks []string
|
||||
apiVersionStr string
|
||||
)
|
||||
|
||||
address = strings.TrimSuffix(address, "/")
|
||||
chunks = strings.Split(address, "/")
|
||||
apiVersionStr = chunks[len(chunks)-1]
|
||||
chunks := strings.Split(address, "/")
|
||||
apiVersionStr := chunks[len(chunks)-1]
|
||||
if apiVersionStr == "v1" {
|
||||
return strings.Join(chunks[:len(chunks)-1], "/"), nil
|
||||
}
|
||||
|
||||
for k, v := range apiVersions {
|
||||
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
|
||||
}
|
||||
|
||||
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://") {
|
||||
address = "https://" + address
|
||||
}
|
||||
|
@ -105,69 +112,64 @@ func newV1EndpointFromStr(address string, tlsConfig *tls.Config, userAgent strin
|
|||
|
||||
uri, err := url.Parse(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, invalidParam(err)
|
||||
}
|
||||
|
||||
// TODO(tiborvass): make sure a ConnectTimeout transport is used
|
||||
tr := NewTransport(tlsConfig)
|
||||
tr := newTransport(tlsConfig)
|
||||
|
||||
return &V1Endpoint{
|
||||
return &v1Endpoint{
|
||||
IsSecure: tlsConfig == nil || !tlsConfig.InsecureSkipVerify,
|
||||
URL: uri,
|
||||
client: HTTPClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)),
|
||||
client: httpClient(transport.NewTransport(tr, Headers(userAgent, metaHeaders)...)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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/"
|
||||
}
|
||||
|
||||
// Path returns a formatted string for the URL
|
||||
// of this endpoint with the given path appended.
|
||||
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) {
|
||||
// ping returns a v1PingResult which indicates whether the registry is standalone or not.
|
||||
func (e *v1Endpoint) ping() (v1PingResult, error) {
|
||||
if e.String() == IndexServer {
|
||||
// Skip the check, we know this one is valid
|
||||
// (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)
|
||||
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 {
|
||||
return PingResult{}, err
|
||||
return v1PingResult{}, invalidParam(err)
|
||||
}
|
||||
|
||||
resp, err := e.client.Do(req)
|
||||
if err != nil {
|
||||
return PingResult{}, err
|
||||
return v1PingResult{}, invalidParam(err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
jsonString, err := io.ReadAll(resp.Body)
|
||||
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
|
||||
// versions of the registry. default to true
|
||||
info := PingResult{
|
||||
info := v1PingResult{
|
||||
Standalone: true,
|
||||
}
|
||||
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
|
||||
}
|
||||
if hdr := resp.Header.Get("X-Docker-Registry-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")
|
||||
|
||||
|
@ -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
|
||||
info.Standalone = false
|
||||
}
|
||||
logrus.Debugf("PingResult.Standalone: %t", info.Standalone)
|
||||
logrus.Debugf("v1PingResult.Standalone: %t", info.Standalone)
|
||||
return info, nil
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/docker/distribution/registry/api/errcode"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func translateV2AuthError(err error) error {
|
||||
|
@ -21,3 +22,15 @@ func translateV2AuthError(err error) error {
|
|||
|
||||
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...))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package registry // import "github.com/docker/docker/registry"
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
@ -16,15 +15,12 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// HostCertsDir returns the config directory for a specific host
|
||||
func HostCertsDir(hostname string) (string, error) {
|
||||
certsDir := CertsDir()
|
||||
|
||||
hostDir := filepath.Join(certsDir, cleanPath(hostname))
|
||||
|
||||
return hostDir, nil
|
||||
// HostCertsDir returns the config directory for a specific host.
|
||||
func HostCertsDir(hostname string) string {
|
||||
return filepath.Join(CertsDir(), cleanPath(hostname))
|
||||
}
|
||||
|
||||
// newTLSConfig constructs a client TLS configuration based on server defaults
|
||||
func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
|
||||
// PreferredServerCipherSuites should have no effect
|
||||
tlsConfig := tlsconfig.ServerDefault()
|
||||
|
@ -32,11 +28,7 @@ func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
|
|||
tlsConfig.InsecureSkipVerify = !isSecure
|
||||
|
||||
if isSecure && CertsDir() != "" {
|
||||
hostDir, err := HostCertsDir(hostname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hostDir := HostCertsDir(hostname)
|
||||
logrus.Debugf("hostDir: %s", hostDir)
|
||||
if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
|
||||
return nil, err
|
||||
|
@ -61,7 +53,7 @@ func hasFile(files []os.DirEntry, name string) bool {
|
|||
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
||||
fs, err := os.ReadDir(directory)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
return invalidParam(err)
|
||||
}
|
||||
|
||||
for _, f := range fs {
|
||||
|
@ -69,7 +61,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
|||
if tlsConfig.RootCAs == nil {
|
||||
systemPool, err := tlsconfig.SystemCertPool()
|
||||
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
|
||||
}
|
||||
|
@ -85,7 +77,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
|||
keyName := certName[:len(certName)-5] + ".key"
|
||||
logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
|
||||
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))
|
||||
if err != nil {
|
||||
|
@ -98,7 +90,7 @@ func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
|
|||
certName := keyName[:len(keyName)-4] + ".cert"
|
||||
logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
func HTTPClient(transport http.RoundTripper) *http.Client {
|
||||
func httpClient(transport http.RoundTripper) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: addRequiredHeadersToRedirectedRequests,
|
||||
|
@ -165,9 +157,9 @@ func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Reque
|
|||
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.
|
||||
func NewTransport(tlsConfig *tls.Config) *http.Transport {
|
||||
func newTransport(tlsConfig *tls.Config) *http.Transport {
|
||||
if tlsConfig == nil {
|
||||
tlsConfig = tlsconfig.ServerDefault()
|
||||
}
|
||||
|
@ -177,7 +169,7 @@ func NewTransport(tlsConfig *tls.Config) *http.Transport {
|
|||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
|
||||
base := &http.Transport{
|
||||
return &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: direct.DialContext,
|
||||
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
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
|
||||
return base
|
||||
}
|
||||
|
|
|
@ -11,102 +11,74 @@ import (
|
|||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"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/pkg/errors"
|
||||
"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.
|
||||
type Service interface {
|
||||
Auth(ctx context.Context, authConfig *types.AuthConfig, userAgent string) (status, token string, err error)
|
||||
LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error)
|
||||
LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err 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)
|
||||
ServiceConfig() *registrytypes.ServiceConfig
|
||||
TLSConfig(hostname string) (*tls.Config, error)
|
||||
Search(ctx context.Context, term string, limit int, authConfig *types.AuthConfig, userAgent string, headers map[string][]string) (*registry.SearchResults, error)
|
||||
ServiceConfig() *registry.ServiceConfig
|
||||
LoadAllowNondistributableArtifacts([]string) error
|
||||
LoadMirrors([]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.
|
||||
type DefaultService struct {
|
||||
type defaultService struct {
|
||||
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.
|
||||
func NewService(options ServiceOptions) (*DefaultService, error) {
|
||||
func NewService(options ServiceOptions) (Service, error) {
|
||||
config, err := newServiceConfig(options)
|
||||
|
||||
return &DefaultService{config: config}, err
|
||||
return &defaultService{config: config}, err
|
||||
}
|
||||
|
||||
// ServiceConfig returns the public registry service configuration.
|
||||
func (s *DefaultService) ServiceConfig() *registrytypes.ServiceConfig {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
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
|
||||
// ServiceConfig returns a copy of the public registry service's configuration.
|
||||
func (s *defaultService) ServiceConfig() *registry.ServiceConfig {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.config.copy()
|
||||
}
|
||||
|
||||
// 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()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.config.LoadAllowNondistributableArtifacts(registries)
|
||||
return s.config.loadAllowNondistributableArtifacts(registries)
|
||||
}
|
||||
|
||||
// LoadMirrors loads registry mirrors for Service
|
||||
func (s *DefaultService) LoadMirrors(mirrors []string) error {
|
||||
func (s *defaultService) LoadMirrors(mirrors []string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.config.LoadMirrors(mirrors)
|
||||
return s.config.loadMirrors(mirrors)
|
||||
}
|
||||
|
||||
// LoadInsecureRegistries loads insecure registries for Service
|
||||
func (s *DefaultService) LoadInsecureRegistries(registries []string) error {
|
||||
func (s *defaultService) LoadInsecureRegistries(registries []string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
return s.config.LoadInsecureRegistries(registries)
|
||||
return s.config.loadInsecureRegistries(registries)
|
||||
}
|
||||
|
||||
// Auth contacts the public registry with the provided credentials,
|
||||
// and returns OK if authentication was successful.
|
||||
// It can be used to verify the validity of a client's credentials.
|
||||
func (s *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
|
||||
var registryHostName = IndexHostname
|
||||
|
||||
|
@ -117,7 +89,7 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
|
|||
}
|
||||
u, err := url.Parse(serverAddress)
|
||||
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
|
||||
}
|
||||
|
@ -127,7 +99,7 @@ func (s *DefaultService) Auth(ctx context.Context, authConfig *types.AuthConfig,
|
|||
// to a mirror.
|
||||
endpoints, err := s.LookupPushEndpoints(registryHostName)
|
||||
if err != nil {
|
||||
return "", "", errdefs.InvalidParameter(err)
|
||||
return "", "", invalidParam(err)
|
||||
}
|
||||
|
||||
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 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
|
||||
if err := validateNoScheme(term); err != nil {
|
||||
return nil, err
|
||||
if hasScheme(term) {
|
||||
return nil, invalidParamf("invalid repository name: repository name (%s) should not have a scheme", term)
|
||||
}
|
||||
|
||||
indexName, remoteName := splitReposSearchTerm(term)
|
||||
|
||||
// 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)
|
||||
s.mu.Unlock()
|
||||
s.mu.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if index.Official {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
remoteName = strings.TrimPrefix(remoteName, "library/")
|
||||
}
|
||||
|
||||
// *TODO: Search multiple indexes.
|
||||
endpoint, err := NewV1Endpoint(index, userAgent, headers)
|
||||
endpoint, err := newV1Endpoint(index, userAgent, headers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -195,12 +170,8 @@ func (s *DefaultService) Search(ctx context.Context, term string, limit int, aut
|
|||
modifiers := Headers(userAgent, nil)
|
||||
v2Client, err := v2AuthHTTPClient(endpoint.URL, endpoint.client.Transport, modifiers, creds, scopes)
|
||||
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
|
||||
}
|
||||
} else {
|
||||
// Copy non transport http client features
|
||||
v2Client.Timeout = endpoint.client.Timeout
|
||||
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)
|
||||
client = v2Client
|
||||
}
|
||||
}
|
||||
|
||||
if client == nil {
|
||||
} else {
|
||||
client = endpoint.client
|
||||
if err := authorizeClient(client, authConfig, endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
r := newSession(client, authConfig, endpoint)
|
||||
|
||||
if index.Official {
|
||||
// If pull "library/foo", it's stored locally under "foo"
|
||||
remoteName = strings.TrimPrefix(remoteName, "library/")
|
||||
}
|
||||
return r.SearchRepositories(remoteName, limit)
|
||||
return newSession(client, endpoint).searchRepositories(remoteName, limit)
|
||||
}
|
||||
|
||||
// ResolveRepository splits a repository name into its components
|
||||
// and configuration of the associated registry.
|
||||
func (s *DefaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
func (s *defaultService) ResolveRepository(name reference.Named) (*RepositoryInfo, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return newRepositoryInfo(s.config, name)
|
||||
}
|
||||
|
||||
|
@ -246,33 +208,20 @@ type APIEndpoint struct {
|
|||
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.
|
||||
// It gives preference to mirrors over the actual registry, and HTTPS over plain HTTP.
|
||||
func (s *DefaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
func (s *defaultService) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
return s.lookupV2Endpoints(hostname)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (s *DefaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
func (s *defaultService) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
allEndpoints, err := s.lookupV2Endpoints(hostname)
|
||||
if err == nil {
|
||||
|
|
|
@ -7,8 +7,7 @@ import (
|
|||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
tlsConfig := tlsconfig.ServerDefault()
|
||||
func (s *defaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) {
|
||||
if hostname == DefaultNamespace || hostname == IndexHostname {
|
||||
for _, mirror := range s.config.Mirrors {
|
||||
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)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -35,19 +34,18 @@ func (s *DefaultService) lookupV2Endpoints(hostname string) (endpoints []APIEndp
|
|||
Version: APIVersion2,
|
||||
Official: true,
|
||||
TrimHostname: true,
|
||||
TLSConfig: tlsConfig,
|
||||
TLSConfig: tlsconfig.ServerDefault(),
|
||||
})
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
ana := allowNondistributableArtifacts(s.config, hostname)
|
||||
|
||||
tlsConfig, err = s.tlsConfig(hostname)
|
||||
tlsConfig, err := newTLSConfig(hostname, s.config.isSecureIndex(hostname))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ana := s.config.allowNondistributableArtifacts(hostname)
|
||||
endpoints = []APIEndpoint{
|
||||
{
|
||||
URL: &url.URL{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"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/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/jsonmessage"
|
||||
|
@ -21,12 +21,10 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// A Session is used to communicate with a V1 registry
|
||||
type Session struct {
|
||||
indexEndpoint *V1Endpoint
|
||||
// A session is used to communicate with a V1 registry
|
||||
type session struct {
|
||||
indexEndpoint *v1Endpoint
|
||||
client *http.Client
|
||||
// TODO(tiborvass): remove authConfig
|
||||
authConfig *types.AuthConfig
|
||||
id string
|
||||
}
|
||||
|
||||
|
@ -41,7 +39,7 @@ type authTransport struct {
|
|||
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.
|
||||
//
|
||||
|
@ -54,7 +52,7 @@ type authTransport struct {
|
|||
// 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.
|
||||
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 {
|
||||
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
|
||||
|
||||
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers
|
||||
// alongside all our requests.
|
||||
if endpoint.String() != IndexServer && endpoint.URL.Scheme == "https" {
|
||||
info, err := endpoint.Ping()
|
||||
info, err := endpoint.ping()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -167,47 +165,42 @@ func authorizeClient(client *http.Client, authConfig *types.AuthConfig, endpoint
|
|||
|
||||
// Annotate the transport unconditionally so that v2 can
|
||||
// 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)
|
||||
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
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newSession(client *http.Client, authConfig *types.AuthConfig, endpoint *V1Endpoint) *Session {
|
||||
return &Session{
|
||||
authConfig: authConfig,
|
||||
func newSession(client *http.Client, endpoint *v1Endpoint) *session {
|
||||
return &session{
|
||||
client: client,
|
||||
indexEndpoint: endpoint,
|
||||
id: stringid.GenerateRandomID(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewSession creates a new session
|
||||
// TODO(tiborvass): remove authConfig param once registry client v2 is vendored
|
||||
func NewSession(client *http.Client, authConfig *types.AuthConfig, endpoint *V1Endpoint) (*Session, error) {
|
||||
if err := authorizeClient(client, authConfig, endpoint); err != nil {
|
||||
return nil, err
|
||||
// defaultSearchLimit is the default value for maximum number of returned search results.
|
||||
const defaultSearchLimit = 25
|
||||
|
||||
// searchRepositories performs a search against the remote repository
|
||||
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 {
|
||||
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)
|
||||
u := r.indexEndpoint.String() + "search?q=" + url.QueryEscape(term) + "&n=" + url.QueryEscape(fmt.Sprintf("%d", limit))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u, 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.
|
||||
req.Header.Set("X-Docker-Token", "true")
|
||||
|
@ -222,6 +215,6 @@ func (r *Session) SearchRepositories(term string, limit int) (*registrytypes.Sea
|
|||
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")
|
||||
}
|
||||
|
|
|
@ -2,39 +2,9 @@ package registry // import "github.com/docker/docker/registry"
|
|||
|
||||
import (
|
||||
"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
|
||||
// either 1 or 2)
|
||||
type APIVersion int
|
||||
|
@ -58,7 +28,7 @@ var apiVersions = map[APIVersion]string{
|
|||
type RepositoryInfo struct {
|
||||
Name reference.Named
|
||||
// Index points to registry information
|
||||
Index *registrytypes.IndexInfo
|
||||
Index *registry.IndexInfo
|
||||
// Official indicates whether the repository is considered official.
|
||||
// If the registry is official, and the normalized name does not
|
||||
// contain a '/' (e.g. "foo"), then it is considered an official repo.
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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/memory
|
||||
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
|
||||
github.com/docker/docker/api
|
||||
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/urlutil
|
||||
github.com/docker/docker/registry
|
||||
github.com/docker/docker/rootless
|
||||
# github.com/docker/docker-credential-helpers v0.6.4
|
||||
## explicit; go 1.13
|
||||
github.com/docker/docker-credential-helpers/client
|
||||
|
@ -385,5 +384,5 @@ gotest.tools/v3/internal/format
|
|||
gotest.tools/v3/internal/source
|
||||
gotest.tools/v3/poll
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue