github.com/hustcat/docker@v1.3.3-0.20160314103604-901c67a8eeab/distribution/xfer/download.go (about) 1 package xfer 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "time" 8 9 "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/image" 11 "github.com/docker/docker/layer" 12 "github.com/docker/docker/pkg/archive" 13 "github.com/docker/docker/pkg/ioutils" 14 "github.com/docker/docker/pkg/progress" 15 "golang.org/x/net/context" 16 ) 17 18 const maxDownloadAttempts = 5 19 20 // LayerDownloadManager figures out which layers need to be downloaded, then 21 // registers and downloads those, taking into account dependencies between 22 // layers. 23 type LayerDownloadManager struct { 24 layerStore layer.Store 25 tm TransferManager 26 } 27 28 // NewLayerDownloadManager returns a new LayerDownloadManager. 29 func NewLayerDownloadManager(layerStore layer.Store, concurrencyLimit int) *LayerDownloadManager { 30 return &LayerDownloadManager{ 31 layerStore: layerStore, 32 tm: NewTransferManager(concurrencyLimit), 33 } 34 } 35 36 type downloadTransfer struct { 37 Transfer 38 39 layerStore layer.Store 40 layer layer.Layer 41 err error 42 } 43 44 // result returns the layer resulting from the download, if the download 45 // and registration were successful. 46 func (d *downloadTransfer) result() (layer.Layer, error) { 47 return d.layer, d.err 48 } 49 50 // A DownloadDescriptor references a layer that may need to be downloaded. 51 type DownloadDescriptor interface { 52 // Key returns the key used to deduplicate downloads. 53 Key() string 54 // ID returns the ID for display purposes. 55 ID() string 56 // DiffID should return the DiffID for this layer, or an error 57 // if it is unknown (for example, if it has not been downloaded 58 // before). 59 DiffID() (layer.DiffID, error) 60 // Download is called to perform the download. 61 Download(ctx context.Context, progressOutput progress.Output) (io.ReadCloser, int64, error) 62 // Close is called when the download manager is finished with this 63 // descriptor and will not call Download again or read from the reader 64 // that Download returned. 65 Close() 66 } 67 68 // DownloadDescriptorWithRegistered is a DownloadDescriptor that has an 69 // additional Registered method which gets called after a downloaded layer is 70 // registered. This allows the user of the download manager to know the DiffID 71 // of each registered layer. This method is called if a cast to 72 // DownloadDescriptorWithRegistered is successful. 73 type DownloadDescriptorWithRegistered interface { 74 DownloadDescriptor 75 Registered(diffID layer.DiffID) 76 } 77 78 // Download is a blocking function which ensures the requested layers are 79 // present in the layer store. It uses the string returned by the Key method to 80 // deduplicate downloads. If a given layer is not already known to present in 81 // the layer store, and the key is not used by an in-progress download, the 82 // Download method is called to get the layer tar data. Layers are then 83 // registered in the appropriate order. The caller must call the returned 84 // release function once it is is done with the returned RootFS object. 85 func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { 86 var ( 87 topLayer layer.Layer 88 topDownload *downloadTransfer 89 watcher *Watcher 90 missingLayer bool 91 transferKey = "" 92 downloadsByKey = make(map[string]*downloadTransfer) 93 ) 94 95 rootFS := initialRootFS 96 for _, descriptor := range layers { 97 key := descriptor.Key() 98 transferKey += key 99 100 if !missingLayer { 101 missingLayer = true 102 diffID, err := descriptor.DiffID() 103 if err == nil { 104 getRootFS := rootFS 105 getRootFS.Append(diffID) 106 l, err := ldm.layerStore.Get(getRootFS.ChainID()) 107 if err == nil { 108 // Layer already exists. 109 logrus.Debugf("Layer already exists: %s", descriptor.ID()) 110 progress.Update(progressOutput, descriptor.ID(), "Already exists") 111 if topLayer != nil { 112 layer.ReleaseAndLog(ldm.layerStore, topLayer) 113 } 114 topLayer = l 115 missingLayer = false 116 rootFS.Append(diffID) 117 continue 118 } 119 } 120 } 121 122 // Does this layer have the same data as a previous layer in 123 // the stack? If so, avoid downloading it more than once. 124 var topDownloadUncasted Transfer 125 if existingDownload, ok := downloadsByKey[key]; ok { 126 xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload) 127 defer topDownload.Transfer.Release(watcher) 128 topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) 129 topDownload = topDownloadUncasted.(*downloadTransfer) 130 continue 131 } 132 133 // Layer is not known to exist - download and register it. 134 progress.Update(progressOutput, descriptor.ID(), "Pulling fs layer") 135 136 var xferFunc DoFunc 137 if topDownload != nil { 138 xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload) 139 defer topDownload.Transfer.Release(watcher) 140 } else { 141 xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil) 142 } 143 topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) 144 topDownload = topDownloadUncasted.(*downloadTransfer) 145 downloadsByKey[key] = topDownload 146 } 147 148 if topDownload == nil { 149 return rootFS, func() { layer.ReleaseAndLog(ldm.layerStore, topLayer) }, nil 150 } 151 152 // Won't be using the list built up so far - will generate it 153 // from downloaded layers instead. 154 rootFS.DiffIDs = []layer.DiffID{} 155 156 defer func() { 157 if topLayer != nil { 158 layer.ReleaseAndLog(ldm.layerStore, topLayer) 159 } 160 }() 161 162 select { 163 case <-ctx.Done(): 164 topDownload.Transfer.Release(watcher) 165 return rootFS, func() {}, ctx.Err() 166 case <-topDownload.Done(): 167 break 168 } 169 170 l, err := topDownload.result() 171 if err != nil { 172 topDownload.Transfer.Release(watcher) 173 return rootFS, func() {}, err 174 } 175 176 // Must do this exactly len(layers) times, so we don't include the 177 // base layer on Windows. 178 for range layers { 179 if l == nil { 180 topDownload.Transfer.Release(watcher) 181 return rootFS, func() {}, errors.New("internal error: too few parent layers") 182 } 183 rootFS.DiffIDs = append([]layer.DiffID{l.DiffID()}, rootFS.DiffIDs...) 184 l = l.Parent() 185 } 186 return rootFS, func() { topDownload.Transfer.Release(watcher) }, err 187 } 188 189 // makeDownloadFunc returns a function that performs the layer download and 190 // registration. If parentDownload is non-nil, it waits for that download to 191 // complete before the registration step, and registers the downloaded data 192 // on top of parentDownload's resulting layer. Otherwise, it registers the 193 // layer on top of the ChainID given by parentLayer. 194 func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer) DoFunc { 195 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 196 d := &downloadTransfer{ 197 Transfer: NewTransfer(), 198 layerStore: ldm.layerStore, 199 } 200 201 go func() { 202 defer func() { 203 close(progressChan) 204 }() 205 206 progressOutput := progress.ChanOutput(progressChan) 207 208 select { 209 case <-start: 210 default: 211 progress.Update(progressOutput, descriptor.ID(), "Waiting") 212 <-start 213 } 214 215 if parentDownload != nil { 216 // Did the parent download already fail or get 217 // cancelled? 218 select { 219 case <-parentDownload.Done(): 220 _, err := parentDownload.result() 221 if err != nil { 222 d.err = err 223 return 224 } 225 default: 226 } 227 } 228 229 var ( 230 downloadReader io.ReadCloser 231 size int64 232 err error 233 retries int 234 ) 235 236 defer descriptor.Close() 237 238 for { 239 downloadReader, size, err = descriptor.Download(d.Transfer.Context(), progressOutput) 240 if err == nil { 241 break 242 } 243 244 // If an error was returned because the context 245 // was cancelled, we shouldn't retry. 246 select { 247 case <-d.Transfer.Context().Done(): 248 d.err = err 249 return 250 default: 251 } 252 253 retries++ 254 if _, isDNR := err.(DoNotRetry); isDNR || retries == maxDownloadAttempts { 255 logrus.Errorf("Download failed: %v", err) 256 d.err = err 257 return 258 } 259 260 logrus.Errorf("Download failed, retrying: %v", err) 261 delay := retries * 5 262 ticker := time.NewTicker(time.Second) 263 264 selectLoop: 265 for { 266 progress.Updatef(progressOutput, descriptor.ID(), "Retrying in %d seconds", delay) 267 select { 268 case <-ticker.C: 269 delay-- 270 if delay == 0 { 271 ticker.Stop() 272 break selectLoop 273 } 274 case <-d.Transfer.Context().Done(): 275 ticker.Stop() 276 d.err = errors.New("download cancelled during retry delay") 277 return 278 } 279 280 } 281 } 282 283 close(inactive) 284 285 if parentDownload != nil { 286 select { 287 case <-d.Transfer.Context().Done(): 288 d.err = errors.New("layer registration cancelled") 289 downloadReader.Close() 290 return 291 case <-parentDownload.Done(): 292 } 293 294 l, err := parentDownload.result() 295 if err != nil { 296 d.err = err 297 downloadReader.Close() 298 return 299 } 300 parentLayer = l.ChainID() 301 } 302 303 reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(d.Transfer.Context(), downloadReader), progressOutput, size, descriptor.ID(), "Extracting") 304 defer reader.Close() 305 306 inflatedLayerData, err := archive.DecompressStream(reader) 307 if err != nil { 308 d.err = fmt.Errorf("could not get decompression stream: %v", err) 309 return 310 } 311 312 d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer) 313 if err != nil { 314 select { 315 case <-d.Transfer.Context().Done(): 316 d.err = errors.New("layer registration cancelled") 317 default: 318 d.err = fmt.Errorf("failed to register layer: %v", err) 319 } 320 return 321 } 322 323 progress.Update(progressOutput, descriptor.ID(), "Pull complete") 324 withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered) 325 if hasRegistered { 326 withRegistered.Registered(d.layer.DiffID()) 327 } 328 329 // Doesn't actually need to be its own goroutine, but 330 // done like this so we can defer close(c). 331 go func() { 332 <-d.Transfer.Released() 333 if d.layer != nil { 334 layer.ReleaseAndLog(d.layerStore, d.layer) 335 } 336 }() 337 }() 338 339 return d 340 } 341 } 342 343 // makeDownloadFuncFromDownload returns a function that performs the layer 344 // registration when the layer data is coming from an existing download. It 345 // waits for sourceDownload and parentDownload to complete, and then 346 // reregisters the data from sourceDownload's top layer on top of 347 // parentDownload. This function does not log progress output because it would 348 // interfere with the progress reporting for sourceDownload, which has the same 349 // Key. 350 func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer) DoFunc { 351 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 352 d := &downloadTransfer{ 353 Transfer: NewTransfer(), 354 layerStore: ldm.layerStore, 355 } 356 357 go func() { 358 defer func() { 359 close(progressChan) 360 }() 361 362 <-start 363 364 close(inactive) 365 366 select { 367 case <-d.Transfer.Context().Done(): 368 d.err = errors.New("layer registration cancelled") 369 return 370 case <-parentDownload.Done(): 371 } 372 373 l, err := parentDownload.result() 374 if err != nil { 375 d.err = err 376 return 377 } 378 parentLayer := l.ChainID() 379 380 // sourceDownload should have already finished if 381 // parentDownload finished, but wait for it explicitly 382 // to be sure. 383 select { 384 case <-d.Transfer.Context().Done(): 385 d.err = errors.New("layer registration cancelled") 386 return 387 case <-sourceDownload.Done(): 388 } 389 390 l, err = sourceDownload.result() 391 if err != nil { 392 d.err = err 393 return 394 } 395 396 layerReader, err := l.TarStream() 397 if err != nil { 398 d.err = err 399 return 400 } 401 defer layerReader.Close() 402 403 d.layer, err = d.layerStore.Register(layerReader, parentLayer) 404 if err != nil { 405 d.err = fmt.Errorf("failed to register layer: %v", err) 406 return 407 } 408 409 withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered) 410 if hasRegistered { 411 withRegistered.Registered(d.layer.DiffID()) 412 } 413 414 // Doesn't actually need to be its own goroutine, but 415 // done like this so we can defer close(c). 416 go func() { 417 <-d.Transfer.Released() 418 if d.layer != nil { 419 layer.ReleaseAndLog(d.layerStore, d.layer) 420 } 421 }() 422 }() 423 424 return d 425 } 426 }