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  }