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