github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/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 continue 130 } 131 } 132 } 133 134 // Does this layer have the same data as a previous layer in 135 // the stack? If so, avoid downloading it more than once. 136 var topDownloadUncasted Transfer 137 if existingDownload, ok := downloadsByKey[key]; ok { 138 xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload) 139 defer topDownload.Transfer.Release(watcher) 140 topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) 141 topDownload = topDownloadUncasted.(*downloadTransfer) 142 continue 143 } 144 145 // Layer is not known to exist - download and register it. 146 progress.Update(progressOutput, descriptor.ID(), "Pulling fs layer") 147 148 var xferFunc DoFunc 149 if topDownload != nil { 150 xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload) 151 defer topDownload.Transfer.Release(watcher) 152 } else { 153 xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil) 154 } 155 topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) 156 topDownload = topDownloadUncasted.(*downloadTransfer) 157 downloadsByKey[key] = topDownload 158 } 159 160 if topDownload == nil { 161 return rootFS, func() { 162 if topLayer != nil { 163 layer.ReleaseAndLog(ldm.layerStore, topLayer) 164 } 165 }, nil 166 } 167 168 // Won't be using the list built up so far - will generate it 169 // from downloaded layers instead. 170 rootFS.DiffIDs = []layer.DiffID{} 171 172 defer func() { 173 if topLayer != nil { 174 layer.ReleaseAndLog(ldm.layerStore, topLayer) 175 } 176 }() 177 178 select { 179 case <-ctx.Done(): 180 topDownload.Transfer.Release(watcher) 181 return rootFS, func() {}, ctx.Err() 182 case <-topDownload.Done(): 183 break 184 } 185 186 l, err := topDownload.result() 187 if err != nil { 188 topDownload.Transfer.Release(watcher) 189 return rootFS, func() {}, err 190 } 191 192 // Must do this exactly len(layers) times, so we don't include the 193 // base layer on Windows. 194 for range layers { 195 if l == nil { 196 topDownload.Transfer.Release(watcher) 197 return rootFS, func() {}, errors.New("internal error: too few parent layers") 198 } 199 rootFS.DiffIDs = append([]layer.DiffID{l.DiffID()}, rootFS.DiffIDs...) 200 l = l.Parent() 201 } 202 return rootFS, func() { topDownload.Transfer.Release(watcher) }, err 203 } 204 205 // makeDownloadFunc returns a function that performs the layer download and 206 // registration. If parentDownload is non-nil, it waits for that download to 207 // complete before the registration step, and registers the downloaded data 208 // on top of parentDownload's resulting layer. Otherwise, it registers the 209 // layer on top of the ChainID given by parentLayer. 210 func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer) DoFunc { 211 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 212 d := &downloadTransfer{ 213 Transfer: NewTransfer(), 214 layerStore: ldm.layerStore, 215 } 216 217 go func() { 218 defer func() { 219 close(progressChan) 220 }() 221 222 progressOutput := progress.ChanOutput(progressChan) 223 224 select { 225 case <-start: 226 default: 227 progress.Update(progressOutput, descriptor.ID(), "Waiting") 228 <-start 229 } 230 231 if parentDownload != nil { 232 // Did the parent download already fail or get 233 // cancelled? 234 select { 235 case <-parentDownload.Done(): 236 _, err := parentDownload.result() 237 if err != nil { 238 d.err = err 239 return 240 } 241 default: 242 } 243 } 244 245 var ( 246 downloadReader io.ReadCloser 247 size int64 248 err error 249 retries int 250 ) 251 252 defer descriptor.Close() 253 254 for { 255 downloadReader, size, err = descriptor.Download(d.Transfer.Context(), progressOutput) 256 if err == nil { 257 break 258 } 259 260 // If an error was returned because the context 261 // was cancelled, we shouldn't retry. 262 select { 263 case <-d.Transfer.Context().Done(): 264 d.err = err 265 return 266 default: 267 } 268 269 retries++ 270 if _, isDNR := err.(DoNotRetry); isDNR || retries == maxDownloadAttempts { 271 logrus.Errorf("Download failed: %v", err) 272 d.err = err 273 return 274 } 275 276 logrus.Errorf("Download failed, retrying: %v", err) 277 delay := retries * 5 278 ticker := time.NewTicker(ldm.waitDuration) 279 280 selectLoop: 281 for { 282 progress.Updatef(progressOutput, descriptor.ID(), "Retrying in %d second%s", delay, (map[bool]string{true: "s"})[delay != 1]) 283 select { 284 case <-ticker.C: 285 delay-- 286 if delay == 0 { 287 ticker.Stop() 288 break selectLoop 289 } 290 case <-d.Transfer.Context().Done(): 291 ticker.Stop() 292 d.err = errors.New("download cancelled during retry delay") 293 return 294 } 295 296 } 297 } 298 299 close(inactive) 300 301 if parentDownload != nil { 302 select { 303 case <-d.Transfer.Context().Done(): 304 d.err = errors.New("layer registration cancelled") 305 downloadReader.Close() 306 return 307 case <-parentDownload.Done(): 308 } 309 310 l, err := parentDownload.result() 311 if err != nil { 312 d.err = err 313 downloadReader.Close() 314 return 315 } 316 parentLayer = l.ChainID() 317 } 318 319 reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(d.Transfer.Context(), downloadReader), progressOutput, size, descriptor.ID(), "Extracting") 320 defer reader.Close() 321 322 inflatedLayerData, err := archive.DecompressStream(reader) 323 if err != nil { 324 d.err = fmt.Errorf("could not get decompression stream: %v", err) 325 return 326 } 327 328 var src distribution.Descriptor 329 if fs, ok := descriptor.(distribution.Describable); ok { 330 src = fs.Descriptor() 331 } 332 if ds, ok := d.layerStore.(layer.DescribableStore); ok { 333 d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, src) 334 } else { 335 d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer) 336 } 337 if err != nil { 338 select { 339 case <-d.Transfer.Context().Done(): 340 d.err = errors.New("layer registration cancelled") 341 default: 342 d.err = fmt.Errorf("failed to register layer: %v", err) 343 } 344 return 345 } 346 347 progress.Update(progressOutput, descriptor.ID(), "Pull complete") 348 withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered) 349 if hasRegistered { 350 withRegistered.Registered(d.layer.DiffID()) 351 } 352 353 // Doesn't actually need to be its own goroutine, but 354 // done like this so we can defer close(c). 355 go func() { 356 <-d.Transfer.Released() 357 if d.layer != nil { 358 layer.ReleaseAndLog(d.layerStore, d.layer) 359 } 360 }() 361 }() 362 363 return d 364 } 365 } 366 367 // makeDownloadFuncFromDownload returns a function that performs the layer 368 // registration when the layer data is coming from an existing download. It 369 // waits for sourceDownload and parentDownload to complete, and then 370 // reregisters the data from sourceDownload's top layer on top of 371 // parentDownload. This function does not log progress output because it would 372 // interfere with the progress reporting for sourceDownload, which has the same 373 // Key. 374 func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer) DoFunc { 375 return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { 376 d := &downloadTransfer{ 377 Transfer: NewTransfer(), 378 layerStore: ldm.layerStore, 379 } 380 381 go func() { 382 defer func() { 383 close(progressChan) 384 }() 385 386 <-start 387 388 close(inactive) 389 390 select { 391 case <-d.Transfer.Context().Done(): 392 d.err = errors.New("layer registration cancelled") 393 return 394 case <-parentDownload.Done(): 395 } 396 397 l, err := parentDownload.result() 398 if err != nil { 399 d.err = err 400 return 401 } 402 parentLayer := l.ChainID() 403 404 // sourceDownload should have already finished if 405 // parentDownload finished, but wait for it explicitly 406 // to be sure. 407 select { 408 case <-d.Transfer.Context().Done(): 409 d.err = errors.New("layer registration cancelled") 410 return 411 case <-sourceDownload.Done(): 412 } 413 414 l, err = sourceDownload.result() 415 if err != nil { 416 d.err = err 417 return 418 } 419 420 layerReader, err := l.TarStream() 421 if err != nil { 422 d.err = err 423 return 424 } 425 defer layerReader.Close() 426 427 var src distribution.Descriptor 428 if fs, ok := l.(distribution.Describable); ok { 429 src = fs.Descriptor() 430 } 431 if ds, ok := d.layerStore.(layer.DescribableStore); ok { 432 d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, src) 433 } else { 434 d.layer, err = d.layerStore.Register(layerReader, parentLayer) 435 } 436 if err != nil { 437 d.err = fmt.Errorf("failed to register layer: %v", err) 438 return 439 } 440 441 withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered) 442 if hasRegistered { 443 withRegistered.Registered(d.layer.DiffID()) 444 } 445 446 // Doesn't actually need to be its own goroutine, but 447 // done like this so we can defer close(c). 448 go func() { 449 <-d.Transfer.Released() 450 if d.layer != nil { 451 layer.ReleaseAndLog(d.layerStore, d.layer) 452 } 453 }() 454 }() 455 456 return d 457 } 458 }