Browse code

Merge pull request #13870 from lindenlab/pull-single-tag

Only request a single repository tag when pulling a specific image:tag

Phil Estes authored on 2015/06/18 04:29:39
Showing 4 changed files
... ...
@@ -219,8 +219,18 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, repoInfo *
219 219
 	}
220 220
 
221 221
 	logrus.Debugf("Retrieving the tag list")
222
-	tagsList, err := r.GetRemoteTags(repoData.Endpoints, repoInfo.RemoteName)
222
+	tagsList := make(map[string]string)
223
+	if askedTag == "" {
224
+		tagsList, err = r.GetRemoteTags(repoData.Endpoints, repoInfo.RemoteName)
225
+	} else {
226
+		var tagId string
227
+		tagId, err = r.GetRemoteTag(repoData.Endpoints, repoInfo.RemoteName, askedTag)
228
+		tagsList[askedTag] = tagId
229
+	}
223 230
 	if err != nil {
231
+		if err == registry.ErrRepoNotFound && askedTag != "" {
232
+			return fmt.Errorf("Tag %s not found in repository %s", askedTag, repoInfo.CanonicalName)
233
+		}
224 234
 		logrus.Errorf("unable to get remote tags: %s", err)
225 235
 		return err
226 236
 	}
... ...
@@ -81,6 +81,7 @@ var (
81 81
 	testRepositories = map[string]map[string]string{
82 82
 		"foo42/bar": {
83 83
 			"latest": "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
84
+			"test":   "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
84 85
 		},
85 86
 	}
86 87
 	mockHosts = map[string][]net.IP{
... ...
@@ -211,18 +211,33 @@ func TestGetRemoteImageLayer(t *testing.T) {
211 211
 	}
212 212
 }
213 213
 
214
+func TestGetRemoteTag(t *testing.T) {
215
+	r := spawnTestRegistrySession(t)
216
+	tag, err := r.GetRemoteTag([]string{makeURL("/v1/")}, REPO, "test")
217
+	if err != nil {
218
+		t.Fatal(err)
219
+	}
220
+	assertEqual(t, tag, imageID, "Expected tag test to map to "+imageID)
221
+
222
+	_, err = r.GetRemoteTag([]string{makeURL("/v1/")}, "foo42/baz", "foo")
223
+	if err != ErrRepoNotFound {
224
+		t.Fatal("Expected ErrRepoNotFound error when fetching tag for bogus repo")
225
+	}
226
+}
227
+
214 228
 func TestGetRemoteTags(t *testing.T) {
215 229
 	r := spawnTestRegistrySession(t)
216 230
 	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO)
217 231
 	if err != nil {
218 232
 		t.Fatal(err)
219 233
 	}
220
-	assertEqual(t, len(tags), 1, "Expected one tag")
234
+	assertEqual(t, len(tags), 2, "Expected two tags")
221 235
 	assertEqual(t, tags["latest"], imageID, "Expected tag latest to map to "+imageID)
236
+	assertEqual(t, tags["test"], imageID, "Expected tag test to map to "+imageID)
222 237
 
223 238
 	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz")
224
-	if err == nil {
225
-		t.Fatal("Expected error when fetching tags for bogus repo")
239
+	if err != ErrRepoNotFound {
240
+		t.Fatal("Expected ErrRepoNotFound error when fetching tags for bogus repo")
226 241
 	}
227 242
 }
228 243
 
... ...
@@ -26,6 +26,10 @@ import (
26 26
 	"github.com/docker/docker/pkg/transport"
27 27
 )
28 28
 
29
+var (
30
+	ErrRepoNotFound = errors.New("Repository not found")
31
+)
32
+
29 33
 type Session struct {
30 34
 	indexEndpoint *Endpoint
31 35
 	client        *http.Client
... ...
@@ -279,6 +283,38 @@ func (r *Session) GetRemoteImageLayer(imgID, registry string, imgSize int64) (io
279 279
 	return res.Body, nil
280 280
 }
281 281
 
282
+func (r *Session) GetRemoteTag(registries []string, repository string, askedTag string) (string, error) {
283
+	if strings.Count(repository, "/") == 0 {
284
+		// This will be removed once the Registry supports auto-resolution on
285
+		// the "library" namespace
286
+		repository = "library/" + repository
287
+	}
288
+	for _, host := range registries {
289
+		endpoint := fmt.Sprintf("%srepositories/%s/tags/%s", host, repository, askedTag)
290
+		res, err := r.client.Get(endpoint)
291
+		if err != nil {
292
+			return "", err
293
+		}
294
+
295
+		logrus.Debugf("Got status code %d from %s", res.StatusCode, endpoint)
296
+		defer res.Body.Close()
297
+
298
+		if res.StatusCode == 404 {
299
+			return "", ErrRepoNotFound
300
+		}
301
+		if res.StatusCode != 200 {
302
+			continue
303
+		}
304
+
305
+		var tagId string
306
+		if err := json.NewDecoder(res.Body).Decode(&tagId); err != nil {
307
+			return "", err
308
+		}
309
+		return tagId, nil
310
+	}
311
+	return "", fmt.Errorf("Could not reach any registry endpoint")
312
+}
313
+
282 314
 func (r *Session) GetRemoteTags(registries []string, repository string) (map[string]string, error) {
283 315
 	if strings.Count(repository, "/") == 0 {
284 316
 		// This will be removed once the Registry supports auto-resolution on
... ...
@@ -296,7 +332,7 @@ func (r *Session) GetRemoteTags(registries []string, repository string) (map[str
296 296
 		defer res.Body.Close()
297 297
 
298 298
 		if res.StatusCode == 404 {
299
-			return nil, fmt.Errorf("Repository not found")
299
+			return nil, ErrRepoNotFound
300 300
 		}
301 301
 		if res.StatusCode != 200 {
302 302
 			continue