Browse code

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>

Srini Brahmaroutu authored on 2014/09/24 07:53:43
Showing 4 changed files
... ...
@@ -23,6 +23,8 @@ It is also possible to specify a non-default registry to pull from.
23 23
 # EXAMPLES
24 24
 
25 25
 # Pull a repository with multiple images
26
+# Note that if the  image is previously downloaded then the status would be
27
+# 'Status: Image is up to date for fedora'
26 28
 
27 29
     $ sudo docker pull fedora
28 30
     Pulling repository fedora
... ...
@@ -31,6 +33,8 @@ It is also possible to specify a non-default registry to pull from.
31 31
     511136ea3c5a: Download complete
32 32
     73bd853d2ea5: Download complete
33 33
 
34
+    Status: Downloaded newer image for fedora
35
+
34 36
     $ sudo docker images
35 37
     REPOSITORY   TAG         IMAGE ID        CREATED      VIRTUAL SIZE
36 38
     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.
39 39
     fedora       latest      105182bb5e8b    5 days ago   372.7 MB
40 40
 
41 41
 # Pull an image, manually specifying path to the registry and tag
42
+# Note that if the  image is previously downloaded then the status would be
43
+# 'Status: Image is up to date for registry.hub.docker.com/fedora:20'
42 44
 
43 45
     $ sudo docker pull registry.hub.docker.com/fedora:20
44 46
     Pulling repository fedora
... ...
@@ -46,6 +52,8 @@ It is also possible to specify a non-default registry to pull from.
46 46
     511136ea3c5a: Download complete 
47 47
     fd241224e9cf: Download complete 
48 48
 
49
+    Status: Downloaded newer image for registry.hub.docker.com/fedora:20
50
+
49 51
     $ sudo docker images
50 52
     REPOSITORY   TAG         IMAGE ID        CREATED      VIRTUAL SIZE
51 53
     fedora       20          3f2fed40e4b0    4 days ago   372.7 MB
... ...
@@ -93,6 +93,8 @@ download the `centos` image.
93 93
     ef52fb1fe610: Download complete
94 94
     . . .
95 95
 
96
+    Status: Downloaded newer image for centos
97
+
96 98
 We can see that each layer of the image has been pulled down and now we
97 99
 can run a container from this image and we won't have to wait to
98 100
 download the image.
... ...
@@ -67,6 +67,8 @@ Once you've found the image you want, you can download it with `docker pull <ima
67 67
     511136ea3c5a: Download complete
68 68
     7064731afe90: Download complete
69 69
 
70
+    Status: Downloaded newer image for centos
71
+
70 72
 You now have an image from which you can run containers.
71 73
 
72 74
 ## Contributing to Docker Hub
... ...
@@ -122,6 +122,8 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
122 122
 	}
123 123
 
124 124
 	errors := make(chan error)
