@@ -280,15 +280,14 @@ func (p dockerPusher) push(ctx context.Context, desc ocispec.Descriptor, ref str
280
280
req .body = func () (io.ReadCloser , error ) {
281
281
pr , pw := io .Pipe ()
282
282
pushw .setPipe (pw )
283
- return io . NopCloser ( pr ) , nil
283
+ return pr , nil
284
284
}
285
285
req .size = desc .Size
286
286
287
287
go func () {
288
288
resp , err := req .doWithRetries (ctx , nil )
289
289
if err != nil {
290
290
pushw .setError (err )
291
- pushw .Close ()
292
291
return
293
292
}
294
293
@@ -298,7 +297,7 @@ func (p dockerPusher) push(ctx context.Context, desc ocispec.Descriptor, ref str
298
297
err := remoteserrors .NewUnexpectedStatusErr (resp )
299
298
log .G (ctx ).WithField ("resp" , resp ).WithField ("body" , string (err .(remoteserrors.ErrUnexpectedStatus ).Body )).Debug ("unexpected response" )
300
299
pushw .setError (err )
301
- pushw . Close ()
300
+ return
302
301
}
303
302
pushw .setResponse (resp )
304
303
}()
@@ -331,10 +330,12 @@ type pushWriter struct {
331
330
332
331
pipe * io.PipeWriter
333
332
334
- pipeC chan * io.PipeWriter
335
- respC chan * http.Response
333
+ done chan struct {}
336
334
closeOnce sync.Once
337
- errC chan error
335
+
336
+ pipeC chan * io.PipeWriter
337
+ respC chan * http.Response
338
+ errC chan error
338
339
339
340
isManifest bool
340
341
@@ -352,19 +353,51 @@ func newPushWriter(db *dockerBase, ref string, expected digest.Digest, tracker S
352
353
pipeC : make (chan * io.PipeWriter , 1 ),
353
354
respC : make (chan * http.Response , 1 ),
354
355
errC : make (chan error , 1 ),
356
+ done : make (chan struct {}),
355
357
isManifest : isManifest ,
356
358
}
357
359
}
358
360
359
361
func (pw * pushWriter ) setPipe (p * io.PipeWriter ) {
360
- pw .pipeC <- p
362
+ select {
363
+ case <- pw .done :
364
+ case pw .pipeC <- p :
365
+ }
361
366
}
362
367
363
368
func (pw * pushWriter ) setError (err error ) {
364
- pw .errC <- err
369
+ select {
370
+ case <- pw .done :
371
+ case pw .errC <- err :
372
+ }
365
373
}
374
+
366
375
func (pw * pushWriter ) setResponse (resp * http.Response ) {
367
- pw .respC <- resp
376
+ select {
377
+ case <- pw .done :
378
+ case pw .respC <- resp :
379
+ }
380
+ }
381
+
382
+ func (pw * pushWriter ) replacePipe (p * io.PipeWriter ) error {
383
+ if pw .pipe == nil {
384
+ pw .pipe = p
385
+ return nil
386
+ }
387
+
388
+ pw .pipe .CloseWithError (content .ErrReset )
389
+ pw .pipe = p
390
+
391
+ // If content has already been written, the bytes
392
+ // cannot be written again and the caller must reset
393
+ status , err := pw .tracker .GetStatus (pw .ref )
394
+ if err != nil {
395
+ return err
396
+ }
397
+ status .Offset = 0
398
+ status .UpdatedAt = time .Now ()
399
+ pw .tracker .SetStatus (pw .ref , status )
400
+ return content .ErrReset
368
401
}
369
402
370
403
func (pw * pushWriter ) Write (p []byte ) (n int , err error ) {
@@ -374,26 +407,18 @@ func (pw *pushWriter) Write(p []byte) (n int, err error) {
374
407
}
375
408
376
409
if pw .pipe == nil {
377
- p , ok := <- pw . pipeC
378
- if ! ok {
410
+ select {
411
+ case <- pw . done :
379
412
return 0 , io .ErrClosedPipe
413
+ case p := <- pw .pipeC :
414
+ pw .replacePipe (p )
380
415
}
381
- pw .pipe = p
382
416
} else {
383
417
select {
384
- case p , ok := <- pw .pipeC :
385
- if ! ok {
386
- return 0 , io .ErrClosedPipe
387
- }
388
- pw .pipe .CloseWithError (content .ErrReset )
389
- pw .pipe = p
390
-
391
- // If content has already been written, the bytes
392
- // cannot be written and the caller must reset
393
- status .Offset = 0
394
- status .UpdatedAt = time .Now ()
395
- pw .tracker .SetStatus (pw .ref , status )
396
- return 0 , content .ErrReset
418
+ case <- pw .done :
419
+ return 0 , io .ErrClosedPipe
420
+ case p := <- pw .pipeC :
421
+ return 0 , pw .replacePipe (p )
397
422
default :
398
423
}
399
424
}
@@ -403,9 +428,13 @@ func (pw *pushWriter) Write(p []byte) (n int, err error) {
403
428
// if the pipe is closed, we might have the original error on the error
404
429
// channel - so we should try and get it
405
430
select {
406
- case err2 := <- pw .errC :
407
- err = err2
408
- default :
431
+ case <- pw .done :
432
+ case err = <- pw .errC :
433
+ pw .Close ()
434
+ case p := <- pw .pipeC :
435
+ return 0 , pw .replacePipe (p )
436
+ case resp := <- pw .respC :
437
+ pw .setResponse (resp )
409
438
}
410
439
}
411
440
status .Offset += int64 (n )
@@ -418,7 +447,7 @@ func (pw *pushWriter) Close() error {
418
447
// Ensure pipeC is closed but handle `Close()` being
419
448
// called multiple times without panicking
420
449
pw .closeOnce .Do (func () {
421
- close (pw .pipeC )
450
+ close (pw .done )
422
451
})
423
452
if pw .pipe != nil {
424
453
status , err := pw .tracker .GetStatus (pw .ref )
@@ -458,30 +487,18 @@ func (pw *pushWriter) Commit(ctx context.Context, size int64, expected digest.Di
458
487
// TODO: timeout waiting for response
459
488
var resp * http.Response
460
489
select {
490
+ case <- pw .done :
491
+ return io .ErrClosedPipe
461
492
case err := <- pw .errC :
493
+ pw .Close ()
462
494
return err
463
495
case resp = <- pw .respC :
464
496
defer resp .Body .Close ()
465
- case p , ok := <- pw .pipeC :
497
+ case p := <- pw .pipeC :
466
498
// check whether the pipe has changed in the commit, because sometimes Write
467
499
// can complete successfully, but the pipe may have changed. In that case, the
468
500
// content needs to be reset.
469
- if ! ok {
470
- return io .ErrClosedPipe
471
- }
472
- pw .pipe .CloseWithError (content .ErrReset )
473
- pw .pipe = p
474
-
475
- // If content has already been written, the bytes
476
- // cannot be written again and the caller must reset
477
- status , err := pw .tracker .GetStatus (pw .ref )
478
- if err != nil {
479
- return err
480
- }
481
- status .Offset = 0
482
- status .UpdatedAt = time .Now ()
483
- pw .tracker .SetStatus (pw .ref , status )
484
- return content .ErrReset
501
+ return pw .replacePipe (p )
485
502
}
486
503
487
504
// 201 is specified return status, some registries return
0 commit comments