github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/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 }