125
+
126
+	layers_downloaded := false
125 127
 	for _, image := range repoData.ImgList {
126 128
 		downloadImage := func(img *registry.ImgData) {
127 129
 			if askedTag != "" && img.Tag != askedTag {
... ...
@@ -158,15 +160,17 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
158 158
 
159 159
 			out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s", img.Tag, localName), nil))
160 160
 			success := false
161
-			var lastErr error
161
+			var lastErr, err error
162
+			var is_downloaded bool
162 163
 			if mirrors != nil {
163 164
 				for _, ep := range mirrors {
164 165
 					out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, mirror: %s", img.Tag, localName, ep), nil))
165
-					if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
166
+					if is_downloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
166 167
 						// Don't report errors when pulling from mirrors.
167 168
 						log.Debugf("Error pulling image (%s) from %s, mirror: %s, %s", img.Tag, localName, ep, err)
168 169
 						continue
169 170
 					}
171
+					layers_downloaded = layers_downloaded || is_downloaded
170 172
 					success = true
171 173
 					break
172 174
 				}
... ...
@@ -174,13 +178,14 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
174 174
 			if !success {
175 175
 				for _, ep := range repoData.Endpoints {
176 176
 					out.Write(sf.FormatProgress(utils.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s, endpoint: %s", img.Tag, localName, ep), nil))
177
-					if err := s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
177
+					if is_downloaded, err = s.pullImage(r, out, img.ID, ep, repoData.Tokens, sf); err != nil {
178 178
 						// It's not ideal that only the last error is returned, it would be better to concatenate the errors.
179 179
 						// As the error is also given to the output stream the user will see the error.
180 180
 						lastErr = err
181 181
 						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))
182 182
 						continue
183 183
 					}
184
+					layers_downloaded = layers_downloaded || is_downloaded
184 185
 					success = true
185 186
 					break
186 187
 				}
... ...
@@ -227,18 +232,24 @@ func (s *TagStore) pullRepository(r *registry.Session, out io.Writer, localName,
227 227
 		}
228 228
 	}
229 229
 
230
+	requestedTag := localName
231
+	if len(askedTag) > 0 {
232
+		requestedTag = localName + ":" + askedTag
233
+	}
234
+	WriteStatus(requestedTag, out, sf, layers_downloaded)
230 235
 	return nil
231 236
 }
232 237
 
233
-func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) error {
238
+func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint string, token []string, sf *utils.StreamFormatter) (bool, error) {
234 239
 	history, err := r.GetRemoteHistory(imgID, endpoint, token)
235 240
 	if err != nil {
236
-		return err
241
+		return false, err
237 242
 	}
238 243
 	out.Write(sf.FormatProgress(utils.TruncateID(imgID), "Pulling dependent layers", nil))
239 244
 	// FIXME: Try to stream the images?
240 245
 	// FIXME: Launch the getRemoteImage() in goroutines
241 246
 
247
+	layers_downloaded := false
242 248
 	for i := len(history) - 1; i >= 0; i-- {
243 249
 		id := history[i]
244 250
 
... ...
@@ -262,15 +273,16 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
262 262
 				imgJSON, imgSize, err = r.GetRemoteImageJSON(id, endpoint, token)
263 263
 				if err != nil && j == retries {
264 264
 					out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
265
-					return err
265
+					return layers_downloaded, err
266 266
 				} else if err != nil {
267 267
 					time.Sleep(time.Duration(j) * 500 * time.Millisecond)
268 268
 					continue
269 269
 				}
270 270
 				img, err = image.NewImgJSON(imgJSON)
271
+				layers_downloaded = true
271 272
 				if err != nil && j == retries {
272 273
 					out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
273
-					return fmt.Errorf("Failed to parse json: %s", err)
274
+					return layers_downloaded, fmt.Errorf("Failed to parse json: %s", err)
274 275
 				} else if err != nil {
275 276
 					time.Sleep(time.Duration(j) * 500 * time.Millisecond)
276 277
 					continue
... ...
@@ -295,8 +307,9 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
295 295
 					continue
296 296
 				} else if err != nil {
297 297
 					out.Write(sf.FormatProgress(utils.TruncateID(id), "Error pulling dependent layers", nil))
298
-					return err
298
+					return layers_downloaded, err
299 299
 				}
300
+				layers_downloaded = true
300 301
 				defer layer.Close()
301 302
 
302 303
 				err = s.graph.Register(img, imgJSON,
... ...
@@ -306,14 +319,21 @@ func (s *TagStore) pullImage(r *registry.Session, out io.Writer, imgID, endpoint
306 306
 					continue
307 307
 				} else if err != nil {
308 308
 					out.Write(sf.FormatProgress(utils.TruncateID(id), "Error downloading dependent layers", nil))
309
-					return err
309
+					return layers_downloaded, err
310 310
 				} else {
311 311
 					break
312 312
 				}
313 313
 			}
314 314
 		}
315 315
 		out.Write(sf.FormatProgress(utils.TruncateID(id), "Download complete", nil))
316
+	}
317
+	return layers_downloaded, nil
318
+}
316 319
 
320
+func WriteStatus(requestedTag string, out io.Writer, sf *utils.StreamFormatter, layers_downloaded bool) {
321
+	if layers_downloaded {
322
+		out.Write(sf.FormatStatus("", "Status: Downloaded newer image for %s", requestedTag))
323
+	} else {
324
+		out.Write(sf.FormatStatus("", "Status: Image is up to date for %s", requestedTag))
317 325
 	}
318
-	return nil
319 326
 }