github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/plugin/blobstore.go (about) 1 package plugin 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 10 "github.com/docker/docker/distribution/xfer" 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/chrootarchive" 15 "github.com/docker/docker/pkg/progress" 16 "github.com/opencontainers/go-digest" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 "golang.org/x/net/context" 20 ) 21 22 type blobstore interface { 23 New() (WriteCommitCloser, error) 24 Get(dgst digest.Digest) (io.ReadCloser, error) 25 Size(dgst digest.Digest) (int64, error) 26 } 27 28 type basicBlobStore struct { 29 path string 30 } 31 32 func newBasicBlobStore(p string) (*basicBlobStore, error) { 33 tmpdir := filepath.Join(p, "tmp") 34 if err := os.MkdirAll(tmpdir, 0700); err != nil { 35 return nil, errors.Wrapf(err, "failed to mkdir %v", p) 36 } 37 return &basicBlobStore{path: p}, nil 38 } 39 40 func (b *basicBlobStore) New() (WriteCommitCloser, error) { 41 f, err := ioutil.TempFile(filepath.Join(b.path, "tmp"), ".insertion") 42 if err != nil { 43 return nil, errors.Wrap(err, "failed to create temp file") 44 } 45 return newInsertion(f), nil 46 } 47 48 func (b *basicBlobStore) Get(dgst digest.Digest) (io.ReadCloser, error) { 49 return os.Open(filepath.Join(b.path, string(dgst.Algorithm()), dgst.Hex())) 50 } 51 52 func (b *basicBlobStore) Size(dgst digest.Digest) (int64, error) { 53 stat, err := os.Stat(filepath.Join(b.path, string(dgst.Algorithm()), dgst.Hex())) 54 if err != nil { 55 return 0, err 56 } 57 return stat.Size(), nil 58 } 59 60 func (b *basicBlobStore) gc(whitelist map[digest.Digest]struct{}) { 61 for _, alg := range []string{string(digest.Canonical)} { 62 items, err := ioutil.ReadDir(filepath.Join(b.path, alg)) 63 if err != nil { 64 continue 65 } 66 for _, fi := range items { 67 if _, exists := whitelist[digest.Digest(alg+":"+fi.Name())]; !exists { 68 p := filepath.Join(b.path, alg, fi.Name()) 69 err := os.RemoveAll(p) 70 logrus.Debugf("cleaned up blob %v: %v", p, err) 71 } 72 } 73 } 74 75 } 76 77 // WriteCommitCloser defines object that can be committed to blobstore. 78 type WriteCommitCloser interface { 79 io.WriteCloser 80 Commit() (digest.Digest, error) 81 } 82 83 type insertion struct { 84 io.Writer 85 f *os.File 86 digester digest.Digester 87 closed bool 88 } 89 90 func newInsertion(tempFile *os.File) *insertion { 91 digester := digest.Canonical.Digester() 92 return &insertion{f: tempFile, digester: digester, Writer: io.MultiWriter(tempFile, digester.Hash())} 93 } 94 95 func (i *insertion) Commit() (digest.Digest, error) { 96 p := i.f.Name() 97 d := filepath.Join(filepath.Join(p, "../../")) 98 i.f.Sync() 99 defer os.RemoveAll(p) 100 if err := i.f.Close(); err != nil { 101 return "", err 102 } 103 i.closed = true 104 dgst := i.digester.Digest() 105 if err := os.MkdirAll(filepath.Join(d, string(dgst.Algorithm())), 0700); err != nil { 106 return "", errors.Wrapf(err, "failed to mkdir %v", d) 107 } 108 if err := os.Rename(p, filepath.Join(d, string(dgst.Algorithm()), dgst.Hex())); err != nil { 109 return "", errors.Wrapf(err, "failed to rename %v", p) 110 } 111 return dgst, nil 112 } 113 114 func (i *insertion) Close() error { 115 if i.closed { 116 return nil 117 } 118 defer os.RemoveAll(i.f.Name()) 119 return i.f.Close() 120 } 121 122 type downloadManager struct { 123 blobStore blobstore 124 tmpDir string 125 blobs []digest.Digest 126 configDigest digest.Digest 127 } 128 129 func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { 130 // TODO @jhowardmsft LCOW: May need revisiting. 131 for _, l := range layers { 132 b, err := dm.blobStore.New() 133 if err != nil { 134 return initialRootFS, nil, err 135 } 136 defer b.Close() 137 rc, _, err := l.Download(ctx, progressOutput) 138 if err != nil { 139 return initialRootFS, nil, errors.Wrap(err, "failed to download") 140 } 141 defer rc.Close() 142 r := io.TeeReader(rc, b) 143 inflatedLayerData, err := archive.DecompressStream(r) 144 if err != nil { 145 return initialRootFS, nil, err 146 } 147 digester := digest.Canonical.Digester() 148 if _, err := chrootarchive.ApplyLayer(dm.tmpDir, io.TeeReader(inflatedLayerData, digester.Hash())); err != nil { 149 return initialRootFS, nil, err 150 } 151 initialRootFS.Append(layer.DiffID(digester.Digest())) 152 d, err := b.Commit() 153 if err != nil { 154 return initialRootFS, nil, err 155 } 156 dm.blobs = append(dm.blobs, d) 157 } 158 return initialRootFS, nil, nil 159 } 160 161 func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) { 162 b, err := dm.blobStore.New() 163 if err != nil { 164 return "", err 165 } 166 defer b.Close() 167 n, err := b.Write(dt) 168 if err != nil { 169 return "", err 170 } 171 if n != len(dt) { 172 return "", io.ErrShortWrite 173 } 174 d, err := b.Commit() 175 dm.configDigest = d 176 return d, err 177 } 178 179 func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) { 180 return nil, fmt.Errorf("digest not found") 181 } 182 func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) { 183 return configToRootFS(c) 184 }