Browse code

Merge pull request #13374 from RichardScothern/v2-mirror

V2 mirror support

Tibor Vass authored on 2015/05/28 10:15:26
Showing 2 changed files
... ...
@@ -55,6 +55,25 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
55 55
 	}
56 56
 	defer s.poolRemove("pull", utils.ImageReference(repoInfo.LocalName, tag))
57 57
 
58
+	logName := repoInfo.LocalName
59
+	if tag != "" {
60
+		logName = utils.ImageReference(logName, tag)
61
+	}
62
+
63
+	// Attempt pulling official content from a provided v2 mirror
64
+	if repoInfo.Index.Official {
65
+		v2mirrorEndpoint, v2mirrorRepoInfo, err := configureV2Mirror(repoInfo, s.registryService)
66
+		if err != nil {
67
+			logrus.Errorf("Error configuring mirrors: %s", err)
68
+			return err
69
+		}
70
+
71
+		if v2mirrorEndpoint != nil {
72
+			logrus.Debugf("Attempting to pull from v2 mirror: %s", v2mirrorEndpoint.URL)
73
+			return s.pullFromV2Mirror(v2mirrorEndpoint, v2mirrorRepoInfo, imagePullConfig, tag, sf, logName)
74
+		}
75
+	}
76
+
58 77
 	logrus.Debugf("pulling image from host %q with remote name %q", repoInfo.Index.Name, repoInfo.RemoteName)
59 78
 
60 79
 	endpoint, err := repoInfo.GetEndpoint(imagePullConfig.MetaHeaders)
... ...
@@ -73,11 +92,6 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
73 73
 		return err
74 74
 	}
75 75
 
