Browse code

Merge pull request #16385 from RichardScothern/v1-deprecation

Add a daemon flag to prevent contact with v1 registries.

Arnaud Porterie authored on 2015/10/01 02:10:06
Showing 14 changed files
... ...
@@ -100,3 +100,9 @@ docker was automatically creating the `/host/path` if it didn't already exist.
100 100
 
101 101
 This auto-creation of the host path is deprecated and docker will error out if
102 102
 the path does not exist.
103
+
104
+### Interacting with V1 registries
105
+
106
+Version 1.9 adds a flag (`--no-legacy-registry=false`) which prevents the docker daemon from `pull`, `push`, and `login` operations against v1 registries.  Though disabled by default, this signals the intent to deprecate the v1 protocol.
107
+
108
+
... ...
@@ -49,6 +49,7 @@ weight=1
49 49
       --log-driver="json-file"               Default driver for container logs
50 50
       --log-opt=[]                           Log driver specific options
51 51
       --mtu=0                                Set the containers network MTU
52
+      --no-legacy-registry=false             Do not contact legacy registries
52 53
       -p, --pidfile="/var/run/docker.pid"    Path to use for daemon PID file
53 54
       --registry-mirror=[]                   Preferred Docker registry mirror
54 55
       -s, --storage-driver=""                Storage driver to use
... ...
@@ -457,6 +458,10 @@ because its use creates security vulnerabilities it should ONLY be enabled for
457 457
 testing purposes.  For increased security, users should add their CA to their 
458 458
 system's list of trusted CAs instead of enabling `--insecure-registry`.
459 459
 
460
+## Legacy Registries
461
+
462
+Enabling `--no-legacy-registry` forces a docker daemon to only interact with registries which support the V2 protocol.  Specifically, the daemon will not attempt `push`, `pull` and `login` to v1 registries.  The exception to this is `search` which can still be performed on v1 registries.
463
+
460 464
 ## Running a Docker daemon behind a HTTPS_PROXY
461 465
 
462 466
 When running inside a LAN that uses a `HTTPS` proxy, the Docker Hub
... ...
@@ -60,6 +60,9 @@ func (p *v1Puller) Pull(tag string) (fallback bool, err error) {
60 60
 		// TODO(dmcgowan): Check if should fallback
61 61
 		return false, err
62 62
 	}
63
+	out := p.config.OutStream
64
+	out.Write(p.sf.FormatStatus("", "%s: this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.", p.repoInfo.CanonicalName))
65
+
63 66
 	return false, nil
64 67
 }
65 68
 
