зеркало из https://github.com/microsoft/docker.git
'docker push' shows an additional progress bar while it buffers the archive to disk. Fixes #451.
This commit is contained in:
Родитель
baacae8345
Коммит
965e8a02d2
|
@ -475,7 +475,7 @@ func (srv *Server) CmdImport(stdin io.ReadCloser, stdout rcli.DockerConn, args .
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout)
|
||||
archive = ProgressReader(resp.Body, int(resp.ContentLength), stdout, "Importing %v/%v (%v)")
|
||||
}
|
||||
img, err := srv.runtime.graph.Create(archive, nil, "Imported from "+src, "")
|
||||
if err != nil {
|
||||
|
|
7
graph.go
7
graph.go
|
@ -2,6 +2,7 @@ package docker
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -131,7 +132,9 @@ func (graph *Graph) Register(layerData Archive, img *Image) error {
|
|||
|
||||
// TempLayerArchive creates a temporary archive of the given image's filesystem layer.
|
||||
// The archive is stored on disk and will be automatically deleted as soon as has been read.
|
||||
func (graph *Graph) TempLayerArchive(id string, compression Compression) (*TempArchive, error) {
|
||||
// If output is not nil, a human-readable progress bar will be written to it.
|
||||
// FIXME: does this belong in Graph? How about MktempFile, let the caller use it for archives?
|
||||
func (graph *Graph) TempLayerArchive(id string, compression Compression, output io.Writer) (*TempArchive, error) {
|
||||
image, err := graph.Get(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -144,7 +147,7 @@ func (graph *Graph) TempLayerArchive(id string, compression Compression) (*TempA
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTempArchive(archive, tmp.Root)
|
||||
return NewTempArchive(ProgressReader(ioutil.NopCloser(archive), 0, output, "Buffering to disk %v/%v (%v)"), tmp.Root)
|
||||
}
|
||||
|
||||
// Mktemp creates a temporary sub-directory inside the graph's filesystem.
|
||||
|
|
|
@ -136,7 +136,7 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId string, authConfig *a
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return img, ProgressReader(res.Body, int(res.ContentLength), stdout), nil
|
||||
return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil
|
||||
}
|
||||
|
||||
func (graph *Graph) PullImage(stdout io.Writer, imgId string, authConfig *auth.AuthConfig) error {
|
||||
|
@ -274,12 +274,12 @@ func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth
|
|||
// a) Implementing S3's proprietary streaming logic, or
|
||||
// b) Stream directly to the registry instead of S3.
|
||||
// I prefer option b. because it doesn't lock us into a proprietary cloud service.
|
||||
tmpLayer, err := graph.TempLayerArchive(img.Id, Xz)
|
||||
tmpLayer, err := graph.TempLayerArchive(img.Id, Xz, stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(tmpLayer.Name())
|
||||
req3, err := http.NewRequest("PUT", url.String(), ProgressReader(tmpLayer, int(tmpLayer.Size), stdout))
|
||||
req3, err := http.NewRequest("PUT", url.String(), ProgressReader(tmpLayer, int(tmpLayer.Size), stdout, "Uploading %v/%v (%v)"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
31
utils.go
31
utils.go
|
@ -72,23 +72,30 @@ type progressReader struct {
|
|||
readTotal int // Expected stream length (bytes)
|
||||
readProgress int // How much has been read so far (bytes)
|
||||
lastUpdate int // How many bytes read at least update
|
||||
template string // Template to print. Default "%v/%v (%v)"
|
||||
}
|
||||
|
||||
func (r *progressReader) Read(p []byte) (n int, err error) {
|
||||
read, err := io.ReadCloser(r.reader).Read(p)
|
||||
r.readProgress += read
|
||||
|
||||
// Only update progress for every 1% read
|
||||
updateEvery := int(0.01 * float64(r.readTotal))
|
||||
if r.readProgress-r.lastUpdate > updateEvery || r.readProgress == r.readTotal {
|
||||
fmt.Fprintf(r.output, "%d/%d (%.0f%%)\r",
|
||||
r.readProgress,
|
||||
r.readTotal,
|
||||
float64(r.readProgress)/float64(r.readTotal)*100)
|
||||
updateEvery := 4096
|
||||
if r.readTotal > 0 {
|
||||
// Only update progress for every 1% read
|
||||
if increment := int(0.01 * float64(r.readTotal)); increment > updateEvery {
|
||||
updateEvery = increment
|
||||
}
|
||||
}
|
||||
if r.readProgress-r.lastUpdate > updateEvery || err != nil {
|
||||
if r.readTotal > 0 {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, r.readTotal, fmt.Sprintf("%.0f%%", float64(r.readProgress)/float64(r.readTotal)*100))
|
||||
} else {
|
||||
fmt.Fprintf(r.output, r.template+"\r", r.readProgress, "?", "n/a")
|
||||
}
|
||||
r.lastUpdate = r.readProgress
|
||||
}
|
||||
// Send newline when complete
|
||||
if err == io.EOF {
|
||||
if err != nil {
|
||||
fmt.Fprintf(r.output, "\n")
|
||||
}
|
||||
|
||||
|
@ -97,8 +104,11 @@ func (r *progressReader) Read(p []byte) (n int, err error) {
|
|||
func (r *progressReader) Close() error {
|
||||
return io.ReadCloser(r.reader).Close()
|
||||
}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer) *progressReader {
|
||||
return &progressReader{r, output, size, 0, 0}
|
||||
func ProgressReader(r io.ReadCloser, size int, output io.Writer, template string) *progressReader {
|
||||
if template == "" {
|
||||
template = "%v/%v (%v)"
|
||||
}
|
||||
return &progressReader{r, output, size, 0, 0, template}
|
||||
}
|
||||
|
||||
// HumanDuration returns a human-readable approximation of a duration
|
||||
|
@ -395,6 +405,7 @@ type KernelVersionInfo struct {
|
|||
Specific int
|
||||
}
|
||||
|
||||
// FIXME: this doens't build on Darwin
|
||||
func GetKernelVersion() (*KernelVersionInfo, error) {
|
||||
var uts syscall.Utsname
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче