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 | ||||
| 
 | ||||
|     example: | ||||
|       Name: "tardis" | ||||
|       Driver: "custom" | ||||
|       Mountpoint: "/var/lib/docker/volumes/tardis" | ||||
|       Status: | ||||
|         hello: "world" | ||||
|   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: | ||||
|           device: "tmpfs" | ||||
|           o: "size=100m,uid=1000" | ||||
|           type: "tmpfs" | ||||
|       Labels: | ||||
|         com.example.some-label: "some-value" | ||||
|         com.example.some-other-label: "some-other-value" | ||||
|       Scope: "local" | ||||
|       CreatedAt: "2016-06-07T20:31:11.853781916Z" | ||||
|         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" | ||||
| 
 | ||||
|   Network: | ||||
|     type: "object" | ||||
|  | @ -2035,15 +2070,27 @@ definitions: | |||
|           ``` | ||||
|         type: "array" | ||||
|         items: | ||||
|           type: "object" | ||||
|           additionalProperties: | ||||
|             type: "string" | ||||
|           $ref: "#/definitions/IPAMConfig" | ||||
|       Options: | ||||
|         description: "Driver-specific options, specified as a map." | ||||
|         type: "object" | ||||
|         additionalProperties: | ||||
|           type: "string" | ||||
| 
 | ||||
|   IPAMConfig: | ||||
|     type: "object" | ||||
|     properties: | ||||
|       Subnet: | ||||
|         type: "string" | ||||
|       IPRange: | ||||
|         type: "string" | ||||
|       Gateway: | ||||
|         type: "string" | ||||
|       AuxiliaryAddresses: | ||||
|         type: "object" | ||||
|         additionalProperties: | ||||
|           type: "string" | ||||
| 
 | ||||
|   NetworkContainer: | ||||
|     type: "object" | ||||
|     properties: | ||||
|  | @ -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,14 +376,17 @@ 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)
 | ||||
| 	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
 | ||||
| 	OomKillDisable       *bool           // Whether to disable OOM Killer or not
 | ||||
| 	PidsLimit            *int64          // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
 | ||||
| 	Ulimits              []*units.Ulimit // List of ulimits to be set in the container
 | ||||
| 
 | ||||
| 	// 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
 | ||||
| 	OomKillDisable    *bool           // Whether to disable OOM Killer or not
 | ||||
| 	PidsLimit         *int64          // Setting PIDs limit for a container; Set `0` or `-1` for unlimited, or `null` to not change.
 | ||||
| 	Ulimits           []*units.Ulimit // List of ulimits to be set in the container
 | ||||
| 
 | ||||
| 	// Applicable to Windows
 | ||||
| 	CPUCount           int64  `json:"CpuCount"`   // CPU count
 | ||||
|  |  | |||
|  | @ -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{ | ||||
| 		Name:     IndexName, | ||||
| 		Mirrors:  config.Mirrors, | ||||
| 		Secure:   true, | ||||
| 		Official: true, | ||||
| 	config.IndexConfigs = map[string]*registry.IndexInfo{ | ||||
| 		IndexName: { | ||||
| 			Name:     IndexName, | ||||
| 			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,43 +170,30 @@ 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 | ||||
| 			v2Client.Jar = endpoint.client.Jar | ||||
| 
 | ||||
| 			logrus.Debugf("using v2 client for search to %s", endpoint.URL) | ||||
| 			client = v2Client | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 		// Copy non transport http client features
 | ||||
| 		v2Client.Timeout = endpoint.client.Timeout | ||||
| 		v2Client.CheckRedirect = endpoint.client.CheckRedirect | ||||
| 		v2Client.Jar = endpoint.client.Jar | ||||
| 
 | ||||
| 	if client == nil { | ||||
| 		logrus.Debugf("using v2 client for search to %s", endpoint.URL) | ||||
| 		client = v2Client | ||||
| 	} 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,13 +21,11 @@ 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 | ||||
| 	id            string | ||||
| } | ||||
| 
 | ||||
| type authTransport struct { | ||||
|  | @ -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