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