diff --git a/api/client/load.go b/api/client/load.go index 1df0c08605..059d151704 100644 --- a/api/client/load.go +++ b/api/client/load.go @@ -19,6 +19,7 @@ import ( func (cli *DockerCli) CmdLoad(args ...string) error { cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true) infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN") + quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the load output") cmd.Require(flag.Exact, 0) cmd.ParseFlags(args, true) @@ -31,8 +32,10 @@ func (cli *DockerCli) CmdLoad(args ...string) error { defer file.Close() input = file } - - response, err := cli.client.ImageLoad(context.Background(), input, true) + if !cli.isTerminalOut { + *quiet = true + } + response, err := cli.client.ImageLoad(context.Background(), input, *quiet) if err != nil { return err } diff --git a/api/server/router/local/image.go b/api/server/router/local/image.go index 3f925a28b5..3427649f59 100644 --- a/api/server/router/local/image.go +++ b/api/server/router/local/image.go @@ -270,7 +270,12 @@ func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *htt } func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { - return s.daemon.LoadImage(r.Body, w) + if err := httputils.ParseForm(r); err != nil { + return err + } + quiet := httputils.BoolValueOrDefault(r, "quiet", true) + w.Header().Set("Content-Type", "application/json") + return s.daemon.LoadImage(r.Body, w, quiet) } func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { diff --git a/daemon/daemon.go b/daemon/daemon.go index 0e949a6138..33c3f10caf 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -1187,9 +1187,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) { // LoadImage uploads a set of images into the repository. This is the // complement of ImageExport. The input stream is an uncompressed tar // ball containing images and metadata. -func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer) error { +func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore) - return imageExporter.Load(inTar, outStream) + return imageExporter.Load(inTar, outStream, quiet) } // ImageHistory returns a slice of ImageHistory structures for the specified image diff --git a/image/image.go b/image/image.go index b153a8231a..e5e4dc6f7e 100644 --- a/image/image.go +++ b/image/image.go @@ -116,7 +116,7 @@ type History struct { // Exporter provides interface for exporting and importing images type Exporter interface { - Load(io.ReadCloser, io.Writer) error + Load(io.ReadCloser, io.Writer, bool) error // TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error Save([]string, io.Writer) error } diff --git a/image/tarexport/load.go b/image/tarexport/load.go index 9724c5eff0..4d69dd606c 100644 --- a/image/tarexport/load.go +++ b/image/tarexport/load.go @@ -14,11 +14,24 @@ import ( "github.com/docker/docker/layer" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/chrootarchive" + "github.com/docker/docker/pkg/progress" + "github.com/docker/docker/pkg/streamformatter" + "github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/symlink" "github.com/docker/docker/reference" ) -func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { +func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error { + var ( + sf = streamformatter.NewJSONStreamFormatter() + progressOutput progress.Output + ) + if !quiet { + progressOutput = sf.NewProgressOutput(outStream, false) + } else { + progressOutput = nil + } + tmpDir, err := ioutil.TempDir("", "docker-import-") if err != nil { return err @@ -36,7 +49,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { manifestFile, err := os.Open(manifestPath) if err != nil { if os.IsNotExist(err) { - return l.legacyLoad(tmpDir, outStream) + return l.legacyLoad(tmpDir, outStream, progressOutput) } return manifestFile.Close() } @@ -77,7 +90,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { r.Append(diffID) newLayer, err := l.ls.Get(r.ChainID()) if err != nil { - newLayer, err = l.loadLayer(layerPath, rootFS) + newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), progressOutput) if err != nil { return err } @@ -111,7 +124,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error { return nil } -func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Layer, error) { +func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) { rawTar, err := os.Open(filename) if err != nil { logrus.Debugf("Error reading embedded tar: %v", err) @@ -125,6 +138,17 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Lay } defer inflatedLayerData.Close() + if progressOutput != nil { + fileInfo, err := os.Stat(filename) + if err != nil { + logrus.Debugf("Error statting file: %v", err) + return nil, err + } + + progressReader := progress.NewProgressReader(inflatedLayerData, progressOutput, fileInfo.Size(), stringid.TruncateID(id), "Loading layer") + + return l.ls.Register(progressReader, rootFS.ChainID()) + } return l.ls.Register(inflatedLayerData, rootFS.ChainID()) } @@ -139,7 +163,7 @@ func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, ou return nil } -func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { +func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error { legacyLoadedMap := make(map[string]image.ID) dirs, err := ioutil.ReadDir(tmpDir) @@ -150,7 +174,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { // every dir represents an image for _, d := range dirs { if d.IsDir() { - if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil { + if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap, progressOutput); err != nil { return err } } @@ -196,7 +220,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error { return nil } -func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID) error { +func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID, progressOutput progress.Output) error { if _, loaded := loadedMap[oldID]; loaded { return nil } @@ -220,7 +244,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str for { var loaded bool if parentID, loaded = loadedMap[img.Parent]; !loaded { - if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap); err != nil { + if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap, progressOutput); err != nil { return err } } else { @@ -247,7 +271,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str if err != nil { return err } - newLayer, err := l.loadLayer(layerPath, *rootFS) + newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, progressOutput) if err != nil { return err }