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