github.com/demonoid81/containerd@v1.3.4/snapshots/storage/bolt.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package storage
    18  
    19  import (
    20  	"context"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/containerd/containerd/errdefs"
    27  	"github.com/containerd/containerd/metadata/boltutil"
    28  	"github.com/containerd/containerd/snapshots"
    29  	"github.com/pkg/errors"
    30  	bolt "go.etcd.io/bbolt"
    31  )
    32  
    33  var (
    34  	bucketKeyStorageVersion = []byte("v1")
    35  	bucketKeySnapshot       = []byte("snapshots")
    36  	bucketKeyParents        = []byte("parents")
    37  
    38  	bucketKeyID     = []byte("id")
    39  	bucketKeyParent = []byte("parent")
    40  	bucketKeyKind   = []byte("kind")
    41  	bucketKeyInodes = []byte("inodes")
    42  	bucketKeySize   = []byte("size")
    43  
    44  	// ErrNoTransaction is returned when an operation is attempted with
    45  	// a context which is not inside of a transaction.
    46  	ErrNoTransaction = errors.New("no transaction in context")
    47  )
    48  
    49  // parentKey returns a composite key of the parent and child identifiers. The
    50  // parts of the key are separated by a zero byte.
    51  func parentKey(parent, child uint64) []byte {
    52  	b := make([]byte, binary.Size([]uint64{parent, child})+1)
    53  	i := binary.PutUvarint(b, parent)
    54  	j := binary.PutUvarint(b[i+1:], child)
    55  	return b[0 : i+j+1]
    56  }
    57  
    58  // parentPrefixKey returns the parent part of the composite key with the
    59  // zero byte separator.
    60  func parentPrefixKey(parent uint64) []byte {
    61  	b := make([]byte, binary.Size(parent)+1)
    62  	i := binary.PutUvarint(b, parent)
    63  	return b[0 : i+1]
    64  }
    65  
    66  // getParentPrefix returns the first part of the composite key which
    67  // represents the parent identifier.
    68  func getParentPrefix(b []byte) uint64 {
    69  	parent, _ := binary.Uvarint(b)
    70  	return parent
    71  }
    72  
    73  // GetInfo returns the snapshot Info directly from the metadata. Requires a
    74  // context with a storage transaction.
    75  func GetInfo(ctx context.Context, key string) (string, snapshots.Info, snapshots.Usage, error) {
    76  	var (
    77  		id uint64
    78  		su snapshots.Usage
    79  		si = snapshots.Info{
    80  			Name: key,
    81  		}
    82  	)
    83  	err := withSnapshotBucket(ctx, key, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
    84  		getUsage(bkt, &su)
    85  		return readSnapshot(bkt, &id, &si)
    86  	})
    87  	if err != nil {
    88  		return "", snapshots.Info{}, snapshots.Usage{}, err
    89  	}
    90  
    91  	return fmt.Sprintf("%d", id), si, su, nil
    92  }
    93  
    94  // UpdateInfo updates an existing snapshot info's data
    95  func UpdateInfo(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) {
    96  	updated := snapshots.Info{
    97  		Name: info.Name,
    98  	}
    99  	err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   100  		sbkt := bkt.Bucket([]byte(info.Name))
   101  		if sbkt == nil {
   102  			return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
   103  		}
   104  		if err := readSnapshot(sbkt, nil, &updated); err != nil {
   105  			return err
   106  		}
   107  
   108  		if len(fieldpaths) > 0 {
   109  			for _, path := range fieldpaths {
   110  				if strings.HasPrefix(path, "labels.") {
   111  					if updated.Labels == nil {
   112  						updated.Labels = map[string]string{}
   113  					}
   114  
   115  					key := strings.TrimPrefix(path, "labels.")
   116  					updated.Labels[key] = info.Labels[key]
   117  					continue
   118  				}
   119  
   120  				switch path {
   121  				case "labels":
   122  					updated.Labels = info.Labels
   123  				default:
   124  					return errors.Wrapf(errdefs.ErrInvalidArgument, "cannot update %q field on snapshot %q", path, info.Name)
   125  				}
   126  			}
   127  		} else {
   128  			// Set mutable fields
   129  			updated.Labels = info.Labels
   130  		}
   131  		updated.Updated = time.Now().UTC()
   132  		if err := boltutil.WriteTimestamps(sbkt, updated.Created, updated.Updated); err != nil {
   133  			return err
   134  		}
   135  
   136  		return boltutil.WriteLabels(sbkt, updated.Labels)
   137  	})
   138  	if err != nil {
   139  		return snapshots.Info{}, err
   140  	}
   141  	return updated, nil
   142  }
   143  
   144  // WalkInfo iterates through all metadata Info for the stored snapshots and
   145  // calls the provided function for each. Requires a context with a storage
   146  // transaction.
   147  func WalkInfo(ctx context.Context, fn func(context.Context, snapshots.Info) error) error {
   148  	return withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   149  		return bkt.ForEach(func(k, v []byte) error {
   150  			// skip non buckets
   151  			if v != nil {
   152  				return nil
   153  			}
   154  			var (
   155  				sbkt = bkt.Bucket(k)
   156  				si   = snapshots.Info{
   157  					Name: string(k),
   158  				}
   159  			)
   160  			if err := readSnapshot(sbkt, nil, &si); err != nil {
   161  				return err
   162  			}
   163  
   164  			return fn(ctx, si)
   165  		})
   166  	})
   167  }
   168  
   169  // GetSnapshot returns the metadata for the active or view snapshot transaction
   170  // referenced by the given key. Requires a context with a storage transaction.
   171  func GetSnapshot(ctx context.Context, key string) (s Snapshot, err error) {
   172  	err = withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   173  		sbkt := bkt.Bucket([]byte(key))
   174  		if sbkt == nil {
   175  			return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
   176  		}
   177  
   178  		s.ID = fmt.Sprintf("%d", readID(sbkt))
   179  		s.Kind = readKind(sbkt)
   180  
   181  		if s.Kind != snapshots.KindActive && s.Kind != snapshots.KindView {
   182  			return errors.Wrapf(errdefs.ErrFailedPrecondition, "requested snapshot %v not active or view", key)
   183  		}
   184  
   185  		if parentKey := sbkt.Get(bucketKeyParent); len(parentKey) > 0 {
   186  			spbkt := bkt.Bucket(parentKey)
   187  			if spbkt == nil {
   188  				return errors.Wrap(errdefs.ErrNotFound, "parent does not exist")
   189  			}
   190  
   191  			s.ParentIDs, err = parents(bkt, spbkt, readID(spbkt))
   192  			if err != nil {
   193  				return errors.Wrap(err, "failed to get parent chain")
   194  			}
   195  		}
   196  		return nil
   197  	})
   198  	if err != nil {
   199  		return Snapshot{}, err
   200  	}
   201  
   202  	return
   203  }
   204  
   205  // CreateSnapshot inserts a record for an active or view snapshot with the provided parent.
   206  func CreateSnapshot(ctx context.Context, kind snapshots.Kind, key, parent string, opts ...snapshots.Opt) (s Snapshot, err error) {
   207  	switch kind {
   208  	case snapshots.KindActive, snapshots.KindView:
   209  	default:
   210  		return Snapshot{}, errors.Wrapf(errdefs.ErrInvalidArgument, "snapshot type %v invalid; only snapshots of type Active or View can be created", kind)
   211  	}
   212  	var base snapshots.Info
   213  	for _, opt := range opts {
   214  		if err := opt(&base); err != nil {
   215  			return Snapshot{}, err
   216  		}
   217  	}
   218  
   219  	err = createBucketIfNotExists(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   220  		var (
   221  			spbkt *bolt.Bucket
   222  		)
   223  		if parent != "" {
   224  			spbkt = bkt.Bucket([]byte(parent))
   225  			if spbkt == nil {
   226  				return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q bucket", parent)
   227  			}
   228  
   229  			if readKind(spbkt) != snapshots.KindCommitted {
   230  				return errors.Wrapf(errdefs.ErrInvalidArgument, "parent %q is not committed snapshot", parent)
   231  			}
   232  		}
   233  		sbkt, err := bkt.CreateBucket([]byte(key))
   234  		if err != nil {
   235  			if err == bolt.ErrBucketExists {
   236  				err = errors.Wrapf(errdefs.ErrAlreadyExists, "snapshot %v", key)
   237  			}
   238  			return err
   239  		}
   240  
   241  		id, err := bkt.NextSequence()
   242  		if err != nil {
   243  			return errors.Wrapf(err, "unable to get identifier for snapshot %q", key)
   244  		}
   245  
   246  		t := time.Now().UTC()
   247  		si := snapshots.Info{
   248  			Parent:  parent,
   249  			Kind:    kind,
   250  			Labels:  base.Labels,
   251  			Created: t,
   252  			Updated: t,
   253  		}
   254  		if err := putSnapshot(sbkt, id, si); err != nil {
   255  			return err
   256  		}
   257  
   258  		if spbkt != nil {
   259  			pid := readID(spbkt)
   260  
   261  			// Store a backlink from the key to the parent. Store the snapshot name
   262  			// as the value to allow following the backlink to the snapshot value.
   263  			if err := pbkt.Put(parentKey(pid, id), []byte(key)); err != nil {
   264  				return errors.Wrapf(err, "failed to write parent link for snapshot %q", key)
   265  			}
   266  
   267  			s.ParentIDs, err = parents(bkt, spbkt, pid)
   268  			if err != nil {
   269  				return errors.Wrapf(err, "failed to get parent chain for snapshot %q", key)
   270  			}
   271  		}
   272  
   273  		s.ID = fmt.Sprintf("%d", id)
   274  		s.Kind = kind
   275  		return nil
   276  	})
   277  	if err != nil {
   278  		return Snapshot{}, err
   279  	}
   280  
   281  	return
   282  }
   283  
   284  // Remove removes a snapshot from the metastore. The string identifier for the
   285  // snapshot is returned as well as the kind. The provided context must contain a
   286  // writable transaction.
   287  func Remove(ctx context.Context, key string) (string, snapshots.Kind, error) {
   288  	var (
   289  		id uint64
   290  		si snapshots.Info
   291  	)
   292  
   293  	if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   294  		sbkt := bkt.Bucket([]byte(key))
   295  		if sbkt == nil {
   296  			return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
   297  		}
   298  
   299  		if err := readSnapshot(sbkt, &id, &si); err != nil {
   300  			return errors.Wrapf(err, "failed to read snapshot %s", key)
   301  		}
   302  
   303  		if pbkt != nil {
   304  			k, _ := pbkt.Cursor().Seek(parentPrefixKey(id))
   305  			if getParentPrefix(k) == id {
   306  				return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot remove snapshot with child")
   307  			}
   308  
   309  			if si.Parent != "" {
   310  				spbkt := bkt.Bucket([]byte(si.Parent))
   311  				if spbkt == nil {
   312  					return errors.Wrapf(errdefs.ErrNotFound, "snapshot %v", key)
   313  				}
   314  
   315  				if err := pbkt.Delete(parentKey(readID(spbkt), id)); err != nil {
   316  					return errors.Wrap(err, "failed to delete parent link")
   317  				}
   318  			}
   319  		}
   320  
   321  		if err := bkt.DeleteBucket([]byte(key)); err != nil {
   322  			return errors.Wrap(err, "failed to delete snapshot")
   323  		}
   324  
   325  		return nil
   326  	}); err != nil {
   327  		return "", 0, err
   328  	}
   329  
   330  	return fmt.Sprintf("%d", id), si.Kind, nil
   331  }
   332  
   333  // CommitActive renames the active snapshot transaction referenced by `key`
   334  // as a committed snapshot referenced by `Name`. The resulting snapshot  will be
   335  // committed and readonly. The `key` reference will no longer be available for
   336  // lookup or removal. The returned string identifier for the committed snapshot
   337  // is the same identifier of the original active snapshot. The provided context
   338  // must contain a writable transaction.
   339  func CommitActive(ctx context.Context, key, name string, usage snapshots.Usage, opts ...snapshots.Opt) (string, error) {
   340  	var (
   341  		id   uint64
   342  		base snapshots.Info
   343  	)
   344  	for _, opt := range opts {
   345  		if err := opt(&base); err != nil {
   346  			return "", err
   347  		}
   348  	}
   349  
   350  	if err := withBucket(ctx, func(ctx context.Context, bkt, pbkt *bolt.Bucket) error {
   351  		dbkt, err := bkt.CreateBucket([]byte(name))
   352  		if err != nil {
   353  			if err == bolt.ErrBucketExists {
   354  				err = errdefs.ErrAlreadyExists
   355  			}
   356  			return errors.Wrapf(err, "committed snapshot %v", name)
   357  		}
   358  		sbkt := bkt.Bucket([]byte(key))
   359  		if sbkt == nil {
   360  			return errors.Wrapf(errdefs.ErrNotFound, "failed to get active snapshot %q", key)
   361  		}
   362  
   363  		var si snapshots.Info
   364  		if err := readSnapshot(sbkt, &id, &si); err != nil {
   365  			return errors.Wrapf(err, "failed to read active snapshot %q", key)
   366  		}
   367  
   368  		if si.Kind != snapshots.KindActive {
   369  			return errors.Wrapf(errdefs.ErrFailedPrecondition, "snapshot %q is not active", key)
   370  		}
   371  		si.Kind = snapshots.KindCommitted
   372  		si.Created = time.Now().UTC()
   373  		si.Updated = si.Created
   374  
   375  		// Replace labels, do not inherit
   376  		si.Labels = base.Labels
   377  
   378  		if err := putSnapshot(dbkt, id, si); err != nil {
   379  			return err
   380  		}
   381  		if err := putUsage(dbkt, usage); err != nil {
   382  			return err
   383  		}
   384  		if err := bkt.DeleteBucket([]byte(key)); err != nil {
   385  			return errors.Wrapf(err, "failed to delete active snapshot %q", key)
   386  		}
   387  		if si.Parent != "" {
   388  			spbkt := bkt.Bucket([]byte(si.Parent))
   389  			if spbkt == nil {
   390  				return errors.Wrapf(errdefs.ErrNotFound, "missing parent %q of snapshot %q", si.Parent, key)
   391  			}
   392  			pid := readID(spbkt)
   393  
   394  			// Updates parent back link to use new key
   395  			if err := pbkt.Put(parentKey(pid, id), []byte(name)); err != nil {
   396  				return errors.Wrapf(err, "failed to update parent link %q from %q to %q", pid, key, name)
   397  			}
   398  		}
   399  
   400  		return nil
   401  	}); err != nil {
   402  		return "", err
   403  	}
   404  
   405  	return fmt.Sprintf("%d", id), nil
   406  }
   407  
   408  // IDMap returns all the IDs mapped to their key
   409  func IDMap(ctx context.Context) (map[string]string, error) {
   410  	m := map[string]string{}
   411  	if err := withBucket(ctx, func(ctx context.Context, bkt, _ *bolt.Bucket) error {
   412  		return bkt.ForEach(func(k, v []byte) error {
   413  			// skip non buckets
   414  			if v != nil {
   415  				return nil
   416  			}
   417  			id := readID(bkt.Bucket(k))
   418  			m[fmt.Sprintf("%d", id)] = string(k)
   419  			return nil
   420  		})
   421  	}); err != nil {
   422  		return nil, err
   423  	}
   424  
   425  	return m, nil
   426  }
   427  
   428  func withSnapshotBucket(ctx context.Context, key string, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
   429  	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
   430  	if !ok {
   431  		return ErrNoTransaction
   432  	}
   433  	vbkt := tx.Bucket(bucketKeyStorageVersion)
   434  	if vbkt == nil {
   435  		return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
   436  	}
   437  	bkt := vbkt.Bucket(bucketKeySnapshot)
   438  	if bkt == nil {
   439  		return errors.Wrap(errdefs.ErrNotFound, "snapshots bucket does not exist")
   440  	}
   441  	bkt = bkt.Bucket([]byte(key))
   442  	if bkt == nil {
   443  		return errors.Wrap(errdefs.ErrNotFound, "snapshot does not exist")
   444  	}
   445  
   446  	return fn(ctx, bkt, vbkt.Bucket(bucketKeyParents))
   447  }
   448  
   449  func withBucket(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
   450  	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
   451  	if !ok {
   452  		return ErrNoTransaction
   453  	}
   454  	bkt := tx.Bucket(bucketKeyStorageVersion)
   455  	if bkt == nil {
   456  		return errors.Wrap(errdefs.ErrNotFound, "bucket does not exist")
   457  	}
   458  	return fn(ctx, bkt.Bucket(bucketKeySnapshot), bkt.Bucket(bucketKeyParents))
   459  }
   460  
   461  func createBucketIfNotExists(ctx context.Context, fn func(context.Context, *bolt.Bucket, *bolt.Bucket) error) error {
   462  	tx, ok := ctx.Value(transactionKey{}).(*bolt.Tx)
   463  	if !ok {
   464  		return ErrNoTransaction
   465  	}
   466  
   467  	bkt, err := tx.CreateBucketIfNotExists(bucketKeyStorageVersion)
   468  	if err != nil {
   469  		return errors.Wrap(err, "failed to create version bucket")
   470  	}
   471  	sbkt, err := bkt.CreateBucketIfNotExists(bucketKeySnapshot)
   472  	if err != nil {
   473  		return errors.Wrap(err, "failed to create snapshots bucket")
   474  	}
   475  	pbkt, err := bkt.CreateBucketIfNotExists(bucketKeyParents)
   476  	if err != nil {
   477  		return errors.Wrap(err, "failed to create parents bucket")
   478  	}
   479  	return fn(ctx, sbkt, pbkt)
   480  }
   481  
   482  func parents(bkt, pbkt *bolt.Bucket, parent uint64) (parents []string, err error) {
   483  	for {
   484  		parents = append(parents, fmt.Sprintf("%d", parent))
   485  
   486  		parentKey := pbkt.Get(bucketKeyParent)
   487  		if len(parentKey) == 0 {
   488  			return
   489  		}
   490  		pbkt = bkt.Bucket(parentKey)
   491  		if pbkt == nil {
   492  			return nil, errors.Wrap(errdefs.ErrNotFound, "missing parent")
   493  		}
   494  
   495  		parent = readID(pbkt)
   496  	}
   497  }
   498  
   499  func readKind(bkt *bolt.Bucket) (k snapshots.Kind) {
   500  	kind := bkt.Get(bucketKeyKind)
   501  	if len(kind) == 1 {
   502  		k = snapshots.Kind(kind[0])
   503  	}
   504  	return
   505  }
   506  
   507  func readID(bkt *bolt.Bucket) uint64 {
   508  	id, _ := binary.Uvarint(bkt.Get(bucketKeyID))
   509  	return id
   510  }
   511  
   512  func readSnapshot(bkt *bolt.Bucket, id *uint64, si *snapshots.Info) error {
   513  	if id != nil {
   514  		*id = readID(bkt)
   515  	}
   516  	if si != nil {
   517  		si.Kind = readKind(bkt)
   518  		si.Parent = string(bkt.Get(bucketKeyParent))
   519  
   520  		if err := boltutil.ReadTimestamps(bkt, &si.Created, &si.Updated); err != nil {
   521  			return err
   522  		}
   523  
   524  		labels, err := boltutil.ReadLabels(bkt)
   525  		if err != nil {
   526  			return err
   527  		}
   528  		si.Labels = labels
   529  	}
   530  
   531  	return nil
   532  }
   533  
   534  func putSnapshot(bkt *bolt.Bucket, id uint64, si snapshots.Info) error {
   535  	idEncoded, err := encodeID(id)
   536  	if err != nil {
   537  		return err
   538  	}
   539  
   540  	updates := [][2][]byte{
   541  		{bucketKeyID, idEncoded},
   542  		{bucketKeyKind, []byte{byte(si.Kind)}},
   543  	}
   544  	if si.Parent != "" {
   545  		updates = append(updates, [2][]byte{bucketKeyParent, []byte(si.Parent)})
   546  	}
   547  	for _, v := range updates {
   548  		if err := bkt.Put(v[0], v[1]); err != nil {
   549  			return err
   550  		}
   551  	}
   552  	if err := boltutil.WriteTimestamps(bkt, si.Created, si.Updated); err != nil {
   553  		return err
   554  	}
   555  	return boltutil.WriteLabels(bkt, si.Labels)
   556  }
   557  
   558  func getUsage(bkt *bolt.Bucket, usage *snapshots.Usage) {
   559  	usage.Inodes, _ = binary.Varint(bkt.Get(bucketKeyInodes))
   560  	usage.Size, _ = binary.Varint(bkt.Get(bucketKeySize))
   561  }
   562  
   563  func putUsage(bkt *bolt.Bucket, usage snapshots.Usage) error {
   564  	for _, v := range []struct {
   565  		key   []byte
   566  		value int64
   567  	}{
   568  		{bucketKeyInodes, usage.Inodes},
   569  		{bucketKeySize, usage.Size},
   570  	} {
   571  		e, err := encodeSize(v.value)
   572  		if err != nil {
   573  			return err
   574  		}
   575  		if err := bkt.Put(v.key, e); err != nil {
   576  			return err
   577  		}
   578  	}
   579  	return nil
   580  }
   581  
   582  func encodeSize(size int64) ([]byte, error) {
   583  	var (
   584  		buf         [binary.MaxVarintLen64]byte
   585  		sizeEncoded = buf[:]
   586  	)
   587  	sizeEncoded = sizeEncoded[:binary.PutVarint(sizeEncoded, size)]
   588  
   589  	if len(sizeEncoded) == 0 {
   590  		return nil, fmt.Errorf("failed encoding size = %v", size)
   591  	}
   592  	return sizeEncoded, nil
   593  }
   594  
   595  func encodeID(id uint64) ([]byte, error) {
   596  	var (
   597  		buf       [binary.MaxVarintLen64]byte
   598  		idEncoded = buf[:]
   599  	)
   600  	idEncoded = idEncoded[:binary.PutUvarint(idEncoded, id)]
   601  
   602  	if len(idEncoded) == 0 {
   603  		return nil, fmt.Errorf("failed encoding id = %v", id)
   604  	}
   605  	return idEncoded, nil
   606  }