... ...
@@ -32,10 +32,13 @@ func init() {
32 32
 type DockerRegistrySuite struct {
33 33
 	ds  *DockerSuite
34 34
 	reg *testRegistryV2
35
+	d   *Daemon
35 36
 }
36 37
 
37 38
 func (s *DockerRegistrySuite) SetUpTest(c *check.C) {
39
+	testRequires(c, DaemonIsLinux)
38 40
 	s.reg = setupRegistry(c)
41
+	s.d = NewDaemon(c)
39 42
 }
40 43
 
41 44
 func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
... ...
@@ -45,6 +48,7 @@ func (s *DockerRegistrySuite) TearDownTest(c *check.C) {
45 45
 	if s.ds != nil {
46 46
 		s.ds.TearDownTest(c)
47 47
 	}
48
+	s.d.Stop()
48 49
 }
49 50
 
50 51
 func init() {
51 52
new file mode 100644
... ...
@@ -0,0 +1,147 @@
0
+package main
1
+
2
+import (
3
+	"fmt"
4
+	"github.com/go-check/check"
5
+	"io/ioutil"
6
+	"net/http"
7
+	"os"
8
+)
9
+
10
+func makefile(contents string) (string, func(), error) {
11
+	cleanup := func() {
12
+
13
+	}
14
+
15
+	f, err := ioutil.TempFile(".", "tmp")
16
+	if err != nil {
17
+		return "", cleanup, err
18
+	}
19
+	err = ioutil.WriteFile(f.Name(), []byte(contents), os.ModePerm)
20
+	if err != nil {
21
+		return "", cleanup, err
22
+	}
23
+
24
+	cleanup = func() {
25
+		err := os.Remove(f.Name())
26
+		if err != nil {
27
+			fmt.Println("Error removing tmpfile")
28
+		}
29
+	}
30
+	return f.Name(), cleanup, nil
31
+
32
+}
33
+
34
+// TestV2Only ensures that a daemon in v2-only mode does not
35
+// attempt to contact any v1 registry endpoints.
36
+func (s *DockerRegistrySuite) TestV2Only(c *check.C) {
37
+	reg, err := newTestRegistry(c)
38
+	if err != nil {
39
+		c.Fatal(err.Error())
40
+	}
41
+
42
+	reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
43
+		w.WriteHeader(404)
44
+	})
45
+
46
+	reg.registerHandler("/v1/.*", func(w http.ResponseWriter, r *http.Request) {
47
+		c.Fatal("V1 registry contacted")
48
+	})
49
+
50
+	repoName := fmt.Sprintf("%s/busybox", reg.hostport)
51
+
52
+	err = s.d.Start("--insecure-registry", reg.hostport, "--no-legacy-registry=true")
53
+	if err != nil {
54
+		c.Fatalf("Error starting daemon: %s", err.Error())
55
+	}
56
+
57
+	dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
58
+	if err != nil {
59
+		c.Fatalf("Unable to create test dockerfile")
60
+	}
61
+	defer cleanup()
62
+
63
+	s.d.Cmd("build", "--file", dockerfileName, ".")
64
+
65
+	s.d.Cmd("run", repoName)
66
+	s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
67
+	s.d.Cmd("tag", "busybox", repoName)
68
+	s.d.Cmd("push", repoName)
69
+	s.d.Cmd("pull", repoName)
70
+}
71
+
72
+// TestV1 starts a daemon in 'normal' mode
73
+// and ensure v1 endpoints are hit for the following operations:
74
+// login, push, pull, build & run
75
+func (s *DockerRegistrySuite) TestV1(c *check.C) {
76
+	reg, err := newTestRegistry(c)
77
+	if err != nil {
78
+		c.Fatal(err.Error())
79
+	}
80
+
81
+	v2Pings := 0
82
+	reg.registerHandler("/v2/", func(w http.ResponseWriter, r *http.Request) {
83
+		v2Pings++
84
+		// V2 ping 404 causes fallback to v1
85
+		w.WriteHeader(404)
86
+	})
87
+
88
+	v1Pings := 0
89
+	reg.registerHandler("/v1/_ping", func(w http.ResponseWriter, r *http.Request) {
90
+		v1Pings++
91
+	})
92
+
93
+	v1Logins := 0
94
+	reg.registerHandler("/v1/users/", func(w http.ResponseWriter, r *http.Request) {
95
+		v1Logins++
96
+	})
97
+
98
+	v1Repo := 0
99
+	reg.registerHandler("/v1/repositories/busybox/", func(w http.ResponseWriter, r *http.Request) {
100
+		v1Repo++
101
+	})
102
+
103
+	reg.registerHandler("/v1/repositories/busybox/images", func(w http.ResponseWriter, r *http.Request) {
104
+		v1Repo++
105
+	})
106
+
107
+	err = s.d.Start("--insecure-registry", reg.hostport, "--no-legacy-registry=false")
108
+	if err != nil {
109
+		c.Fatalf("Error starting daemon: %s", err.Error())
110
+	}
111
+
112
+	dockerfileName, cleanup, err := makefile(fmt.Sprintf("FROM %s/busybox", reg.hostport))
113
+	if err != nil {
114
+		c.Fatalf("Unable to create test dockerfile")
115
+	}
116
+	defer cleanup()
117
+
118
+	s.d.Cmd("build", "--file", dockerfileName, ".")
119
+	if v1Repo == 0 {
120
+		c.Errorf("Expected v1 repository access after build")
121
+	}
122
+
123
+	repoName := fmt.Sprintf("%s/busybox", reg.hostport)
124
+	s.d.Cmd("run", repoName)
125
+	if v1Repo == 1 {
126
+		c.Errorf("Expected v1 repository access after run")
127
+	}
128
+
129
+	s.d.Cmd("login", "-u", "richard", "-p", "testtest", "-e", "testuser@testdomain.com", reg.hostport)
130
+	if v1Logins == 0 {
131
+		c.Errorf("Expected v1 login attempt")
132
+	}
133
+
134
+	s.d.Cmd("tag", "busybox", repoName)
135
+	s.d.Cmd("push", repoName)
136
+
137
+	if v1Repo != 2 || v1Pings != 1 {
138
+		c.Error("Not all endpoints contacted after push", v1Repo, v1Pings)
139
+	}
140
+
141
+	s.d.Cmd("pull", repoName)
142
+	if v1Repo != 3 {
143
+		c.Errorf("Expected v1 repository access after pull")
144
+	}
145
+
146
+}
0 147
new file mode 100644
... ...
@@ -0,0 +1,56 @@
0
+package main
1
+
2
+import (
3
+	"net/http"
4
+	"net/http/httptest"
5
+	"regexp"
6
+	"strings"
7
+	"sync"
8
+
9
+	"github.com/go-check/check"
10
+)
11
+
12
+type handlerFunc func(w http.ResponseWriter, r *http.Request)
13
+
14
+type testRegistry struct {
15
+	server   *httptest.Server
16
+	hostport string
17
+	handlers map[string]handlerFunc
18
+	mu       sync.Mutex
19
+}
20
+
21
+func (tr *testRegistry) registerHandler(path string, h handlerFunc) {
22
+	tr.mu.Lock()
23
+	defer tr.mu.Unlock()
24
+	tr.handlers[path] = h
25
+}
26
+
27
+func newTestRegistry(c *check.C) (*testRegistry, error) {
28
+	testReg := &testRegistry{handlers: make(map[string]handlerFunc)}
29
+
30
+	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
31
+		url := r.URL.String()
32
+
33
+		var matched bool
34
+		var err error
35
+		for re, function := range testReg.handlers {
36
+			matched, err = regexp.MatchString(re, url)
37
+			if err != nil {
38
+				c.Fatalf("Error with handler regexp")
39
+				return
40
+			}
41
+			if matched {
42
+				function(w, r)
43
+				break
44
+			}
45
+		}
46
+
47
+		if !matched {
48
+			c.Fatal("Unable to match", url, "with regexp")
49
+		}
50
+	}))
51
+
52
+	testReg.server = ts
53
+	testReg.hostport = strings.Replace(ts.URL, "http://", "", 1)
54
+	return testReg, nil
55
+}
... ...
@@ -40,6 +40,7 @@ docker-daemon - Enable daemon mode
40 40
 [**--log-driver**[=*json-file*]]
