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  }