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  }