github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/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  }
    63  
    64  // DownloadDescriptorWithRegistered is a DownloadDescriptor that has an
    65  // additional Registered method which gets called after a downloaded layer is
    66  // registered. This allows the user of the download manager to know the DiffID
    67  // of each registered layer. This method is called if a cast to
    68  // DownloadDescriptorWithRegistered is successful.
    69  type DownloadDescriptorWithRegistered interface {
    70  	DownloadDescriptor
    71  	Registered(diffID layer.DiffID)
    72  }
    73  
    74  // Download is a blocking function which ensures the requested layers are
    75  // present in the layer store. It uses the string returned by the Key method to
    76  // deduplicate downloads. If a given layer is not already known to present in
    77  // the layer store, and the key is not used by an in-progress download, the
    78  // Download method is called to get the layer tar data. Layers are then
    79  // registered in the appropriate order.  The caller must call the returned
    80  // release function once it is is done with the returned RootFS object.
    81  func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
    82  	var (
    83  		topLayer       layer.Layer
    84  		topDownload    *downloadTransfer
    85  		watcher        *Watcher
    86  		missingLayer   bool
    87  		transferKey    = ""
    88  		downloadsByKey = make(map[string]*downloadTransfer)
    89  	)
    90  
    91  	rootFS := initialRootFS
    92  	for _, descriptor := range layers {
    93  		key := descriptor.Key()
    94  		transferKey += key
    95  
    96  		if !missingLayer {
    97  			missingLayer = true
    98  			diffID, err := descriptor.DiffID()
    99  			if err == nil {
   100  				getRootFS := rootFS
   101  				getRootFS.Append(diffID)
   102  				l, err := ldm.layerStore.Get(getRootFS.ChainID())
   103  				if err == nil {
   104  					// Layer already exists.
   105  					logrus.Debugf("Layer already exists: %s", descriptor.ID())
   106  					progress.Update(progressOutput, descriptor.ID(), "Already exists")
   107  					if topLayer != nil {
   108  						layer.ReleaseAndLog(ldm.layerStore, topLayer)
   109  					}
   110  					topLayer = l
   111  					missingLayer = false
   112  					rootFS.Append(diffID)
   113  					continue
   114  				}
   115  			}
   116  		}
   117  
   118  		// Does this layer have the same data as a previous layer in
   119  		// the stack? If so, avoid downloading it more than once.
   120  		var topDownloadUncasted Transfer
   121  		if existingDownload, ok := downloadsByKey[key]; ok {
   122  			xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload)
   123  			defer topDownload.Transfer.Release(watcher)
   124  			topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
   125  			topDownload = topDownloadUncasted.(*downloadTransfer)
   126  			continue
   127  		}
   128  
   129  		// Layer is not known to exist - download and register it.
   130  		progress.Update(progressOutput, descriptor.ID(), "Pulling fs layer")
   131  
   132  		var xferFunc DoFunc
   133  		if topDownload != nil {
   134  			xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload)
   135  			defer topDownload.Transfer.Release(watcher)
   136  		} else {
   137  			xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil)
   138  		}
   139  		topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
   140  		topDownload = topDownloadUncasted.(*downloadTransfer)
   141  		downloadsByKey[key] = topDownload
   142  	}
   143  
   144  	if topDownload == nil {
   145  		return rootFS, func() { layer.ReleaseAndLog(ldm.layerStore, topLayer) }, nil
   146  	}
   147  
   148  	// Won't be using the list built up so far - will generate it
   149  	// from downloaded layers instead.
   150  	rootFS.DiffIDs = []layer.DiffID{}
   151  
   152  	defer func() {
   153  		if topLayer != nil {
   154  			layer.ReleaseAndLog(ldm.layerStore, topLayer)
   155  		}
   156  	}()
   157  
   158  	select {
   159  	case <-ctx.Done():
   160  		topDownload.Transfer.Release(watcher)
   161  		return rootFS, func() {}, ctx.Err()
   162  	case <-topDownload.Done():
   163  		break
   164  	}
   165  
   166  	l, err := topDownload.result()
   167  	if err != nil {
   168  		topDownload.Transfer.Release(watcher)
   169  		return rootFS, func() {}, err
   170  	}
   171  
   172  	// Must do this exactly len(layers) times, so we don't include the
   173  	// base layer on Windows.
   174  	for range layers {
   175  		if l == nil {
   176  			topDownload.Transfer.Release(watcher)
   177  			return rootFS, func() {}, errors.New("internal error: too few parent layers")
   178  		}
   179  		rootFS.DiffIDs = append([]layer.DiffID{l.DiffID()}, rootFS.DiffIDs...)
   180  		l = l.Parent()
   181  	}
   182  	return rootFS, func() { topDownload.Transfer.Release(watcher) }, err
   183  }
   184  
   185  // makeDownloadFunc returns a function that performs the layer download and
   186  // registration. If parentDownload is non-nil, it waits for that download to
   187  // complete before the registration step, and registers the downloaded data
   188  // on top of parentDownload's resulting layer. Otherwise, it registers the
   189  // layer on top of the ChainID given by parentLayer.
   190  func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer) DoFunc {
   191  	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
   192  		d := &downloadTransfer{
   193  			Transfer:   NewTransfer(),
   194  			layerStore: ldm.layerStore,
   195  		}
   196  
   197  		go func() {
   198  			defer func() {
   199  				close(progressChan)
   200  			}()
   201  
   202  			progressOutput := progress.ChanOutput(progressChan)
   203  
   204  			select {
   205  			case <-start:
   206  			default:
   207  				progress.Update(progressOutput, descriptor.ID(), "Waiting")
   208  				<-start
   209  			}
   210  
   211  			if parentDownload != nil {
   212  				// Did the parent download already fail or get
   213  				// cancelled?
   214  				select {
   215  				case <-parentDownload.Done():
   216  					_, err := parentDownload.result()
   217  					if err != nil {
   218  						d.err = err
   219  						return
   220  					}
   221  				default:
   222  				}
   223  			}
   224  
   225  			var (
   226  				downloadReader io.ReadCloser
   227  				size           int64
   228  				err            error
   229  				retries        int
   230  			)
   231  
   232  			for {
   233  				downloadReader, size, err = descriptor.Download(d.Transfer.Context(), progressOutput)
   234  				if err == nil {
   235  					break
   236  				}
   237  
   238  				// If an error was returned because the context
   239  				// was cancelled, we shouldn't retry.
   240  				select {
   241  				case <-d.Transfer.Context().Done():
   242  					d.err = err
   243  					return
   244  				default:
   245  				}
   246  
   247  				retries++
   248  				if _, isDNR := err.(DoNotRetry); isDNR || retries == maxDownloadAttempts {
   249  					logrus.Errorf("Download failed: %v", err)
   250  					d.err = err
   251  					return
   252  				}
   253  
   254  				logrus.Errorf("Download failed, retrying: %v", err)
   255  				delay := retries * 5
   256  				ticker := time.NewTicker(time.Second)
   257  
   258  			selectLoop:
   259  				for {
   260  					progress.Updatef(progressOutput, descriptor.ID(), "Retrying in %d seconds", delay)
   261  					select {
   262  					case <-ticker.C:
   263  						delay--
   264  						if delay == 0 {
   265  							ticker.Stop()
   266  							break selectLoop
   267  						}
   268  					case <-d.Transfer.Context().Done():
   269  						ticker.Stop()
   270  						d.err = errors.New("download cancelled during retry delay")
   271  						return
   272  					}
   273  
   274  				}
   275  			}
   276  
   277  			close(inactive)
   278  
   279  			if parentDownload != nil {
   280  				select {
   281  				case <-d.Transfer.Context().Done():
   282  					d.err = errors.New("layer registration cancelled")
   283  					downloadReader.Close()
   284  					return
   285  				case <-parentDownload.Done():
   286  				}
   287  
   288  				l, err := parentDownload.result()
   289  				if err != nil {
   290  					d.err = err
   291  					downloadReader.Close()
   292  					return
   293  				}
   294  				parentLayer = l.ChainID()
   295  			}
   296  
   297  			reader := progress.NewProgressReader(ioutils.NewCancelReadCloser(d.Transfer.Context(), downloadReader), progressOutput, size, descriptor.ID(), "Extracting")
   298  			defer reader.Close()
   299  
   300  			inflatedLayerData, err := archive.DecompressStream(reader)
   301  			if err != nil {
   302  				d.err = fmt.Errorf("could not get decompression stream: %v", err)
   303  				return
   304  			}
   305  
   306  			d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer)
   307  			if err != nil {
   308  				select {
   309  				case <-d.Transfer.Context().Done():
   310  					d.err = errors.New("layer registration cancelled")
   311  				default:
   312  					d.err = fmt.Errorf("failed to register layer: %v", err)
   313  				}
   314  				return
   315  			}
   316  
   317  			progress.Update(progressOutput, descriptor.ID(), "Pull complete")
   318  			withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered)
   319  			if hasRegistered {
   320  				withRegistered.Registered(d.layer.DiffID())
   321  			}
   322  
   323  			// Doesn't actually need to be its own goroutine, but
   324  			// done like this so we can defer close(c).
   325  			go func() {
   326  				<-d.Transfer.Released()
   327  				if d.layer != nil {
   328  					layer.ReleaseAndLog(d.layerStore, d.layer)
   329  				}
   330  			}()
   331  		}()
   332  
   333  		return d
   334  	}
   335  }
   336  
   337  // makeDownloadFuncFromDownload returns a function that performs the layer
   338  // registration when the layer data is coming from an existing download. It
   339  // waits for sourceDownload and parentDownload to complete, and then
   340  // reregisters the data from sourceDownload's top layer on top of
   341  // parentDownload. This function does not log progress output because it would
   342  // interfere with the progress reporting for sourceDownload, which has the same
   343  // Key.
   344  func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer) DoFunc {
   345  	return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
   346  		d := &downloadTransfer{
   347  			Transfer:   NewTransfer(),
   348  			layerStore: ldm.layerStore,
   349  		}
   350  
   351  		go func() {
   352  			defer func() {
   353  				close(progressChan)
   354  			}()
   355  
   356  			<-start
   357  
   358  			close(inactive)
   359  
   360  			select {
   361  			case <-d.Transfer.Context().Done():
   362  				d.err = errors.New("layer registration cancelled")
   363  				return
   364  			case <-parentDownload.Done():
   365  			}
   366  
   367  			l, err := parentDownload.result()
   368  			if err != nil {
   369  				d.err = err
   370  				return
   371  			}
   372  			parentLayer := l.ChainID()
   373  
   374  			// sourceDownload should have already finished if
   375  			// parentDownload finished, but wait for it explicitly
   376  			// to be sure.
   377  			select {
   378  			case <-d.Transfer.Context().Done():
   379  				d.err = errors.New("layer registration cancelled")
   380  				return
   381  			case <-sourceDownload.Done():
   382  			}
   383  
   384  			l, err = sourceDownload.result()
   385  			if err != nil {
   386  				d.err = err
   387  				return
   388  			}
   389  
   390  			layerReader, err := l.TarStream()
   391  			if err != nil {
   392  				d.err = err
   393  				return
   394  			}
   395  			defer layerReader.Close()
   396  
   397  			d.layer, err = d.layerStore.Register(layerReader, parentLayer)
   398  			if err != nil {
   399  				d.err = fmt.Errorf("failed to register layer: %v", err)
   400  				return
   401  			}
   402  
   403  			withRegistered, hasRegistered := descriptor.(DownloadDescriptorWithRegistered)
   404  			if hasRegistered {
   405  				withRegistered.Registered(d.layer.DiffID())
   406  			}
   407  
   408  			// Doesn't actually need to be its own goroutine, but
   409  			// done like this so we can defer close(c).
   410  			go func() {
   411  				<-d.Transfer.Released()
   412  				if d.layer != nil {
   413  					layer.ReleaseAndLog(d.layerStore, d.layer)
   414  				}
   415  			}()
   416  		}()
   417  
   418  		return d
   419  	}
   420  }