mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Print a status message when pull command is executed
Using repo tag in the status message for better usability, as per review comments Added documentation and Changed code to print Status after downloads are complete Addresses #2404 Signed-off-by: Srini Brahmaroutu <srbrahma@us.ibm.com>
This commit is contained in:
parent
4c8b8861ec
commit
ecff6303a3
4 changed files with 43 additions and 11 deletions
|
@ -23,6 +23,8 @@ It is also possible to specify a non-default registry to pull from.
|
||||||
# EXAMPLES
|
# EXAMPLES
|
||||||
|
|
||||||
# Pull a repository with multiple images
|
# Pull a repository with multiple images
|
||||||
|
# Note that if the image is previously downloaded then the status would be
|
||||||
|
# 'Status: Image is up to date for fedora'
|
||||||
|
|
||||||
$ sudo docker pull fedora
|
$ sudo docker pull fedora
|
||||||
Pulling repository fedora
|
Pulling repository fedora
|
||||||
|
@ -31,6 +33,8 @@ It is also possible to specify a non-default registry to pull from.
|
||||||
511136ea3c5a: Download complete
|
511136ea3c5a: Download complete
|
||||||
73bd853d2ea5: Download complete
|
73bd853d2ea5: Download complete
|
||||||
|
|
||||||
|
Status: Downloaded newer image for fedora
|
||||||
|
|
||||||
$ sudo docker images
|
$ sudo docker images
|
||||||
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||||
fedora rawhide ad57ef8d78d7 5 days ago 359.3 MB
|
fedora rawhide ad57ef8d78d7 5 days ago 359.3 MB
|
||||||
|
@ -39,6 +43,8 @@ It is also possible to specify a non-default registry to pull from.
|
||||||
fedora latest 105182bb5e8b 5 days ago 372.7 MB
|
fedora latest 105182bb5e8b 5 days ago 372.7 MB
|
||||||
|
|
||||||
# Pull an image, manually specifying path to the registry and tag
|
# Pull an image, manually specifying path to the registry and tag
|
||||||
|
# Note that if the image is previously downloaded then the status would be
|
||||||
|
# 'Status: Image is up to date for registry.hub.docker.com/fedora:20'
|
||||||
|
|
||||||
$ sudo docker pull registry.hub.docker.com/fedora:20
|
$ sudo docker pull registry.hub.docker.com/fedora:20
|
||||||
Pulling repository fedora
|
Pulling repository fedora
|
||||||
|
@ -46,6 +52,8 @@ It is also possible to specify a non-default registry to pull from.
|
||||||
511136ea3c5a: Download complete
|
511136ea3c5a: Download complete
|
||||||
fd241224e9cf: Download complete
|
fd241224e9cf: Download complete
|
||||||
|
|
||||||
|
Status: Downloaded newer image for registry.hub.docker.com/fedora:20
|
||||||
|
|
||||||
$ sudo docker images
|
$ sudo docker images
|
||||||
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||||
fedora 20 3f2fed40e4b0 4 days ago 372.7 MB
|
fedora 20 3f2fed40e4b0 4 days ago 372.7 MB
|
||||||
|
|
|
@ -93,6 +93,8 @@ download the `centos` image.
|
||||||
ef52fb1fe610: Download complete
|
ef52fb1fe610: Download complete
|
||||||
. . .
|
. . .
|
||||||
|
|
||||||
|
Status: Downloaded newer image for centos
|
||||||
|
|
||||||
We can see that each layer of the image has been pulled down and now we
|
We can see that each layer of the image has been pulled down and now we
|
||||||
can run a container from this image and we won't have to wait to
|
can run a container from this image and we won't have to wait to
|
||||||
download the image.
|
download the image.
|
||||||
|
|
|
@ -67,6 +67,8 @@ Once you've found the image you want, you can download it with `docker pull <ima
|
||||||
511136ea3c5a: Download complete
|
511136ea3c5a: Download complete
|
||||||
7064731afe90: Download complete
|
7064731afe90: Download complete
|
||||||
|
|
||||||
|
Status: Downloaded newer image for centos
|
||||||
|
|
||||||
You now have an image from which you can run containers.
|
You now have an image from which you can run containers.
|
||||||
|
|
||||||
## Contributing to Docker Hub
|
## Contributing to Docker Hub
|
||||||
|
|
|
@ -122,6 +122,8 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
||||||
}
|
}
|
||||||
|
|
||||||
errors := make(chan error)
|
errors := make(chan error)
|
||||||
|
|
||||||
|
layers_downloaded := false
|
||||||
for _, image := range repoData.ImgList {
|
for _, image := range repoData.ImgList {
|
||||||
downloadImage := func(img *registry.ImgData) {
|
downloadImage := func(img *registry.ImgData) {
|
||||||
if askedTag != "" && img.Tag != askedTag {
|
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))
|
out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s", img.Tag, localName), nil))
|
||||||
success := false
|
success := false
|
||||||
var lastErr error
|
var lastErr, err error
|
||||||
|
var is_downloaded bool
|
||||||
if mirrors != nil {
|
if mirrors != nil {
|
||||||
for _, ep := range mirrors {
|
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))
|
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.
|
// Don't report errors when pulling from mirrors.
|
||||||
log.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, localName, ep, err)
|
log.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, localName, ep, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
layers_downloaded = layers_downloaded || is_downloaded
|
||||||
success = true
|
success = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -174,13 +178,14 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
|
||||||
if !success {
|
if !success {
|
||||||
for _, ep := range repoData.Endpoints {
|
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))
|
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.
|
// 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.
|
// As the error is also given to the output stream the user will see the error.
|
||||||
lastErr = err
|
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))
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
layers_downloaded = layers_downloaded || is_downloaded
|
||||||
success = true
|
success = true
|
||||||
break
|
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
|
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)
|
history, err := r.GetRemoteHistory(imgID, endpoint, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return false, err
|
||||||
}
|
}
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling dependent layers", nil))
|
out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling dependent layers", nil))
|
||||||
// FIXME: Try to stream the images?
|
// FIXME: Try to stream the images?
|
||||||
// FIXME: Launch the getRemoteImage() in goroutines
|
// FIXME: Launch the getRemoteImage() in goroutines
|
||||||
|
|
||||||
|
layers_downloaded := false
|
||||||
for i := len(history) - 1; i >= 0; i-- {
|
for i := len(history) - 1; i >= 0; i-- {
|
||||||
id := history[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)
|
imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
|
||||||
if err != nil && j == retries {
|
if err != nil && j == retries {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
||||||
return err
|
return layers_downloaded, err
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
img, err = image.NewImgJSON(imgJSON)
|
img, err = image.NewImgJSON(imgJSON)
|
||||||
|
layers_downloaded = true
|
||||||
if err != nil && j == retries {
|
if err != nil && j == retries {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
|
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 {
|
} else if err != nil {
|
||||||
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
time.Sleep(time.Duration(j) * 500 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
|
@ -295,8 +307,9 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", 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()
|
defer layer.Close()
|
||||||
|
|
||||||
err = s.graph.Register(img, imgJSON,
|
err = s.graph.Register(img, imgJSON,
|
||||||
|
@ -306,14 +319,21 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error downloading dependent layers", nil))
|
out.Write(sf.FormatProgress(utils.TruncateID(id), "Error downloading dependent layers", nil))
|
||||||
return err
|
return layers_downloaded, err
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.Write(sf.FormatProgress(utils.TruncateID(id), "Download complete", nil))
|
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
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue