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  }