github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/metadata/leases.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 metadata
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	"github.com/containerd/containerd/errdefs"
    27  	"github.com/containerd/containerd/filters"
    28  	"github.com/containerd/containerd/leases"
    29  	"github.com/containerd/containerd/metadata/boltutil"
    30  	"github.com/containerd/containerd/namespaces"
    31  	digest "github.com/opencontainers/go-digest"
    32  	"github.com/pkg/errors"
    33  	bolt "go.etcd.io/bbolt"
    34  )
    35  
    36  // LeaseManager manages the create/delete lifecycle of leases
    37  // and also returns existing leases
    38  type LeaseManager struct {
    39  	db *DB
    40  }
    41  
    42  // NewLeaseManager creates a new lease manager for managing leases using
    43  // the provided database transaction.
    44  func NewLeaseManager(db *DB) *LeaseManager {
    45  	return &LeaseManager{
    46  		db: db,
    47  	}
    48  }
    49  
    50  // Create creates a new lease using the provided lease
    51  func (lm *LeaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
    52  	var l leases.Lease
    53  	for _, opt := range opts {
    54  		if err := opt(&l); err != nil {
    55  			return leases.Lease{}, err
    56  		}
    57  	}
    58  	if l.ID == "" {
    59  		return leases.Lease{}, errors.New("lease id must be provided")
    60  	}
    61  
    62  	namespace, err := namespaces.NamespaceRequired(ctx)
    63  	if err != nil {
    64  		return leases.Lease{}, err
    65  	}
    66  
    67  	if err := update(ctx, lm.db, func(tx *bolt.Tx) error {
    68  		topbkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
    69  		if err != nil {
    70  			return err
    71  		}
    72  
    73  		txbkt, err := topbkt.CreateBucket([]byte(l.ID))
    74  		if err != nil {
    75  			if err == bolt.ErrBucketExists {
    76  				err = errdefs.ErrAlreadyExists
    77  			}
    78  			return errors.Wrapf(err, "lease %q", l.ID)
    79  		}
    80  
    81  		t := time.Now().UTC()
    82  		createdAt, err := t.MarshalBinary()
    83  		if err != nil {
    84  			return err
    85  		}
    86  		if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil {
    87  			return err
    88  		}
    89  
    90  		if l.Labels != nil {
    91  			if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil {
    92  				return err
    93  			}
    94  		}
    95  		l.CreatedAt = t
    96  
    97  		return nil
    98  	}); err != nil {
    99  		return leases.Lease{}, err
   100  	}
   101  	return l, nil
   102  }
   103  
   104  // Delete deletes the lease with the provided lease ID
   105  func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease, _ ...leases.DeleteOpt) error {
   106  	namespace, err := namespaces.NamespaceRequired(ctx)
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	return update(ctx, lm.db, func(tx *bolt.Tx) error {
   112  		topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
   113  		if topbkt == nil {
   114  			return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
   115  		}
   116  		if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil {
   117  			if err == bolt.ErrBucketNotFound {
   118  				err = errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
   119  			}
   120  			return err
   121  		}
   122  
   123  		atomic.AddUint32(&lm.db.dirty, 1)
   124  
   125  		return nil
   126  	})
   127  }
   128  
   129  // List lists all active leases
   130  func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, error) {
   131  	namespace, err := namespaces.NamespaceRequired(ctx)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  
   136  	filter, err := filters.ParseAll(fs...)
   137  	if err != nil {
   138  		return nil, errors.Wrap(errdefs.ErrInvalidArgument, err.Error())
   139  	}
   140  
   141  	var ll []leases.Lease
   142  
   143  	if err := view(ctx, lm.db, func(tx *bolt.Tx) error {
   144  		topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
   145  		if topbkt == nil {
   146  			return nil
   147  		}
   148  
   149  		return topbkt.ForEach(func(k, v []byte) error {
   150  			if v != nil {
   151  				return nil
   152  			}
   153  			txbkt := topbkt.Bucket(k)
   154  
   155  			l := leases.Lease{
   156  				ID: string(k),
   157  			}
   158  
   159  			if v := txbkt.Get(bucketKeyCreatedAt); v != nil {
   160  				t := &l.CreatedAt
   161  				if err := t.UnmarshalBinary(v); err != nil {
   162  					return err
   163  				}
   164  			}
   165  
   166  			labels, err := boltutil.ReadLabels(txbkt)
   167  			if err != nil {
   168  				return err
   169  			}
   170  			l.Labels = labels
   171  
   172  			if filter.Match(adaptLease(l)) {
   173  				ll = append(ll, l)
   174  			}
   175  
   176  			return nil
   177  		})
   178  	}); err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	return ll, nil
   183  }
   184  
   185  // AddResource references the resource by the provided lease.
   186  func (lm *LeaseManager) AddResource(ctx context.Context, lease leases.Lease, r leases.Resource) error {
   187  	namespace, err := namespaces.NamespaceRequired(ctx)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	return update(ctx, lm.db, func(tx *bolt.Tx) error {
   193  		topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID))
   194  		if topbkt == nil {
   195  			return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
   196  		}
   197  
   198  		keys, ref, err := parseLeaseResource(r)
   199  		if err != nil {
   200  			return err
   201  		}
   202  
   203  		bkt := topbkt
   204  		for _, key := range keys {
   205  			bkt, err = bkt.CreateBucketIfNotExists([]byte(key))
   206  			if err != nil {
   207  				return err
   208  			}
   209  		}
   210  		return bkt.Put([]byte(ref), nil)
   211  	})
   212  }
   213  
   214  // DeleteResource dereferences the resource by the provided lease.
   215  func (lm *LeaseManager) DeleteResource(ctx context.Context, lease leases.Lease, r leases.Resource) error {
   216  	namespace, err := namespaces.NamespaceRequired(ctx)
   217  	if err != nil {
   218  		return err
   219  	}
   220  
   221  	return update(ctx, lm.db, func(tx *bolt.Tx) error {
   222  		topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID))
   223  		if topbkt == nil {
   224  			return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
   225  		}
   226  
   227  		keys, ref, err := parseLeaseResource(r)
   228  		if err != nil {
   229  			return err
   230  		}
   231  
   232  		bkt := topbkt
   233  		for _, key := range keys {
   234  			if bkt == nil {
   235  				break
   236  			}
   237  			bkt = bkt.Bucket([]byte(key))
   238  		}
   239  
   240  		if bkt != nil {
   241  			if err := bkt.Delete([]byte(ref)); err != nil {
   242  				return err
   243  			}
   244  		}
   245  
   246  		atomic.AddUint32(&lm.db.dirty, 1)
   247  
   248  		return nil
   249  	})
   250  }
   251  
   252  // ListResources lists all the resources referenced by the lease.
   253  func (lm *LeaseManager) ListResources(ctx context.Context, lease leases.Lease) ([]leases.Resource, error) {
   254  	namespace, err := namespaces.NamespaceRequired(ctx)
   255  	if err != nil {
   256  		return nil, err
   257  	}
   258  
   259  	var rs []leases.Resource
   260  
   261  	if err := view(ctx, lm.db, func(tx *bolt.Tx) error {
   262  
   263  		topbkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lease.ID))
   264  		if topbkt == nil {
   265  			return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
   266  		}
   267  
   268  		// content resources
   269  		if cbkt := topbkt.Bucket(bucketKeyObjectContent); cbkt != nil {
   270  			if err := cbkt.ForEach(func(k, _ []byte) error {
   271  				rs = append(rs, leases.Resource{
   272  					ID:   string(k),
   273  					Type: string(bucketKeyObjectContent),
   274  				})
   275  
   276  				return nil
   277  			}); err != nil {
   278  				return err
   279  			}
   280  		}
   281  
   282  		// ingest resources
   283  		if lbkt := topbkt.Bucket(bucketKeyObjectIngests); lbkt != nil {
   284  			if err := lbkt.ForEach(func(k, _ []byte) error {
   285  				rs = append(rs, leases.Resource{
   286  					ID:   string(k),
   287  					Type: string(bucketKeyObjectIngests),
   288  				})
   289  
   290  				return nil
   291  			}); err != nil {
   292  				return err
   293  			}
   294  		}
   295  
   296  		// snapshot resources
   297  		if sbkt := topbkt.Bucket(bucketKeyObjectSnapshots); sbkt != nil {
   298  			if err := sbkt.ForEach(func(sk, sv []byte) error {
   299  				if sv != nil {
   300  					return nil
   301  				}
   302  
   303  				snbkt := sbkt.Bucket(sk)
   304  				return snbkt.ForEach(func(k, _ []byte) error {
   305  					rs = append(rs, leases.Resource{
   306  						ID:   string(k),
   307  						Type: fmt.Sprintf("%s/%s", bucketKeyObjectSnapshots, sk),
   308  					})
   309  					return nil
   310  				})
   311  			}); err != nil {
   312  				return err
   313  			}
   314  		}
   315  
   316  		return nil
   317  	}); err != nil {
   318  		return nil, err
   319  	}
   320  	return rs, nil
   321  }
   322  
   323  func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
   324  	lid, ok := leases.FromContext(ctx)
   325  	if !ok {
   326  		return nil
   327  	}
   328  
   329  	namespace, ok := namespaces.Namespace(ctx)
   330  	if !ok {
   331  		panic("namespace must already be checked")
   332  	}
   333  
   334  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
   335  	if bkt == nil {
   336  		return errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
   337  	}
   338  
   339  	bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectSnapshots)
   340  	if err != nil {
   341  		return err
   342  	}
   343  
   344  	bkt, err = bkt.CreateBucketIfNotExists([]byte(snapshotter))
   345  	if err != nil {
   346  		return err
   347  	}
   348  
   349  	return bkt.Put([]byte(key), nil)
   350  }
   351  
   352  func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
   353  	lid, ok := leases.FromContext(ctx)
   354  	if !ok {
   355  		return nil
   356  	}
   357  
   358  	namespace, ok := namespaces.Namespace(ctx)
   359  	if !ok {
   360  		panic("namespace must already be checked")
   361  	}
   362  
   363  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectSnapshots, []byte(snapshotter))
   364  	if bkt == nil {
   365  		// Key does not exist so we return nil
   366  		return nil
   367  	}
   368  
   369  	return bkt.Delete([]byte(key))
   370  }
   371  
   372  func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
   373  	lid, ok := leases.FromContext(ctx)
   374  	if !ok {
   375  		return nil
   376  	}
   377  
   378  	namespace, ok := namespaces.Namespace(ctx)
   379  	if !ok {
   380  		panic("namespace must already be required")
   381  	}
   382  
   383  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
   384  	if bkt == nil {
   385  		return errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
   386  	}
   387  
   388  	bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectContent)
   389  	if err != nil {
   390  		return err
   391  	}
   392  
   393  	return bkt.Put([]byte(dgst.String()), nil)
   394  }
   395  
   396  func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
   397  	lid, ok := leases.FromContext(ctx)
   398  	if !ok {
   399  		return nil
   400  	}
   401  
   402  	namespace, ok := namespaces.Namespace(ctx)
   403  	if !ok {
   404  		panic("namespace must already be checked")
   405  	}
   406  
   407  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectContent)
   408  	if bkt == nil {
   409  		// Key does not exist so we return nil
   410  		return nil
   411  	}
   412  
   413  	return bkt.Delete([]byte(dgst.String()))
   414  }
   415  
   416  func addIngestLease(ctx context.Context, tx *bolt.Tx, ref string) (bool, error) {
   417  	lid, ok := leases.FromContext(ctx)
   418  	if !ok {
   419  		return false, nil
   420  	}
   421  
   422  	namespace, ok := namespaces.Namespace(ctx)
   423  	if !ok {
   424  		panic("namespace must already be required")
   425  	}
   426  
   427  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
   428  	if bkt == nil {
   429  		return false, errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
   430  	}
   431  
   432  	bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectIngests)
   433  	if err != nil {
   434  		return false, err
   435  	}
   436  
   437  	if err := bkt.Put([]byte(ref), nil); err != nil {
   438  		return false, err
   439  	}
   440  
   441  	return true, nil
   442  }
   443  
   444  func removeIngestLease(ctx context.Context, tx *bolt.Tx, ref string) error {
   445  	lid, ok := leases.FromContext(ctx)
   446  	if !ok {
   447  		return nil
   448  	}
   449  
   450  	namespace, ok := namespaces.Namespace(ctx)
   451  	if !ok {
   452  		panic("namespace must already be checked")
   453  	}
   454  
   455  	bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectIngests)
   456  	if bkt == nil {
   457  		// Key does not exist so we return nil
   458  		return nil
   459  	}
   460  
   461  	return bkt.Delete([]byte(ref))
   462  }
   463  
   464  func parseLeaseResource(r leases.Resource) ([]string, string, error) {
   465  	var (
   466  		ref  = r.ID
   467  		typ  = r.Type
   468  		keys = strings.Split(typ, "/")
   469  	)
   470  
   471  	switch k := keys[0]; k {
   472  	case string(bucketKeyObjectContent),
   473  		string(bucketKeyObjectIngests):
   474  
   475  		if len(keys) != 1 {
   476  			return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid resource type %s", typ)
   477  		}
   478  
   479  		if k == string(bucketKeyObjectContent) {
   480  			dgst, err := digest.Parse(ref)
   481  			if err != nil {
   482  				return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid content resource id %s: %v", ref, err)
   483  			}
   484  			ref = dgst.String()
   485  		}
   486  	case string(bucketKeyObjectSnapshots):
   487  		if len(keys) != 2 {
   488  			return nil, "", errors.Wrapf(errdefs.ErrInvalidArgument, "invalid snapshot resource type %s", typ)
   489  		}
   490  	default:
   491  		return nil, "", errors.Wrapf(errdefs.ErrNotImplemented, "resource type %s not supported yet", typ)
   492  	}
   493  
   494  	return keys, ref, nil
   495  }