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