github.com/demonoid81/containerd@v1.3.4/metadata/content_test.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 "bytes" 21 "context" 22 "fmt" 23 "path/filepath" 24 "sync/atomic" 25 "testing" 26 27 "github.com/containerd/containerd/content" 28 "github.com/containerd/containerd/content/local" 29 "github.com/containerd/containerd/content/testsuite" 30 "github.com/containerd/containerd/errdefs" 31 "github.com/containerd/containerd/leases" 32 "github.com/containerd/containerd/namespaces" 33 digest "github.com/opencontainers/go-digest" 34 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 35 "github.com/pkg/errors" 36 bolt "go.etcd.io/bbolt" 37 ) 38 39 func createContentStore(ctx context.Context, root string, opts ...DBOpt) (context.Context, content.Store, func() error, error) { 40 // TODO: Use mocked or in-memory store 41 cs, err := local.NewStore(root) 42 if err != nil { 43 return nil, nil, nil, err 44 } 45 46 db, err := bolt.Open(filepath.Join(root, "metadata.db"), 0660, nil) 47 if err != nil { 48 return nil, nil, nil, err 49 } 50 51 var ( 52 count uint64 53 name = testsuite.Name(ctx) 54 ) 55 wrap := func(ctx context.Context) (context.Context, func(context.Context) error, error) { 56 n := atomic.AddUint64(&count, 1) 57 return namespaces.WithNamespace(ctx, fmt.Sprintf("%s-n%d", name, n)), func(context.Context) error { 58 return nil 59 }, nil 60 } 61 ctx = testsuite.SetContextWrapper(ctx, wrap) 62 63 return ctx, NewDB(db, cs, nil, opts...).ContentStore(), func() error { 64 return db.Close() 65 }, nil 66 } 67 68 func createContentStoreWithPolicy(opts ...DBOpt) testsuite.StoreInitFn { 69 return func(ctx context.Context, root string) (context.Context, content.Store, func() error, error) { 70 return createContentStore(ctx, root, opts...) 71 } 72 } 73 74 func TestContent(t *testing.T) { 75 testsuite.ContentSuite(t, "metadata", createContentStoreWithPolicy()) 76 testsuite.ContentCrossNSSharedSuite(t, "metadata", createContentStoreWithPolicy()) 77 testsuite.ContentCrossNSIsolatedSuite( 78 t, "metadata", createContentStoreWithPolicy([]DBOpt{ 79 WithPolicyIsolated, 80 }...)) 81 } 82 83 func TestContentLeased(t *testing.T) { 84 ctx, db, cancel := testDB(t) 85 defer cancel() 86 87 cs := db.ContentStore() 88 89 blob := []byte("any content") 90 expected := digest.FromBytes(blob) 91 92 lctx, _, err := createLease(ctx, db, "lease-1") 93 if err != nil { 94 t.Fatal(err) 95 } 96 if err := content.WriteBlob(lctx, cs, "test-1", bytes.NewReader(blob), 97 ocispec.Descriptor{Size: int64(len(blob)), Digest: expected}); err != nil { 98 t.Fatal(err) 99 } 100 if err := checkContentLeased(lctx, db, expected); err != nil { 101 t.Fatal("lease checked failed:", err) 102 } 103 if err := checkIngestLeased(lctx, db, "test-1"); err == nil { 104 t.Fatal("test-1 should not be leased after write") 105 } else if !errdefs.IsNotFound(err) { 106 t.Fatal("lease checked failed:", err) 107 } 108 109 lctx, _, err = createLease(ctx, db, "lease-2") 110 if err != nil { 111 t.Fatal(err) 112 } 113 114 if _, err := cs.Writer(lctx, 115 content.WithRef("test-2"), 116 content.WithDescriptor(ocispec.Descriptor{Size: int64(len(blob)), Digest: expected})); err == nil { 117 t.Fatal("expected already exist error") 118 } else if !errdefs.IsAlreadyExists(err) { 119 t.Fatal(err) 120 } 121 if err := checkContentLeased(lctx, db, expected); err != nil { 122 t.Fatal("lease checked failed:", err) 123 } 124 if err := checkIngestLeased(lctx, db, "test-2"); err == nil { 125 t.Fatal("test-2 should not be leased") 126 } else if !errdefs.IsNotFound(err) { 127 t.Fatal("lease checked failed:", err) 128 } 129 } 130 131 func TestIngestLeased(t *testing.T) { 132 ctx, db, cancel := testDB(t) 133 defer cancel() 134 135 cs := db.ContentStore() 136 137 blob := []byte("any content") 138 expected := digest.FromBytes(blob) 139 140 lctx, _, err := createLease(ctx, db, "lease-1") 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 w, err := cs.Writer(lctx, 146 content.WithRef("test-1"), 147 content.WithDescriptor(ocispec.Descriptor{Size: int64(len(blob)), Digest: expected})) 148 if err != nil { 149 t.Fatal(err) 150 } 151 err = checkIngestLeased(lctx, db, "test-1") 152 w.Close() 153 if err != nil { 154 t.Fatal("lease checked failed:", err) 155 } 156 157 if err := cs.Abort(lctx, "test-1"); err != nil { 158 t.Fatal(err) 159 } 160 161 if err := checkIngestLeased(lctx, db, "test-1"); err == nil { 162 t.Fatal("test-1 should not be leased after write") 163 } else if !errdefs.IsNotFound(err) { 164 t.Fatal("lease checked failed:", err) 165 } 166 } 167 168 func createLease(ctx context.Context, db *DB, name string) (context.Context, func() error, error) { 169 lm := NewLeaseManager(db) 170 if _, err := lm.Create(ctx, leases.WithID(name)); err != nil { 171 return nil, nil, err 172 } 173 return leases.WithLease(ctx, name), func() error { 174 return lm.Delete(ctx, leases.Lease{ 175 ID: name, 176 }) 177 }, nil 178 } 179 180 func checkContentLeased(ctx context.Context, db *DB, dgst digest.Digest) error { 181 ns, ok := namespaces.Namespace(ctx) 182 if !ok { 183 return errors.New("no namespace in context") 184 } 185 lease, ok := leases.FromContext(ctx) 186 if !ok { 187 return errors.New("no lease in context") 188 } 189 190 return db.View(func(tx *bolt.Tx) error { 191 bkt := getBucket(tx, bucketKeyVersion, []byte(ns), bucketKeyObjectLeases, []byte(lease), bucketKeyObjectContent) 192 if bkt == nil { 193 return errors.Wrapf(errdefs.ErrNotFound, "bucket not found %s", lease) 194 } 195 v := bkt.Get([]byte(dgst.String())) 196 if v == nil { 197 return errors.Wrap(errdefs.ErrNotFound, "object not leased") 198 } 199 200 return nil 201 }) 202 } 203 204 func checkIngestLeased(ctx context.Context, db *DB, ref string) error { 205 ns, ok := namespaces.Namespace(ctx) 206 if !ok { 207 return errors.New("no namespace in context") 208 } 209 lease, ok := leases.FromContext(ctx) 210 if !ok { 211 return errors.New("no lease in context") 212 } 213 214 return db.View(func(tx *bolt.Tx) error { 215 bkt := getBucket(tx, bucketKeyVersion, []byte(ns), bucketKeyObjectLeases, []byte(lease), bucketKeyObjectIngests) 216 if bkt == nil { 217 return errors.Wrapf(errdefs.ErrNotFound, "bucket not found %s", lease) 218 } 219 v := bkt.Get([]byte(ref)) 220 if v == nil { 221 return errors.Wrap(errdefs.ErrNotFound, "object not leased") 222 } 223 224 return nil 225 }) 226 }