github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/distribution/xfer/download.go (about)

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