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