* Add godoc documentation where it was missing
* Change identifier names that don't match Go style, such as INDEX_NAME
* Rename RegistryInfo to PingResult, which more accurately describes
what this structure is for. It also has the benefit of making the name
not stutter if used outside the package.
Updates #14756
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
... | ... |
@@ -22,7 +22,7 @@ import ( |
22 | 22 |
// |
23 | 23 |
// Usage: docker login SERVER |
24 | 24 |
func (cli *DockerCli) CmdLogin(args ...string) error { |
25 |
- cmd := Cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) |
|
25 |
+ cmd := Cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.IndexServer+"\" is the default.", true) |
|
26 | 26 |
cmd.Require(flag.Max, 1) |
27 | 27 |
|
28 | 28 |
var username, password, email string |
... | ... |
@@ -33,7 +33,7 @@ func (cli *DockerCli) CmdLogin(args ...string) error { |
33 | 33 |
|
34 | 34 |
cmd.ParseFlags(args, true) |
35 | 35 |
|
36 |
- serverAddress := registry.INDEXSERVER |
|
36 |
+ serverAddress := registry.IndexServer |
|
37 | 37 |
if len(cmd.Args()) > 0 { |
38 | 38 |
serverAddress = cmd.Arg(0) |
39 | 39 |
} |
... | ... |
@@ -14,12 +14,12 @@ import ( |
14 | 14 |
// |
15 | 15 |
// Usage: docker logout [SERVER] |
16 | 16 |
func (cli *DockerCli) CmdLogout(args ...string) error { |
17 |
- cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true) |
|
17 |
+ cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.IndexServer+"\" is the default.", true) |
|
18 | 18 |
cmd.Require(flag.Max, 1) |
19 | 19 |
|
20 | 20 |
cmd.ParseFlags(args, true) |
21 | 21 |
|
22 |
- serverAddress := registry.INDEXSERVER |
|
22 |
+ serverAddress := registry.IndexServer |
|
23 | 23 |
if len(cmd.Args()) > 0 { |
24 | 24 |
serverAddress = cmd.Arg(0) |
25 | 25 |
} |
... | ... |
@@ -74,7 +74,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) { |
74 | 74 |
NEventsListener: daemon.EventsService.SubscribersCount(), |
75 | 75 |
KernelVersion: kernelVersion, |
76 | 76 |
OperatingSystem: operatingSystem, |
77 |
- IndexServerAddress: registry.INDEXSERVER, |
|
77 |
+ IndexServerAddress: registry.IndexServer, |
|
78 | 78 |
RegistryConfig: daemon.RegistryService.Config, |
79 | 79 |
InitSha1: dockerversion.INITSHA1, |
80 | 80 |
InitPath: initPath, |
... | ... |
@@ -33,7 +33,7 @@ func (p *v1Puller) Pull(tag string) (fallback bool, err error) { |
33 | 33 |
return true, registry.ErrNoSupport{errors.New("Cannot pull by digest with v1 registry")} |
34 | 34 |
} |
35 | 35 |
|
36 |
- tlsConfig, err := p.registryService.TlsConfig(p.repoInfo.Index.Name) |
|
36 |
+ tlsConfig, err := p.registryService.TLSConfig(p.repoInfo.Index.Name) |
|
37 | 37 |
if err != nil { |
38 | 38 |
return false, err |
39 | 39 |
} |
... | ... |
@@ -29,7 +29,7 @@ type v1Pusher struct { |
29 | 29 |
} |
30 | 30 |
|
31 | 31 |
func (p *v1Pusher) Push() (fallback bool, err error) { |
32 |
- tlsConfig, err := p.registryService.TlsConfig(p.repoInfo.Index.Name) |
|
32 |
+ tlsConfig, err := p.registryService.TLSConfig(p.repoInfo.Index.Name) |
|
33 | 33 |
if err != nil { |
34 | 34 |
return false, err |
35 | 35 |
} |
... | ... |
@@ -36,7 +36,7 @@ func loginV1(authConfig *cliconfig.AuthConfig, registryEndpoint *Endpoint) (stri |
36 | 36 |
return "", fmt.Errorf("Server Error: Server Address not set.") |
37 | 37 |
} |
38 | 38 |
|
39 |
- loginAgainstOfficialIndex := serverAddress == INDEXSERVER |
|
39 |
+ loginAgainstOfficialIndex := serverAddress == IndexServer |
|
40 | 40 |
|
41 | 41 |
// to avoid sending the server address to the server it should be removed before being marshalled |
42 | 42 |
authCopy := *authConfig |
... | ... |
@@ -220,7 +220,7 @@ func tryV2TokenAuthLogin(authConfig *cliconfig.AuthConfig, params map[string]str |
220 | 220 |
return nil |
221 | 221 |
} |
222 | 222 |
|
223 |
-// this method matches a auth configuration to a server address or a url |
|
223 |
+// ResolveAuthConfig matches an auth configuration to a server address or a URL |
|
224 | 224 |
func ResolveAuthConfig(config *cliconfig.ConfigFile, index *IndexInfo) cliconfig.AuthConfig { |
225 | 225 |
configKey := index.GetAuthConfigKey() |
226 | 226 |
// First try the happy case |
... | ... |
@@ -37,7 +37,7 @@ func setupTempConfigFile() (*cliconfig.ConfigFile, error) { |
37 | 37 |
root = filepath.Join(root, cliconfig.ConfigFileName) |
38 | 38 |
configFile := cliconfig.NewConfigFile(root) |
39 | 39 |
|
40 |
- for _, registry := range []string{"testIndex", INDEXSERVER} { |
|
40 |
+ for _, registry := range []string{"testIndex", IndexServer} { |
|
41 | 41 |
configFile.AuthConfigs[registry] = cliconfig.AuthConfig{ |
42 | 42 |
Username: "docker-user", |
43 | 43 |
Password: "docker-pass", |
... | ... |
@@ -82,7 +82,7 @@ func TestResolveAuthConfigIndexServer(t *testing.T) { |
82 | 82 |
} |
83 | 83 |
defer os.RemoveAll(configFile.Filename()) |
84 | 84 |
|
85 |
- indexConfig := configFile.AuthConfigs[INDEXSERVER] |
|
85 |
+ indexConfig := configFile.AuthConfigs[IndexServer] |
|
86 | 86 |
|
87 | 87 |
officialIndex := &IndexInfo{ |
88 | 88 |
Official: true, |
... | ... |
@@ -92,10 +92,10 @@ func TestResolveAuthConfigIndexServer(t *testing.T) { |
92 | 92 |
} |
93 | 93 |
|
94 | 94 |
resolved := ResolveAuthConfig(configFile, officialIndex) |
95 |
- assertEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to return INDEXSERVER") |
|
95 |
+ assertEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to return IndexServer") |
|
96 | 96 |
|
97 | 97 |
resolved = ResolveAuthConfig(configFile, privateIndex) |
98 |
- assertNotEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to not return INDEXSERVER") |
|
98 |
+ assertNotEqual(t, resolved, indexConfig, "Expected ResolveAuthConfig to not return IndexServer") |
|
99 | 99 |
} |
100 | 100 |
|
101 | 101 |
func TestResolveAuthConfigFullURL(t *testing.T) { |
... | ... |
@@ -120,7 +120,7 @@ func TestResolveAuthConfigFullURL(t *testing.T) { |
120 | 120 |
Password: "baz-pass", |
121 | 121 |
Email: "baz@example.com", |
122 | 122 |
} |
123 |
- configFile.AuthConfigs[INDEXSERVER] = officialAuth |
|
123 |
+ configFile.AuthConfigs[IndexServer] = officialAuth |
|
124 | 124 |
|
125 | 125 |
expectedAuths := map[string]cliconfig.AuthConfig{ |
126 | 126 |
"registry.example.com": registryAuth, |
... | ... |
@@ -21,24 +21,33 @@ type Options struct { |
21 | 21 |
} |
22 | 22 |
|
23 | 23 |
const ( |
24 |
- DEFAULT_NAMESPACE = "docker.io" |
|
25 |
- DEFAULT_V2_REGISTRY = "https://registry-1.docker.io" |
|
26 |
- DEFAULT_REGISTRY_VERSION_HEADER = "Docker-Distribution-Api-Version" |
|
27 |
- DEFAULT_V1_REGISTRY = "https://index.docker.io" |
|
28 |
- |
|
29 |
- CERTS_DIR = "/etc/docker/certs.d" |
|
30 |
- |
|
31 |
- // Only used for user auth + account creation |
|
32 |
- REGISTRYSERVER = DEFAULT_V2_REGISTRY |
|
33 |
- INDEXSERVER = DEFAULT_V1_REGISTRY + "/v1/" |
|
34 |
- INDEXNAME = "docker.io" |
|
35 |
- |
|
36 |
- // INDEXSERVER = "https://registry-stage.hub.docker.com/v1/" |
|
24 |
+ // DefaultNamespace is the default namespace |
|
25 |
+ DefaultNamespace = "docker.io" |
|
26 |
+ // DefaultV2Registry is the URI of the default v2 registry |
|
27 |
+ DefaultV2Registry = "https://registry-1.docker.io" |
|
28 |
+ // DefaultRegistryVersionHeader is the name of the default HTTP header |
|
29 |
+ // that carries Registry version info |
|
30 |
+ DefaultRegistryVersionHeader = "Docker-Distribution-Api-Version" |
|
31 |
+ // DefaultV1Registry is the URI of the default v1 registry |
|
32 |
+ DefaultV1Registry = "https://index.docker.io" |
|
33 |
+ |
|
34 |
+ // CertsDir is the directory where certificates are stored |
|
35 |
+ CertsDir = "/etc/docker/certs.d" |
|
36 |
+ |
|
37 |
+ // IndexServer is the v1 registry server used for user auth + account creation |
|
38 |
+ IndexServer = DefaultV1Registry + "/v1/" |
|
39 |
+ // IndexName is the name of the index |
|
40 |
+ IndexName = "docker.io" |
|
41 |
+ |
|
42 |
+ // IndexServer = "https://registry-stage.hub.docker.com/v1/" |
|
37 | 43 |
) |
38 | 44 |
|
39 | 45 |
var ( |
46 |
+ // ErrInvalidRepositoryName is an error returned if the repository name did |
|
47 |
+ // not have the correct form |
|
40 | 48 |
ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")") |
41 |
- emptyServiceConfig = NewServiceConfig(nil) |
|
49 |
+ |
|
50 |
+ emptyServiceConfig = NewServiceConfig(nil) |
|
42 | 51 |
) |
43 | 52 |
|
44 | 53 |
// InstallFlags adds command-line options to the top-level flag parser for |
... | ... |
@@ -116,8 +125,8 @@ func NewServiceConfig(options *Options) *ServiceConfig { |
116 | 116 |
} |
117 | 117 |
|
118 | 118 |
// Configure public registry. |
119 |
- config.IndexConfigs[INDEXNAME] = &IndexInfo{ |
|
120 |
- Name: INDEXNAME, |
|
119 |
+ config.IndexConfigs[IndexName] = &IndexInfo{ |
|
120 |
+ Name: IndexName, |
|
121 | 121 |
Mirrors: config.Mirrors, |
122 | 122 |
Secure: true, |
123 | 123 |
Official: true, |
... | ... |
@@ -196,8 +205,8 @@ func ValidateMirror(val string) (string, error) { |
196 | 196 |
// ValidateIndexName validates an index name. |
197 | 197 |
func ValidateIndexName(val string) (string, error) { |
198 | 198 |
// 'index.docker.io' => 'docker.io' |
199 |
- if val == "index."+INDEXNAME { |
|
200 |
- val = INDEXNAME |
|
199 |
+ if val == "index."+IndexName { |
|
200 |
+ val = IndexName |
|
201 | 201 |
} |
202 | 202 |
if strings.HasPrefix(val, "-") || strings.HasSuffix(val, "-") { |
203 | 203 |
return "", fmt.Errorf("Invalid index name (%s). Cannot begin or end with a hyphen.", val) |
... | ... |
@@ -267,7 +276,7 @@ func (config *ServiceConfig) NewIndexInfo(indexName string) (*IndexInfo, error) |
267 | 267 |
// index as the AuthConfig key, and uses the (host)name[:port] for private indexes. |
268 | 268 |
func (index *IndexInfo) GetAuthConfigKey() string { |
269 | 269 |
if index.Official { |
270 |
- return INDEXSERVER |
|
270 |
+ return IndexServer |
|
271 | 271 |
} |
272 | 272 |
return index.Name |
273 | 273 |
} |
... | ... |
@@ -280,7 +289,7 @@ func splitReposName(reposName string) (string, string) { |
280 | 280 |
!strings.Contains(nameParts[0], ":") && nameParts[0] != "localhost") { |
281 | 281 |
// This is a Docker Index repos (ex: samalba/hipache or ubuntu) |
282 | 282 |
// 'docker.io' |
283 |
- indexName = INDEXNAME |
|
283 |
+ indexName = IndexName |
|
284 | 284 |
remoteName = reposName |
285 | 285 |
} else { |
286 | 286 |
indexName = nameParts[0] |
... | ... |
@@ -111,6 +111,7 @@ func newEndpoint(address string, tlsConfig *tls.Config, metaHeaders http.Header) |
111 | 111 |
return endpoint, nil |
112 | 112 |
} |
113 | 113 |
|
114 |
+// GetEndpoint returns a new endpoint with the specified headers |
|
114 | 115 |
func (repoInfo *RepositoryInfo) GetEndpoint(metaHeaders http.Header) (*Endpoint, error) { |
115 | 116 |
return NewEndpoint(repoInfo.Index, metaHeaders) |
116 | 117 |
} |
... | ... |
@@ -142,7 +143,10 @@ func (e *Endpoint) Path(path string) string { |
142 | 142 |
return fmt.Sprintf("%s/v%d/%s", e.URL, e.Version, path) |
143 | 143 |
} |
144 | 144 |
|
145 |
-func (e *Endpoint) Ping() (RegistryInfo, error) { |
|
145 |
+// Ping pings the remote endpoint with v2 and v1 pings to determine the API |
|
146 |
+// version. It returns a PingResult containing the discovered version. The |
|
147 |
+// PingResult also indicates whether the registry is standalone or not. |
|
148 |
+func (e *Endpoint) Ping() (PingResult, error) { |
|
146 | 149 |
// The ping logic to use is determined by the registry endpoint version. |
147 | 150 |
switch e.Version { |
148 | 151 |
case APIVersion1: |
... | ... |
@@ -167,49 +171,49 @@ func (e *Endpoint) Ping() (RegistryInfo, error) { |
167 | 167 |
} |
168 | 168 |
|
169 | 169 |
e.Version = APIVersionUnknown |
170 |
- return RegistryInfo{}, fmt.Errorf("unable to ping registry endpoint %s\nv2 ping attempt failed with error: %s\n v1 ping attempt failed with error: %s", e, errV2, errV1) |
|
170 |
+ return PingResult{}, fmt.Errorf("unable to ping registry endpoint %s\nv2 ping attempt failed with error: %s\n v1 ping attempt failed with error: %s", e, errV2, errV1) |
|
171 | 171 |
} |
172 | 172 |
|
173 |
-func (e *Endpoint) pingV1() (RegistryInfo, error) { |
|
173 |
+func (e *Endpoint) pingV1() (PingResult, error) { |
|
174 | 174 |
logrus.Debugf("attempting v1 ping for registry endpoint %s", e) |
175 | 175 |
|
176 |
- if e.String() == INDEXSERVER { |
|
176 |
+ if e.String() == IndexServer { |
|
177 | 177 |
// Skip the check, we know this one is valid |
178 | 178 |
// (and we never want to fallback to http in case of error) |
179 |
- return RegistryInfo{Standalone: false}, nil |
|
179 |
+ return PingResult{Standalone: false}, nil |
|
180 | 180 |
} |
181 | 181 |
|
182 | 182 |
req, err := http.NewRequest("GET", e.Path("_ping"), nil) |
183 | 183 |
if err != nil { |
184 |
- return RegistryInfo{Standalone: false}, err |
|
184 |
+ return PingResult{Standalone: false}, err |
|
185 | 185 |
} |
186 | 186 |
|
187 | 187 |
resp, err := e.client.Do(req) |
188 | 188 |
if err != nil { |
189 |
- return RegistryInfo{Standalone: false}, err |
|
189 |
+ return PingResult{Standalone: false}, err |
|
190 | 190 |
} |
191 | 191 |
|
192 | 192 |
defer resp.Body.Close() |
193 | 193 |
|
194 | 194 |
jsonString, err := ioutil.ReadAll(resp.Body) |
195 | 195 |
if err != nil { |
196 |
- return RegistryInfo{Standalone: false}, fmt.Errorf("error while reading the http response: %s", err) |
|
196 |
+ return PingResult{Standalone: false}, fmt.Errorf("error while reading the http response: %s", err) |
|
197 | 197 |
} |
198 | 198 |
|
199 | 199 |
// If the header is absent, we assume true for compatibility with earlier |
200 | 200 |
// versions of the registry. default to true |
201 |
- info := RegistryInfo{ |
|
201 |
+ info := PingResult{ |
|
202 | 202 |
Standalone: true, |
203 | 203 |
} |
204 | 204 |
if err := json.Unmarshal(jsonString, &info); err != nil { |
205 |
- logrus.Debugf("Error unmarshalling the _ping RegistryInfo: %s", err) |
|
205 |
+ logrus.Debugf("Error unmarshalling the _ping PingResult: %s", err) |
|
206 | 206 |
// don't stop here. Just assume sane defaults |
207 | 207 |
} |
208 | 208 |
if hdr := resp.Header.Get("X-Docker-Registry-Version"); hdr != "" { |
209 | 209 |
logrus.Debugf("Registry version header: '%s'", hdr) |
210 | 210 |
info.Version = hdr |
211 | 211 |
} |
212 |
- logrus.Debugf("RegistryInfo.Version: %q", info.Version) |
|
212 |
+ logrus.Debugf("PingResult.Version: %q", info.Version) |
|
213 | 213 |
|
214 | 214 |
standalone := resp.Header.Get("X-Docker-Registry-Standalone") |
215 | 215 |
logrus.Debugf("Registry standalone header: '%s'", standalone) |
... | ... |
@@ -220,21 +224,21 @@ func (e *Endpoint) pingV1() (RegistryInfo, error) { |
220 | 220 |
// there is a header set, and it is not "true" or "1", so assume fails |
221 | 221 |
info.Standalone = false |
222 | 222 |
} |
223 |
- logrus.Debugf("RegistryInfo.Standalone: %t", info.Standalone) |
|
223 |
+ logrus.Debugf("PingResult.Standalone: %t", info.Standalone) |
|
224 | 224 |
return info, nil |
225 | 225 |
} |
226 | 226 |
|
227 |
-func (e *Endpoint) pingV2() (RegistryInfo, error) { |
|
227 |
+func (e *Endpoint) pingV2() (PingResult, error) { |
|
228 | 228 |
logrus.Debugf("attempting v2 ping for registry endpoint %s", e) |
229 | 229 |
|
230 | 230 |
req, err := http.NewRequest("GET", e.Path(""), nil) |
231 | 231 |
if err != nil { |
232 |
- return RegistryInfo{}, err |
|
232 |
+ return PingResult{}, err |
|
233 | 233 |
} |
234 | 234 |
|
235 | 235 |
resp, err := e.client.Do(req) |
236 | 236 |
if err != nil { |
237 |
- return RegistryInfo{}, err |
|
237 |
+ return PingResult{}, err |
|
238 | 238 |
} |
239 | 239 |
defer resp.Body.Close() |
240 | 240 |
|
... | ... |
@@ -253,21 +257,21 @@ HeaderLoop: |
253 | 253 |
} |
254 | 254 |
|
255 | 255 |
if !supportsV2 { |
256 |
- return RegistryInfo{}, fmt.Errorf("%s does not appear to be a v2 registry endpoint", e) |
|
256 |
+ return PingResult{}, fmt.Errorf("%s does not appear to be a v2 registry endpoint", e) |
|
257 | 257 |
} |
258 | 258 |
|
259 | 259 |
if resp.StatusCode == http.StatusOK { |
260 | 260 |
// It would seem that no authentication/authorization is required. |
261 | 261 |
// So we don't need to parse/add any authorization schemes. |
262 |
- return RegistryInfo{Standalone: true}, nil |
|
262 |
+ return PingResult{Standalone: true}, nil |
|
263 | 263 |
} |
264 | 264 |
|
265 | 265 |
if resp.StatusCode == http.StatusUnauthorized { |
266 | 266 |
// Parse the WWW-Authenticate Header and store the challenges |
267 | 267 |
// on this endpoint object. |
268 | 268 |
e.AuthChallenges = parseAuthHeader(resp.Header) |
269 |
- return RegistryInfo{}, nil |
|
269 |
+ return PingResult{}, nil |
|
270 | 270 |
} |
271 | 271 |
|
272 |
- return RegistryInfo{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode)) |
|
272 |
+ return PingResult{}, fmt.Errorf("v2 registry endpoint returned status %d: %q", resp.StatusCode, http.StatusText(resp.StatusCode)) |
|
273 | 273 |
} |
... | ... |
@@ -12,7 +12,7 @@ func TestEndpointParse(t *testing.T) { |
12 | 12 |
str string |
13 | 13 |
expected string |
14 | 14 |
}{ |
15 |
- {INDEXSERVER, INDEXSERVER}, |
|
15 |
+ {IndexServer, IndexServer}, |
|
16 | 16 |
{"http://0.0.0.0:5000/v1/", "http://0.0.0.0:5000/v1/"}, |
17 | 17 |
{"http://0.0.0.0:5000/v2/", "http://0.0.0.0:5000/v2/"}, |
18 | 18 |
{"http://0.0.0.0:5000", "http://0.0.0.0:5000/v0/"}, |
... | ... |
@@ -21,19 +21,12 @@ import ( |
21 | 21 |
) |
22 | 22 |
|
23 | 23 |
var ( |
24 |
+ // ErrAlreadyExists is an error returned if an image being pushed |
|
25 |
+ // already exists on the remote side |
|
24 | 26 |
ErrAlreadyExists = errors.New("Image already exists") |
25 |
- ErrDoesNotExist = errors.New("Image does not exist") |
|
26 | 27 |
errLoginRequired = errors.New("Authentication is required.") |
27 | 28 |
) |
28 | 29 |
|
29 |
-type TimeoutType uint32 |
|
30 |
- |
|
31 |
-const ( |
|
32 |
- NoTimeout TimeoutType = iota |
|
33 |
- ReceiveTimeout |
|
34 |
- ConnectTimeout |
|
35 |
-) |
|
36 |
- |
|
37 | 30 |
// dockerUserAgent is the User-Agent the Docker client uses to identify itself. |
38 | 31 |
// It is populated on init(), comprising version information of different components. |
39 | 32 |
var dockerUserAgent string |
... | ... |
@@ -74,10 +67,12 @@ func DockerHeaders(metaHeaders http.Header) []transport.RequestModifier { |
74 | 74 |
return modifiers |
75 | 75 |
} |
76 | 76 |
|
77 |
+// HTTPClient returns a HTTP client structure which uses the given transport |
|
78 |
+// and contains the necessary headers for redirected requests |
|
77 | 79 |
func HTTPClient(transport http.RoundTripper) *http.Client { |
78 | 80 |
return &http.Client{ |
79 | 81 |
Transport: transport, |
80 |
- CheckRedirect: AddRequiredHeadersToRedirectedRequests, |
|
82 |
+ CheckRedirect: addRequiredHeadersToRedirectedRequests, |
|
81 | 83 |
} |
82 | 84 |
} |
83 | 85 |
|
... | ... |
@@ -98,7 +93,9 @@ func trustedLocation(req *http.Request) bool { |
98 | 98 |
return false |
99 | 99 |
} |
100 | 100 |
|
101 |
-func AddRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error { |
|
101 |
+// addRequiredHeadersToRedirectedRequests adds the necessary redirection headers |
|
102 |
+// for redirected requests |
|
103 |
+func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error { |
|
102 | 104 |
if via != nil && via[0] != nil { |
103 | 105 |
if trustedLocation(req) && trustedLocation(via[0]) { |
104 | 106 |
req.Header = via[0].Header |
... | ... |
@@ -124,6 +121,8 @@ func shouldV2Fallback(err errcode.Error) bool { |
124 | 124 |
return false |
125 | 125 |
} |
126 | 126 |
|
127 |
+// ErrNoSupport is an error type used for errors indicating that an operation |
|
128 |
+// is not supported. It encapsulates a more specific error. |
|
127 | 129 |
type ErrNoSupport struct{ Err error } |
128 | 130 |
|
129 | 131 |
func (e ErrNoSupport) Error() string { |
... | ... |
@@ -133,6 +132,8 @@ func (e ErrNoSupport) Error() string { |
133 | 133 |
return e.Err.Error() |
134 | 134 |
} |
135 | 135 |
|
136 |
+// ContinueOnError returns true if we should fallback to the next endpoint |
|
137 |
+// as a result of this error. |
|
136 | 138 |
func ContinueOnError(err error) bool { |
137 | 139 |
switch v := err.(type) { |
138 | 140 |
case errcode.Errors: |
... | ... |
@@ -145,6 +146,8 @@ func ContinueOnError(err error) bool { |
145 | 145 |
return false |
146 | 146 |
} |
147 | 147 |
|
148 |
+// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the |
|
149 |
+// default TLS configuration. |
|
148 | 150 |
func NewTransport(tlsConfig *tls.Config) *http.Transport { |
149 | 151 |
if tlsConfig == nil { |
150 | 152 |
var cfg = tlsconfig.ServerDefault |
... | ... |
@@ -145,7 +145,7 @@ func makeURL(req string) string { |
145 | 145 |
return testHTTPServer.URL + req |
146 | 146 |
} |
147 | 147 |
|
148 |
-func makeHttpsURL(req string) string { |
|
148 |
+func makeHTTPSURL(req string) string { |
|
149 | 149 |
return testHTTPSServer.URL + req |
150 | 150 |
} |
151 | 151 |
|
... | ... |
@@ -156,16 +156,16 @@ func makeIndex(req string) *IndexInfo { |
156 | 156 |
return index |
157 | 157 |
} |
158 | 158 |
|
159 |
-func makeHttpsIndex(req string) *IndexInfo { |
|
159 |
+func makeHTTPSIndex(req string) *IndexInfo { |
|
160 | 160 |
index := &IndexInfo{ |
161 |
- Name: makeHttpsURL(req), |
|
161 |
+ Name: makeHTTPSURL(req), |
|
162 | 162 |
} |
163 | 163 |
return index |
164 | 164 |
} |
165 | 165 |
|
166 | 166 |
func makePublicIndex() *IndexInfo { |
167 | 167 |
index := &IndexInfo{ |
168 |
- Name: INDEXSERVER, |
|
168 |
+ Name: IndexServer, |
|
169 | 169 |
Secure: true, |
170 | 170 |
Official: true, |
171 | 171 |
} |
... | ... |
@@ -468,7 +468,7 @@ func TestPing(t *testing.T) { |
468 | 468 |
* WARNING: Don't push on the repos uncommented, it'll block the tests |
469 | 469 |
* |
470 | 470 |
func TestWait(t *testing.T) { |
471 |
- logrus.Println("Test HTTP server ready and waiting:", testHttpServer.URL) |
|
471 |
+ logrus.Println("Test HTTP server ready and waiting:", testHTTPServer.URL) |
|
472 | 472 |
c := make(chan int) |
473 | 473 |
<-c |
474 | 474 |
} |
... | ... |
@@ -63,7 +63,7 @@ func TestPingRegistryEndpoint(t *testing.T) { |
63 | 63 |
} |
64 | 64 |
|
65 | 65 |
testPing(makeIndex("/v1/"), true, "Expected standalone to be true (default)") |
66 |
- testPing(makeHttpsIndex("/v1/"), true, "Expected standalone to be true (default)") |
|
66 |
+ testPing(makeHTTPSIndex("/v1/"), true, "Expected standalone to be true (default)") |
|
67 | 67 |
testPing(makePublicIndex(), false, "Expected standalone to be false for public index") |
68 | 68 |
} |
69 | 69 |
|
... | ... |
@@ -119,7 +119,7 @@ func TestEndpoint(t *testing.T) { |
119 | 119 |
} |
120 | 120 |
assertInsecureIndex(index) |
121 | 121 |
|
122 |
- index.Name = makeHttpsURL("/v1/") |
|
122 |
+ index.Name = makeHTTPSURL("/v1/") |
|
123 | 123 |
endpoint = expandEndpoint(index) |
124 | 124 |
assertEqual(t, endpoint.String(), index.Name, "Expected endpoint to be "+index.Name) |
125 | 125 |
if endpoint.Version != APIVersion1 { |
... | ... |
@@ -127,7 +127,7 @@ func TestEndpoint(t *testing.T) { |
127 | 127 |
} |
128 | 128 |
assertSecureIndex(index) |
129 | 129 |
|
130 |
- index.Name = makeHttpsURL("") |
|
130 |
+ index.Name = makeHTTPSURL("") |
|
131 | 131 |
endpoint = expandEndpoint(index) |
132 | 132 |
assertEqual(t, endpoint.String(), index.Name+"/v1/", index.Name+": Expected endpoint to be "+index.Name+"/v1/") |
133 | 133 |
if endpoint.Version != APIVersion1 { |
... | ... |
@@ -135,7 +135,7 @@ func TestEndpoint(t *testing.T) { |
135 | 135 |
} |
136 | 136 |
assertSecureIndex(index) |
137 | 137 |
|
138 |
- httpsURL := makeHttpsURL("") |
|
138 |
+ httpsURL := makeHTTPSURL("") |
|
139 | 139 |
index.Name = strings.SplitN(httpsURL, "://", 2)[1] |
140 | 140 |
endpoint = expandEndpoint(index) |
141 | 141 |
assertEqual(t, endpoint.String(), httpsURL+"/v1/", index.Name+": Expected endpoint to be "+httpsURL+"/v1/") |
... | ... |
@@ -332,7 +332,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
332 | 332 |
expectedRepoInfos := map[string]RepositoryInfo{ |
333 | 333 |
"fooo/bar": { |
334 | 334 |
Index: &IndexInfo{ |
335 |
- Name: INDEXNAME, |
|
335 |
+ Name: IndexName, |
|
336 | 336 |
Official: true, |
337 | 337 |
}, |
338 | 338 |
RemoteName: "fooo/bar", |
... | ... |
@@ -342,7 +342,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
342 | 342 |
}, |
343 | 343 |
"library/ubuntu": { |
344 | 344 |
Index: &IndexInfo{ |
345 |
- Name: INDEXNAME, |
|
345 |
+ Name: IndexName, |
|
346 | 346 |
Official: true, |
347 | 347 |
}, |
348 | 348 |
RemoteName: "library/ubuntu", |
... | ... |
@@ -352,7 +352,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
352 | 352 |
}, |
353 | 353 |
"nonlibrary/ubuntu": { |
354 | 354 |
Index: &IndexInfo{ |
355 |
- Name: INDEXNAME, |
|
355 |
+ Name: IndexName, |
|
356 | 356 |
Official: true, |
357 | 357 |
}, |
358 | 358 |
RemoteName: "nonlibrary/ubuntu", |
... | ... |
@@ -362,7 +362,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
362 | 362 |
}, |
363 | 363 |
"ubuntu": { |
364 | 364 |
Index: &IndexInfo{ |
365 |
- Name: INDEXNAME, |
|
365 |
+ Name: IndexName, |
|
366 | 366 |
Official: true, |
367 | 367 |
}, |
368 | 368 |
RemoteName: "library/ubuntu", |
... | ... |
@@ -372,7 +372,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
372 | 372 |
}, |
373 | 373 |
"other/library": { |
374 | 374 |
Index: &IndexInfo{ |
375 |
- Name: INDEXNAME, |
|
375 |
+ Name: IndexName, |
|
376 | 376 |
Official: true, |
377 | 377 |
}, |
378 | 378 |
RemoteName: "other/library", |
... | ... |
@@ -480,9 +480,9 @@ func TestParseRepositoryInfo(t *testing.T) { |
480 | 480 |
CanonicalName: "localhost/privatebase", |
481 | 481 |
Official: false, |
482 | 482 |
}, |
483 |
- INDEXNAME + "/public/moonbase": { |
|
483 |
+ IndexName + "/public/moonbase": { |
|
484 | 484 |
Index: &IndexInfo{ |
485 |
- Name: INDEXNAME, |
|
485 |
+ Name: IndexName, |
|
486 | 486 |
Official: true, |
487 | 487 |
}, |
488 | 488 |
RemoteName: "public/moonbase", |
... | ... |
@@ -490,9 +490,9 @@ func TestParseRepositoryInfo(t *testing.T) { |
490 | 490 |
CanonicalName: "docker.io/public/moonbase", |
491 | 491 |
Official: false, |
492 | 492 |
}, |
493 |
- "index." + INDEXNAME + "/public/moonbase": { |
|
493 |
+ "index." + IndexName + "/public/moonbase": { |
|
494 | 494 |
Index: &IndexInfo{ |
495 |
- Name: INDEXNAME, |
|
495 |
+ Name: IndexName, |
|
496 | 496 |
Official: true, |
497 | 497 |
}, |
498 | 498 |
RemoteName: "public/moonbase", |
... | ... |
@@ -502,7 +502,7 @@ func TestParseRepositoryInfo(t *testing.T) { |
502 | 502 |
}, |
503 | 503 |
"ubuntu-12.04-base": { |
504 | 504 |
Index: &IndexInfo{ |
505 |
- Name: INDEXNAME, |
|
505 |
+ Name: IndexName, |
|
506 | 506 |
Official: true, |
507 | 507 |
}, |
508 | 508 |
RemoteName: "library/ubuntu-12.04-base", |
... | ... |
@@ -510,9 +510,9 @@ func TestParseRepositoryInfo(t *testing.T) { |
510 | 510 |
CanonicalName: "docker.io/library/ubuntu-12.04-base", |
511 | 511 |
Official: true, |
512 | 512 |
}, |
513 |
- INDEXNAME + "/ubuntu-12.04-base": { |
|
513 |
+ IndexName + "/ubuntu-12.04-base": { |
|
514 | 514 |
Index: &IndexInfo{ |
515 |
- Name: INDEXNAME, |
|
515 |
+ Name: IndexName, |
|
516 | 516 |
Official: true, |
517 | 517 |
}, |
518 | 518 |
RemoteName: "library/ubuntu-12.04-base", |
... | ... |
@@ -520,9 +520,9 @@ func TestParseRepositoryInfo(t *testing.T) { |
520 | 520 |
CanonicalName: "docker.io/library/ubuntu-12.04-base", |
521 | 521 |
Official: true, |
522 | 522 |
}, |
523 |
- "index." + INDEXNAME + "/ubuntu-12.04-base": { |
|
523 |
+ "index." + IndexName + "/ubuntu-12.04-base": { |
|
524 | 524 |
Index: &IndexInfo{ |
525 |
- Name: INDEXNAME, |
|
525 |
+ Name: IndexName, |
|
526 | 526 |
Official: true, |
527 | 527 |
}, |
528 | 528 |
RemoteName: "library/ubuntu-12.04-base", |
... | ... |
@@ -563,16 +563,16 @@ func TestNewIndexInfo(t *testing.T) { |
563 | 563 |
} |
564 | 564 |
|
565 | 565 |
config := NewServiceConfig(nil) |
566 |
- noMirrors := make([]string, 0) |
|
566 |
+ noMirrors := []string{} |
|
567 | 567 |
expectedIndexInfos := map[string]*IndexInfo{ |
568 |
- INDEXNAME: { |
|
569 |
- Name: INDEXNAME, |
|
568 |
+ IndexName: { |
|
569 |
+ Name: IndexName, |
|
570 | 570 |
Official: true, |
571 | 571 |
Secure: true, |
572 | 572 |
Mirrors: noMirrors, |
573 | 573 |
}, |
574 |
- "index." + INDEXNAME: { |
|
575 |
- Name: INDEXNAME, |
|
574 |
+ "index." + IndexName: { |
|
575 |
+ Name: IndexName, |
|
576 | 576 |
Official: true, |
577 | 577 |
Secure: true, |
578 | 578 |
Mirrors: noMirrors, |
... | ... |
@@ -596,14 +596,14 @@ func TestNewIndexInfo(t *testing.T) { |
596 | 596 |
config = makeServiceConfig(publicMirrors, []string{"example.com"}) |
597 | 597 |
|
598 | 598 |
expectedIndexInfos = map[string]*IndexInfo{ |
599 |
- INDEXNAME: { |
|
600 |
- Name: INDEXNAME, |
|
599 |
+ IndexName: { |
|
600 |
+ Name: IndexName, |
|
601 | 601 |
Official: true, |
602 | 602 |
Secure: true, |
603 | 603 |
Mirrors: publicMirrors, |
604 | 604 |
}, |
605 |
- "index." + INDEXNAME: { |
|
606 |
- Name: INDEXNAME, |
|
605 |
+ "index." + IndexName: { |
|
606 |
+ Name: IndexName, |
|
607 | 607 |
Official: true, |
608 | 608 |
Secure: true, |
609 | 609 |
Mirrors: publicMirrors, |
... | ... |
@@ -814,7 +814,7 @@ func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { |
814 | 814 |
reqFrom.Header.Add("Authorization", "super_secret") |
815 | 815 |
reqTo, _ := http.NewRequest("GET", urls[1], nil) |
816 | 816 |
|
817 |
- AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) |
|
817 |
+ addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) |
|
818 | 818 |
|
819 | 819 |
if len(reqTo.Header) != 1 { |
820 | 820 |
t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header)) |
... | ... |
@@ -838,7 +838,7 @@ func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) { |
838 | 838 |
reqFrom.Header.Add("Authorization", "super_secret") |
839 | 839 |
reqTo, _ := http.NewRequest("GET", urls[1], nil) |
840 | 840 |
|
841 |
- AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) |
|
841 |
+ addRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom}) |
|
842 | 842 |
|
843 | 843 |
if len(reqTo.Header) != 2 { |
844 | 844 |
t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header)) |
... | ... |
@@ -860,7 +860,7 @@ func TestIsSecureIndex(t *testing.T) { |
860 | 860 |
insecureRegistries []string |
861 | 861 |
expected bool |
862 | 862 |
}{ |
863 |
- {INDEXNAME, nil, true}, |
|
863 |
+ {IndexName, nil, true}, |
|
864 | 864 |
{"example.com", []string{}, true}, |
865 | 865 |
{"example.com", []string{"example.com"}, false}, |
866 | 866 |
{"localhost", []string{"localhost:5000"}, false}, |
... | ... |
@@ -17,12 +17,14 @@ import ( |
17 | 17 |
"github.com/docker/docker/pkg/tlsconfig" |
18 | 18 |
) |
19 | 19 |
|
20 |
+// Service is a registry service. It tracks configuration data such as a list |
|
21 |
+// of mirrors. |
|
20 | 22 |
type Service struct { |
21 | 23 |
Config *ServiceConfig |
22 | 24 |
} |
23 | 25 |
|
24 | 26 |
// NewService returns a new instance of Service ready to be |
25 |
-// installed no an engine. |
|
27 |
+// installed into an engine. |
|
26 | 28 |
func NewService(options *Options) *Service { |
27 | 29 |
return &Service{ |
28 | 30 |
Config: NewServiceConfig(options), |
... | ... |
@@ -36,7 +38,7 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) { |
36 | 36 |
addr := authConfig.ServerAddress |
37 | 37 |
if addr == "" { |
38 | 38 |
// Use the official registry address if not specified. |
39 |
- addr = INDEXSERVER |
|
39 |
+ addr = IndexServer |
|
40 | 40 |
} |
41 | 41 |
index, err := s.ResolveIndex(addr) |
42 | 42 |
if err != nil { |
... | ... |
@@ -81,6 +83,7 @@ func (s *Service) ResolveIndex(name string) (*IndexInfo, error) { |
81 | 81 |
return s.Config.NewIndexInfo(name) |
82 | 82 |
} |
83 | 83 |
|
84 |
+// APIEndpoint represents a remote API endpoint |
|
84 | 85 |
type APIEndpoint struct { |
85 | 86 |
Mirror bool |
86 | 87 |
URL string |
... | ... |
@@ -92,12 +95,13 @@ type APIEndpoint struct { |
92 | 92 |
Versions []auth.APIVersion |
93 | 93 |
} |
94 | 94 |
|
95 |
+// ToV1Endpoint returns a V1 API endpoint based on the APIEndpoint |
|
95 | 96 |
func (e APIEndpoint) ToV1Endpoint(metaHeaders http.Header) (*Endpoint, error) { |
96 | 97 |
return newEndpoint(e.URL, e.TLSConfig, metaHeaders) |
97 | 98 |
} |
98 | 99 |
|
99 |
-func (s *Service) TlsConfig(hostname string) (*tls.Config, error) { |
|
100 |
- // we construct a client tls config from server defaults |
|
100 |
+// TLSConfig constructs a client TLS configuration based on server defaults |
|
101 |
+func (s *Service) TLSConfig(hostname string) (*tls.Config, error) { |
|
101 | 102 |
// PreferredServerCipherSuites should have no effect |
102 | 103 |
tlsConfig := tlsconfig.ServerDefault |
103 | 104 |
|
... | ... |
@@ -115,7 +119,7 @@ func (s *Service) TlsConfig(hostname string) (*tls.Config, error) { |
115 | 115 |
return false |
116 | 116 |
} |
117 | 117 |
|
118 |
- hostDir := filepath.Join(CERTS_DIR, hostname) |
|
118 |
+ hostDir := filepath.Join(CertsDir, hostname) |
|
119 | 119 |
logrus.Debugf("hostDir: %s", hostDir) |
120 | 120 |
fs, err := ioutil.ReadDir(hostDir) |
121 | 121 |
if err != nil && !os.IsNotExist(err) { |
... | ... |
@@ -163,20 +167,23 @@ func (s *Service) TlsConfig(hostname string) (*tls.Config, error) { |
163 | 163 |
} |
164 | 164 |
|
165 | 165 |
func (s *Service) tlsConfigForMirror(mirror string) (*tls.Config, error) { |
166 |
- mirrorUrl, err := url.Parse(mirror) |
|
166 |
+ mirrorURL, err := url.Parse(mirror) |
|
167 | 167 |
if err != nil { |
168 | 168 |
return nil, err |
169 | 169 |
} |
170 |
- return s.TlsConfig(mirrorUrl.Host) |
|
170 |
+ return s.TLSConfig(mirrorURL.Host) |
|
171 | 171 |
} |
172 | 172 |
|
173 |
+// LookupEndpoints creates an list of endpoints to try, in order of preference. |
|
174 |
+// It gives preference to v2 endpoints over v1, mirrors over the actual |
|
175 |
+// registry, and HTTPS over plain HTTP. |
|
173 | 176 |
func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) { |
174 | 177 |
var cfg = tlsconfig.ServerDefault |
175 | 178 |
tlsConfig := &cfg |
176 |
- if strings.HasPrefix(repoName, DEFAULT_NAMESPACE+"/") { |
|
179 |
+ if strings.HasPrefix(repoName, DefaultNamespace+"/") { |
|
177 | 180 |
// v2 mirrors |
178 | 181 |
for _, mirror := range s.Config.Mirrors { |
179 |
- mirrorTlsConfig, err := s.tlsConfigForMirror(mirror) |
|
182 |
+ mirrorTLSConfig, err := s.tlsConfigForMirror(mirror) |
|
180 | 183 |
if err != nil { |
181 | 184 |
return nil, err |
182 | 185 |
} |
... | ... |
@@ -186,12 +193,12 @@ func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err |
186 | 186 |
Version: APIVersion2, |
187 | 187 |
Mirror: true, |
188 | 188 |
TrimHostname: true, |
189 |
- TLSConfig: mirrorTlsConfig, |
|
189 |
+ TLSConfig: mirrorTLSConfig, |
|
190 | 190 |
}) |
191 | 191 |
} |
192 | 192 |
// v2 registry |
193 | 193 |
endpoints = append(endpoints, APIEndpoint{ |
194 |
- URL: DEFAULT_V2_REGISTRY, |
|
194 |
+ URL: DefaultV2Registry, |
|
195 | 195 |
Version: APIVersion2, |
196 | 196 |
Official: true, |
197 | 197 |
TrimHostname: true, |
... | ... |
@@ -199,7 +206,7 @@ func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err |
199 | 199 |
}) |
200 | 200 |
// v1 registry |
201 | 201 |
endpoints = append(endpoints, APIEndpoint{ |
202 |
- URL: DEFAULT_V1_REGISTRY, |
|
202 |
+ URL: DefaultV1Registry, |
|
203 | 203 |
Version: APIVersion1, |
204 | 204 |
Official: true, |
205 | 205 |
TrimHostname: true, |
... | ... |
@@ -214,7 +221,7 @@ func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err |
214 | 214 |
} |
215 | 215 |
hostname := repoName[:slashIndex] |
216 | 216 |
|
217 |
- tlsConfig, err = s.TlsConfig(hostname) |
|
217 |
+ tlsConfig, err = s.TLSConfig(hostname) |
|
218 | 218 |
if err != nil { |
219 | 219 |
return nil, err |
220 | 220 |
} |
... | ... |
@@ -232,7 +239,7 @@ func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err |
232 | 232 |
Version: APIVersion2, |
233 | 233 |
TrimHostname: true, |
234 | 234 |
TLSConfig: tlsConfig, |
235 |
- VersionHeader: DEFAULT_REGISTRY_VERSION_HEADER, |
|
235 |
+ VersionHeader: DefaultRegistryVersionHeader, |
|
236 | 236 |
Versions: v2Versions, |
237 | 237 |
}, |
238 | 238 |
{ |
... | ... |
@@ -250,7 +257,7 @@ func (s *Service) LookupEndpoints(repoName string) (endpoints []APIEndpoint, err |
250 | 250 |
TrimHostname: true, |
251 | 251 |
// used to check if supposed to be secure via InsecureSkipVerify |
252 | 252 |
TLSConfig: tlsConfig, |
253 |
- VersionHeader: DEFAULT_REGISTRY_VERSION_HEADER, |
|
253 |
+ VersionHeader: DefaultRegistryVersionHeader, |
|
254 | 254 |
Versions: v2Versions, |
255 | 255 |
}, APIEndpoint{ |
256 | 256 |
URL: "http://" + hostname, |
... | ... |
@@ -28,9 +28,12 @@ import ( |
28 | 28 |
) |
29 | 29 |
|
30 | 30 |
var ( |
31 |
+ // ErrRepoNotFound is returned if the repository didn't exist on the |
|
32 |
+ // remote side |
|
31 | 33 |
ErrRepoNotFound = errors.New("Repository not found") |
32 | 34 |
) |
33 | 35 |
|
36 |
+// A Session is used to communicate with a V1 registry |
|
34 | 37 |
type Session struct { |
35 | 38 |
indexEndpoint *Endpoint |
36 | 39 |
client *http.Client |
... | ... |
@@ -90,9 +93,11 @@ func cloneRequest(r *http.Request) *http.Request { |
90 | 90 |
return r2 |
91 | 91 |
} |
92 | 92 |
|
93 |
+// RoundTrip changes a HTTP request's headers to add the necessary |
|
94 |
+// authentication-related headers |
|
93 | 95 |
func (tr *authTransport) RoundTrip(orig *http.Request) (*http.Response, error) { |
94 | 96 |
// Authorization should not be set on 302 redirect for untrusted locations. |
95 |
- // This logic mirrors the behavior in AddRequiredHeadersToRedirectedRequests. |
|
97 |
+ // This logic mirrors the behavior in addRequiredHeadersToRedirectedRequests. |
|
96 | 98 |
// As the authorization logic is currently implemented in RoundTrip, |
97 | 99 |
// a 302 redirect is detected by looking at the Referer header as go http package adds said header. |
98 | 100 |
// This is safe as Docker doesn't set Referer in other scenarios. |
... | ... |
@@ -154,6 +159,7 @@ func (tr *authTransport) CancelRequest(req *http.Request) { |
154 | 154 |
} |
155 | 155 |
} |
156 | 156 |
|
157 |
+// NewSession creates a new session |
|
157 | 158 |
// TODO(tiborvass): remove authConfig param once registry client v2 is vendored |
158 | 159 |
func NewSession(client *http.Client, authConfig *cliconfig.AuthConfig, endpoint *Endpoint) (r *Session, err error) { |
159 | 160 |
r = &Session{ |
... | ... |
@@ -167,7 +173,7 @@ func NewSession(client *http.Client, authConfig *cliconfig.AuthConfig, endpoint |
167 | 167 |
|
168 | 168 |
// If we're working with a standalone private registry over HTTPS, send Basic Auth headers |
169 | 169 |
// alongside all our requests. |
170 |
- if endpoint.VersionString(1) != INDEXSERVER && endpoint.URL.Scheme == "https" { |
|
170 |
+ if endpoint.VersionString(1) != IndexServer && endpoint.URL.Scheme == "https" { |
|
171 | 171 |
info, err := endpoint.Ping() |
172 | 172 |
if err != nil { |
173 | 173 |
return nil, err |
... | ... |
@@ -196,8 +202,8 @@ func (r *Session) ID() string { |
196 | 196 |
return r.id |
197 | 197 |
} |
198 | 198 |
|
199 |
-// Retrieve the history of a given image from the Registry. |
|
200 |
-// Return a list of the parent's json (requested image included) |
|
199 |
+// GetRemoteHistory retrieves the history of a given image from the registry. |
|
200 |
+// It returns a list of the parent's JSON files (including the requested image). |
|
201 | 201 |
func (r *Session) GetRemoteHistory(imgID, registry string) ([]string, error) { |
202 | 202 |
res, err := r.client.Get(registry + "images/" + imgID + "/ancestry") |
203 | 203 |
if err != nil { |
... | ... |
@@ -220,7 +226,7 @@ func (r *Session) GetRemoteHistory(imgID, registry string) ([]string, error) { |
220 | 220 |
return history, nil |
221 | 221 |
} |
222 | 222 |
|
223 |
-// Check if an image exists in the Registry |
|
223 |
+// LookupRemoteImage checks if an image exists in the registry |
|
224 | 224 |
func (r *Session) LookupRemoteImage(imgID, registry string) error { |
225 | 225 |
res, err := r.client.Get(registry + "images/" + imgID + "/json") |
226 | 226 |
if err != nil { |
... | ... |
@@ -233,7 +239,7 @@ func (r *Session) LookupRemoteImage(imgID, registry string) error { |
233 | 233 |
return nil |
234 | 234 |
} |
235 | 235 |
|
236 |
-// Retrieve an image from the Registry. |
|
236 |
+// GetRemoteImageJSON retrieves an image's JSON metadata from the registry. |
|
237 | 237 |
func (r *Session) GetRemoteImageJSON(imgID, registry string) ([]byte, int, error) { |
238 | 238 |
res, err := r.client.Get(registry + "images/" + imgID + "/json") |
239 | 239 |
if err != nil { |
... | ... |
@@ -259,6 +265,7 @@ func (r *Session) GetRemoteImageJSON(imgID, registry string) ([]byte, int, error |
259 | 259 |
return jsonString, imageSize, nil |
260 | 260 |
} |
261 | 261 |
|
262 |
+// GetRemoteImageLayer retrieves an image layer from the registry |
|
262 | 263 |
func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io.ReadCloser, error) { |
263 | 264 |
var ( |
264 | 265 |
retries = 5 |
... | ... |
@@ -308,9 +315,13 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io |
308 | 308 |
return res.Body, nil |
309 | 309 |
} |
310 | 310 |
|
311 |
+// GetRemoteTag retrieves the tag named in the askedTag argument from the given |
|
312 |
+// repository. It queries each of the registries supplied in the registries |
|
313 |
+// argument, and returns data from the first one that answers the query |
|
314 |
+// successfully. |
|
311 | 315 |
func (r *Session) GetRemoteTag(registries []string, repository string, askedTag string) (string, error) { |
312 | 316 |
if strings.Count(repository, "/") == 0 { |
313 |
- // This will be removed once the Registry supports auto-resolution on |
|
317 |
+ // This will be removed once the registry supports auto-resolution on |
|
314 | 318 |
// the "library" namespace |
315 | 319 |
repository = "library/" + repository |
316 | 320 |
} |
... | ... |
@@ -331,18 +342,22 @@ func (r *Session) GetRemoteTag(registries []string, repository string, askedTag |
331 | 331 |
continue |
332 | 332 |
} |
333 | 333 |
|
334 |
- var tagId string |
|
335 |
- if err := json.NewDecoder(res.Body).Decode(&tagId); err != nil { |
|
334 |
+ var tagID string |
|
335 |
+ if err := json.NewDecoder(res.Body).Decode(&tagID); err != nil { |
|
336 | 336 |
return "", err |
337 | 337 |
} |
338 |
- return tagId, nil |
|
338 |
+ return tagID, nil |
|
339 | 339 |
} |
340 | 340 |
return "", fmt.Errorf("Could not reach any registry endpoint") |
341 | 341 |
} |
342 | 342 |
|
343 |
+// GetRemoteTags retrieves all tags from the given repository. It queries each |
|
344 |
+// of the registries supplied in the registries argument, and returns data from |
|
345 |
+// the first one that answers the query successfully. It returns a map with |
|
346 |
+// tag names as the keys and image IDs as the values. |
|
343 | 347 |
func (r *Session) GetRemoteTags(registries []string, repository string) (map[string]string, error) { |
344 | 348 |
if strings.Count(repository, "/") == 0 { |
345 |
- // This will be removed once the Registry supports auto-resolution on |
|
349 |
+ // This will be removed once the registry supports auto-resolution on |
|
346 | 350 |
// the "library" namespace |
347 | 351 |
repository = "library/" + repository |
348 | 352 |
} |
... | ... |
@@ -379,7 +394,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) { |
379 | 379 |
return nil, err |
380 | 380 |
} |
381 | 381 |
var urlScheme = parsedURL.Scheme |
382 |
- // The Registry's URL scheme has to match the Index' |
|
382 |
+ // The registry's URL scheme has to match the Index' |
|
383 | 383 |
for _, ep := range headers { |
384 | 384 |
epList := strings.Split(ep, ",") |
385 | 385 |
for _, epListElement := range epList { |
... | ... |
@@ -391,6 +406,7 @@ func buildEndpointsList(headers []string, indexEp string) ([]string, error) { |
391 | 391 |
return endpoints, nil |
392 | 392 |
} |
393 | 393 |
|
394 |
+// GetRepositoryData returns lists of images and endpoints for the repository |
|
394 | 395 |
func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) { |
395 | 396 |
repositoryTarget := fmt.Sprintf("%srepositories/%s/images", r.indexEndpoint.VersionString(1), remote) |
396 | 397 |
|
... | ... |
@@ -457,8 +473,8 @@ func (r *Session) GetRepositoryData(remote string) (*RepositoryData, error) { |
457 | 457 |
}, nil |
458 | 458 |
} |
459 | 459 |
|
460 |
+// PushImageChecksumRegistry uploads checksums for an image |
|
460 | 461 |
func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string) error { |
461 |
- |
|
462 | 462 |
u := registry + "images/" + imgData.ID + "/checksum" |
463 | 463 |
|
464 | 464 |
logrus.Debugf("[registry] Calling PUT %s", u) |
... | ... |
@@ -494,7 +510,7 @@ func (r *Session) PushImageChecksumRegistry(imgData *ImgData, registry string) e |
494 | 494 |
return nil |
495 | 495 |
} |
496 | 496 |
|
497 |
-// Push a local image to the registry |
|
497 |
+// PushImageJSONRegistry pushes JSON metadata for a local image to the registry |
|
498 | 498 |
func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, registry string) error { |
499 | 499 |
|
500 | 500 |
u := registry + "images/" + imgData.ID + "/json" |
... | ... |
@@ -531,8 +547,8 @@ func (r *Session) PushImageJSONRegistry(imgData *ImgData, jsonRaw []byte, regist |
531 | 531 |
return nil |
532 | 532 |
} |
533 | 533 |
|
534 |
+// PushImageLayerRegistry sends the checksum of an image layer to the registry |
|
534 | 535 |
func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry string, jsonRaw []byte) (checksum string, checksumPayload string, err error) { |
535 |
- |
|
536 | 536 |
u := registry + "images/" + imgID + "/layer" |
537 | 537 |
|
538 | 538 |
logrus.Debugf("[registry] Calling PUT %s", u) |
... | ... |
@@ -576,7 +592,7 @@ func (r *Session) PushImageLayerRegistry(imgID string, layer io.Reader, registry |
576 | 576 |
return tarsumLayer.Sum(jsonRaw), checksumPayload, nil |
577 | 577 |
} |
578 | 578 |
|
579 |
-// push a tag on the registry. |
|
579 |
+// PushRegistryTag pushes a tag on the registry. |
|
580 | 580 |
// Remote has the format '<user>/<repo> |
581 | 581 |
func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error { |
582 | 582 |
// "jsonify" the string |
... | ... |
@@ -600,6 +616,7 @@ func (r *Session) PushRegistryTag(remote, revision, tag, registry string) error |
600 | 600 |
return nil |
601 | 601 |
} |
602 | 602 |
|
603 |
+// PushImageJSONIndex uploads an image list to the repository |
|
603 | 604 |
func (r *Session) PushImageJSONIndex(remote string, imgList []*ImgData, validate bool, regs []string) (*RepositoryData, error) { |
604 | 605 |
cleanImgList := []*ImgData{} |
605 | 606 |
if validate { |
... | ... |
@@ -705,6 +722,7 @@ func shouldRedirect(response *http.Response) bool { |
705 | 705 |
return response.StatusCode >= 300 && response.StatusCode < 400 |
706 | 706 |
} |
707 | 707 |
|
708 |
+// SearchRepositories performs a search against the remote repository |
|
708 | 709 |
func (r *Session) SearchRepositories(term string) (*SearchResults, error) { |
709 | 710 |
logrus.Debugf("Index server: %s", r.indexEndpoint) |
710 | 711 |
u := r.indexEndpoint.VersionString(1) + "search?q=" + url.QueryEscape(term) |
... | ... |
@@ -727,6 +745,7 @@ func (r *Session) SearchRepositories(term string) (*SearchResults, error) { |
727 | 727 |
return result, json.NewDecoder(res.Body).Decode(result) |
728 | 728 |
} |
729 | 729 |
|
730 |
+// GetAuthConfig returns the authentication settings for a session |
|
730 | 731 |
// TODO(tiborvass): remove this once registry client v2 is vendored |
731 | 732 |
func (r *Session) GetAuthConfig(withPasswd bool) *cliconfig.AuthConfig { |
732 | 733 |
password := "" |
... | ... |
@@ -1,38 +1,66 @@ |
1 | 1 |
package registry |
2 | 2 |
|
3 |
+// SearchResult describes a search result returned from a registry |
|
3 | 4 |
type SearchResult struct { |
4 |
- StarCount int `json:"star_count"` |
|
5 |
- IsOfficial bool `json:"is_official"` |
|
6 |
- Name string `json:"name"` |
|
7 |
- IsTrusted bool `json:"is_trusted"` |
|
8 |
- IsAutomated bool `json:"is_automated"` |
|
5 |
+ // StarCount indicates the number of stars this repository has |
|
6 |
+ StarCount int `json:"star_count"` |
|
7 |
+ // IsOfficial indicates whether the result is an official repository or not |
|
8 |
+ IsOfficial bool `json:"is_official"` |
|
9 |
+ // Name is the name of the repository |
|
10 |
+ Name string `json:"name"` |
|
11 |
+ // IsOfficial indicates whether the result is trusted |
|
12 |
+ IsTrusted bool `json:"is_trusted"` |
|
13 |
+ // IsAutomated indicates whether the result is automated |
|
14 |
+ IsAutomated bool `json:"is_automated"` |
|
15 |
+ // Description is a textual description of the repository |
|
9 | 16 |
Description string `json:"description"` |
10 | 17 |
} |
11 | 18 |
|
19 |
+// SearchResults lists a collection search results returned from a registry |
|
12 | 20 |
type SearchResults struct { |
13 |
- Query string `json:"query"` |
|
14 |
- NumResults int `json:"num_results"` |
|
15 |
- Results []SearchResult `json:"results"` |
|
21 |
+ // Query contains the query string that generated the search results |
|
22 |
+ Query string `json:"query"` |
|
23 |
+ // NumResults indicates the number of results the query returned |
|
24 |
+ NumResults int `json:"num_results"` |
|
25 |
+ // Results is a slice containing the acutal results for the search |
|
26 |
+ Results []SearchResult `json:"results"` |
|
16 | 27 |
} |
17 | 28 |
|
29 |
+// RepositoryData tracks the image list, list of endpoints, and list of tokens |
|
30 |
+// for a repository |
|
18 | 31 |
type RepositoryData struct { |
19 |
- ImgList map[string]*ImgData |
|
32 |
+ // ImgList is a list of images in the repository |
|
33 |
+ ImgList map[string]*ImgData |
|
34 |
+ // Endpoints is a list of endpoints returned in X-Docker-Endpoints |
|
20 | 35 |
Endpoints []string |
21 |
- Tokens []string |
|
36 |
+ // Tokens is currently unused (remove it?) |
|
37 |
+ Tokens []string |
|
22 | 38 |
} |
23 | 39 |
|
40 |
+// ImgData is used to transfer image checksums to and from the registry |
|
24 | 41 |
type ImgData struct { |
42 |
+ // ID is an opaque string that identifies the image |
|
25 | 43 |
ID string `json:"id"` |
26 | 44 |
Checksum string `json:"checksum,omitempty"` |
27 | 45 |
ChecksumPayload string `json:"-"` |
28 | 46 |
Tag string `json:",omitempty"` |
29 | 47 |
} |
30 | 48 |
|
31 |
-type RegistryInfo struct { |
|
32 |
- Version string `json:"version"` |
|
33 |
- Standalone bool `json:"standalone"` |
|
49 |
+// PingResult contains the information returned when pinging a registry. It |
|
50 |
+// indicates the registry's version and whether the registry claims to be a |
|
51 |
+// standalone registry. |
|
52 |
+type PingResult struct { |
|
53 |
+ // Version is the registry version supplied by the registry in a HTTP |
|
54 |
+ // header |
|
55 |
+ Version string `json:"version"` |
|
56 |
+ // Standalone is set to true if the registry indicates it is a |
|
57 |
+ // standalone registry in the X-Docker-Registry-Standalone |
|
58 |
+ // header |
|
59 |
+ Standalone bool `json:"standalone"` |
|
34 | 60 |
} |
35 | 61 |
|
62 |
+// APIVersion is an integral representation of an API version (presently |
|
63 |
+// either 1 or 2) |
|
36 | 64 |
type APIVersion int |
37 | 65 |
|
38 | 66 |
func (av APIVersion) String() string { |
... | ... |
@@ -51,6 +79,8 @@ const ( |
51 | 51 |
APIVersion2 |
52 | 52 |
) |
53 | 53 |
|
54 |
+// IndexInfo contains information about a registry |
|
55 |
+// |
|
54 | 56 |
// RepositoryInfo Examples: |
55 | 57 |
// { |
56 | 58 |
// "Index" : { |
... | ... |
@@ -64,7 +94,7 @@ const ( |
64 | 64 |
// "CanonicalName" : "docker.io/debian" |
65 | 65 |
// "Official" : true, |
66 | 66 |
// } |
67 |
- |
|
67 |
+// |
|
68 | 68 |
// { |
69 | 69 |
// "Index" : { |
70 | 70 |
// "Name" : "127.0.0.1:5000", |
... | ... |
@@ -78,16 +108,33 @@ const ( |
78 | 78 |
// "Official" : false, |
79 | 79 |
// } |
80 | 80 |
type IndexInfo struct { |
81 |
- Name string |
|
82 |
- Mirrors []string |
|
83 |
- Secure bool |
|
81 |
+ // Name is the name of the registry, such as "docker.io" |
|
82 |
+ Name string |
|
83 |
+ // Mirrors is a list of mirrors, expressed as URIs |
|
84 |
+ Mirrors []string |
|
85 |
+ // Secure is set to false if the registry is part of the list of |
|
86 |
+ // insecure registries. Insecure registries accept HTTP and/or accept |
|
87 |
+ // HTTPS with certificates from unknown CAs. |
|
88 |
+ Secure bool |
|
89 |
+ // Official indicates whether this is an official registry |
|
84 | 90 |
Official bool |
85 | 91 |
} |
86 | 92 |
|
93 |
+// RepositoryInfo describes a repository |
|
87 | 94 |
type RepositoryInfo struct { |
88 |
- Index *IndexInfo |
|
89 |
- RemoteName string |
|
90 |
- LocalName string |
|
95 |
+ // Index points to registry information |
|
96 |
+ Index *IndexInfo |
|
97 |
+ // RemoteName is the remote name of the repository, such as |
|
98 |
+ // "library/ubuntu-12.04-base" |
|
99 |
+ RemoteName string |
|
100 |
+ // LocalName is the local name of the repository, such as |
|
101 |
+ // "ubuntu-12.04-base" |
|
102 |
+ LocalName string |
|
103 |
+ // CanonicalName is the canonical name of the repository, such as |
|
104 |
+ // "docker.io/library/ubuntu-12.04-base" |
|
91 | 105 |
CanonicalName string |
92 |
- Official bool |
|
106 |
+ // Official indicates whether the repository is considered official. |
|
107 |
+ // If the registry is official, and the normalized name does not |
|
108 |
+ // contain a '/' (e.g. "foo"), then it is considered an official repo. |
|
109 |
+ Official bool |
|
93 | 110 |
} |