github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/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/Sirupsen/logrus" 11 "github.com/docker/docker/distribution/xfer" 12 "github.com/docker/docker/image" 13 "github.com/docker/docker/layer" 14 "github.com/docker/docker/pkg/archive" 15 "github.com/docker/docker/pkg/chrootarchive" 16 "github.com/docker/docker/pkg/progress" 17 "github.com/opencontainers/go-digest" 18 "github.com/pkg/errors" 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, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { 130 for _, l := range layers { 131 b, err := dm.blobStore.New() 132 if err != nil { 133 return initialRootFS, nil, err 134 } 135 defer b.Close() 136 rc, _, err := l.Download(ctx, progressOutput) 137 if err != nil { 138 return initialRootFS, nil, errors.Wrap(err, "failed to download") 139 } 140 defer rc.Close() 141 r := io.TeeReader(rc, b) 142 inflatedLayerData, err := archive.DecompressStream(r) 143 if err != nil { 144 return initialRootFS, nil, err 145 } 146 digester := digest.Canonical.Digester() 147 if _, err := chrootarchive.ApplyLayer(dm.tmpDir, io.TeeReader(inflatedLayerData, digester.Hash())); err != nil { 148 return initialRootFS, nil, err 149 } 150 initialRootFS.Append(layer.DiffID(digester.Digest())) 151 d, err := b.Commit() 152 if err != nil { 153 return initialRootFS, nil, err 154 } 155 dm.blobs = append(dm.blobs, d) 156 } 157 return initialRootFS, nil, nil 158 } 159 160 func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) { 161 b, err := dm.blobStore.New() 162 if err != nil { 163 return "", err 164 } 165 defer b.Close() 166 n, err := b.Write(dt) 167 if err != nil { 168 return "", err 169 } 170 if n != len(dt) { 171 return "", io.ErrShortWrite 172 } 173 d, err := b.Commit() 174 dm.configDigest = d 175 return d, err 176 } 177 178 func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) { 179 return nil, fmt.Errorf("digest not found") 180 } 181 func (dm *downloadManager) RootFSFromConfig(c []byte) (*image.RootFS, error) { 182 return configToRootFS(c) 183 }