github.com/reds/docker@v1.11.2-rc1/distribution/xfer/upload.go (about)

     1  package xfer
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/distribution"
     9  	"github.com/docker/docker/layer"
    10  	"github.com/docker/docker/pkg/progress"
    11  	"golang.org/x/net/context"
    12  )
    13  
    14  const maxUploadAttempts = 5
    15  
    16  // LayerUploadManager provides task management and progress reporting for
    17  // uploads.
    18  type LayerUploadManager struct {
    19  	tm TransferManager
    20  }
    21  
    22  // NewLayerUploadManager returns a new LayerUploadManager.
    23  func NewLayerUploadManager(concurrencyLimit int) *LayerUploadManager {
    24  	return &LayerUploadManager{
    25  		tm: NewTransferManager(concurrencyLimit),
    26  	}
    27  }
    28  
    29  type uploadTransfer struct {
    30  	Transfer
    31  
    32  	remoteDescriptor distribution.Descriptor
    33  	err              error
    34  }
    35  
    36  // An UploadDescriptor references a layer that may need to be uploaded.
    37  type UploadDescriptor interface {
    38  	// Key returns the key used to deduplicate uploads.
    39  	Key() string
    40  	// ID returns the ID for display purposes.
    41  	ID() string
    42  	// DiffID should return the DiffID for this layer.
    43  	DiffID() layer.DiffID
    44  	// Upload is called to perform the Upload.
    45  	Upload(ctx context.Context, progressOutput progress.Output) (distribution.Descriptor, error)
    46  	// SetRemoteDescriptor provides the distribution.Descriptor that was
    47  	// returned by Upload. This descriptor is not to be confused with
    48  	// the UploadDescriptor interface, which is used for internally
    49  	// identifying layers that are being uploaded.
    50  	SetRemoteDescriptor(descriptor distribution.Descriptor)
    51  }
    52  
    53  // Upload is a blocking function which ensures the listed layers are present on
    54  // the remote registry. It uses the string returned by the Key method to
    55  // deduplicate uploads.
    56  func (lum *LayerUploadManager) Upload(ctx context.Context, layers []UploadDescriptor, progressOutput progress.Output) error {
    57  	var (
    58  		uploads          []*uploadTransfer
    59  		dedupDescriptors = make(map[string]*uploadTransfer)
    60  	)
    61  
    62  	for _, descriptor := range layers {
    63  		progress.Update(progressOutput, descriptor.ID(), "Preparing")
    64  
    65  		key := descriptor.Key()
    66  		if _, present := dedupDescriptors[key]; present {
    67  			continue
    68  		}
    69  
    70  		xferFunc := lum.makeUploadFunc(descriptor)
    71  		upload, watcher := lum.tm.Transfer(descriptor.Key(), xferFunc, progressOutput)
    72  		defer upload.Release(watcher)
    73  		uploads = append(uploads, upload.(*uploadTransfer))
    74  		dedupDescriptors[key] = upload.(*uploadTransfer)
    75  	}
    76  
    77  	for _, upload := range uploads {
    78  		select {
    79  		case <-ctx.Done():
    80  			return ctx.Err()
    81  		case <-upload.Transfer.Done():
    82  			if upload.err != nil {
    83  				return upload.err
    84  			}
    85  		}
    86  	}
    87  	for _, l := range layers {
    88  		l.SetRemoteDescriptor(dedupDescriptors[l.Key()].remoteDescriptor)
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  func (lum *LayerUploadManager) makeUploadFunc(descriptor UploadDescriptor) DoFunc {
    95  	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
    96  		u := &uploadTransfer{
    97  			Transfer: NewTransfer(),
    98  		}
    99  
   100  		go func() {
   101  			defer func() {
   102  				close(progressChan)
   103  			}()
   104  
   105  			progressOutput := progress.ChanOutput(progressChan)
   106  
   107  			select {
   108  			case <-start:
   109  			default:
   110  				progress.Update(progressOutput, descriptor.ID(), "Waiting")
   111  				<-start
   112  			}
   113  
   114  			retries := 0
   115  			for {
   116  				remoteDescriptor, err := descriptor.Upload(u.Transfer.Context(), progressOutput)
   117  				if err == nil {
   118  					u.remoteDescriptor = remoteDescriptor
   119  					break
   120  				}
   121  
   122  				// If an error was returned because the context
   123  				// was cancelled, we shouldn't retry.
   124  				select {
   125  				case <-u.Transfer.Context().Done():
   126  					u.err = err
   127  					return
   128  				default:
   129  				}
   130  
   131  				retries++
   132  				if _, isDNR := err.(DoNotRetry); isDNR || retries == maxUploadAttempts {
   133  					logrus.Errorf("Upload failed: %v", err)
   134  					u.err = err
   135  					return
   136  				}
   137  
   138  				logrus.Errorf("Upload failed, retrying: %v", err)
   139  				delay := retries * 5
   140  				ticker := time.NewTicker(time.Second)
   141  
   142  			selectLoop:
   143  				for {
   144  					progress.Updatef(progressOutput, descriptor.ID(), "Retrying in %d second%s", delay, (map[bool]string{true: "s"})[delay != 1])
   145  					select {
   146  					case <-ticker.C:
   147  						delay--
   148  						if delay == 0 {
   149  							ticker.Stop()
   150  							break selectLoop
   151  						}
   152  					case <-u.Transfer.Context().Done():
   153  						ticker.Stop()
   154  						u.err = errors.New("upload cancelled during retry delay")
   155  						return
   156  					}
   157  				}
   158  			}
   159  		}()
   160  
   161  		return u
   162  	}
   163  }