diff --git a/metadata/adaptors.go b/metadata/adaptors.go index 8145f9ace..38539fdc1 100644 --- a/metadata/adaptors.go +++ b/metadata/adaptors.go @@ -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 diff --git a/metadata/content_test.go b/metadata/content_test.go index 8ca5eb4be..7fab6cfce 100644 --- a/metadata/content_test.go +++ b/metadata/content_test.go @@ -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") } diff --git a/metadata/leases.go b/metadata/leases.go index a1d303516..25a2a6692 100644 --- a/metadata/leases.go +++ b/metadata/leases.go @@ -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 { diff --git a/metadata/leases_test.go b/metadata/leases_test.go index 8f9456d6c..73b29efdf 100644 --- a/metadata/leases_test.go +++ b/metadata/leases_test.go @@ -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) + } + } +}