github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/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/leases"
    11  	"github.com/containerd/containerd/mount"
    12  	"github.com/containerd/containerd/snapshots"
    13  	"github.com/demonoid81/moby/daemon/graphdriver"
    14  	"github.com/demonoid81/moby/layer"
    15  	"github.com/demonoid81/moby/pkg/idtools"
    16  	"github.com/moby/buildkit/identity"
    17  	"github.com/moby/buildkit/snapshot"
    18  	digest "github.com/opencontainers/go-digest"
    19  	"github.com/pkg/errors"
    20  	bolt "go.etcd.io/bbolt"
    21  )
    22  
    23  var keyParent = []byte("parent")
    24  var keyCommitted = []byte("committed")
    25  var keyIsCommitted = []byte("iscommitted")
    26  var keyChainID = []byte("chainid")
    27  var keySize = []byte("size")
    28  
    29  // Opt defines options for creating the snapshotter
    30  type Opt struct {
    31  	GraphDriver     graphdriver.Driver
    32  	LayerStore      layer.Store
    33  	Root            string
    34  	IdentityMapping *idtools.IdentityMapping
    35  }
    36  
    37  type graphIDRegistrar interface {
    38  	RegisterByGraphID(string, layer.ChainID, layer.DiffID, string, int64) (layer.Layer, error)
    39  	Release(layer.Layer) ([]layer.Metadata, error)
    40  	checksumCalculator
    41  }
    42  
    43  type checksumCalculator interface {
    44  	ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID layer.DiffID, size int64, err error)
    45  }
    46  
    47  type snapshotter struct {
    48  	opt Opt
    49  
    50  	refs map[string]layer.Layer
    51  	db   *bolt.DB
    52  	mu   sync.Mutex
    53  	reg  graphIDRegistrar
    54  }
    55  
    56  // NewSnapshotter creates a new snapshotter
    57  func NewSnapshotter(opt Opt, prevLM leases.Manager) (snapshot.Snapshotter, leases.Manager, error) {
    58  	dbPath := filepath.Join(opt.Root, "snapshots.db")
    59  	db, err := bolt.Open(dbPath, 0600, nil)
    60  	if err != nil {
    61  		return nil, 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, 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  
    76  	lm := newLeaseManager(s, prevLM)
    77  
    78  	ll, err := lm.List(context.TODO())
    79  	if err != nil {
    80  		return nil, nil, err
    81  	}
    82  	for _, l := range ll {
    83  		rr, err := lm.ListResources(context.TODO(), l)
    84  		if err != nil {
    85  			return nil, nil, err
    86  		}
    87  		for _, r := range rr {
    88  			if r.Type == "snapshots/default" {
    89  				lm.addRef(l.ID, r.ID)
    90  			}
    91  		}
    92  	}
    93  
    94  	return s, lm, nil
    95  }
    96  
    97  func (s *snapshotter) Name() string {
    98  	return "default"
    99  }
   100  
   101  func (s *snapshotter) IdentityMapping() *idtools.IdentityMapping {
   102  	return s.opt.IdentityMapping
   103  }
   104  
   105  func (s *snapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) error {
   106  	origParent := parent
   107  	if parent != "" {
   108  		if l, err := s.getLayer(parent, false); err != nil {
   109  			return errors.Wrapf(err, "failed to get parent layer %s", parent)
   110  		} else if l != nil {
   111  			parent, err = getGraphID(l)
   112  			if err != nil {
   113  				return errors.Wrapf(err, "failed to get parent graphid %s", l.ChainID())
   114  			}
   115  		} else {
   116  			parent, _ = s.getGraphDriverID(parent)
   117  		}
   118  	}
   119  	if err := s.opt.GraphDriver.Create(key, parent, nil); err != nil {
   120  		return err
   121  	}
   122  	return s.db.Update(func(tx *bolt.Tx) error {
   123  		b, err := tx.CreateBucketIfNotExists([]byte(key))
   124  		if err != nil {
   125  			return err
   126  		}
   127  		return b.Put(keyParent, []byte(origParent))
   128  	})
   129  }
   130  
   131  func (s *snapshotter) chainID(key string) (layer.ChainID, bool) {
   132  	if strings.HasPrefix(key, "sha256:") {
   133  		dgst, err := digest.Parse(key)
   134  		if err != nil {
   135  			return "", false
   136  		}
   137  		return layer.ChainID(dgst), true
   138  	}
   139  	return "", false
   140  }
   141  
   142  func (s *snapshotter) GetLayer(key string) (layer.Layer, error) {
   143  	return s.getLayer(key, true)
   144  }
   145  
   146  func (s *snapshotter) getLayer(key string, withCommitted bool) (layer.Layer, error) {
   147  	s.mu.Lock()
   148  	l, ok := s.refs[key]
   149  	if !ok {
   150  		id, ok := s.chainID(key)
   151  		if !ok {
   152  			if !withCommitted {
   153  				s.mu.Unlock()
   154  				return nil, nil
   155  			}
   156  			if err := s.db.View(func(tx *bolt.Tx) error {
   157  				b := tx.Bucket([]byte(key))
   158  				if b == nil {
   159  					return nil
   160  				}
   161  				v := b.Get(keyChainID)
   162  				if v != nil {
   163  					id = layer.ChainID(v)
   164  				}
   165  				return nil
   166  			}); err != nil {
   167  				s.mu.Unlock()
   168  				return nil, errors.WithStack(err)
   169  			}
   170  			if id == "" {
   171  				s.mu.Unlock()
   172  				return nil, nil
   173  			}
   174  		}
   175  		var err error
   176  		l, err = s.opt.LayerStore.Get(id)
   177  		if err != nil {
   178  			s.mu.Unlock()
   179  			return nil, errors.WithStack(err)
   180  		}
   181  		s.refs[key] = l
   182  		if err := s.db.Update(func(tx *bolt.Tx) error {
   183  			_, err := tx.CreateBucketIfNotExists([]byte(key))
   184  			return errors.WithStack(err)
   185  		}); err != nil {
   186  			s.mu.Unlock()
   187  			return nil, err
   188  		}
   189  	}
   190  	s.mu.Unlock()
   191  
   192  	return l, nil
   193  }
   194  
   195  func (s *snapshotter) getGraphDriverID(key string) (string, bool) {
   196  	var gdID string
   197  	if err := s.db.View(func(tx *bolt.Tx) error {
   198  		b := tx.Bucket([]byte(key))
   199  		if b == nil {
   200  			return errors.Errorf("not found") // TODO: typed
   201  		}
   202  		v := b.Get(keyCommitted)
   203  		if v != nil {
   204  			gdID = string(v)
   205  		}
   206  		return nil
   207  	}); err != nil || gdID == "" {
   208  		return key, false
   209  	}
   210  	return gdID, true
   211  }
   212  
   213  func (s *snapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) {
   214  	inf := snapshots.Info{
   215  		Kind: snapshots.KindActive,
   216  	}
   217  
   218  	l, err := s.getLayer(key, false)
   219  	if err != nil {
   220  		return snapshots.Info{}, err
   221  	}
   222  	if l != nil {
   223  		if p := l.Parent(); p != nil {
   224  			inf.Parent = p.ChainID().String()
   225  		}
   226  		inf.Kind = snapshots.KindCommitted
   227  		inf.Name = key
   228  		return inf, nil
   229  	}
   230  
   231  	l, err = s.getLayer(key, true)
   232  	if err != nil {
   233  		return snapshots.Info{}, err
   234  	}
   235  
   236  	id, committed := s.getGraphDriverID(key)
   237  	if committed {
   238  		inf.Kind = snapshots.KindCommitted
   239  	}
   240  
   241  	if err := s.db.View(func(tx *bolt.Tx) error {
   242  		b := tx.Bucket([]byte(id))
   243  		if b == nil && l == nil {
   244  			return errors.Errorf("snapshot %s not found", id) // TODO: typed
   245  		}
   246  		inf.Name = key
   247  		if b != nil {
   248  			v := b.Get(keyParent)
   249  			if v != nil {
   250  				inf.Parent = string(v)
   251  				return nil
   252  			}
   253  		}
   254  		if l != nil {
   255  			if p := l.Parent(); p != nil {
   256  				inf.Parent = p.ChainID().String()
   257  			}
   258  			inf.Kind = snapshots.KindCommitted
   259  		}
   260  		return nil
   261  	}); err != nil {
   262  		return snapshots.Info{}, err
   263  	}
   264  	return inf, nil
   265  }
   266  
   267  func (s *snapshotter) Mounts(ctx context.Context, key string) (snapshot.Mountable, error) {
   268  	l, err := s.getLayer(key, true)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  	if l != nil {
   273  		id := identity.NewID()
   274  		var rwlayer layer.RWLayer
   275  		return &mountable{
   276  			idmap: s.opt.IdentityMapping,
   277  			acquire: func() ([]mount.Mount, func() error, error) {
   278  				rwlayer, err = s.opt.LayerStore.CreateRWLayer(id, l.ChainID(), nil)
   279  				if err != nil {
   280  					return nil, nil, err
   281  				}
   282  				rootfs, err := rwlayer.Mount("")
   283  				if err != nil {
   284  					return nil, nil, err
   285  				}
   286  				return []mount.Mount{{
   287  						Source:  rootfs.Path(),
   288  						Type:    "bind",
   289  						Options: []string{"rbind"},
   290  					}}, func() error {
   291  						_, err := s.opt.LayerStore.ReleaseRWLayer(rwlayer)
   292  						return err
   293  					}, nil
   294  			},
   295  		}, nil
   296  	}
   297  
   298  	id, _ := s.getGraphDriverID(key)
   299  
   300  	return &mountable{
   301  		idmap: s.opt.IdentityMapping,
   302  		acquire: func() ([]mount.Mount, func() error, error) {
   303  			rootfs, err := s.opt.GraphDriver.Get(id, "")
   304  			if err != nil {
   305  				return nil, nil, err
   306  			}
   307  			return []mount.Mount{{
   308  					Source:  rootfs.Path(),
   309  					Type:    "bind",
   310  					Options: []string{"rbind"},
   311  				}}, func() error {
   312  					return s.opt.GraphDriver.Put(id)
   313  				}, nil
   314  		},
   315  	}, nil
   316  }
   317  
   318  func (s *snapshotter) Remove(ctx context.Context, key string) error {
   319  	return errors.Errorf("calling snapshot.remove is forbidden")
   320  }
   321  
   322  func (s *snapshotter) remove(ctx context.Context, key string) error {
   323  	l, err := s.getLayer(key, true)
   324  	if err != nil {
   325  		return err
   326  	}
   327  
   328  	id, _ := s.getGraphDriverID(key)
   329  
   330  	var found bool
   331  	var alreadyCommitted bool
   332  	if err := s.db.Update(func(tx *bolt.Tx) error {
   333  		b := tx.Bucket([]byte(key))
   334  		found = b != nil
   335  
   336  		if b != nil {
   337  			if b.Get(keyIsCommitted) != nil {
   338  				alreadyCommitted = true
   339  				return nil
   340  			}
   341  		}
   342  		if found {
   343  			tx.DeleteBucket([]byte(key))
   344  			if id != key {
   345  				tx.DeleteBucket([]byte(id))
   346  			}
   347  		}
   348  		return nil
   349  	}); err != nil {
   350  		return err
   351  	}
   352  
   353  	if alreadyCommitted {
   354  		return nil
   355  	}
   356  
   357  	if l != nil {
   358  		s.mu.Lock()
   359  		delete(s.refs, key)
   360  		s.mu.Unlock()
   361  		_, err := s.opt.LayerStore.Release(l)
   362  		return err
   363  	}
   364  
   365  	if !found { // this happens when removing views
   366  		return nil
   367  	}
   368  
   369  	return s.opt.GraphDriver.Remove(id)
   370  }
   371  
   372  func (s *snapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error {
   373  	return s.db.Update(func(tx *bolt.Tx) error {
   374  		b, err := tx.CreateBucketIfNotExists([]byte(name))
   375  		if err != nil {
   376  			return err
   377  		}
   378  		if err := b.Put(keyCommitted, []byte(key)); err != nil {
   379  			return err
   380  		}
   381  		b, err = tx.CreateBucketIfNotExists([]byte(key))
   382  		if err != nil {
   383  			return err
   384  		}
   385  		if err := b.Put(keyIsCommitted, []byte{}); err != nil {
   386  			return err
   387  		}
   388  		return nil
   389  	})
   390  }
   391  
   392  func (s *snapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) (snapshot.Mountable, error) {
   393  	return s.Mounts(ctx, parent)
   394  }
   395  
   396  func (s *snapshotter) Walk(context.Context, snapshots.WalkFunc, ...string) error {
   397  	return errors.Errorf("not-implemented")
   398  }
   399  
   400  func (s *snapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
   401  	// not implemented
   402  	return s.Stat(ctx, info.Name)
   403  }
   404  
   405  func (s *snapshotter) Usage(ctx context.Context, key string) (us snapshots.Usage, retErr error) {
   406  	usage := snapshots.Usage{}
   407  	if l, err := s.getLayer(key, true); err != nil {
   408  		return usage, err
   409  	} else if l != nil {
   410  		s, err := l.DiffSize()
   411  		if err != nil {
   412  			return usage, err
   413  		}
   414  		usage.Size = s
   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  	return m.idmap
   536  }