github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/builder/builder-next/adapters/snapshot/snapshot.go (about)

     1  package snapshot
     2  
     3  import (
     4  	"context"
     5  	"path/filepath"
     6  	"strconv"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/containerd/containerd/mount"
    11  	"github.com/containerd/containerd/snapshots"
    12  	"github.com/docker/docker/daemon/graphdriver"
    13  	"github.com/docker/docker/layer"
    14  	"github.com/docker/docker/pkg/idtools"
    15  	"github.com/moby/buildkit/identity"
    16  	"github.com/moby/buildkit/snapshot"
    17  	digest "github.com/opencontainers/go-digest"
    18  	"github.com/pkg/errors"
    19  	bolt "go.etcd.io/bbolt"
    20  )
    21  
    22  var keyParent = []byte("parent")
    23  var keyCommitted = []byte("committed")
    24  var keyChainID = []byte("chainid")
    25  var keySize = []byte("size")
    26  
    27  // Opt defines options for creating the snapshotter
    28  type Opt struct {
    29  	GraphDriver     graphdriver.Driver
    30  	LayerStore      layer.Store
    31  	Root            string
    32  	IdentityMapping *idtools.IdentityMapping
    33  }
    34  
    35  type graphIDRegistrar interface {
    36  	RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error)
    37  	Release(layer.Layer) ([]layer.Metadata, error)
    38  	checksumCalculator
    39  }
    40  
    41  type checksumCalculator interface {
    42  	ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error)
    43  }
    44  
    45  type snapshotter struct {
    46  	opt Opt
    47  
    48  	refs map[string]layer.Layer
    49  	db   *bolt.DB
    50  	mu   sync.Mutex
    51  	reg  graphIDRegistrar
    52  }
    53  
    54  var _ snapshot.SnapshotterBase = &snapshotter{}
    55  
    56  // NewSnapshotter creates a new snapshotter
    57  func NewSnapshotter(opt Opt) (snapshot.SnapshotterBase, error) {
    58  	dbPath := filepath.Join(opt.Root, "snapshots.db")
    59  	db, err := bolt.Open(dbPath, 0600, nil)
    60  	if err != nil {
    61  		return nil, errors.Wrapf(err, "failed to open database file %s", dbPath)
    62  	}
    63  
    64  	reg, ok := opt.LayerStore.(graphIDRegistrar)
    65  	if !ok {
    66  		return nil, errors.Errorf("layerstore doesn't support graphID registration")
    67  	}
    68  
    69  	s := &snapshotter{
    70  		opt:  opt,
    71  		db:   db,
    72  		refs: map[string]layer.Layer{},
    73  		reg:  reg,
    74  	}
    75  	return s, nil
    76  }
    77  
    78  func (s *snapshotter) Name() string {
    79  	return "default"
    80  }
    81  
    82  func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping {
    83  	return s.opt.IdentityMapping
    84  }
    85  
    86  func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error {
    87  	origParent := parent
    88  	if parent != "" {
    89  		if l, err := s.getLayer(parent, false); err != nil {
    90  			return err
    91  		} else if l != nil {
    92  			parent, err = getGraphID(l)
    93  			if err != nil {
    94  				return err
    95  			}
    96  		} else {
    97  			parent, _ = s.getGraphDriverID(parent)
    98  		}
    99  	}
   100  	if err := s.opt.GraphDriver.Create(key, parent, nil); err != nil {
   101  		return err
   102  	}
   103  	return s.db.Update(func(tx *bolt.Tx) error {
   104  		b, err := tx.CreateBucketIfNotExists([]byte(key))
   105  		if err != nil {
   106  			return err
   107  		}
   108  		return b.Put(keyParent, []byte(origParent))
   109  	})
   110  }
   111  
   112  func (s *snapshotter) chainID(key string) (layer.ChainID, bool) {
   113  	if strings.HasPrefix(key, "sha256:") {
   114  		dgst, err := digest.Parse(key)
   115  		if err != nil {
   116  			return "", false
   117  		}
   118  		return layer.ChainID(dgst), true
   119  	}
   120  	return "", false
   121  }
   122  
   123  func (s *snapshotter) GetLayer(key string) (layer.Layer, error) {
   124  	return s.getLayer(key, true)
   125  }
   126  
   127  func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) {
   128  	s.mu.Lock()
   129  	l, ok := s.refs[key]
   130  	if !ok {
   131  		id, ok := s.chainID(key)
   132  		if !ok {
   133  			if !withCommitted {
   134  				s.mu.Unlock()
   135  				return nil, nil
   136  			}
   137  			if err := s.db.View(func(tx *bolt.Tx) error {
   138  				b := tx.Bucket([]byte(key))
   139  				if b == nil {
   140  					return nil
   141  				}
   142  				v := b.Get(keyChainID)
   143  				if v != nil {
   144  					id = layer.ChainID(v)
   145  				}
   146  				return nil
   147  			}); err != nil {
   148  				s.mu.Unlock()
   149  				return nil, err
   150  			}
   151  			if id == "" {
   152  				s.mu.Unlock()
   153  				return nil, nil
   154  			}
   155  		}
   156  		var err error
   157  		l, err = s.opt.LayerStore.Get(id)
   158  		if err != nil {
   159  			s.mu.Unlock()
   160  			return nil, err
   161  		}
   162  		s.refs[key] = l
   163  		if err := s.db.Update(func(tx *bolt.Tx) error {
   164  			_, err := tx.CreateBucketIfNotExists([]byte(key))
   165  			return err
   166  		}); err != nil {
   167  			s.mu.Unlock()
   168  			return nil, err
   169  		}
   170  	}
   171  	s.mu.Unlock()
   172  
   173  	return l, nil
   174  }
   175  
   176  func (s *snapshotter) getGraphDriverID(key string) (string, bool) {
   177  	var gdID string
   178  	if err := s.db.View(func(tx *bolt.Tx) error {
   179  		b := tx.Bucket([]byte(key))
   180  		if b == nil {
   181  			return errors.Errorf("not found") // TODO: typed
   182  		}
   183  		v := b.Get(keyCommitted)
   184  		if v != nil {
   185  			gdID = string(v)
   186  		}
   187  		return nil
   188  	}); err != nil || gdID == "" {
   189  		return key, false
   190  	}
   191  	return gdID, true
   192  }
   193  
   194  func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
   195  	inf := snapshots.Info{
   196  		Kind: snapshots.KindActive,
   197  	}
   198  
   199  	l, err := s.getLayer(key, false)
   200  	if err != nil {
   201  		return snapshots.Info{}, err
   202  	}
   203  	if l != nil {
   204  		if p := l.Parent(); p != nil {
   205  			inf.Parent = p.ChainID().String()
   206  		}
   207  		inf.Kind = snapshots.KindCommitted
   208  		inf.Name = key
   209  		return inf, nil
   210  	}
   211  
   212  	l, err = s.getLayer(key, true)
   213  	if err != nil {
   214  		return snapshots.Info{}, err
   215  	}
   216  
   217  	id, committed := s.getGraphDriverID(key)
   218  	if committed {
   219  		inf.Kind = snapshots.KindCommitted
   220  	}
   221  
   222  	if err := s.db.View(func(tx *bolt.Tx) error {
   223  		b := tx.Bucket([]byte(id))
   224  		if b == nil && l == nil {
   225  			return errors.Errorf("snapshot %s not found", id) // TODO: typed
   226  		}
   227  		inf.Name = key
   228  		if b != nil {
   229  			v := b.Get(keyParent)
   230  			if v != nil {
   231  				inf.Parent = string(v)
   232  				return nil
   233  			}
   234  		}
   235  		if l != nil {
   236  			if p := l.Parent(); p != nil {
   237  				inf.Parent = p.ChainID().String()
   238  			}
   239  			inf.Kind = snapshots.KindCommitted
   240  		}
   241  		return nil
   242  	}); err != nil {
   243  		return snapshots.Info{}, err
   244  	}
   245  	return inf, nil
   246  }
   247  
   248  func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountable, error) {
   249  	l, err := s.getLayer(key, true)
   250  	if err != nil {
   251  		return nil, err
   252  	}
   253  	if l != nil {
   254  		id := identity.NewID()
   255  		var rwlayer layer.RWLayer
   256  		return &mountable{
   257  			idmap: s.opt.IdentityMapping,
   258  			acquire: func() ([]mount.Mount, func() error, error) {
   259  				rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil)
   260  				if err != nil {
   261  					return nil, nil, err
   262  				}
   263  				rootfs, err := rwlayer.Mount("")
   264  				if err != nil {
   265  					return nil, nil, err
   266  				}
   267  				return []mount.Mount{{
   268  						Source:  rootfs.Path(),
   269  						Type:    "bind",
   270  						Options: []string{"rbind"},
   271  					}}, func() error {
   272  						_, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer)
   273  						return err
   274  					}, nil
   275  			},
   276  		}, nil
   277  	}
   278  
   279  	id, _ := s.getGraphDriverID(key)
   280  
   281  	return &mountable{
   282  		idmap: s.opt.IdentityMapping,
   283  		acquire: func() ([]mount.Mount, func() error, error) {
   284  			rootfs, err := s.opt.GraphDriver.Get(id, "")
   285  			if err != nil {
   286  				return nil, nil, err
   287  			}
   288  			return []mount.Mount{{
   289  					Source:  rootfs.Path(),
   290  					Type:    "bind",
   291  					Options: []string{"rbind"},
   292  				}}, func() error {
   293  					return s.opt.GraphDriver.Put(id)
   294  				}, nil
   295  		},
   296  	}, nil
   297  }
   298  
   299  func (s *snapshotter) Remove(ctx context.Context, key string) error {
   300  	l, err := s.getLayer(key, true)
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	id, _ := s.getGraphDriverID(key)
   306  
   307  	var found bool
   308  	if err := s.db.Update(func(tx *bolt.Tx) error {
   309  		found = tx.Bucket([]byte(key)) != nil
   310  		if found {
   311  			tx.DeleteBucket([]byte(key))
   312  			if id != key {
   313  				tx.DeleteBucket([]byte(id))
   314  			}
   315  		}
   316  		return nil
   317  	}); err != nil {
   318  		return err
   319  	}
   320  
   321  	if l != nil {
   322  		s.mu.Lock()
   323  		delete(s.refs, key)
   324  		s.mu.Unlock()
   325  		_, err := s.opt.LayerStore.Release(l)
   326  		return err
   327  	}
   328  
   329  	if !found { // this happens when removing views
   330  		return nil
   331  	}
   332  
   333  	return s.opt.GraphDriver.Remove(id)
   334  }
   335  
   336  func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
   337  	return s.db.Update(func(tx *bolt.Tx) error {
   338  		b, err := tx.CreateBucketIfNotExists([]byte(name))
   339  		if err != nil {
   340  			return err
   341  		}
   342  		return b.Put(keyCommitted, []byte(key))
   343  	})
   344  }
   345  
   346  func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (snapshot.Mountable, error) {
   347  	return s.Mounts(ctx, parent)
   348  }
   349  
   350  func (s *snapshotter) Walk(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
   351  	return errors.Errorf("not-implemented")
   352  }
   353  
   354  func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
   355  	// not implemented
   356  	return s.Stat(ctx, info.Name)
   357  }
   358  
   359  func (s *snapshotter) Usage(ctx context.Context, key string) (us snapshots.Usage, retErr error) {
   360  	usage := snapshots.Usage{}
   361  	if l, err := s.getLayer(key, true); err != nil {
   362  		return usage, err
   363  	} else if l != nil {
   364  		s, err := l.DiffSize()
   365  		if err != nil {
   366  			return usage, err
   367  		}
   368  		usage.Size = s
   369  		return usage, nil
   370  	}
   371  
   372  	size := int64(-1)
   373  	if err := s.db.View(func(tx *bolt.Tx) error {
   374  		b := tx.Bucket([]byte(key))
   375  		if b == nil {
   376  			return nil
   377  		}
   378  		v := b.Get(keySize)
   379  		if v != nil {
   380  			s, err := strconv.Atoi(string(v))
   381  			if err != nil {
   382  				return err
   383  			}
   384  			size = int64(s)
   385  		}
   386  		return nil
   387  	}); err != nil {
   388  		return usage, err
   389  	}
   390  
   391  	if size != -1 {
   392  		usage.Size = size
   393  		return usage, nil
   394  	}
   395  
   396  	id, _ := s.getGraphDriverID(key)
   397  
   398  	info, err := s.Stat(ctx, key)
   399  	if err != nil {
   400  		return usage, err
   401  	}
   402  	var parent string
   403  	if info.Parent != "" {
   404  		if l, err := s.getLayer(info.Parent, false); err != nil {
   405  			return usage, err
   406  		} else if l != nil {
   407  			parent, err = getGraphID(l)
   408  			if err != nil {
   409  				return usage, err
   410  			}
   411  		} else {
   412  			parent, _ = s.getGraphDriverID(info.Parent)
   413  		}
   414  	}
   415  
   416  	diffSize, err := s.opt.GraphDriver.DiffSize(id, parent)
   417  	if err != nil {
   418  		return usage, err
   419  	}
   420  
   421  	if err := s.db.Update(func(tx *bolt.Tx) error {
   422  		b, err := tx.CreateBucketIfNotExists([]byte(key))
   423  		if err != nil {
   424  			return err
   425  		}
   426  		return b.Put(keySize, []byte(strconv.Itoa(int(diffSize))))
   427  	}); err != nil {
   428  		return usage, err
   429  	}
   430  	usage.Size = diffSize
   431  	return usage, nil
   432  }
   433  
   434  func (s *snapshotter) Close() error {
   435  	return s.db.Close()
   436  }
   437  
   438  type mountable struct {
   439  	mu       sync.Mutex
   440  	mounts   []mount.Mount
   441  	acquire  func() ([]mount.Mount, func() error, error)
   442  	release  func() error
   443  	refCount int
   444  	idmap    *idtools.IdentityMapping
   445  }
   446  
   447  func (m *mountable) Mount() ([]mount.Mount, func() error, error) {
   448  	m.mu.Lock()
   449  	defer m.mu.Unlock()
   450  
   451  	if m.mounts != nil {
   452  		m.refCount++
   453  		return m.mounts, m.releaseMount, nil
   454  	}
   455  
   456  	mounts, release, err := m.acquire()
   457  	if err != nil {
   458  		return nil, nil, err
   459  	}
   460  	m.mounts = mounts
   461  	m.release = release
   462  	m.refCount = 1
   463  
   464  	return m.mounts, m.releaseMount, nil
   465  }
   466  
   467  func (m *mountable) releaseMount() error {
   468  	m.mu.Lock()
   469  	defer m.mu.Unlock()
   470  
   471  	if m.refCount > 1 {
   472  		m.refCount--
   473  		return nil
   474  	}
   475  
   476  	m.refCount = 0
   477  	if m.release == nil {
   478  		return nil
   479  	}
   480  
   481  	m.mounts = nil
   482  	defer func() {
   483  		m.release = nil
   484  	}()
   485  	return m.release()
   486  }
   487  
   488  func (m *mountable) IdentityMapping() *idtools.IdentityMapping {
   489  	return m.idmap
   490  }