Browse code

Change poolAdd to return a boolean instead of an error

Previously, its other return value was used even when it returned an
error. This is awkward and goes against the convention. It also could
have resulted in a nil pointer dereference when an error was returned
because of an unknown pool type. This changes the unknown pool type
error to a panic (since the pool types are hardcoded at call sites and
must always be "push" or "pull"), and returns a "found" boolean instead
of an error.

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2015/08/12 01:44:50
Showing 7 changed files
... ...
@@ -106,8 +106,8 @@ func (s *TagStore) recursiveLoad(address, tmpImageDir string) error {
106 106
 		}
107 107
 
108 108
 		// ensure no two downloads of the same layer happen at the same time
109
-		if ps, err := s.poolAdd("pull", "layer:"+img.ID); err != nil {
110
-			logrus.Debugf("Image (id: %s) load is already running, waiting: %v", img.ID, err)
109
+		if ps, found := s.poolAdd("pull", "layer:"+img.ID); found {
110
+			logrus.Debugf("Image (id: %s) load is already running, waiting", img.ID)
111 111
 			ps.Wait(nil, nil)
112 112
 			return nil
113 113
 		}
... ...
@@ -17,20 +17,17 @@ func TestPools(t *testing.T) {
17 17
 		pushingPool: make(map[string]*progressreader.ProgressStatus),
18 18
 	}
19 19
 
20
-	if _, err := s.poolAdd("pull", "test1"); err != nil {
21
-		t.Fatal(err)
22
-	}
23
-	if _, err := s.poolAdd("pull", "test2"); err != nil {
24
-		t.Fatal(err)
20
+	if _, found := s.poolAdd("pull", "test1"); found {
21
+		t.Fatal("Expected pull test1 not to be in progress")
25 22
 	}
26
-	if _, err := s.poolAdd("push", "test1"); err == nil || err.Error() != "pull test1 is already in progress" {
27
-		t.Fatalf("Expected `pull test1 is already in progress`")
23
+	if _, found := s.poolAdd("pull", "test2"); found {
24
+		t.Fatal("Expected pull test2 not to be in progress")
28 25
 	}
29
-	if _, err := s.poolAdd("pull", "test1"); err == nil || err.Error() != "pull test1 is already in progress" {
30
-		t.Fatalf("Expected `pull test1 is already in progress`")
26
+	if _, found := s.poolAdd("push", "test1"); !found {
27
+		t.Fatalf("Expected pull test1 to be in progress`")
31 28
 	}
32
-	if _, err := s.poolAdd("wait", "test3"); err == nil || err.Error() != "Unknown pool type" {
33
-		t.Fatalf("Expected `Unknown pool type`")
29
+	if _, found := s.poolAdd("pull", "test1"); !found {
30
+		t.Fatalf("Expected pull test1 to be in progress`")
34 31
 	}
35 32
 	if err := s.poolRemove("pull", "test2"); err != nil {
36 33
 		t.Fatal(err)
... ...
@@ -44,7 +41,4 @@ func TestPools(t *testing.T) {
44 44
 	if err := s.poolRemove("push", "test1"); err != nil {
45 45
 		t.Fatal(err)
46 46
 	}
47
-	if err := s.poolRemove("wait", "test3"); err == nil || err.Error() != "Unknown pool type" {
48
-		t.Fatalf("Expected `Unknown pool type`")
49
-	}
50 47
 }
... ...
@@ -138,8 +138,8 @@ func (p *v1Puller) pullRepository(askedTag string) error {
138 138
 			}
139 139
 
140 140
 			// ensure no two downloads of the same image happen at the same time
141
-			ps, err := p.poolAdd("pull", "img:"+img.ID)
142
-			if err != nil {
141
+			ps, found := p.poolAdd("pull", "img:"+img.ID)
142
+			if found {
143 143
 				msg := p.sf.FormatProgress(stringid.TruncateID(img.ID), "Layer already being pulled by another client. Waiting.", nil)
144 144
 				ps.Wait(out, msg)
145 145
 				out.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), "Download complete", nil))
... ...
@@ -155,7 +155,7 @@ func (p *v1Puller) pullRepository(askedTag string) error {
155 155
 
156 156
 			ps.Write(p.sf.FormatProgress(stringid.TruncateID(img.ID), fmt.Sprintf("Pulling image (%s) from %s", img.Tag, p.repoInfo.CanonicalName), nil))
157 157
 			success := false
158
-			var lastErr error
158
+			var lastErr, err error
159 159
 			var isDownloaded bool
160 160
 			for _, ep := range p.repoInfo.Index.Mirrors {
161 161
 				ep += "v1/"
... ...
@@ -244,9 +244,9 @@ func (p *v1Puller) pullImage(out io.Writer, imgID, endpoint string, token []stri
244 244
 		id := history[i]
245 245
 
246 246
 		// ensure no two downloads of the same layer happen at the same time
247
-		ps, err := p.poolAdd("pull", "layer:"+id)
248
-		if err != nil {
249
-			logrus.Debugf("Image (id: %s) pull is already running, skipping: %v", id, err)
247
+		ps, found := p.poolAdd("pull", "layer:"+id)
248
+		if found {
249
+			logrus.Debugf("Image (id: %s) pull is already running, skipping", id)
250 250
 			msg := p.sf.FormatProgress(stringid.TruncateID(imgID), "Layer already being pulled by another client. Waiting.", nil)
251 251
 			ps.Wait(out, msg)
252 252
 		} else {
... ...
@@ -73,8 +73,8 @@ func (p *v2Puller) pullV2Repository(tag string) (err error) {
73 73
 
74 74
 	}
75 75
 
76
-	ps, err := p.poolAdd("pull", taggedName)
77
-	if err != nil {
76
+	ps, found := p.poolAdd("pull", taggedName)
77
+	if found {
78 78
 		// Another pull of the same repository is already taking place; just wait for it to finish
79 79
 		msg := p.sf.FormatStatus("", "Repository %s already being pulled by another client. Waiting.", p.repoInfo.CanonicalName)
80 80
 		ps.Wait(p.config.OutStream, msg)
... ...
@@ -119,8 +119,8 @@ func (p *v2Puller) download(di *downloadInfo) {
119 119
 
120 120
 	out := di.out
121 121
 
122
-	ps, err := p.poolAdd("pull", "img:"+di.img.ID)
123
-	if err != nil {
122
+	ps, found := p.poolAdd("pull", "img:"+di.img.ID)
123
+	if found {
124 124
 		msg := p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Layer already being pulled by another client. Waiting.", nil)
125 125
 		ps.Wait(out, msg)
126 126
 		out.Write(p.sf.FormatProgress(stringid.TruncateID(di.img.ID), "Download complete", nil))
... ...
@@ -214,7 +214,6 @@ func (p *v1Pusher) pushImageToEndpoint(endpoint string, imageIDs []string, tags
214 214
 
215 215
 // pushRepository pushes layers that do not already exist on the registry.
216 216
 func (p *v1Pusher) pushRepository(tag string) error {
217
-
218 217
 	logrus.Debugf("Local repo: %s", p.localRepo)
219 218
 	p.out = ioutils.NewWriteFlusher(p.config.OutStream)
220 219
 	imgList, tags, err := p.getImageList(tag)
... ...
@@ -229,8 +228,8 @@ func (p *v1Pusher) pushRepository(tag string) error {
229 229
 		logrus.Debugf("Pushing ID: %s with Tag: %s", data.ID, data.Tag)
230 230
 	}
231 231
 
232
-	if _, err := p.poolAdd("push", p.repoInfo.LocalName); err != nil {
233
-		return err
232
+	if _, found := p.poolAdd("push", p.repoInfo.LocalName); found {
233
+		return fmt.Errorf("push or pull %s is already in progress", p.repoInfo.LocalName)
234 234
 	}
235 235
 	defer p.poolRemove("push", p.repoInfo.LocalName)
236 236
 
... ...
@@ -57,8 +57,8 @@ func (p *v2Pusher) getImageTags(askedTag string) ([]string, error) {
57 57
 
58 58
 func (p *v2Pusher) pushV2Repository(tag string) error {
59 59
 	localName := p.repoInfo.LocalName
60
-	if _, err := p.poolAdd("push", localName); err != nil {
61
-		return err
60
+	if _, found := p.poolAdd("push", localName); found {
61
+		return fmt.Errorf("push or pull %s is already in progress", localName)
62 62
 	}
63 63
 	defer p.poolRemove("push", localName)
64 64
 
... ...
@@ -428,27 +428,32 @@ func validateDigest(dgst string) error {
428 428
 	return nil
429 429
 }
430 430
 
431
-func (store *TagStore) poolAdd(kind, key string) (*progressreader.ProgressStatus, error) {
431
+// poolAdd checks if a push or pull is already running, and returns (ps, true)
432
+// if a running operation is found. Otherwise, it creates a new one and returns
433
+// (ps, false).
434
+func (store *TagStore) poolAdd(kind, key string) (*progressreader.ProgressStatus, bool) {
432 435
 	store.Lock()
433 436
 	defer store.Unlock()
434 437
 
435 438
 	if p, exists := store.pullingPool[key]; exists {
436
-		return p, fmt.Errorf("pull %s is already in progress", key)
439
+		return p, true
437 440
 	}
438 441
 	if p, exists := store.pushingPool[key]; exists {
439
-		return p, fmt.Errorf("push %s is already in progress", key)
442
+		return p, true
440 443
 	}
441 444
 
442 445
 	ps := progressreader.NewProgressStatus()
446
+
443 447
 	switch kind {
444 448
 	case "pull":
445 449
 		store.pullingPool[key] = ps
446 450
 	case "push":
447 451
 		store.pushingPool[key] = ps
448 452
 	default:
449
-		return nil, fmt.Errorf("Unknown pool type")
453
+		panic("Unknown pool type")
450 454
 	}
451
-	return ps, nil
455
+
456
+	return ps, false
452 457
 }
453 458
 
454 459
 func (store *TagStore) poolRemove(kind, key string) error {