Browse code

Merge pull request #15646 from tonistiigi/15589-pull-cancellation

Fix pull on client disconnect

David Calavera authored on 2015/08/28 00:54:08
Showing 2 changed files
... ...
@@ -77,7 +77,7 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
77 77
 	if err != nil {
78 78
 		if c != nil {
79 79
 			// Another pull of the same repository is already taking place; just wait for it to finish
80
-			p.sf.FormatStatus("", "Repository %s already being pulled by another client. Waiting.", p.repoInfo.CanonicalName)
80
+			p.config.OutStream.Write(p.sf.FormatStatus("", "Repository %s already being pulled by another client. Waiting.", p.repoInfo.CanonicalName))
81 81
 			<-c
82 82
 			return nil
83 83
 		}
... ...
@@ -223,6 +223,9 @@ func (p *v2Puller) pullV2Tag(tag, taggedName string) (verified bool, err error)
223 223
 	go func() {
224 224
 		if _, err := io.Copy(out, pipeReader); err != nil {
225 225
 			logrus.Errorf("error copying from layer download progress reader: %s", err)
226
+			if err := pipeReader.CloseWithError(err); err != nil {
227
+				logrus.Errorf("error closing the progress reader: %s", err)
228
+			}
226 229
 		}
227 230
 	}()
228 231
 	defer func() {
... ...
@@ -369,3 +369,40 @@ func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) {
369 369
 		}
370 370
 	})
371 371
 }
372
+
373
+// Test that pull continues after client has disconnected. #15589
374
+func (s *DockerTrustSuite) TestPullClientDisconnect(c *check.C) {
375
+	testRequires(c, Network)
376
+
377
+	repoName := "hello-world:latest"
378
+
379
+	dockerCmdWithError("rmi", repoName) // clean just in case
380
+
381
+	pullCmd := exec.Command(dockerBinary, "pull", repoName)
382
+
383
+	stdout, err := pullCmd.StdoutPipe()
384
+	c.Assert(err, check.IsNil)
385
+
386
+	err = pullCmd.Start()
387
+	c.Assert(err, check.IsNil)
388
+
389
+	// cancel as soon as we get some output
390
+	buf := make([]byte, 10)
391
+	_, err = stdout.Read(buf)
392
+	c.Assert(err, check.IsNil)
393
+
394
+	err = pullCmd.Process.Kill()
395
+	c.Assert(err, check.IsNil)
396
+
397
+	maxAttempts := 20
398
+	for i := 0; ; i++ {
399
+		if _, _, err := dockerCmdWithError("inspect", repoName); err == nil {
400
+			break
401
+		}
402
+		if i >= maxAttempts {
403
+			c.Fatal("Timeout reached. Image was not pulled after client disconnected.")
404
+		}
405
+		time.Sleep(500 * time.Millisecond)
406
+	}
407
+
408
+}