Only request a single repository tag when pulling a specific image:tag
| ... | ... |
@@ -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 |