From 5aa304f9696d8fcc2e39628ce68a39bfc932adb4 Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Mon, 9 Dec 2013 14:58:04 +0000 Subject: [PATCH 1/6] Fix registry pushing/tagging * docker push host:port/namespace/repo wouldn't push multiple tags for the same image * getImageList was unnecessarily complex returning a nested array of ImgData when a correctly ordered list of images was sufficient * removed various bits of redundancy Docker-DCO-1.0-Signed-off-by: Danny Yates (github: codeaholics) --- server.go | 144 +++++++++++++++++++++++++----------------------------- 1 file changed, 67 insertions(+), 77 deletions(-) diff --git a/server.go b/server.go index 2405380ea3..80ac5128fc 100644 --- a/server.go +++ b/server.go @@ -1126,122 +1126,112 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut } // Retrieve the all the images to be uploaded in the correct order -// Note: we can't use a map as it is not ordered -func (srv *Server) getImageList(localRepo map[string]string) ([][]*registry.ImgData, error) { - imgList := map[string]*registry.ImgData{} - depGraph := utils.NewDependencyGraph() +func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[string][]string, error) { + var ( + imageList []string + imagesSeen map[string]bool = make(map[string]bool) + tagsByImage map[string][]string = make(map[string][]string) + ) for tag, id := range localRepo { - img, err := srv.runtime.graph.Get(id) + var imageListForThisTag []string + + tagsByImage[id] = append(tagsByImage[id], tag) + + for img, err := srv.runtime.graph.Get(id); img != nil; img, err = img.GetParent() { if err != nil { - return nil, err + return nil, nil, err } - depGraph.NewNode(img.ID) - img.WalkHistory(func(current *Image) error { - imgList[current.ID] = ®istry.ImgData{ - ID: current.ID, - Tag: tag, + + if imagesSeen[img.ID] { + // This image is already on the list, we can ignore it and all its parents + break } - parent, err := current.GetParent() - if err != nil { - return err - } - if parent == nil { - return nil - } - depGraph.NewNode(parent.ID) - depGraph.AddDependency(current.ID, parent.ID) - return nil - }) + + imagesSeen[img.ID] = true + imageListForThisTag = append(imageListForThisTag, img.ID) } - traversalMap, err := depGraph.GenerateTraversalMap() - if err != nil { - return nil, err + // reverse the image list for this tag (so the "most"-parent image is first) + for i, j := 0, len(imageListForThisTag) - 1; i < j; i, j = i + 1, j - 1 { + imageListForThisTag[i], imageListForThisTag[j] = imageListForThisTag[j], imageListForThisTag[i] } - utils.Debugf("Traversal map: %v", traversalMap) - result := [][]*registry.ImgData{} - for _, round := range traversalMap { - dataRound := []*registry.ImgData{} - for _, imgID := range round { - dataRound = append(dataRound, imgList[imgID]) - } - result = append(result, dataRound) - } - return result, nil + // append to main image list + imageList = append(imageList, imageListForThisTag...) } -func flatten(slc [][]*registry.ImgData) []*registry.ImgData { - result := []*registry.ImgData{} - for _, x := range slc { - result = append(result, x...) - } - return result + utils.Debugf("Image list: %v", imageList) + utils.Debugf("Tags by image: %v", tagsByImage) + + return imageList, tagsByImage, nil } func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName, remoteName string, localRepo map[string]string, sf *utils.StreamFormatter) error { out = utils.NewWriteFlusher(out) - imgList, err := srv.getImageList(localRepo) + utils.Debugf("Local repo: %s", localRepo) + imgList, tagsByImage, err := srv.getImageList(localRepo) if err != nil { return err } - flattenedImgList := flatten(imgList) + out.Write(sf.FormatStatus("", "Sending image list")) var repoData *registry.RepositoryData - repoData, err = r.PushImageJSONIndex(remoteName, flattenedImgList, false, nil) + var imageIndex []*registry.ImgData + + for imgId, tags := range tagsByImage { + for _, tag := range tags { + imageIndex = append(imageIndex, ®istry.ImgData{ + ID: imgId, + Tag: tag, + }) + } + } + + repoData, err = r.PushImageJSONIndex(remoteName, imageIndex, false, nil) if err != nil { return err } for _, ep := range repoData.Endpoints { out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo))) - // This section can not be parallelized (each round depends on the previous one) - for i, round := range imgList { - // FIXME: This section can be parallelized - for _, elem := range round { + + for _, imgId := range imgList { var pushTags func() error pushTags = func() error { - if i < (len(imgList) - 1) { - // Only tag the top layer in the repository - return nil - } + for _, tag := range tagsByImage[imgId] { + out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", utils.TruncateID(imgId), ep+"repositories/"+remoteName+"/tags/"+tag)) - out.Write(sf.FormatStatus("", "Pushing tags for rev [%s] on {%s}", utils.TruncateID(elem.ID), ep+"repositories/"+remoteName+"/tags/"+elem.Tag)) - if err := r.PushRegistryTag(remoteName, elem.ID, elem.Tag, ep, repoData.Tokens); err != nil { + if err := r.PushRegistryTag(remoteName, imgId, tag, ep, repoData.Tokens); err != nil { return err } + } + return nil } - if _, exists := repoData.ImgList[elem.ID]; exists { + + if r.LookupRemoteImage(imgId, ep, repoData.Tokens) { if err := pushTags(); err != nil { return err } - out.Write(sf.FormatProgress(utils.TruncateID(elem.ID), "Image already pushed, skipping", nil)) + out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", utils.TruncateID(imgId))) continue - } else if r.LookupRemoteImage(elem.ID, ep, repoData.Tokens) { - if err := pushTags(); err != nil { - return err } - out.Write(sf.FormatProgress(utils.TruncateID(elem.ID), "Image already pushed, skipping", nil)) - continue - } - checksum, err := srv.pushImage(r, out, remoteName, elem.ID, ep, repoData.Tokens, sf) + + _, err := srv.pushImage(r, out, remoteName, imgId, ep, repoData.Tokens, sf) if err != nil { // FIXME: Continue on error? return err } - elem.Checksum = checksum if err := pushTags(); err != nil { return err } } } - } - if _, err := r.PushImageJSONIndex(remoteName, flattenedImgList, true, repoData.Endpoints); err != nil { + if _, err := r.PushImageJSONIndex(remoteName, imageIndex, true, repoData.Endpoints); err != nil { return err } @@ -1667,24 +1657,24 @@ func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) { } } return srv.deleteImage(img, repository, tag) -} + } func (srv *Server) canDeleteImage(imgID string) error { - for _, container := range srv.runtime.List() { - parent, err := srv.runtime.repositories.LookupImage(container.Image) - if err != nil { + for _, container := range srv.runtime.List() { + parent, err := srv.runtime.repositories.LookupImage(container.Image) + if err != nil { return err - } + } - if err := parent.WalkHistory(func(p *Image) error { + if err := parent.WalkHistory(func(p *Image) error { if imgID == p.ID { return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it", utils.TruncateID(imgID), utils.TruncateID(container.ID)) - } - return nil - }); err != nil { + } + return nil + }); err != nil { return err + } } - } return nil } @@ -1700,7 +1690,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) imageMap := make(map[string][]string) for _, img := range images { imageMap[img.Parent] = append(imageMap[img.Parent], img.ID) - } + } sort.Strings(imageMap[imgID]) // Loop on the children of the given image and check the config for _, elem := range imageMap[imgID] { From 6b48761ce92e5f9590fd8b2723176ab72d297e6c Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Mon, 9 Dec 2013 15:05:46 +0000 Subject: [PATCH 2/6] Remove unneeded DependencyGraph Docker-DCO-1.0-Signed-off-by: Danny Yates (github: codeaholics) --- utils/utils.go | 116 -------------------------------------------- utils/utils_test.go | 56 --------------------- 2 files changed, 172 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index d5dbf35f0a..d079fd068a 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -890,122 +890,6 @@ func UserLookup(uid string) (*User, error) { return nil, fmt.Errorf("User not found in /etc/passwd") } -type DependencyGraph struct { - nodes map[string]*DependencyNode -} - -type DependencyNode struct { - id string - deps map[*DependencyNode]bool -} - -func NewDependencyGraph() DependencyGraph { - return DependencyGraph{ - nodes: map[string]*DependencyNode{}, - } -} - -func (graph *DependencyGraph) addNode(node *DependencyNode) string { - if graph.nodes[node.id] == nil { - graph.nodes[node.id] = node - } - return node.id -} - -func (graph *DependencyGraph) NewNode(id string) string { - if graph.nodes[id] != nil { - return id - } - nd := &DependencyNode{ - id: id, - deps: map[*DependencyNode]bool{}, - } - graph.addNode(nd) - return id -} - -func (graph *DependencyGraph) AddDependency(node, to string) error { - if graph.nodes[node] == nil { - return fmt.Errorf("Node %s does not belong to this graph", node) - } - - if graph.nodes[to] == nil { - return fmt.Errorf("Node %s does not belong to this graph", to) - } - - if node == to { - return fmt.Errorf("Dependency loops are forbidden!") - } - - graph.nodes[node].addDependency(graph.nodes[to]) - return nil -} - -func (node *DependencyNode) addDependency(to *DependencyNode) bool { - node.deps[to] = true - return node.deps[to] -} - -func (node *DependencyNode) Degree() int { - return len(node.deps) -} - -// The magic happens here :: -func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) { - Debugf("Generating traversal map. Nodes: %d", len(graph.nodes)) - result := [][]string{} - processed := map[*DependencyNode]bool{} - // As long as we haven't processed all nodes... - for len(processed) < len(graph.nodes) { - // Use a temporary buffer for processed nodes, otherwise - // nodes that depend on each other could end up in the same round. - tmpProcessed := []*DependencyNode{} - for _, node := range graph.nodes { - // If the node has more dependencies than what we have cleared, - // it won't be valid for this round. - if node.Degree() > len(processed) { - continue - } - // If it's already processed, get to the next one - if processed[node] { - continue - } - // It's not been processed yet and has 0 deps. Add it! - // (this is a shortcut for what we're doing below) - if node.Degree() == 0 { - tmpProcessed = append(tmpProcessed, node) - continue - } - // If at least one dep hasn't been processed yet, we can't - // add it. - ok := true - for dep := range node.deps { - if !processed[dep] { - ok = false - break - } - } - // All deps have already been processed. Add it! - if ok { - tmpProcessed = append(tmpProcessed, node) - } - } - Debugf("Round %d: found %d available nodes", len(result), len(tmpProcessed)) - // If no progress has been made this round, - // that means we have circular dependencies. - if len(tmpProcessed) == 0 { - return nil, fmt.Errorf("Could not find a solution to this dependency graph") - } - round := []string{} - for _, nd := range tmpProcessed { - round = append(round, nd.id) - processed[nd] = true - } - result = append(result, round) - } - return result, nil -} - // An StatusError reports an unsuccessful exit by a command. type StatusError struct { Status string diff --git a/utils/utils_test.go b/utils/utils_test.go index 1f23755d11..c8be7b1928 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -416,62 +416,6 @@ func TestParseRelease(t *testing.T) { assertParseRelease(t, "3.8.0-19-generic", &KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0, Flavor: "19-generic"}, 0) } -func TestDependencyGraphCircular(t *testing.T) { - g1 := NewDependencyGraph() - a := g1.NewNode("a") - b := g1.NewNode("b") - g1.AddDependency(a, b) - g1.AddDependency(b, a) - res, err := g1.GenerateTraversalMap() - if res != nil { - t.Fatalf("Expected nil result") - } - if err == nil { - t.Fatalf("Expected error (circular graph can not be resolved)") - } -} - -func TestDependencyGraph(t *testing.T) { - g1 := NewDependencyGraph() - a := g1.NewNode("a") - b := g1.NewNode("b") - c := g1.NewNode("c") - d := g1.NewNode("d") - g1.AddDependency(b, a) - g1.AddDependency(c, a) - g1.AddDependency(d, c) - g1.AddDependency(d, b) - res, err := g1.GenerateTraversalMap() - - if err != nil { - t.Fatalf("%s", err) - } - - if res == nil { - t.Fatalf("Unexpected nil result") - } - - if len(res) != 3 { - t.Fatalf("Expected map of length 3, found %d instead", len(res)) - } - - if len(res[0]) != 1 || res[0][0] != "a" { - t.Fatalf("Expected [a], found %v instead", res[0]) - } - - if len(res[1]) != 2 { - t.Fatalf("Expected 2 nodes for step 2, found %d", len(res[1])) - } - - if (res[1][0] != "b" && res[1][1] != "b") || (res[1][0] != "c" && res[1][1] != "c") { - t.Fatalf("Expected [b, c], found %v instead", res[1]) - } - - if len(res[2]) != 1 || res[2][0] != "d" { - t.Fatalf("Expected [d], found %v instead", res[2]) - } -} - func TestParsePortMapping(t *testing.T) { data, err := PartParser("ip:public:private", "192.168.1.1:80:8080") if err != nil { From df4ea946a653fa3864574fd8110c6c618dde41d8 Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Mon, 9 Dec 2013 17:01:32 +0000 Subject: [PATCH 3/6] Further simplification Docker-DCO-1.0-Signed-off-by: Danny Yates (github: codeaholics) --- server.go | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/server.go b/server.go index 80ac5128fc..813ab6db5b 100644 --- a/server.go +++ b/server.go @@ -1139,9 +1139,9 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri tagsByImage[id] = append(tagsByImage[id], tag) for img, err := srv.runtime.graph.Get(id); img != nil; img, err = img.GetParent() { - if err != nil { + if err != nil { return nil, nil, err - } + } if imagesSeen[img.ID] { // This image is already on the list, we can ignore it and all its parents @@ -1150,16 +1150,16 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri imagesSeen[img.ID] = true imageListForThisTag = append(imageListForThisTag, img.ID) - } + } // reverse the image list for this tag (so the "most"-parent image is first) for i, j := 0, len(imageListForThisTag) - 1; i < j; i, j = i + 1, j - 1 { imageListForThisTag[i], imageListForThisTag[j] = imageListForThisTag[j], imageListForThisTag[i] - } + } // append to main image list imageList = append(imageList, imageListForThisTag...) -} + } utils.Debugf("Image list: %v", imageList) utils.Debugf("Tags by image: %v", tagsByImage) @@ -1198,38 +1198,24 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName out.Write(sf.FormatStatus("", "Pushing repository %s (%d tags)", localName, len(localRepo))) for _, imgId := range imgList { - var pushTags func() error - pushTags = func() error { - for _, tag := range tagsByImage[imgId] { - out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", utils.TruncateID(imgId), ep+"repositories/"+remoteName+"/tags/"+tag)) - - if err := r.PushRegistryTag(remoteName, imgId, tag, ep, repoData.Tokens); err != nil { - return err - } - } - - return nil - } - if r.LookupRemoteImage(imgId, ep, repoData.Tokens) { - if err := pushTags(); err != nil { + out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", utils.TruncateID(imgId))) + } else { + if _, err := srv.pushImage(r, out, remoteName, imgId, ep, repoData.Tokens, sf); err != nil { + // FIXME: Continue on error? return err } - out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", utils.TruncateID(imgId))) - continue - } - - _, err := srv.pushImage(r, out, remoteName, imgId, ep, repoData.Tokens, sf) - if err != nil { - // FIXME: Continue on error? - return err } - if err := pushTags(); err != nil { + for _, tag := range tagsByImage[imgId] { + out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", utils.TruncateID(imgId), ep+"repositories/"+remoteName+"/tags/"+tag)) + + if err := r.PushRegistryTag(remoteName, imgId, tag, ep, repoData.Tokens); err != nil { return err } } } + } if _, err := r.PushImageJSONIndex(remoteName, imageIndex, true, repoData.Endpoints); err != nil { return err From 145501bee3f37ccb022189e7b76f31abcbdbed3c Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Mon, 9 Dec 2013 17:01:53 +0000 Subject: [PATCH 4/6] Fix indentation to match rest of file (tabs, not spaces) Docker-DCO-1.0-Signed-off-by: Danny Yates (github: codeaholics) --- server.go | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/server.go b/server.go index 813ab6db5b..7695b44a5c 100644 --- a/server.go +++ b/server.go @@ -1134,14 +1134,14 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri ) for tag, id := range localRepo { - var imageListForThisTag []string + var imageListForThisTag []string tagsByImage[id] = append(tagsByImage[id], tag) for img, err := srv.runtime.graph.Get(id); img != nil; img, err = img.GetParent() { - if err != nil { - return nil, nil, err - } + if err != nil { + return nil, nil, err + } if imagesSeen[img.ID] { // This image is already on the list, we can ignore it and all its parents @@ -1152,13 +1152,13 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri imageListForThisTag = append(imageListForThisTag, img.ID) } - // reverse the image list for this tag (so the "most"-parent image is first) - for i, j := 0, len(imageListForThisTag) - 1; i < j; i, j = i + 1, j - 1 { - imageListForThisTag[i], imageListForThisTag[j] = imageListForThisTag[j], imageListForThisTag[i] - } + // reverse the image list for this tag (so the "most"-parent image is first) + for i, j := 0, len(imageListForThisTag) - 1; i < j; i, j = i + 1, j - 1 { + imageListForThisTag[i], imageListForThisTag[j] = imageListForThisTag[j], imageListForThisTag[i] + } - // append to main image list - imageList = append(imageList, imageListForThisTag...) + // append to main image list + imageList = append(imageList, imageListForThisTag...) } utils.Debugf("Image list: %v", imageList) @@ -1201,16 +1201,16 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName if r.LookupRemoteImage(imgId, ep, repoData.Tokens) { out.Write(sf.FormatStatus("", "Image %s already pushed, skipping", utils.TruncateID(imgId))) } else { - if _, err := srv.pushImage(r, out, remoteName, imgId, ep, repoData.Tokens, sf); err != nil { - // FIXME: Continue on error? - return err - } + if _, err := srv.pushImage(r, out, remoteName, imgId, ep, repoData.Tokens, sf); err != nil { + // FIXME: Continue on error? + return err } + } - for _, tag := range tagsByImage[imgId] { - out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", utils.TruncateID(imgId), ep+"repositories/"+remoteName+"/tags/"+tag)) + for _, tag := range tagsByImage[imgId] { + out.Write(sf.FormatStatus("", "Pushing tag for rev [%s] on {%s}", utils.TruncateID(imgId), ep+"repositories/"+remoteName+"/tags/"+tag)) - if err := r.PushRegistryTag(remoteName, imgId, tag, ep, repoData.Tokens); err != nil { + if err := r.PushRegistryTag(remoteName, imgId, tag, ep, repoData.Tokens); err != nil { return err } } From c4535c833dfdaef275d765968a2ed5513ef09097 Mon Sep 17 00:00:00 2001 From: Danny Yates Date: Wed, 8 Jan 2014 10:37:22 +0000 Subject: [PATCH 5/6] gofmt Docker-DCO-1.0-Signed-off-by: Danny Yates (github: codeaholics) --- server.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/server.go b/server.go index 7695b44a5c..32553105fd 100644 --- a/server.go +++ b/server.go @@ -1128,8 +1128,8 @@ func (srv *Server) ImagePull(localName string, tag string, out io.Writer, sf *ut // Retrieve the all the images to be uploaded in the correct order func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[string][]string, error) { var ( - imageList []string - imagesSeen map[string]bool = make(map[string]bool) + imageList []string + imagesSeen map[string]bool = make(map[string]bool) tagsByImage map[string][]string = make(map[string][]string) ) @@ -1153,7 +1153,7 @@ func (srv *Server) getImageList(localRepo map[string]string) ([]string, map[stri } // reverse the image list for this tag (so the "most"-parent image is first) - for i, j := 0, len(imageListForThisTag) - 1; i < j; i, j = i + 1, j - 1 { + for i, j := 0, len(imageListForThisTag)-1; i < j; i, j = i+1, j-1 { imageListForThisTag[i], imageListForThisTag[j] = imageListForThisTag[j], imageListForThisTag[i] } @@ -1183,7 +1183,7 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName for imgId, tags := range tagsByImage { for _, tag := range tags { imageIndex = append(imageIndex, ®istry.ImgData{ - ID: imgId, + ID: imgId, Tag: tag, }) } @@ -1643,24 +1643,24 @@ func (srv *Server) ImageDelete(name string, autoPrune bool) ([]APIRmi, error) { } } return srv.deleteImage(img, repository, tag) - } +} func (srv *Server) canDeleteImage(imgID string) error { - for _, container := range srv.runtime.List() { - parent, err := srv.runtime.repositories.LookupImage(container.Image) - if err != nil { + for _, container := range srv.runtime.List() { + parent, err := srv.runtime.repositories.LookupImage(container.Image) + if err != nil { return err - } + } - if err := parent.WalkHistory(func(p *Image) error { + if err := parent.WalkHistory(func(p *Image) error { if imgID == p.ID { return fmt.Errorf("Conflict, cannot delete %s because the container %s is using it", utils.TruncateID(imgID), utils.TruncateID(container.ID)) - } - return nil - }); err != nil { - return err } + return nil + }); err != nil { + return err } + } return nil } @@ -1676,7 +1676,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) imageMap := make(map[string][]string) for _, img := range images { imageMap[img.Parent] = append(imageMap[img.Parent], img.ID) - } + } sort.Strings(imageMap[imgID]) // Loop on the children of the given image and check the config for _, elem := range imageMap[imgID] { From a2aab7757e236a895abf7b06836d8e3b84236429 Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Mon, 20 Jan 2014 13:39:35 -0800 Subject: [PATCH 6/6] Make sure new repositories can be pushed with multiple tags Docker-DCO-1.1-Signed-off-by: Michael Crosby (github: crosbymichael) --- registry/registry.go | 3 +++ server.go | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/registry/registry.go b/registry/registry.go index a0da733ed9..b2d26a2dba 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -205,15 +205,18 @@ func (r *Registry) GetRemoteHistory(imgID, registry string, token []string) ([]s } // Check if an image exists in the Registry +// TODO: This method should return the errors instead of masking them and returning false func (r *Registry) LookupRemoteImage(imgID, registry string, token []string) bool { req, err := r.reqFactory.NewRequest("GET", registry+"images/"+imgID+"/json", nil) if err != nil { + utils.Errorf("Error in LookupRemoteImage %s", err) return false } setTokenAuth(req, token) res, err := doWithCookies(r.client, req) if err != nil { + utils.Errorf("Error in LookupRemoteImage %s", err) return false } res.Body.Close() diff --git a/server.go b/server.go index 74604570f6..4e05f83e3a 100644 --- a/server.go +++ b/server.go @@ -1263,15 +1263,34 @@ func (srv *Server) pushRepository(r *registry.Registry, out io.Writer, localName var repoData *registry.RepositoryData var imageIndex []*registry.ImgData - for imgId, tags := range tagsByImage { - for _, tag := range tags { + for _, imgId := range imgList { + if tags, exists := tagsByImage[imgId]; exists { + // If an image has tags you must add an entry in the image index + // for each tag + for _, tag := range tags { + imageIndex = append(imageIndex, ®istry.ImgData{ + ID: imgId, + Tag: tag, + }) + } + } else { + // If the image does not have a tag it still needs to be sent to the + // registry with an empty tag so that it is accociated with the repository imageIndex = append(imageIndex, ®istry.ImgData{ ID: imgId, - Tag: tag, + Tag: "", }) + } } + utils.Debugf("Preparing to push %s with the following images and tags\n", localRepo) + for _, data := range imageIndex { + utils.Debugf("Pushing ID: %s with Tag: %s\n", data.ID, data.Tag) + } + + // Register all the images in a repository with the registry + // If an image is not in this list it will not be associated with the repository repoData, err = r.PushImageJSONIndex(remoteName, imageIndex, false, nil) if err != nil { return err