41 41
 [**--log-opt**[=*map[]*]]
42 42
 [**--mtu**[=*0*]]
43
+[**--no-legacy-registry**[=*false*]]
43 44
 [**-p**|**--pidfile**[=*/var/run/docker.pid*]]
44 45
 [**--registry-mirror**[=*[]*]]
45 46
 [**-s**|**--storage-driver**[=*STORAGE-DRIVER*]]
... ...
@@ -170,6 +171,9 @@ unix://[/path/to/socket] to use.
170 170
 **--mtu**=VALUE
171 171
   Set the containers network mtu. Default is `0`.
172 172
 
173
+**--no-legacy-registry**=*true*|*false*
174
+  Do not contact legacy registries
175
+
173 176
 **-p**, **--pidfile**=""
174 177
   Path to use for daemon PID file. Default is `/var/run/docker.pid`
175 178
 
... ...
@@ -44,6 +44,10 @@ var (
44 44
 	ErrInvalidRepositoryName = errors.New("Invalid repository name (ex: \"registry.domain.tld/myrepos\")")
45 45
 
46 46
 	emptyServiceConfig = NewServiceConfig(nil)
47
+
48
+	// V2Only controls access to legacy registries.  If it is set to true via the
49
+	// command line flag the daemon will not attempt to contact v1 legacy registries
50
+	V2Only = false
47 51
 )
48 52
 
49 53
 // InstallFlags adds command-line options to the top-level flag parser for
