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