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