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 }