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