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