Support lease filters

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2018-07-17 23:34:38 -07:00
parent 00a99c0472
commit 29b72d4ff0
No known key found for this signature in database
GPG Key ID: F58C5D0A4405ACDB
4 changed files with 183 additions and 5 deletions

View File

@ -23,6 +23,7 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
)
func adaptImage(o interface{}) filters.Adaptor {
@ -119,6 +120,23 @@ func adaptContentStatus(status content.Status) filters.Adaptor {
})
}
func adaptLease(lease leases.Lease) filters.Adaptor {
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
if len(fieldpath) == 0 {
return "", false
}
switch fieldpath[0] {
case "id":
return lease.ID, len(lease.ID) > 0
case "labels":
return checkMap(fieldpath[1:], lease.Labels)
}
return "", false
})
}
func checkMap(fieldpath []string, m map[string]string) (string, bool) {
if len(m) == 0 {
return "", false

View File

@ -109,14 +109,16 @@ func TestContentLeased(t *testing.T) {
func createLease(ctx context.Context, db *DB, name string) (context.Context, func() error, error) {
if err := db.Update(func(tx *bolt.Tx) error {
_, err := NewLeaseManager(tx).Create(ctx, name, nil)
_, err := NewLeaseManager(tx).Create(ctx, leases.WithID(name))
return err
}); err != nil {
return nil, nil, err
}
return leases.WithLease(ctx, name), func() error {
return db.Update(func(tx *bolt.Tx) error {
return NewLeaseManager(tx).Delete(ctx, name)
return NewLeaseManager(tx).Delete(ctx, leases.Lease{
ID: name,
})
})
}, nil
}
@ -126,7 +128,7 @@ func checkContentLeased(ctx context.Context, db *DB, dgst digest.Digest) error {
if !ok {
return errors.New("no namespace in context")
}
lease, ok := leases.Lease(ctx)
lease, ok := leases.FromContext(ctx)
if !ok {
return errors.New("no lease in context")
}

View File

@ -22,6 +22,7 @@ import (
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/metadata/boltutil"
"github.com/containerd/containerd/namespaces"
@ -110,12 +111,17 @@ func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease) error {
}
// List lists all active leases
func (lm *LeaseManager) List(ctx context.Context, filter ...string) ([]leases.Lease, error) {
func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, error) {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
filter, err := filters.ParseAll(fs...)
if err != nil {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error())
}
var ll []leases.Lease
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
@ -146,7 +152,9 @@ func (lm *LeaseManager) List(ctx context.Context, filter ...string) ([]leases.Le
}
l.Labels = labels
ll = append(ll, l)
if filter.Match(adaptLease(l)) {
ll = append(ll, l)
}
return nil
}); err != nil {

View File

@ -107,3 +107,153 @@ func TestLeases(t *testing.T) {
t.Fatalf("Expected no leases, found %d: %v", len(listed), listed)
}
}
func TestLeasesList(t *testing.T) {
ctx, db, cancel := testEnv(t)
defer cancel()
testset := [][]leases.Opt{
{
leases.WithID("lease1"),
leases.WithLabels(map[string]string{
"label1": "value1",
"label3": "other",
}),
},
{
leases.WithID("lease2"),
leases.WithLabels(map[string]string{
"label1": "value1",
"label2": "",
"label3": "other",
}),
},
{
leases.WithID("lease3"),
leases.WithLabels(map[string]string{
"label1": "value2",
"label2": "something",
}),
},
}
// Insert all
for _, opts := range testset {
if err := db.Update(func(tx *bolt.Tx) error {
lm := NewLeaseManager(tx)
_, err := lm.Create(ctx, opts...)
return err
}); err != nil {
t.Fatal(err)
}
}
for _, testcase := range []struct {
name string
filters []string
expected []string
}{
{
name: "All",
filters: []string{},
expected: []string{"lease1", "lease2", "lease3"},
},
{
name: "ID",
filters: []string{"id==lease1"},
expected: []string{"lease1"},
},
{
name: "IDx2",
filters: []string{"id==lease1", "id==lease2"},
expected: []string{"lease1", "lease2"},
},
{
name: "Label1",
filters: []string{"labels.label1"},
expected: []string{"lease1", "lease2", "lease3"},
},
{
name: "Label1value1",
filters: []string{"labels.label1==value1"},
expected: []string{"lease1", "lease2"},
},
{
name: "Label1value2",
filters: []string{"labels.label1==value2"},
expected: []string{"lease3"},
},
{
name: "Label2",
filters: []string{"labels.label2"},
expected: []string{"lease3"},
},
{
name: "Label3",
filters: []string{"labels.label2", "labels.label3"},
expected: []string{"lease1", "lease2", "lease3"},
},
} {
t.Run(testcase.name, func(t *testing.T) {
if err := db.View(func(tx *bolt.Tx) error {
lm := NewLeaseManager(tx)
results, err := lm.List(ctx, testcase.filters...)
if err != nil {
return err
}
if len(results) != len(testcase.expected) {
t.Errorf("length of result does not match expected: %v != %v", len(results), len(testcase.expected))
}
expectedMap := map[string]struct{}{}
for _, expected := range testcase.expected {
expectedMap[expected] = struct{}{}
}
for _, result := range results {
if _, ok := expectedMap[result.ID]; !ok {
t.Errorf("unexpected match: %v", result.ID)
} else {
delete(expectedMap, result.ID)
}
}
if len(expectedMap) > 0 {
for match := range expectedMap {
t.Errorf("missing match: %v", match)
}
}
return nil
}); err != nil {
t.Fatal(err)
}
})
}
// delete everything to test it
for _, opts := range testset {
var lease leases.Lease
for _, opt := range opts {
if err := opt(&lease); err != nil {
t.Fatal(err)
}
}
if err := db.Update(func(tx *bolt.Tx) error {
lm := NewLeaseManager(tx)
return lm.Delete(ctx, lease)
}); err != nil {
t.Fatal(err)
}
// try it again, get nil
if err := db.Update(func(tx *bolt.Tx) error {
lm := NewLeaseManager(tx)
return lm.Delete(ctx, lease)
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
}
}