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