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 }