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