github.com/kunnos/engine@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  }