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