... ...
@@ -53,6 +57,7 @@ func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) str
53 53
 	cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
54 54
 	options.InsecureRegistries = opts.NewListOpts(ValidateIndexName)
55 55
 	cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
56
+	cmd.BoolVar(&V2Only, []string{"-no-legacy-registry"}, false, "Do not contact legacy registries")
56 57
 }
57 58
 
58 59
 type netIPNet net.IPNet
... ...
@@ -42,8 +42,9 @@ func scanForAPIVersion(address string) (string, APIVersion) {
42 42
 	return address, APIVersionUnknown
43 43
 }
44 44
 
45
-// NewEndpoint parses the given address to return a registry endpoint.
46
-func NewEndpoint(index *IndexInfo, metaHeaders http.Header) (*Endpoint, error) {
45
+// NewEndpoint parses the given address to return a registry endpoint.  v can be used to
46
+// specify a specific endpoint version
47
+func NewEndpoint(index *IndexInfo, metaHeaders http.Header, v APIVersion) (*Endpoint, error) {
47 48
 	tlsConfig, err := newTLSConfig(index.Name, index.Secure)
48 49
 	if err != nil {
49 50
 		return nil, err
... ...
@@ -52,6 +53,9 @@ func NewEndpoint(index *IndexInfo, metaHeaders http.Header) (*Endpoint, error) {
52 52
 	if err != nil {
53 53
 		return nil, err
54 54
 	}
55
+	if v != APIVersionUnknown {
56
+		endpoint.Version = v
57
+	}
55 58
 	if err := validateEndpoint(endpoint); err != nil {
56 59
 		return nil, err
57 60
 	}
... ...
@@ -111,11 +115,6 @@ 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
115
-func (repoInfo *RepositoryInfo) GetEndpoint(metaHeaders http.Header) (*Endpoint, error) {
116
-	return NewEndpoint(repoInfo.Index, metaHeaders)
117
-}
118
-
119 114
 // Endpoint stores basic information about a registry endpoint.
120 115
 type Endpoint struct {
121 116
 	client         *http.Client
... ...
@@ -49,6 +49,10 @@ func init() {
49 49
 	httpVersion = append(httpVersion, useragent.VersionInfo{"arch", runtime.GOARCH})
50 50
 
51 51
 	dockerUserAgent = useragent.AppendVersions("", httpVersion...)
52
+
53
+	if runtime.GOOS != "linux" {
54
+		V2Only = true
55
+	}
52 56
 }
53 57
 
54 58
 func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
... ...
@@ -23,7 +23,7 @@ const (
23 23
 
24 24
 func spawnTestRegistrySession(t *testing.T) *Session {
25 25
 	authConfig := &cliconfig.AuthConfig{}
26
-	endpoint, err := NewEndpoint(makeIndex("/v1/"), nil)
26
+	endpoint, err := NewEndpoint(makeIndex("/v1/"), nil, APIVersionUnknown)
27 27
 	if err != nil {
28 28
 		t.Fatal(err)
29 29
 	}
... ...
@@ -50,7 +50,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
50 50
 
51 51
 func TestPingRegistryEndpoint(t *testing.T) {
52 52
 	testPing := func(index *IndexInfo, expectedStandalone bool, assertMessage string) {
53
-		ep, err := NewEndpoint(index, nil)
53
+		ep, err := NewEndpoint(index, nil, APIVersionUnknown)
54 54
 		if err != nil {
55 55
 			t.Fatal(err)
56 56
 		}
... ...
@@ -70,7 +70,7 @@ func TestPingRegistryEndpoint(t *testing.T) {
70 70
 func TestEndpoint(t *testing.T) {
71 71
 	// Simple wrapper to fail test if err != nil
72 72
 	expandEndpoint := func(index *IndexInfo) *Endpoint {
73
-		endpoint, err := NewEndpoint(index, nil)
73
+		endpoint, err := NewEndpoint(index, nil, APIVersionUnknown)
74 74
 		if err != nil {
75 75
 			t.Fatal(err)
76 76
 		}
... ...
@@ -79,7 +79,7 @@ func TestEndpoint(t *testing.T) {
79 79
 
80 80
 	assertInsecureIndex := func(index *IndexInfo) {
81 81
 		index.Secure = true
82
-		_, err := NewEndpoint(index, nil)
82
+		_, err := NewEndpoint(index, nil, APIVersionUnknown)
83 83
 		assertNotEqual(t, err, nil, index.Name+": Expected error for insecure index")
84 84
 		assertEqual(t, strings.Contains(err.Error(), "insecure-registry"), true, index.Name+": Expected insecure-registry  error for insecure index")
85 85
 		index.Secure = false
... ...
@@ -87,7 +87,7 @@ func TestEndpoint(t *testing.T) {
87 87
 
88 88
 	assertSecureIndex := func(index *IndexInfo) {
89 89
 		index.Secure = true
90
-		_, err := NewEndpoint(index, nil)
90
+		_, err := NewEndpoint(index, nil, APIVersionUnknown)
91 91
 		assertNotEqual(t, err, nil, index.Name+": Expected cert error for secure index")
92 92
 		assertEqual(t, strings.Contains(err.Error(), "certificate signed by unknown authority"), true, index.Name+": Expected cert error for secure index")
93 93
 		index.Secure = false
... ...
@@ -153,7 +153,7 @@ func TestEndpoint(t *testing.T) {
153 153
 	}
154 154
 	for _, address := range badEndpoints {
155 155
 		index.Name = address
156
-		_, err := NewEndpoint(index, nil)
156
+		_, err := NewEndpoint(index, nil, APIVersionUnknown)
157 157
 		checkNotEqual(t, err, nil, "Expected error while expanding bad endpoint")
158 158
 	}
159 159
 }
... ...
@@ -2,15 +2,11 @@ package registry
2 2
 
3 3
 import (
4 4
 	"crypto/tls"
5
-	"fmt"
6 5
 	"net/http"
7 6
 	"net/url"
8
-	"runtime"
9
-	"strings"
10 7
 
11 8
 	"github.com/docker/distribution/registry/client/auth"
12 9
 	"github.com/docker/docker/cliconfig"
13
-	"github.com/docker/docker/pkg/tlsconfig"
14 10
 )
15 11
 
16 12
 // Service is a registry service. It tracks configuration data such as a list
... ...
@@ -40,7 +36,14 @@ func (s *Service) Auth(authConfig *cliconfig.AuthConfig) (string, error) {
40 40
 	if err != nil {
41 41
 		return "", err
42 42
 	}
43
-	endpoint, err := NewEndpoint(index, nil)
43
+
44
+	endpointVersion := APIVersion(APIVersionUnknown)
45
+	if V2Only {
46
+		// Override the endpoint to only attempt a v2 ping
47
+		endpointVersion = APIVersion2
48
+	}
49
+
50
+	endpoint, err := NewEndpoint(index, nil, endpointVersion)
44 51
 	if err != nil {
45 52
 		return "", err
46 53
 	}
... ...
@@ -57,10 +60,11 @@ func (s *Service) Search(term string, authConfig *cliconfig.AuthConfig, headers
57 57
 	}
58 58
 
59 59
 	// *TODO: Search multiple indexes.
60
-	endpoint, err := repoInfo.GetEndpoint(http.Header(headers))
60
+	endpoint, err := NewEndpoint(repoInfo.Index, http.Header(headers), APIVersionUnknown)
61 61
 	if err != nil {
62 62
 		return nil, err
63 63
 	}
64
+
64 65
 	r, err := NewSession(endpoint.client, authConfig, endpoint)
65 66
 	if err != nil {
66 67
 		return nil, err
... ...
@@ -132,97 +136,20 @@ func (s *Service) LookupPushEndpoints(repoName string) (endpoints []APIEndpoint,
132 132
 }
133 133
 
134 134
 func (s *Service) lookupEndpoints(repoName string) (endpoints []APIEndpoint, err error) {
135
-	var cfg = tlsconfig.ServerDefault
136
-	tlsConfig := &cfg
137
-	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
138
-		// v2 mirrors
139
-		for _, mirror := range s.Config.Mirrors {
140
-			mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
141
-			if err != nil {
142
-				return nil, err
143
-			}
144
-			endpoints = append(endpoints, APIEndpoint{
145
-				URL: mirror,
146
-				// guess mirrors are v2
147
-				Version:      APIVersion2,
148
-				Mirror:       true,
149
-				TrimHostname: true,
150
-				TLSConfig:    mirrorTLSConfig,
151
-			})
152
-		}
153
-		// v2 registry
154
-		endpoints = append(endpoints, APIEndpoint{
155
-			URL:          DefaultV2Registry,
156
-			Version:      APIVersion2,
157
-			Official:     true,
158
-			TrimHostname: true,
159
-			TLSConfig:    tlsConfig,
160
-		})
161
-		if runtime.GOOS == "linux" { // do not inherit legacy API for OSes supported in the future
162
-			// v1 registry
163
-			endpoints = append(endpoints, APIEndpoint{
164
-				URL:          DefaultV1Registry,
165
-				Version:      APIVersion1,
166
-				Official:     true,
167
-				TrimHostname: true,
168
-				TLSConfig:    tlsConfig,
169
-			})
170
-		}
171
-		return endpoints, nil
172
-	}
173
-
174
-	slashIndex := strings.IndexRune(repoName, '/')
175
-	if slashIndex <= 0 {
176
-		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
177
-	}
178
-	hostname := repoName[:slashIndex]
179
-
180
-	tlsConfig, err = s.TLSConfig(hostname)
135
+	endpoints, err = s.lookupV2Endpoints(repoName)
181 136
 	if err != nil {
182 137
 		return nil, err
183 138
 	}
184
-	isSecure := !tlsConfig.InsecureSkipVerify
185 139
 
186
-	v2Versions := []auth.APIVersion{
187
-		{
188
-			Type:    "registry",
189
-			Version: "2.0",
190
-		},
191
-	}
192
-	endpoints = []APIEndpoint{
193
-		{
194
-			URL:           "https://" + hostname,
195
-			Version:       APIVersion2,
196
-			TrimHostname:  true,
197
-			TLSConfig:     tlsConfig,
198
-			VersionHeader: DefaultRegistryVersionHeader,
199
-			Versions:      v2Versions,
200
-		},
201
-		{
202
-			URL:          "https://" + hostname,
203
-			Version:      APIVersion1,
204
-			TrimHostname: true,
205
-			TLSConfig:    tlsConfig,
206
-		},
140
+	if V2Only {
141
+		return endpoints, nil
207 142
 	}
208 143
 
209
-	if !isSecure {
210
-		endpoints = append(endpoints, APIEndpoint{
211
-			URL:          "http://" + hostname,
212
-			Version:      APIVersion2,
213
-			TrimHostname: true,
214
-			// used to check if supposed to be secure via InsecureSkipVerify
215
-			TLSConfig:     tlsConfig,
216
-			VersionHeader: DefaultRegistryVersionHeader,
217
-			Versions:      v2Versions,
218
-		}, APIEndpoint{
219
-			URL:          "http://" + hostname,
220
-			Version:      APIVersion1,
221
-			TrimHostname: true,
222
-			// used to check if supposed to be secure via InsecureSkipVerify
223
-			TLSConfig: tlsConfig,
224
-		})
144
+	legacyEndpoints, err := s.lookupV1Endpoints(repoName)
145
+	if err != nil {
146
+		return nil, err
225 147
 	}
148
+	endpoints = append(endpoints, legacyEndpoints...)
226 149
 
227 150
 	return endpoints, nil
228 151
 }
229 152
new file mode 100644
... ...
@@ -0,0 +1,54 @@
0
+package registry
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+
6
+	"github.com/docker/docker/pkg/tlsconfig"
7
+)
8
+
9
+func (s *Service) lookupV1Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
10
+	var cfg = tlsconfig.ServerDefault
11
+	tlsConfig := &cfg
12
+	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
13
+		endpoints = append(endpoints, APIEndpoint{
14
+			URL:          DefaultV1Registry,
15
+			Version:      APIVersion1,
16
+			Official:     true,
17
+			TrimHostname: true,
18
+			TLSConfig:    tlsConfig,
19
+		})
20
+		return endpoints, nil
21
+	}
22
+
23
+	slashIndex := strings.IndexRune(repoName, '/')
24
+	if slashIndex <= 0 {
25
+		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
26
+	}
27
+	hostname := repoName[:slashIndex]
28
+
29
+	tlsConfig, err = s.TLSConfig(hostname)
30
+	if err != nil {
31
+		return nil, err
32
+	}
33
+
34
+	endpoints = []APIEndpoint{
35
+		{
36
+			URL:          "https://" + hostname,
37
+			Version:      APIVersion1,
38
+			TrimHostname: true,
39
+			TLSConfig:    tlsConfig,
40
+		},
41
+	}
42
+
43
+	if tlsConfig.InsecureSkipVerify {
44
+		endpoints = append(endpoints, APIEndpoint{ // or this
45
+			URL:          "http://" + hostname,
46
+			Version:      APIVersion1,
47
+			TrimHostname: true,
48
+			// used to check if supposed to be secure via InsecureSkipVerify
49
+			TLSConfig: tlsConfig,
50
+		})
51
+	}
52
+	return endpoints, nil
53
+}
0 54
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+package registry
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+
6
+	"github.com/docker/distribution/registry/client/auth"
7
+	"github.com/docker/docker/pkg/tlsconfig"
8
+)
9
+
10
+func (s *Service) lookupV2Endpoints(repoName string) (endpoints []APIEndpoint, err error) {
11
+	var cfg = tlsconfig.ServerDefault
12
+	tlsConfig := &cfg
13
+	if strings.HasPrefix(repoName, DefaultNamespace+"/") {
14
+		// v2 mirrors
15
+		for _, mirror := range s.Config.Mirrors {
16
+			mirrorTLSConfig, err := s.tlsConfigForMirror(mirror)
17
+			if err != nil {
18
+				return nil, err
19
+			}
20
+			endpoints = append(endpoints, APIEndpoint{
21
+				URL: mirror,
22
+				// guess mirrors are v2
23
+				Version:      APIVersion2,
24
+				Mirror:       true,
25
+				TrimHostname: true,
26
+				TLSConfig:    mirrorTLSConfig,
27
+			})
28
+		}
29
+		// v2 registry
30
+		endpoints = append(endpoints, APIEndpoint{
31
+			URL:          DefaultV2Registry,
32
+			Version:      APIVersion2,
33
+			Official:     true,
34
+			TrimHostname: true,
35
+			TLSConfig:    tlsConfig,
36
+		})
37
+
38
+		return endpoints, nil
39
+	}
40
+
41
+	slashIndex := strings.IndexRune(repoName, '/')
42
+	if slashIndex <= 0 {
43
+		return nil, fmt.Errorf("invalid repo name: missing '/':  %s", repoName)
44
+	}
45
+	hostname := repoName[:slashIndex]
46
+
47
+	tlsConfig, err = s.TLSConfig(hostname)
48
+	if err != nil {
49
+		return nil, err
50
+	}
51
+
52
+	v2Versions := []auth.APIVersion{
53
+		{
54
+			Type:    "registry",
55
+			Version: "2.0",
56
+		},
57
+	}
58
+	endpoints = []APIEndpoint{
59
+		{
60
+			URL:           "https://" + hostname,
61
+			Version:       APIVersion2,
62
+			TrimHostname:  true,
63
+			TLSConfig:     tlsConfig,
64
+			VersionHeader: DefaultRegistryVersionHeader,
65
+			Versions:      v2Versions,
66
+		},
67
+	}
68
+
69
+	if tlsConfig.InsecureSkipVerify {
70
+		endpoints = append(endpoints, APIEndpoint{
71
+			URL:          "http://" + hostname,
72
+			Version:      APIVersion2,
73
+			TrimHostname: true,
74
+			// used to check if supposed to be secure via InsecureSkipVerify
75
+			TLSConfig:     tlsConfig,
76
+			VersionHeader: DefaultRegistryVersionHeader,
77
+			Versions:      v2Versions,
78
+		})
79
+	}
80
+
81
+	return endpoints, nil
82
+}