github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/distribution/xfer/download.go (about)

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