2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-11-12 13:23:19 +00:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2019-02-04 16:56:10 +00:00
|
|
|
package kvmetainfo_test
|
2018-11-12 13:23:19 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
2019-03-18 10:55:06 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-11-12 13:23:19 +00:00
|
|
|
"github.com/vivint/infectious"
|
|
|
|
|
2019-06-24 20:23:07 +01:00
|
|
|
"storj.io/storj/pkg/encryption"
|
2019-05-24 17:51:27 +01:00
|
|
|
"storj.io/storj/pkg/macaroon"
|
2018-11-12 13:23:19 +00:00
|
|
|
"storj.io/storj/pkg/storj"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/memory"
|
|
|
|
"storj.io/storj/private/testcontext"
|
|
|
|
"storj.io/storj/private/testplanet"
|
2019-06-25 10:46:29 +01:00
|
|
|
"storj.io/storj/satellite/console"
|
2019-07-28 06:55:36 +01:00
|
|
|
"storj.io/storj/uplink/ecclient"
|
|
|
|
"storj.io/storj/uplink/eestream"
|
|
|
|
"storj.io/storj/uplink/metainfo/kvmetainfo"
|
|
|
|
"storj.io/storj/uplink/storage/segments"
|
|
|
|
"storj.io/storj/uplink/storage/streams"
|
2018-11-12 13:23:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
TestEncKey = "test-encryption-key"
|
2018-11-30 13:50:52 +00:00
|
|
|
TestBucket = "test-bucket"
|
2018-11-12 13:23:19 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestBucketsBasic(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2018-11-12 13:23:19 +00:00
|
|
|
// Create new bucket
|
2018-11-14 09:26:18 +00:00
|
|
|
bucket, err := db.CreateBucket(ctx, TestBucket, nil)
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, TestBucket, bucket.Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that bucket list include the new bucket
|
2018-11-14 09:26:18 +00:00
|
|
|
bucketList, err := db.ListBuckets(ctx, storj.BucketListOptions{Direction: storj.After})
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, bucketList.More)
|
|
|
|
assert.Equal(t, 1, len(bucketList.Items))
|
|
|
|
assert.Equal(t, TestBucket, bucketList.Items[0].Name)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we can get the new bucket explicitly
|
2018-11-14 09:26:18 +00:00
|
|
|
bucket, err = db.GetBucket(ctx, TestBucket)
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, TestBucket, bucket.Name)
|
2019-07-03 19:07:44 +01:00
|
|
|
assert.Equal(t, storj.EncAESGCM, bucket.PathCipher)
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the bucket
|
2018-11-14 09:26:18 +00:00
|
|
|
err = db.DeleteBucket(ctx, TestBucket)
|
2018-11-12 13:23:19 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
// Check that the bucket list is empty
|
2018-11-14 09:26:18 +00:00
|
|
|
bucketList, err = db.ListBuckets(ctx, storj.BucketListOptions{Direction: storj.After})
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, bucketList.More)
|
|
|
|
assert.Equal(t, 0, len(bucketList.Items))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the bucket cannot be get explicitly
|
2018-11-14 09:26:18 +00:00
|
|
|
bucket, err = db.GetBucket(ctx, TestBucket)
|
2018-11-21 11:17:28 +00:00
|
|
|
assert.True(t, storj.ErrBucketNotFound.Has(err))
|
2018-11-12 13:23:19 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
func TestBucketsReadWrite(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2019-06-21 12:29:31 +01:00
|
|
|
// Create new bucket
|
|
|
|
bucket, err := db.CreateBucket(ctx, TestBucket, nil)
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, TestBucket, bucket.Name)
|
|
|
|
}
|
2018-11-12 13:23:19 +00:00
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
// Check that bucket list include the new bucket
|
2018-11-14 09:26:18 +00:00
|
|
|
bucketList, err := db.ListBuckets(ctx, storj.BucketListOptions{Direction: storj.After})
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, bucketList.More)
|
|
|
|
assert.Equal(t, 1, len(bucketList.Items))
|
|
|
|
assert.Equal(t, TestBucket, bucketList.Items[0].Name)
|
|
|
|
}
|
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
// Check that we can get the new bucket explicitly
|
|
|
|
bucket, err = db.GetBucket(ctx, TestBucket)
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, TestBucket, bucket.Name)
|
2019-07-03 19:07:44 +01:00
|
|
|
assert.Equal(t, storj.EncAESGCM, bucket.PathCipher)
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
// Delete the bucket
|
|
|
|
err = db.DeleteBucket(ctx, TestBucket)
|
2018-11-12 13:23:19 +00:00
|
|
|
assert.NoError(t, err)
|
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
// Check that the bucket list is empty
|
2018-11-14 09:26:18 +00:00
|
|
|
bucketList, err = db.ListBuckets(ctx, storj.BucketListOptions{Direction: storj.After})
|
2018-11-12 13:23:19 +00:00
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, bucketList.More)
|
|
|
|
assert.Equal(t, 0, len(bucketList.Items))
|
|
|
|
}
|
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
// Check that the bucket cannot be get explicitly
|
2018-11-14 09:26:18 +00:00
|
|
|
bucket, err = db.GetBucket(ctx, TestBucket)
|
2018-11-21 11:17:28 +00:00
|
|
|
assert.True(t, storj.ErrBucketNotFound.Has(err))
|
2018-11-12 13:23:19 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestErrNoBucket(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2018-11-14 09:26:18 +00:00
|
|
|
_, err := db.CreateBucket(ctx, "", nil)
|
2018-11-12 13:23:19 +00:00
|
|
|
assert.True(t, storj.ErrNoBucket.Has(err))
|
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
_, err = db.GetBucket(ctx, "")
|
2018-11-12 13:23:19 +00:00
|
|
|
assert.True(t, storj.ErrNoBucket.Has(err))
|
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
err = db.DeleteBucket(ctx, "")
|
2018-11-12 13:23:19 +00:00
|
|
|
assert.True(t, storj.ErrNoBucket.Has(err))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
func TestBucketCreateCipher(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2019-07-03 19:07:44 +01:00
|
|
|
forAllCiphers(func(cipher storj.CipherSuite) {
|
2018-11-14 09:26:18 +00:00
|
|
|
bucket, err := db.CreateBucket(ctx, "test", &storj.Bucket{PathCipher: cipher})
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, cipher, bucket.PathCipher)
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket, err = db.GetBucket(ctx, "test")
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.Equal(t, cipher, bucket.PathCipher)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = db.DeleteBucket(ctx, "test")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2018-11-12 13:23:19 +00:00
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
func TestListBucketsEmpty(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2019-07-12 13:57:02 +01:00
|
|
|
bucketList, err := db.ListBuckets(ctx, storj.BucketListOptions{Direction: storj.Forward})
|
|
|
|
if assert.NoError(t, err) {
|
|
|
|
assert.False(t, bucketList.More)
|
|
|
|
assert.Equal(t, 0, len(bucketList.Items))
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestListBuckets(t *testing.T) {
|
2019-06-24 20:23:07 +01:00
|
|
|
runTest(t, func(t *testing.T, ctx context.Context, planet *testplanet.Planet, db *kvmetainfo.DB, streams streams.Store) {
|
2019-06-24 10:52:25 +01:00
|
|
|
bucketNames := []string{"a00", "aa0", "b00", "bb0", "c00"}
|
2018-11-12 13:23:19 +00:00
|
|
|
|
|
|
|
for _, name := range bucketNames {
|
2018-11-14 09:26:18 +00:00
|
|
|
_, err := db.CreateBucket(ctx, name, nil)
|
2019-03-18 10:55:06 +00:00
|
|
|
require.NoError(t, err)
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range []struct {
|
|
|
|
cursor string
|
|
|
|
dir storj.ListDirection
|
|
|
|
limit int
|
|
|
|
more bool
|
|
|
|
result []string
|
|
|
|
}{
|
2019-06-24 10:52:25 +01:00
|
|
|
{cursor: "", dir: storj.Forward, limit: 0, more: false, result: []string{"a00", "aa0", "b00", "bb0", "c00"}},
|
|
|
|
{cursor: "`", dir: storj.Forward, limit: 0, more: false, result: []string{"a00", "aa0", "b00", "bb0", "c00"}},
|
|
|
|
{cursor: "b00", dir: storj.Forward, limit: 0, more: false, result: []string{"b00", "bb0", "c00"}},
|
|
|
|
{cursor: "c00", dir: storj.Forward, limit: 0, more: false, result: []string{"c00"}},
|
2018-11-12 13:23:19 +00:00
|
|
|
{cursor: "ca", dir: storj.Forward, limit: 0, more: false, result: []string{}},
|
2019-06-24 10:52:25 +01:00
|
|
|
{cursor: "", dir: storj.Forward, limit: 1, more: true, result: []string{"a00"}},
|
|
|
|
{cursor: "`", dir: storj.Forward, limit: 1, more: true, result: []string{"a00"}},
|
|
|
|
{cursor: "aa0", dir: storj.Forward, limit: 1, more: true, result: []string{"aa0"}},
|
|
|
|
{cursor: "c00", dir: storj.Forward, limit: 1, more: false, result: []string{"c00"}},
|
2018-11-12 13:23:19 +00:00
|
|
|
{cursor: "ca", dir: storj.Forward, limit: 1, more: false, result: []string{}},
|
2019-06-24 10:52:25 +01:00
|
|
|
{cursor: "", dir: storj.Forward, limit: 2, more: true, result: []string{"a00", "aa0"}},
|
|
|
|
{cursor: "`", dir: storj.Forward, limit: 2, more: true, result: []string{"a00", "aa0"}},
|
|
|
|
{cursor: "aa0", dir: storj.Forward, limit: 2, more: true, result: []string{"aa0", "b00"}},
|
|
|
|
{cursor: "bb0", dir: storj.Forward, limit: 2, more: false, result: []string{"bb0", "c00"}},
|
|
|
|
{cursor: "c00", dir: storj.Forward, limit: 2, more: false, result: []string{"c00"}},
|
2018-11-12 13:23:19 +00:00
|
|
|
{cursor: "ca", dir: storj.Forward, limit: 2, more: false, result: []string{}},
|
|
|
|
} {
|
|
|
|
errTag := fmt.Sprintf("%d. %+v", i, tt)
|
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
bucketList, err := db.ListBuckets(ctx, storj.BucketListOptions{
|
2018-11-12 13:23:19 +00:00
|
|
|
Cursor: tt.cursor,
|
|
|
|
Direction: tt.dir,
|
|
|
|
Limit: tt.limit,
|
|
|
|
})
|
|
|
|
|
|
|
|
if assert.NoError(t, err, errTag) {
|
|
|
|
assert.Equal(t, tt.more, bucketList.More, errTag)
|
|
|
|
assert.Equal(t, tt.result, getBucketNames(bucketList), errTag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBucketNames(bucketList storj.BucketList) []string {
|
|
|
|
names := make([]string, len(bucketList.Items))
|
|
|
|
|
|
|
|
for i, item := range bucketList.Items {
|
|
|
|
names[i] = item.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
return names
|
|
|
|
}
|
|
|
|
|
2019-06-24 20:23:07 +01:00
|
|
|
func runTest(t *testing.T, test func(*testing.T, context.Context, *testplanet.Planet, *kvmetainfo.DB, streams.Store)) {
|
2019-04-22 10:07:50 +01:00
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 4, UplinkCount: 1,
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
2019-12-19 11:29:00 +00:00
|
|
|
db, streams, err := newMetainfoParts(planet, newTestEncStore(TestEncKey))
|
2019-04-22 10:07:50 +01:00
|
|
|
require.NoError(t, err)
|
2018-11-12 13:23:19 +00:00
|
|
|
|
2019-06-24 20:23:07 +01:00
|
|
|
test(t, ctx, planet, db, streams)
|
2019-04-22 10:07:50 +01:00
|
|
|
})
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-19 11:29:00 +00:00
|
|
|
func newTestEncStore(keyStr string) *encryption.Store {
|
|
|
|
key := new(storj.Key)
|
|
|
|
copy(key[:], keyStr)
|
|
|
|
|
|
|
|
store := encryption.NewStore()
|
|
|
|
store.SetDefaultKey(key)
|
|
|
|
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMetainfoParts(planet *testplanet.Planet, encStore *encryption.Store) (*kvmetainfo.DB, streams.Store, error) {
|
2018-11-12 13:23:19 +00:00
|
|
|
// TODO(kaloyan): We should have a better way for configuring the Satellite's API Key
|
2019-02-05 17:22:17 +00:00
|
|
|
// add project to satisfy constraint
|
|
|
|
project, err := planet.Satellites[0].DB.Console().Projects().Insert(context.Background(), &console.Project{
|
|
|
|
Name: "testProject",
|
|
|
|
})
|
2019-05-24 17:51:27 +01:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2019-05-24 17:51:27 +01:00
|
|
|
}
|
2019-02-05 17:22:17 +00:00
|
|
|
|
2019-05-24 17:51:27 +01:00
|
|
|
apiKey, err := macaroon.NewAPIKey([]byte("testSecret"))
|
2019-02-05 17:22:17 +00:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2019-02-05 17:22:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
apiKeyInfo := console.APIKeyInfo{
|
|
|
|
ProjectID: project.ID,
|
|
|
|
Name: "testKey",
|
2019-05-24 17:51:27 +01:00
|
|
|
Secret: []byte("testSecret"),
|
2019-02-05 17:22:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// add api key to db
|
2019-05-24 17:51:27 +01:00
|
|
|
_, err = planet.Satellites[0].DB.Console().APIKeys().Create(context.Background(), apiKey.Head(), apiKeyInfo)
|
2019-02-05 17:22:17 +00:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2019-02-05 17:22:17 +00:00
|
|
|
}
|
|
|
|
|
2019-09-19 17:19:29 +01:00
|
|
|
metainfo, err := planet.Uplinks[0].DialMetainfo(context.Background(), planet.Satellites[0], apiKey)
|
2018-11-12 13:23:19 +00:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
2019-06-25 16:36:23 +01:00
|
|
|
// TODO(leak): call metainfo.Close somehow
|
2018-11-12 13:23:19 +00:00
|
|
|
|
2019-09-19 05:46:39 +01:00
|
|
|
ec := ecclient.NewClient(planet.Uplinks[0].Log.Named("ecclient"), planet.Uplinks[0].Dialer, 0)
|
2018-11-12 13:23:19 +00:00
|
|
|
fc, err := infectious.NewFEC(2, 4)
|
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
2019-03-18 10:55:06 +00:00
|
|
|
rs, err := eestream.NewRedundancyStrategy(eestream.NewRSScheme(fc, 1*memory.KiB.Int()), 0, 0)
|
2018-11-12 13:23:19 +00:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-29 15:49:16 +00:00
|
|
|
segments := segments.NewSegmentStore(metainfo, ec, rs)
|
2018-11-12 13:23:19 +00:00
|
|
|
|
2019-06-11 18:14:05 +01:00
|
|
|
const stripesPerBlock = 2
|
|
|
|
blockSize := stripesPerBlock * rs.StripeSize()
|
2019-06-19 09:11:27 +01:00
|
|
|
inlineThreshold := 8 * memory.KiB.Int()
|
2019-10-29 15:49:16 +00:00
|
|
|
streams, err := streams.NewStreamStore(metainfo, segments, 64*memory.MiB.Int64(), encStore, blockSize, storj.EncAESGCM, inlineThreshold, 8*memory.MiB.Int64())
|
2018-11-12 13:23:19 +00:00
|
|
|
if err != nil {
|
2019-06-21 12:29:31 +01:00
|
|
|
return nil, nil, err
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|
2019-07-12 13:57:02 +01:00
|
|
|
proj := kvmetainfo.NewProject(streams, int32(blockSize), rs, 64*memory.MiB.Int64(), *metainfo)
|
2019-06-24 20:23:07 +01:00
|
|
|
return kvmetainfo.New(proj, metainfo, streams, segments, encStore), streams, nil
|
2018-11-14 09:26:18 +00:00
|
|
|
}
|
|
|
|
|
2019-07-03 19:07:44 +01:00
|
|
|
func forAllCiphers(test func(cipher storj.CipherSuite)) {
|
|
|
|
for _, cipher := range []storj.CipherSuite{
|
|
|
|
storj.EncNull,
|
|
|
|
storj.EncAESGCM,
|
|
|
|
storj.EncSecretBox,
|
2018-11-14 09:26:18 +00:00
|
|
|
} {
|
|
|
|
test(cipher)
|
|
|
|
}
|
2018-11-12 13:23:19 +00:00
|
|
|
}
|