76
-	logName := repoInfo.LocalName
77
-	if tag != "" {
78
-		logName = utils.ImageReference(logName, tag)
79
-	}
80
-
81 76
 	if len(repoInfo.Index.Mirrors) == 0 && (repoInfo.Index.Official || endpoint.Version == registry.APIVersion2) {
82 77
 		if repoInfo.Official {
83 78
 			s.trustService.UpdateBase()
... ...
@@ -106,6 +120,91 @@ func (s *TagStore) Pull(image string, tag string, imagePullConfig *ImagePullConf
106 106
 	s.eventsService.Log("pull", logName, "")
107 107
 
108 108
 	return nil
109
+
110
+}
111
+
112
+func makeMirrorRepoInfo(repoInfo *registry.RepositoryInfo, mirror string) *registry.RepositoryInfo {
113
+	mirrorRepo := &registry.RepositoryInfo{
114
+		RemoteName:    repoInfo.RemoteName,
115
+		LocalName:     repoInfo.LocalName,
116
+		CanonicalName: repoInfo.CanonicalName,
117
+		Official:      false,
118
+
119
+		Index: &registry.IndexInfo{
120
+			Official: false,
121
+			Secure:   repoInfo.Index.Secure,
122
+			Name:     mirror,
123
+			Mirrors:  []string{},
124
+		},
125
+	}
126
+	return mirrorRepo
127
+}
128
+
129
+func configureV2Mirror(repoInfo *registry.RepositoryInfo, s *registry.Service) (*registry.Endpoint, *registry.RepositoryInfo, error) {
130
+	mirrors := repoInfo.Index.Mirrors
131
+
132
+	if len(mirrors) == 0 {
133
+		// no mirrors configured
134
+		return nil, nil, nil
135
+	}
136
+
137
+	v1MirrorCount := 0
138
+	var v2MirrorEndpoint *registry.Endpoint
139
+	var v2MirrorRepoInfo *registry.RepositoryInfo
140
+	var lastErr error
141
+	for _, mirror := range mirrors {
142
+		mirrorRepoInfo := makeMirrorRepoInfo(repoInfo, mirror)
143
+		endpoint, err := registry.NewEndpoint(mirrorRepoInfo.Index, nil)
144
+		if err != nil {
145
+			logrus.Errorf("Unable to create endpoint for %s: %s", mirror, err)
146
+			lastErr = err
147
+			continue
148
+		}
149
+		if endpoint.Version == 2 {
150
+			if v2MirrorEndpoint == nil {
151
+				v2MirrorEndpoint = endpoint
152
+				v2MirrorRepoInfo = mirrorRepoInfo
153
+			} else {
154
+				// > 1 v2 mirrors given
155
+				return nil, nil, fmt.Errorf("multiple v2 mirrors configured")
156
+			}
157
+		} else {
158
+			v1MirrorCount++
159
+		}
160
+	}
161
+
162
+	if v1MirrorCount == len(mirrors) {
163
+		// OK, but mirrors are v1
164
+		return nil, nil, nil
165
+	}
166
+	if v2MirrorEndpoint != nil && v1MirrorCount == 0 {
167
+		// OK, 1 v2 mirror specified
168
+		return v2MirrorEndpoint, v2MirrorRepoInfo, nil
169
+	}
170
+	if v2MirrorEndpoint != nil && v1MirrorCount > 0 {
171
+		lastErr = fmt.Errorf("v1 and v2 mirrors configured")
172
+	}
173
+	return nil, nil, lastErr
174
+}
175
+
176
+func (s *TagStore) pullFromV2Mirror(mirrorEndpoint *registry.Endpoint, repoInfo *registry.RepositoryInfo,
177
+	imagePullConfig *ImagePullConfig, tag string, sf *streamformatter.StreamFormatter, logName string) error {
178
+
179
+	tr := transport.NewTransport(
180
+		registry.NewTransport(registry.ReceiveTimeout, mirrorEndpoint.IsSecure),
181
+		registry.DockerHeaders(imagePullConfig.MetaHeaders)...,
182
+	)
183
+	client := registry.HTTPClient(tr)
184
+	mirrorSession, err := registry.NewSession(client, &cliconfig.AuthConfig{}, mirrorEndpoint)
185
+	if err != nil {
186
+		return err
187
+	}
188
+	logrus.Debugf("Pulling v2 repository with local name %q from %s", repoInfo.LocalName, mirrorEndpoint.URL)
189
+	if err := s.pullV2Repository(mirrorSession, imagePullConfig.OutStream, repoInfo, tag, sf); err != nil {
190
+		return err
191
+	}
192
+	s.eventsService.Log("pull", logName, "")
193
+	return nil
109 194
 }
110 195
 
111 196
 func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *registry.RepositoryInfo, askedTag string, sf *streamformatter.StreamFormatter) error {
... ...
@@ -185,6 +284,8 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *
185 185
 			var lastErr, err error
186 186
 			var isDownloaded bool
187 187
 			for _, ep := range repoInfo.Index.Mirrors {
188
+				// Ensure endpoint is v1
189
+				ep = ep + "v1/"
188 190
 				out.Write(sf.FormatProgress(stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, repoInfo.CanonicalName, ep), nil))
189 191
 				if isDownloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
190 192
 					// Don't report errors when pulling from mirrors.
... ...
@@ -189,7 +189,7 @@ func ValidateMirror(val string) (string, error) {
189 189
 		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
190 190
 	}
191 191
 
192
-	return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil
192
+	return fmt.Sprintf("%s://%s/", uri.Scheme, uri.Host), nil
193 193
 }
194 194
 
195 195
 // ValidateIndexName validates an index name.
... ...
@@ -358,7 +358,9 @@ func (config *ServiceConfig) NewRepositoryInfo(reposName string) (*RepositoryInf
358 358
 		// *TODO: Decouple index name from hostname (via registry configuration?)
359 359
 		repoInfo.LocalName = repoInfo.Index.Name + "/" + repoInfo.RemoteName
360 360
 		repoInfo.CanonicalName = repoInfo.LocalName
361
+
361 362
 	}
363
+
362 364
 	return repoInfo, nil
363 365
 }
364 366