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