Browse code

Add progress bar to docker load

Signed-off-by: Lei Jitang <leijitang@huawei.com>

Lei Jitang authored on 2016/02/04 11:31:47
Showing 5 changed files
... ...
@@ -19,6 +19,7 @@ import (
19 19
 func (cli *DockerCli) CmdLoad(args ...string) error {
20 20
 	cmd := Cli.Subcmd("load", nil, Cli.DockerCommands["load"].Description, true)
21 21
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
22
+	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the load output")
22 23
 	cmd.Require(flag.Exact, 0)
23 24
 	cmd.ParseFlags(args, true)
24 25
 
... ...
@@ -31,8 +32,10 @@ func (cli *DockerCli) CmdLoad(args ...string) error {
31 31
 		defer file.Close()
32 32
 		input = file
33 33
 	}
34
-
35
-	response, err := cli.client.ImageLoad(context.Background(), input, true)
34
+	if !cli.isTerminalOut {
35
+		*quiet = true
36
+	}
37
+	response, err := cli.client.ImageLoad(context.Background(), input, *quiet)
36 38
 	if err != nil {
37 39
 		return err
38 40
 	}
... ...
@@ -270,7 +270,12 @@ func (s *router) getImagesGet(ctx context.Context, w http.ResponseWriter, r *htt
270 270
 }
271 271
 
272 272
 func (s *router) postImagesLoad(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
273
-	return s.daemon.LoadImage(r.Body, w)
273
+	if err := httputils.ParseForm(r); err != nil {
274
+		return err
275
+	}
276
+	quiet := httputils.BoolValueOrDefault(r, "quiet", true)
277
+	w.Header().Set("Content-Type", "application/json")
278
+	return s.daemon.LoadImage(r.Body, w, quiet)
274 279
 }
275 280
 
276 281
 func (s *router) deleteImages(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
... ...
@@ -1187,9 +1187,9 @@ func (daemon *Daemon) LookupImage(name string) (*types.ImageInspect, error) {
1187 1187
 // LoadImage uploads a set of images into the repository. This is the
1188 1188
 // complement of ImageExport.  The input stream is an uncompressed tar
1189 1189
 // ball containing images and metadata.
1190
-func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer) error {
1190
+func (daemon *Daemon) LoadImage(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
1191 1191
 	imageExporter := tarexport.NewTarExporter(daemon.imageStore, daemon.layerStore, daemon.referenceStore)
1192
-	return imageExporter.Load(inTar, outStream)
1192
+	return imageExporter.Load(inTar, outStream, quiet)
1193 1193
 }
1194 1194
 
1195 1195
 // ImageHistory returns a slice of ImageHistory structures for the specified image
... ...
@@ -116,7 +116,7 @@ type History struct {
116 116
 
117 117
 // Exporter provides interface for exporting and importing images
118 118
 type Exporter interface {
119
-	Load(io.ReadCloser, io.Writer) error
119
+	Load(io.ReadCloser, io.Writer, bool) error
120 120
 	// TODO: Load(net.Context, io.ReadCloser, <- chan StatusMessage) error
121 121
 	Save([]string, io.Writer) error
122 122
 }
... ...
@@ -14,11 +14,24 @@ import (
14 14
 	"github.com/docker/docker/layer"
15 15
 	"github.com/docker/docker/pkg/archive"
16 16
 	"github.com/docker/docker/pkg/chrootarchive"
17
+	"github.com/docker/docker/pkg/progress"
18
+	"github.com/docker/docker/pkg/streamformatter"
19
+	"github.com/docker/docker/pkg/stringid"
17 20
 	"github.com/docker/docker/pkg/symlink"
18 21
 	"github.com/docker/docker/reference"
19 22
 )
20 23
 
21
-func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error {
24
+func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool) error {
25
+	var (
26
+		sf             = streamformatter.NewJSONStreamFormatter()
27
+		progressOutput progress.Output
28
+	)
29
+	if !quiet {
30
+		progressOutput = sf.NewProgressOutput(outStream, false)
31
+	} else {
32
+		progressOutput = nil
33
+	}
34
+
22 35
 	tmpDir, err := ioutil.TempDir("", "docker-import-")
23 36
 	if err != nil {
24 37
 		return err
... ...
@@ -36,7 +49,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error {
36 36
 	manifestFile, err := os.Open(manifestPath)
37 37
 	if err != nil {
38 38
 		if os.IsNotExist(err) {
39
-			return l.legacyLoad(tmpDir, outStream)
39
+			return l.legacyLoad(tmpDir, outStream, progressOutput)
40 40
 		}
41 41
 		return manifestFile.Close()
42 42
 	}
... ...
@@ -77,7 +90,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error {
77 77
 			r.Append(diffID)
78 78
 			newLayer, err := l.ls.Get(r.ChainID())
79 79
 			if err != nil {
80
-				newLayer, err = l.loadLayer(layerPath, rootFS)
80
+				newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), progressOutput)
81 81
 				if err != nil {
82 82
 					return err
83 83
 				}
... ...
@@ -111,7 +124,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer) error {
111 111
 	return nil
112 112
 }
113 113
 
114
-func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Layer, error) {
114
+func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, progressOutput progress.Output) (layer.Layer, error) {
115 115
 	rawTar, err := os.Open(filename)
116 116
 	if err != nil {
117 117
 		logrus.Debugf("Error reading embedded tar: %v", err)
... ...
@@ -125,6 +138,17 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS) (layer.Lay
125 125
 	}
126 126
 	defer inflatedLayerData.Close()
127 127
 
128
+	if progressOutput != nil {
129
+		fileInfo, err := os.Stat(filename)
130
+		if err != nil {
131
+			logrus.Debugf("Error statting file: %v", err)
132
+			return nil, err
133
+		}
134
+
135
+		progressReader := progress.NewProgressReader(inflatedLayerData, progressOutput, fileInfo.Size(), stringid.TruncateID(id), "Loading layer")
136
+
137
+		return l.ls.Register(progressReader, rootFS.ChainID())
138
+	}
128 139
 	return l.ls.Register(inflatedLayerData, rootFS.ChainID())
129 140
 }
130 141
 
... ...
@@ -139,7 +163,7 @@ func (l *tarexporter) setLoadedTag(ref reference.NamedTagged, imgID image.ID, ou
139 139
 	return nil
140 140
 }
141 141
 
142
-func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error {
142
+func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer, progressOutput progress.Output) error {
143 143
 	legacyLoadedMap := make(map[string]image.ID)
144 144
 
145 145
 	dirs, err := ioutil.ReadDir(tmpDir)
... ...
@@ -150,7 +174,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error {
150 150
 	// every dir represents an image
151 151
 	for _, d := range dirs {
152 152
 		if d.IsDir() {
153
-			if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap); err != nil {
153
+			if err := l.legacyLoadImage(d.Name(), tmpDir, legacyLoadedMap, progressOutput); err != nil {
154 154
 				return err
155 155
 			}
156 156
 		}
... ...
@@ -196,7 +220,7 @@ func (l *tarexporter) legacyLoad(tmpDir string, outStream io.Writer) error {
196 196
 	return nil
197 197
 }
198 198
 
199
-func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID) error {
199
+func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[string]image.ID, progressOutput progress.Output) error {
200 200
 	if _, loaded := loadedMap[oldID]; loaded {
201 201
 		return nil
202 202
 	}
... ...
@@ -220,7 +244,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
220 220
 		for {
221 221
 			var loaded bool
222 222
 			if parentID, loaded = loadedMap[img.Parent]; !loaded {
223
-				if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap); err != nil {
223
+				if err := l.legacyLoadImage(img.Parent, sourceDir, loadedMap, progressOutput); err != nil {
224 224
 					return err
225 225
 				}
226 226
 			} else {
... ...
@@ -247,7 +271,7 @@ func (l *tarexporter) legacyLoadImage(oldID, sourceDir string, loadedMap map[str
247 247
 	if err != nil {
248 248
 		return err
249 249
 	}
250
-	newLayer, err := l.loadLayer(layerPath, *rootFS)
250
+	newLayer, err := l.loadLayer(layerPath, *rootFS, oldID, progressOutput)
251 251
 	if err != nil {
252 252
 		return err
253 253
 	}