|
@@ -122,6 +122,8 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
|
|
}
|
|
|
|
|
|
errors := make(chan error)
|
|
|
+
|
|
|
+ layers_downloaded := false
|
|
|
for _, image := range repoData.ImgList {
|
|
|
downloadImage := func(img *registry.ImgData) {
|
|
|
if askedTag != "" && img.Tag != askedTag {
|
|
@@ -158,15 +160,17 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
|
|
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s", img.Tag, localName), nil))
|
|
|
success := false
|
|
|
- var lastErr error
|
|
|
+ var lastErr, err error
|
|
|
+ var is_downloaded bool
|
|
|
if mirrors != nil {
|
|
|
for _, ep := range mirrors {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, localName, ep), nil))
|
|
|
- if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
|
|
|
+ if is_downloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
|
|
|
// Don't report errors when pulling from mirrors.
|
|
|
log.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, localName, ep, err)
|
|
|
continue
|
|
|
}
|
|
|
+ layers_downloaded = layers_downloaded || is_downloaded
|
|
|
success = true
|
|
|
break
|
|
|
}
|
|
@@ -174,13 +178,14 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
|
|
if !success {
|
|
|
for _, ep := range repoData.Endpoints {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, endpoint: %s", img.Tag, localName, ep), nil))
|
|
|
- if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
|
|
|
+ if is_downloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
|
|
|
// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
|
|
|
// As the error is also given to the output stream the user will see the error.
|
|
|
lastErr = err
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Error pulling image (%s) from %s, endpoint: %s, %s", img.Tag, localName, ep, err), nil))
|
|
|
continue
|
|
|
}
|
|
|
+ layers_downloaded = layers_downloaded || is_downloaded
|
|
|
success = true
|
|
|
break
|
|
|
}
|
|
@@ -227,18 +232,24 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ requestedTag := localName
|
|
|
+ if len(askedTag) > 0 {
|
|
|
+ requestedTag = localName + ":" + askedTag
|
|
|
+ }
|
|
|
+ WriteStatus(requestedTag, out, sf, layers_downloaded)
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error {
|
|
|
+func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) (bool, error) {
|
|
|
history, err := r.GetRemoteHistory(imgID, endpoint, token)
|
|
|
if err != nil {
|
|
|
- return err
|
|
|
+ return false, err
|
|
|
}
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling dependent layers", nil))
|
|
|
// FIXME: Try to stream the images?
|
|
|
// FIXME: Launch the getRemoteImage() in goroutines
|
|
|
|
|
|
+ layers_downloaded := false
|
|
|
for i := len(history) - 1; i >= 0; i-- {
|
|
|
id := history[i]
|
|
|
|
|
@@ -262,15 +273,16 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
|
|
imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
|
|
|
if err != nil && j == retries {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
|
|
- return err
|
|
|
+ return layers_downloaded, err
|
|
|
} else if err != nil {
|
|
|
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
|
|
continue
|
|
|
}
|
|
|
img, err = image.NewImgJSON(imgJSON)
|
|
|
+ layers_downloaded = true
|
|
|
if err != nil && j == retries {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
|
|
- return fmt.Errorf("Failed to parse json: %s", err)
|
|
|
+ return layers_downloaded, fmt.Errorf("Failed to parse json: %s", err)
|
|
|
} else if err != nil {
|
|
|
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
|
|
continue
|
|
@@ -295,8 +307,9 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
|
|
continue
|
|
|
} else if err != nil {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
|
|
- return err
|
|
|
+ return layers_downloaded, err
|
|
|
}
|
|
|
+ layers_downloaded = true
|
|
|
defer layer.Close()
|
|
|
|
|
|
err = s.graph.Register(img, imgJSON,
|
|
@@ -306,14 +319,21 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
|
|
continue
|
|
|
} else if err != nil {
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error downloading dependent layers", nil))
|
|
|
- return err
|
|
|
+ return layers_downloaded, err
|
|
|
} else {
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Download complete", nil))
|
|
|
+ }
|
|
|
+ return layers_downloaded, nil
|
|
|
+}
|
|
|
|
|
|
+func WriteStatus(requestedTag string, out io.Writer, sf *utils.StreamFormatter, layers_downloaded bool) {
|
|
|
+ if layers_downloaded {
|
|
|
+ out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
|
|
|
+ } else {
|
|
|
+ out.Write(sf.FormatStatus("", "Status: Image is up to date for %s", requestedTag))
|
|
|
}
|
|
|
- return nil
|
|
|
}
|