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