github.com/lusis/distribution@v2.0.1+incompatible/registry/client/pull.go (about)

     1  package client
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  
     7  	log "github.com/Sirupsen/logrus"
     8  
     9  	"github.com/docker/distribution/manifest"
    10  )
    11  
    12  // simultaneousLayerPullWindow is the size of the parallel layer pull window.
    13  // A layer may not be pulled until the layer preceeding it by the length of the
    14  // pull window has been successfully pulled.
    15  const simultaneousLayerPullWindow = 4
    16  
    17  // Pull implements a client pull workflow for the image defined by the given
    18  // name and tag pair, using the given ObjectStore for local manifest and layer
    19  // storage
    20  func Pull(c Client, objectStore ObjectStore, name, tag string) error {
    21  	manifest, err := c.GetImageManifest(name, tag)
    22  	if err != nil {
    23  		return err
    24  	}
    25  	log.WithField("manifest", manifest).Info("Pulled manifest")
    26  
    27  	if len(manifest.FSLayers) != len(manifest.History) {
    28  		return fmt.Errorf("Length of history not equal to number of layers")
    29  	}
    30  	if len(manifest.FSLayers) == 0 {
    31  		return fmt.Errorf("Image has no layers")
    32  	}
    33  
    34  	errChans := make([]chan error, len(manifest.FSLayers))
    35  	for i := range manifest.FSLayers {
    36  		errChans[i] = make(chan error)
    37  	}
    38  
    39  	// To avoid leak of goroutines we must notify
    40  	// pullLayer goroutines about a cancelation,
    41  	// otherwise they will lock forever.
    42  	cancelCh := make(chan struct{})
    43  
    44  	// Iterate over each layer in the manifest, simultaneously pulling no more
    45  	// than simultaneousLayerPullWindow layers at a time. If an error is
    46  	// received from a layer pull, we abort the push.
    47  	for i := 0; i < len(manifest.FSLayers)+simultaneousLayerPullWindow; i++ {
    48  		dependentLayer := i - simultaneousLayerPullWindow
    49  		if dependentLayer >= 0 {
    50  			err := <-errChans[dependentLayer]
    51  			if err != nil {
    52  				log.WithField("error", err).Warn("Pull aborted")
    53  				close(cancelCh)
    54  				return err
    55  			}
    56  		}
    57  
    58  		if i < len(manifest.FSLayers) {
    59  			go func(i int) {
    60  				select {
    61  				case errChans[i] <- pullLayer(c, objectStore, name, manifest.FSLayers[i]):
    62  				case <-cancelCh: // no chance to recv until cancelCh's closed
    63  				}
    64  			}(i)
    65  		}
    66  	}
    67  
    68  	err = objectStore.WriteManifest(name, tag, manifest)
    69  	if err != nil {
    70  		log.WithFields(log.Fields{
    71  			"error":    err,
    72  			"manifest": manifest,
    73  		}).Warn("Unable to write image manifest")
    74  		return err
    75  	}
    76  
    77  	return nil
    78  }
    79  
    80  func pullLayer(c Client, objectStore ObjectStore, name string, fsLayer manifest.FSLayer) error {
    81  	log.WithField("layer", fsLayer).Info("Pulling layer")
    82  
    83  	layer, err := objectStore.Layer(fsLayer.BlobSum)
    84  	if err != nil {
    85  		log.WithFields(log.Fields{
    86  			"error": err,
    87  			"layer": fsLayer,
    88  		}).Warn("Unable to write local layer")
    89  		return err
    90  	}
    91  
    92  	layerWriter, err := layer.Writer()
    93  	if err == ErrLayerAlreadyExists {
    94  		log.WithField("layer", fsLayer).Info("Layer already exists")
    95  		return nil
    96  	}
    97  	if err == ErrLayerLocked {
    98  		log.WithField("layer", fsLayer).Info("Layer download in progress, waiting")
    99  		layer.Wait()
   100  		return nil
   101  	}
   102  	if err != nil {
   103  		log.WithFields(log.Fields{
   104  			"error": err,
   105  			"layer": fsLayer,
   106  		}).Warn("Unable to write local layer")
   107  		return err
   108  	}
   109  	defer layerWriter.Close()
   110  
   111  	if layerWriter.CurrentSize() > 0 {
   112  		log.WithFields(log.Fields{
   113  			"layer":       fsLayer,
   114  			"currentSize": layerWriter.CurrentSize(),
   115  			"size":        layerWriter.Size(),
   116  		}).Info("Layer partially downloaded, resuming")
   117  	}
   118  
   119  	layerReader, length, err := c.GetBlob(name, fsLayer.BlobSum, layerWriter.CurrentSize())
   120  	if err != nil {
   121  		log.WithFields(log.Fields{
   122  			"error": err,
   123  			"layer": fsLayer,
   124  		}).Warn("Unable to download layer")
   125  		return err
   126  	}
   127  	defer layerReader.Close()
   128  
   129  	layerWriter.SetSize(layerWriter.CurrentSize() + length)
   130  
   131  	_, err = io.Copy(layerWriter, layerReader)
   132  	if err != nil {
   133  		log.WithFields(log.Fields{
   134  			"error": err,
   135  			"layer": fsLayer,
   136  		}).Warn("Unable to download layer")
   137  		return err
   138  	}
   139  	if layerWriter.CurrentSize() != layerWriter.Size() {
   140  		log.WithFields(log.Fields{
   141  			"size":        layerWriter.Size(),
   142  			"currentSize": layerWriter.CurrentSize(),
   143  			"layer":       fsLayer,
   144  		}).Warn("Layer invalid size")
   145  		return fmt.Errorf(
   146  			"Wrote incorrect number of bytes for layer %v. Expected %d, Wrote %d",
   147  			fsLayer, layerWriter.Size(), layerWriter.CurrentSize(),
   148  		)
   149  	}
   150  	return nil
   151  }