// makeDownloadFuncFromDownload returns a func used by the TransferManager to // handle a layer that was already seen in this image pull, or is currently being downloaded func (ldm *LayerDownloader) makeDownloadFuncFromDownload(layer *ImageWithMeta, sourceDownload, parentDownload *downloadTransfer, layers []*ImageWithMeta) xfer.DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) xfer.Transfer { d := &downloadTransfer{ Transfer: xfer.NewTransfer(), layer: layer, } go func() { defer close(progressChan) <-start close(inactive) if parentDownload != nil { select { case <-d.Transfer.Context().Done(): d.err = errors.New("layer download cancelled") return case <-parentDownload.Done(): // wait for parent layer download to complete } if err := parentDownload.result(); err != nil { d.err = err return } } // sourceDownload should have already finished if // parentDownload finished, but wait for it explicitly // to be sure. select { case <-d.Transfer.Context().Done(): d.err = errors.New("layer download cancelled") return case <-sourceDownload.Done(): } if err := sourceDownload.result(); err != nil { d.err = err return } }() return d } }
// makeDownloadFunc returns a func used by xfer.TransferManager to download a layer func (ldm *LayerDownloader) makeDownloadFunc(layer *ImageWithMeta, ic *ImageC, parentDownload *downloadTransfer, layers []*ImageWithMeta) xfer.DoFunc { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) xfer.Transfer { d := &downloadTransfer{ Transfer: xfer.NewTransfer(), layer: layer, } go func() { defer func() { close(progressChan) // remove layer from cache if there was an error attempting to download if d.err != nil { LayerCache().Remove(layer.ID) } }() progressOutput := progress.ChanOutput(progressChan) // wait for TransferManager to give the go-ahead select { case <-start: default: progress.Update(progressOutput, layer.String(), "Waiting") <-start } if parentDownload != nil { // bail if parent download failed or was cancelled select { case <-parentDownload.Done(): if err := parentDownload.result(); err != nil { d.err = err return } default: } } // fetch blob diffID, err := FetchImageBlob(d.Transfer.Context(), ic.Options, layer, progressOutput) if err != nil { d.err = fmt.Errorf("%s/%s returned %s", ic.Image, layer.ID, err) return } layer.DiffID = diffID close(inactive) if parentDownload != nil { select { case <-d.Transfer.Context().Done(): d.err = errors.New("layer download cancelled") return default: <-parentDownload.Done() // block until parent download completes } if err := parentDownload.result(); err != nil { d.err = err return } } // is this the leaf layer? imageLayer := layer.ID == layers[0].ID // if this is the leaf layer, we are done and can now create the image config if imageLayer { imageConfig, err := ic.CreateImageConfig(layers) if err != nil { d.err = err return } // cache and persist the image cache.ImageCache().Add(&imageConfig) cache.ImageCache().Save() // place calculated ImageID in struct ic.ImageID = imageConfig.ImageID if err = updateRepositoryCache(ic); err != nil { d.err = err return } } ldm.m.Lock() defer ldm.m.Unlock() // Write blob to the storage layer if err := ic.WriteImageBlob(layer, progressOutput, imageLayer); err != nil { d.err = err return } // mark the layer as finished downloading LayerCache().Commit(layer) ldm.unregisterDownload(layer) }() return d } }