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 }