242 lines
5.4 KiB
Go
242 lines
5.4 KiB
Go
package metadata
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/boltdb/bolt"
|
|
"github.com/containerd/containerd/errdefs"
|
|
"github.com/containerd/containerd/leases"
|
|
"github.com/containerd/containerd/metadata/boltutil"
|
|
"github.com/containerd/containerd/namespaces"
|
|
digest "github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Lease retains resources to prevent garbage collection before
|
|
// the resources can be fully referenced.
|
|
type Lease struct {
|
|
ID string
|
|
CreatedAt time.Time
|
|
Labels map[string]string
|
|
|
|
Content []string
|
|
Snapshots map[string][]string
|
|
}
|
|
|
|
// LeaseManager manages the create/delete lifecyle of leases
|
|
// and also returns existing leases
|
|
type LeaseManager struct {
|
|
tx *bolt.Tx
|
|
}
|
|
|
|
// NewLeaseManager creates a new lease manager for managing leases using
|
|
// the provided database transaction.
|
|
func NewLeaseManager(tx *bolt.Tx) *LeaseManager {
|
|
return &LeaseManager{
|
|
tx: tx,
|
|
}
|
|
}
|
|
|
|
// Create creates a new lease using the provided lease
|
|
func (lm *LeaseManager) Create(ctx context.Context, lid string, labels map[string]string) (Lease, error) {
|
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
|
if err != nil {
|
|
return Lease{}, err
|
|
}
|
|
|
|
topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
|
|
if err != nil {
|
|
return Lease{}, err
|
|
}
|
|
|
|
txbkt, err := topbkt.CreateBucket([]byte(lid))
|
|
if err != nil {
|
|
if err == bolt.ErrBucketExists {
|
|
err = errdefs.ErrAlreadyExists
|
|
}
|
|
return Lease{}, errors.Wrapf(err, "lease %q", lid)
|
|
}
|
|
|
|
t := time.Now().UTC()
|
|
createdAt, err := t.MarshalBinary()
|
|
if err != nil {
|
|
return Lease{}, err
|
|
}
|
|
if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil {
|
|
return Lease{}, err
|
|
}
|
|
|
|
if labels != nil {
|
|
if err := boltutil.WriteLabels(txbkt, labels); err != nil {
|
|
return Lease{}, err
|
|
}
|
|
}
|
|
|
|
return Lease{
|
|
ID: lid,
|
|
CreatedAt: t,
|
|
Labels: labels,
|
|
}, nil
|
|
}
|
|
|
|
// Delete delets the lease with the provided lease ID
|
|
func (lm *LeaseManager) Delete(ctx context.Context, lid string) error {
|
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
|
|
if topbkt == nil {
|
|
return nil
|
|
}
|
|
if err := topbkt.DeleteBucket([]byte(lid)); err != nil && err != bolt.ErrBucketNotFound {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// List lists all active leases
|
|
func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter ...string) ([]Lease, error) {
|
|
namespace, err := namespaces.NamespaceRequired(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var leases []Lease
|
|
|
|
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
|
|
if topbkt == nil {
|
|
return leases, nil
|
|
}
|
|
|
|
if err := topbkt.ForEach(func(k, v []byte) error {
|
|
if v != nil {
|
|
return nil
|
|
}
|
|
txbkt := topbkt.Bucket(k)
|
|
|
|
l := Lease{
|
|
ID: string(k),
|
|
}
|
|
|
|
if v := txbkt.Get(bucketKeyCreatedAt); v != nil {
|
|
t := &l.CreatedAt
|
|
if err := t.UnmarshalBinary(v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
labels, err := boltutil.ReadLabels(txbkt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
l.Labels = labels
|
|
|
|
// TODO: Read Snapshots
|
|
// TODO: Read Content
|
|
|
|
leases = append(leases, l)
|
|
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return leases, nil
|
|
}
|
|
|
|
func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
|
|
lid, ok := leases.Lease(ctx)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
namespace, ok := namespaces.Namespace(ctx)
|
|
if !ok {
|
|
panic("namespace must already be checked")
|
|
}
|
|
|
|
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
|
|
if bkt == nil {
|
|
return errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
|
|
}
|
|
|
|
bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectSnapshots)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bkt, err = bkt.CreateBucketIfNotExists([]byte(snapshotter))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return bkt.Put([]byte(key), nil)
|
|
}
|
|
|
|
func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
|
|
lid, ok := leases.Lease(ctx)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
namespace, ok := namespaces.Namespace(ctx)
|
|
if !ok {
|
|
panic("namespace must already be checked")
|
|
}
|
|
|
|
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectSnapshots, []byte(snapshotter))
|
|
if bkt == nil {
|
|
// Key does not exist so we return nil
|
|
return nil
|
|
}
|
|
|
|
return bkt.Delete([]byte(key))
|
|
}
|
|
|
|
func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
|
|
lid, ok := leases.Lease(ctx)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
namespace, ok := namespaces.Namespace(ctx)
|
|
if !ok {
|
|
panic("namespace must already be required")
|
|
}
|
|
|
|
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
|
|
if bkt == nil {
|
|
return errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
|
|
}
|
|
|
|
bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectContent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return bkt.Put([]byte(dgst.String()), nil)
|
|
}
|
|
|
|
func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
|
|
lid, ok := leases.Lease(ctx)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
namespace, ok := namespaces.Namespace(ctx)
|
|
if !ok {
|
|
panic("namespace must already be checked")
|
|
}
|
|
|
|
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectContent)
|
|
if bkt == nil {
|
|
// Key does not exist so we return nil
|
|
return nil
|
|
}
|
|
|
|
return bkt.Delete([]byte(dgst.String()))
|
|
}
|