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