Remove PointerDB client (#1520)

This commit is contained in:
Kaloyan Raev 2019-03-22 11:01:49 +02:00 committed by GitHub
parent db64d6590b
commit 30dfc2b20c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 45 additions and 1018 deletions

View File

@ -8,12 +8,11 @@ fi
RUN_PARAMS="${RUN_PARAMS:-} --config-dir ${CONF_PATH}"
if [[ -n "${API_KEY:-}" ]]; then
RUN_PARAMS="${RUN_PARAMS} --client.api-key ${API_KEY}"
RUN_PARAMS="${RUN_PARAMS} --api-key ${API_KEY}"
fi
if [ -n "${SATELLITE_ADDR:-}" ]; then
RUN_PARAMS="${RUN_PARAMS} --client.overlay-addr $SATELLITE_ADDR"
RUN_PARAMS="${RUN_PARAMS} --client.pointer-db-addr $SATELLITE_ADDR"
RUN_PARAMS="${RUN_PARAMS} --satellite-addr $SATELLITE_ADDR"
fi
exec ./gateway run $RUN_PARAMS "$@"

View File

@ -30,9 +30,7 @@ import (
// GatewayFlags configuration flags
type GatewayFlags struct {
Identity identity.Config
APIKey string `default:"" help:"the api key to use for the satellite" setup:"true"`
GenerateTestCerts bool `default:"false" help:"generate sample TLS certs for Minio GW" setup:"true"`
SatelliteAddr string `default:"localhost:7778" help:"the address to use for the satellite" setup:"true"`
GenerateTestCerts bool `default:"false" help:"generate sample TLS certs for Minio GW" setup:"true"`
Server miniogw.ServerConfig
Minio miniogw.MinioConfig
@ -109,12 +107,7 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
}
}
overrides := map[string]interface{}{
"client.api-key": setupCfg.APIKey,
"client.pointer-db-addr": setupCfg.SatelliteAddr,
"client.overlay-addr": setupCfg.SatelliteAddr,
}
overrides := map[string]interface{}{}
accessKeyFlag := cmd.Flag("minio.access-key")
if !accessKeyFlag.Changed {
accessKey, err := generateKey()

View File

@ -224,8 +224,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
"--server.address", process.Address,
"--client.overlay-addr", satellite.Address,
"--client.pointer-db-addr", satellite.Address,
"--satellite-addr", satellite.Address,
"--rs.min-threshold", strconv.Itoa(1 * flags.StorageNodeCount / 5),
"--rs.repair-threshold", strconv.Itoa(2 * flags.StorageNodeCount / 5),
@ -255,7 +254,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
// check if gateway config has an api key, if it's not
// create example project with key and add it to the config
// so that gateway can have access to the satellite
apiKey := vip.GetString("client.api-key")
apiKey := vip.GetString("api-key")
if apiKey == "" {
var consoleAddress string
satelliteConfigErr := readConfigString(&consoleAddress, satellite.Directory, "console.address")
@ -275,7 +274,7 @@ func newNetwork(flags *Flags) (*Processes, error) {
return err
}
vip.Set("client.api-key", apiKey)
vip.Set("api-key", apiKey)
if err := vip.WriteConfig(); err != nil {
return err

View File

@ -19,10 +19,7 @@ import (
// UplinkFlags configuration flags
type UplinkFlags struct {
Identity identity.Config
APIKey string `default:"" help:"the api key to use for the satellite" setup:"true"`
SatelliteAddr string `default:"localhost:7778" help:"the address to use for the satellite" setup:"true"`
Identity identity.Config
uplink.Config
}

View File

@ -55,11 +55,5 @@ func cmdSetup(cmd *cobra.Command, args []string) (err error) {
return err
}
overrides := map[string]interface{}{
"client.api-key": setupCfg.APIKey,
"client.pointer-db-addr": setupCfg.SatelliteAddr,
"client.overlay-addr": setupCfg.SatelliteAddr,
}
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), overrides)
return process.SaveConfigWithAllDefaults(cmd.Flags(), filepath.Join(setupDir, "config.yaml"), nil)
}

View File

@ -1,17 +0,0 @@
# gRPC Client + BoltDB Crud Interface
This is an example gRPC client which makes CRUD requests (create, read, update, delete) for storing pointers at given paths in BoltDB.
The gRPC server at `storj.io/storj/cmd/pointerdb/main.go` needs to be running for this to work.
To run the client:
```
go run examples/pointerdb-client/main.go
```
You can change the port number with a flag if necessary: `-port=<port-number>`
Afterward, you can use [Bolter](https://github.com/hasit/bolter) or a similar BoltDB viewer to make sure your pointer entries were changed as expected.
If changes are made to `storj.io/storj/pkg/pb/pointerdb.proto`, the protobuf file will need to be regenerated by running `go generate` inside `pkg/pb`.
Tests for this example code can be found in `storj.io/storj/pkg/pointerdb/client_test.go`.

View File

@ -1,128 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"context"
"flag"
"fmt"
"os"
"strings"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/peertls/tlsopts"
"storj.io/storj/pkg/pointerdb/pdbclient"
"storj.io/storj/pkg/storage/meta"
"storj.io/storj/pkg/transport"
)
var (
pointerdbClientPort string
ctx = context.Background()
)
func initializeFlags() {
flag.StringVar(&pointerdbClientPort, "pointerdbPort", ":8080", "this is your port")
flag.Parse()
}
func main() {
initializeFlags()
logger, _ := zap.NewDevelopment()
defer printError(logger.Sync)
identity, err := identity.NewFullIdentity(ctx, 12, 4)
if err != nil {
logger.Error("Failed to create full identity: ", zap.Error(err))
os.Exit(1)
}
clientOptions, err := tlsopts.NewOptions(identity, tlsopts.Config{})
if err != nil {
panic(err)
}
transportClient := transport.NewClient(clientOptions)
APIKey := "abc123"
client, err := pdbclient.NewClient(transportClient, pointerdbClientPort, APIKey)
if err != nil {
logger.Error("Failed to dial: ", zap.Error(err))
os.Exit(1)
}
logger.Debug(fmt.Sprintf("client dialed port %s", pointerdbClientPort))
ctx := context.Background()
// Example parameters to pass into API calls
var path = "fold1/fold2/fold3/file.txt"
pointer := &pb.Pointer{
Type: pb.Pointer_INLINE,
InlineSegment: []byte("popcorn"),
}
// Example Put1
err = client.Put(ctx, path, pointer)
if err != nil || status.Code(err) == codes.Internal {
logger.Error("couldn't put pointer in db", zap.Error(err))
} else {
logger.Debug("Success: put pointer in db")
}
// Example Put2
err = client.Put(ctx, "fold1/fold2", pointer)
if err != nil || status.Code(err) == codes.Internal {
logger.Error("couldn't put pointer in db", zap.Error(err))
} else {
logger.Debug("Success: put pointer in db")
}
// Example Get
getRes, _, _, err := client.Get(ctx, path)
if err != nil {
logger.Error("couldn't GET pointer from db", zap.Error(err))
} else {
logger.Info("Success: got Pointer from db",
zap.String("pointer", getRes.String()),
)
}
// Example List with pagination
items, more, err := client.List(ctx, "fold1", "", "", true, 1, meta.None)
if err != nil || status.Code(err) == codes.Internal {
logger.Error("failed to list file paths", zap.Error(err))
} else {
var stringList []string
for _, item := range items {
stringList = append(stringList, item.Path)
}
logger.Debug("Success: listed paths: " + strings.Join(stringList, ", ") + "; more: " + fmt.Sprintf("%t", more))
}
// Example Delete
err = client.Delete(ctx, path)
if err != nil || status.Code(err) == codes.Internal {
logger.Error("Error in deleteing file from db", zap.Error(err))
} else {
logger.Debug("Success: file is deleted from db")
}
}
func printError(fn func() error) {
err := fn()
if err != nil {
fmt.Println(err)
}
}

View File

@ -15,6 +15,6 @@ func TestCompile(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
exe := ctx.Compile("storj.io/storj/examples/pointerdb-client")
exe := ctx.Compile("storj.io/storj/examples/grpc-debug")
assert.NotEmpty(t, exe)
}

View File

@ -35,10 +35,6 @@ func TestBasic(t *testing.T) {
t.Log("UPLINK", uplink.ID(), uplink.Addr())
}
// Example of using pointer db
_, err = planet.Uplinks[0].DialPointerDB(planet.Satellites[0], "apikey")
require.NoError(t, err)
// ping a satellite
_, err = planet.StorageNodes[0].Kademlia.Service.Ping(ctx, planet.Satellites[0].Local())
require.NoError(t, err)

View File

@ -19,7 +19,6 @@ import (
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/peertls/tlsopts"
"storj.io/storj/pkg/pointerdb/pdbclient"
"storj.io/storj/pkg/storage/streams"
"storj.io/storj/pkg/storj"
"storj.io/storj/pkg/stream"
@ -125,12 +124,6 @@ func (uplink *Uplink) Local() pb.Node { return uplink.Info }
// Shutdown shuts down all uplink dependencies
func (uplink *Uplink) Shutdown() error { return nil }
// DialPointerDB dials destination with apikey and returns pointerdb Client
func (uplink *Uplink) DialPointerDB(destination Peer, apikey string) (pdbclient.Client, error) {
// TODO: handle disconnect
return pdbclient.NewClient(uplink.Transport, destination.Addr(), apikey)
}
// DialMetainfo dials destination with apikey and returns metainfo Client
func (uplink *Uplink) DialMetainfo(ctx context.Context, destination Peer, apikey string) (metainfo.Client, error) {
// TODO: handle disconnect
@ -298,8 +291,7 @@ func (uplink *Uplink) Delete(ctx context.Context, satellite *satellite.Peer, buc
func (uplink *Uplink) getConfig(satellite *satellite.Peer) uplink.Config {
config := getDefaultConfig()
config.Client.OverlayAddr = satellite.Addr()
config.Client.PointerDBAddr = satellite.Addr()
config.Client.SatelliteAddr = satellite.Addr()
config.Client.APIKey = uplink.APIKey[satellite.ID()]
config.RS.MinThreshold = 1 * uplink.StorageNodeCount / 5

View File

@ -29,10 +29,10 @@ func TestUploadDownload(t *testing.T) {
_, err = rand.Read(expectedData)
assert.NoError(t, err)
err = planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "test/bucket", "test/path", expectedData)
err = planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/path", expectedData)
assert.NoError(t, err)
data, err := planet.Uplinks[0].Download(ctx, planet.Satellites[0], "test/bucket", "test/path")
data, err := planet.Uplinks[0].Download(ctx, planet.Satellites[0], "testbucket", "test/path")
assert.NoError(t, err)
assert.Equal(t, expectedData, data)

View File

@ -108,8 +108,7 @@ func TestUplink(t *testing.T) {
func getConfig(satellite *satellite.Peer, planet *testplanet.Planet) ul.Config {
config := getDefaultConfig()
config.Client.OverlayAddr = satellite.Addr()
config.Client.PointerDBAddr = satellite.Addr()
config.Client.SatelliteAddr = satellite.Addr()
config.Client.APIKey = planet.Uplinks[0].APIKey[satellite.ID()]
config.RS.MinThreshold = 1 * len(planet.StorageNodes) / 5

View File

@ -102,7 +102,12 @@ func bindConfig(flags FlagSet, prefix string, val reflect.Value, vars map[string
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
fieldval := val.Field(i)
flagname := prefix + hyphenate(snakeCase(field.Name))
flagname := hyphenate(snakeCase(field.Name))
if field.Tag.Get("noprefix") != "true" {
flagname = prefix + flagname
}
if field.Tag.Get("internal") == "true" {
continue
}

View File

@ -5,7 +5,6 @@ package kvmetainfo_test
import (
"context"
"flag"
"fmt"
"testing"
@ -350,19 +349,7 @@ func newMetainfoParts(planet *testplanet.Planet) (*kvmetainfo.DB, buckets.Store,
return nil, nil, nil, err
}
TestAPIKey = apiKey.String()
err = flag.Set("pointer-db.auth.api-key", TestAPIKey)
if err != nil {
return nil, nil, nil, err
}
pdb, err := planet.Uplinks[0].DialPointerDB(planet.Satellites[0], TestAPIKey)
if err != nil {
return nil, nil, nil, err
}
metainfo, err := planet.Uplinks[0].DialMetainfo(context.Background(), planet.Satellites[0], TestAPIKey)
metainfo, err := planet.Uplinks[0].DialMetainfo(context.Background(), planet.Satellites[0], apiKey.String())
if err != nil {
return nil, nil, nil, err
}
@ -390,7 +377,7 @@ func newMetainfoParts(planet *testplanet.Planet) (*kvmetainfo.DB, buckets.Store,
buckets := buckets.NewStore(streams)
return kvmetainfo.New(buckets, streams, segments, pdb, key), buckets, streams, nil
return kvmetainfo.New(metainfo, buckets, streams, segments, key), buckets, streams, nil
}
func forAllCiphers(test func(cipher storj.Cipher)) {

View File

@ -8,12 +8,12 @@ import (
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/internal/memory"
"storj.io/storj/pkg/pointerdb/pdbclient"
"storj.io/storj/pkg/storage/buckets"
"storj.io/storj/pkg/storage/segments"
"storj.io/storj/pkg/storage/streams"
"storj.io/storj/pkg/storj"
"storj.io/storj/storage"
"storj.io/storj/uplink/metainfo"
)
var mon = monkit.Package()
@ -26,21 +26,21 @@ var _ storj.Metainfo = (*DB)(nil)
// DB implements metainfo database
type DB struct {
metainfo metainfo.Client
buckets buckets.Store
streams streams.Store
segments segments.Store
pointers pdbclient.Client
rootKey *storj.Key
}
// New creates a new metainfo database
func New(buckets buckets.Store, streams streams.Store, segments segments.Store, pointers pdbclient.Client, rootKey *storj.Key) *DB {
func New(metainfo metainfo.Client, buckets buckets.Store, streams streams.Store, segments segments.Store, rootKey *storj.Key) *DB {
return &DB{
metainfo: metainfo,
buckets: buckets,
streams: streams,
segments: segments,
pointers: pointers,
rootKey: rootKey,
}
}

View File

@ -235,7 +235,7 @@ func (db *DB) getInfo(ctx context.Context, prefix string, bucket string, path st
return object{}, storj.Object{}, err
}
pointer, _, _, err := db.pointers.Get(ctx, prefix+encryptedPath)
pointer, err := db.metainfo.SegmentInfo(ctx, bucket, storj.JoinPaths(storj.SplitPath(encryptedPath)[1:]...), -1)
if err != nil {
if storage.ErrKeyNotFound.Has(err) {
err = storj.ErrObjectNotFound.Wrap(err)

View File

@ -63,7 +63,6 @@ func (stream *readonlyStream) segment(ctx context.Context, index int64) (segment
copy(segment.EncryptedKeyNonce[:], segmentMeta.KeyNonce)
segment.EncryptedKey = segmentMeta.EncryptedKey
} else {
segmentPath = storj.JoinPaths("l", stream.encryptedPath)
segment.Size = stream.info.LastSegment.Size
segment.EncryptedKeyNonce = stream.info.LastSegment.EncryptedKeyNonce
segment.EncryptedKey = stream.info.LastSegment.EncryptedKey
@ -80,7 +79,15 @@ func (stream *readonlyStream) segment(ctx context.Context, index int64) (segment
return segment, err
}
pointer, _, _, err := stream.db.pointers.Get(ctx, segmentPath)
pathComponents := storj.SplitPath(stream.encryptedPath)
bucket := pathComponents[0]
segmentPath = storj.JoinPaths(pathComponents[1:]...)
if isLastSegment {
index = -1
}
pointer, err := stream.db.metainfo.SegmentInfo(ctx, bucket, segmentPath, index)
if err != nil {
return segment, err
}

View File

@ -7,7 +7,6 @@ import (
"bytes"
"context"
"encoding/hex"
"flag"
"fmt"
"strings"
"testing"
@ -679,19 +678,7 @@ func initEnv(planet *testplanet.Planet) (minio.ObjectLayer, storj.Metainfo, stre
return nil, nil, nil, err
}
TestAPIKey = apiKey.String()
err = flag.Set("pointer-db.auth.api-key", TestAPIKey)
if err != nil {
return nil, nil, nil, err
}
pdb, err := planet.Uplinks[0].DialPointerDB(planet.Satellites[0], TestAPIKey)
if err != nil {
return nil, nil, nil, err
}
metainfo, err := planet.Uplinks[0].DialMetainfo(context.Background(), planet.Satellites[0], TestAPIKey)
metainfo, err := planet.Uplinks[0].DialMetainfo(context.Background(), planet.Satellites[0], apiKey.String())
if err != nil {
return nil, nil, nil, err
}
@ -719,7 +706,7 @@ func initEnv(planet *testplanet.Planet) (minio.ObjectLayer, storj.Metainfo, stre
buckets := buckets.NewStore(streams)
kvmetainfo := kvmetainfo.New(buckets, streams, segments, pdb, key)
kvmetainfo := kvmetainfo.New(metainfo, buckets, streams, segments, key)
gateway := NewStorjGateway(
kvmetainfo,

View File

@ -63,9 +63,6 @@ func TestUploadDownload(t *testing.T) {
_, err = planet.Satellites[0].DB.Console().APIKeys().Create(context.Background(), apiKey, apiKeyInfo)
assert.NoError(t, err)
err = flag.Set("pointer-db.auth.api-key", apiKey.String())
assert.NoError(t, err)
// bind default values to config
var gwCfg config
cfgstruct.Bind(&pflag.FlagSet{}, &gwCfg, true)
@ -77,8 +74,7 @@ func TestUploadDownload(t *testing.T) {
// addresses
gwCfg.Server.Address = "127.0.0.1:7777"
uplinkCfg.Client.OverlayAddr = planet.Satellites[0].Addr()
uplinkCfg.Client.PointerDBAddr = planet.Satellites[0].Addr()
uplinkCfg.Client.SatelliteAddr = planet.Satellites[0].Addr()
// keys
uplinkCfg.Client.APIKey = "apiKey"

View File

@ -1,36 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package auth
import (
"crypto/subtle"
"flag"
)
var (
apiKey = flag.String("pointer-db.auth.api-key", "", "api key")
)
// Purpose of this is to process an API Key to see if it matches the correct client.
//
// **To use, run in** *examples/auth/main.go*:
// `$ go run main.go --key=yourkey`
//
// Default api key is preset with the mocked headers. This will be changed later.
//
// **Where this is going**:
// We're going to be using macaroons to validate a token and permissions. This is a small step to building in that direction.
// ValidateAPIKey : validates the X-API-Key header to an env/flag input
func ValidateAPIKey(header string) bool {
var expected = []byte(*apiKey)
var actual = []byte(header)
// TODO(kaloyan): I had to comment this to make pointerdb_test.go running successfully
// if len(expected) <= 0 {
// return false
// }
return 1 == subtle.ConstantTimeCompare(expected, actual)
}

View File

@ -1,170 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pdbclient
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/auth/grpcauth"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storj"
"storj.io/storj/pkg/transport"
"storj.io/storj/storage"
)
var (
mon = monkit.Package()
)
// PointerDB creates a grpcClient
type PointerDB struct {
client pb.PointerDBClient
}
// New Used as a public function
func New(gcclient pb.PointerDBClient) (pdbc *PointerDB) {
return &PointerDB{client: gcclient}
}
// a compiler trick to make sure *Overlay implements Client
var _ Client = (*PointerDB)(nil)
// ListItem is a single item in a listing
type ListItem struct {
Path storj.Path
Pointer *pb.Pointer
IsPrefix bool
}
// Client services offerred for the interface
type Client interface {
Put(ctx context.Context, path storj.Path, pointer *pb.Pointer) error
Get(ctx context.Context, path storj.Path) (*pb.Pointer, []*pb.Node, *pb.OrderLimit, error)
List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error)
Delete(ctx context.Context, path storj.Path) error
PayerBandwidthAllocation(context.Context, pb.BandwidthAction) (*pb.OrderLimit, error)
// Disconnect() error // TODO: implement
}
// NewClient initializes a new pointerdb client
func NewClient(tc transport.Client, address string, APIKey string) (*PointerDB, error) {
return NewClientContext(context.TODO(), tc, address, APIKey)
}
// NewClientContext initializes a new pointerdb client
func NewClientContext(ctx context.Context, tc transport.Client, address string, APIKey string) (*PointerDB, error) {
apiKeyInjector := grpcauth.NewAPIKeyInjector(APIKey)
conn, err := tc.DialAddress(
ctx,
address,
grpc.WithUnaryInterceptor(apiKeyInjector),
)
if err != nil {
return nil, err
}
return &PointerDB{client: pb.NewPointerDBClient(conn)}, nil
}
// a compiler trick to make sure *PointerDB implements Client
var _ Client = (*PointerDB)(nil)
// Put is the interface to make a PUT request, needs Pointer and APIKey
func (pdb *PointerDB) Put(ctx context.Context, path storj.Path, pointer *pb.Pointer) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = pdb.client.Put(ctx, &pb.PutRequest{Path: path, Pointer: pointer})
return err
}
// Get is the interface to make a GET request, needs PATH and APIKey
func (pdb *PointerDB) Get(ctx context.Context, path storj.Path) (pointer *pb.Pointer, nodes []*pb.Node, pba *pb.OrderLimit, err error) {
defer mon.Task()(&ctx)(&err)
res, err := pdb.client.Get(ctx, &pb.GetRequest{Path: path})
if err != nil {
if status.Code(err) == codes.NotFound {
return nil, nil, nil, storage.ErrKeyNotFound.Wrap(err)
}
return nil, nil, nil, Error.Wrap(err)
}
if res.GetPointer().GetType() == pb.Pointer_INLINE {
return res.GetPointer(), nodes, res.GetPba(), nil
}
pieces := res.GetPointer().GetRemote().GetRemotePieces()
nodes = make([]*pb.Node, len(pieces))
// fill missing nodes with nil values to match the size and order of remote pieces
j := 0
for i := 0; i < len(pieces); i++ {
if j == len(res.GetNodes()) {
break
}
if pieces[i].NodeId == res.GetNodes()[j].Id {
nodes[i] = res.GetNodes()[j]
j++
}
}
return res.GetPointer(), nodes, res.GetPba(), nil
}
// List is the interface to make a LIST request, needs StartingPathKey, Limit, and APIKey
func (pdb *PointerDB) List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error) {
defer mon.Task()(&ctx)(&err)
res, err := pdb.client.List(ctx, &pb.ListRequest{
Prefix: prefix,
StartAfter: startAfter,
EndBefore: endBefore,
Recursive: recursive,
Limit: int32(limit),
MetaFlags: metaFlags,
})
if err != nil {
return nil, false, err
}
list := res.GetItems()
items = make([]ListItem, len(list))
for i, itm := range list {
items[i] = ListItem{
Path: itm.GetPath(),
Pointer: itm.GetPointer(),
IsPrefix: itm.IsPrefix,
}
}
return items, res.GetMore(), nil
}
// Delete is the interface to make a Delete request, needs Path and APIKey
func (pdb *PointerDB) Delete(ctx context.Context, path storj.Path) (err error) {
defer mon.Task()(&ctx)(&err)
_, err = pdb.client.Delete(ctx, &pb.DeleteRequest{Path: path})
return err
}
// PayerBandwidthAllocation gets payer bandwidth allocation message
func (pdb *PointerDB) PayerBandwidthAllocation(ctx context.Context, action pb.BandwidthAction) (resp *pb.OrderLimit, err error) {
defer mon.Task()(&ctx)(&err)
response, err := pdb.client.PayerBandwidthAllocation(ctx, &pb.PayerBandwidthAllocationRequest{Action: action})
if err != nil {
return nil, err
}
return response.GetPba(), nil
}

View File

@ -1,300 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pdbclient
import (
"context"
"errors"
"fmt"
"log"
"strings"
"testing"
"github.com/gogo/protobuf/proto"
"github.com/golang/mock/gomock"
"github.com/golang/protobuf/ptypes"
"github.com/stretchr/testify/assert"
"storj.io/storj/internal/teststorj"
"storj.io/storj/pkg/auth"
"storj.io/storj/pkg/pb"
"storj.io/storj/pkg/storage/meta"
"storj.io/storj/pkg/storj"
)
const (
unauthenticated = "failed API creds"
noPathGiven = "file path not given"
)
var (
ErrUnauthenticated = errors.New(unauthenticated)
ErrNoFileGiven = errors.New(noPathGiven)
)
func TestNewPointerDBClient(t *testing.T) {
// mocked grpcClient so we don't have
// to call the network to test the code
ctrl := gomock.NewController(t)
defer ctrl.Finish()
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{client: gc}
assert.NotNil(t, pdb)
assert.NotNil(t, pdb.client)
}
func makePointer(path storj.Path) pb.PutRequest {
// rps is an example slice of RemotePieces to add to this
// REMOTE pointer type.
var rps []*pb.RemotePiece
rps = append(rps, &pb.RemotePiece{
PieceNum: 1,
NodeId: teststorj.NodeIDFromString("testId"),
})
pr := pb.PutRequest{
Path: path,
Pointer: &pb.Pointer{
Type: pb.Pointer_REMOTE,
Remote: &pb.RemoteSegment{
Redundancy: &pb.RedundancyScheme{
Type: pb.RedundancyScheme_RS,
MinReq: 1,
Total: 3,
RepairThreshold: 2,
SuccessThreshold: 3,
},
RootPieceId: teststorj.PieceIDFromString("testId"),
RemotePieces: rps,
},
SegmentSize: int64(1),
},
}
return pr
}
func TestPut(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
for i, tt := range []struct {
APIKey []byte
path storj.Path
err error
errString string
}{
{[]byte("abc123"), "file1/file2", nil, ""},
{[]byte("wrong key"), "file1/file2", ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), "", ErrNoFileGiven, noPathGiven},
{[]byte("wrong key"), "", ErrUnauthenticated, unauthenticated},
{[]byte(""), "", ErrUnauthenticated, unauthenticated},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
putRequest := makePointer(tt.path)
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{client: gc}
// here we don't care what type of context we pass
gc.EXPECT().Put(gomock.Any(), &putRequest).Return(nil, tt.err)
err := pdb.Put(ctx, tt.path, putRequest.Pointer)
if err != nil {
assert.EqualError(t, err, tt.errString, errTag)
} else {
assert.NoError(t, err, errTag)
}
}
}
func TestGet(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
for i, tt := range []struct {
APIKey []byte
path storj.Path
err error
errString string
}{
{[]byte("wrong key"), "file1/file2", ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), "", ErrNoFileGiven, noPathGiven},
{[]byte("wrong key"), "", ErrUnauthenticated, unauthenticated},
{[]byte(""), "", ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), "file1/file2", nil, ""},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
getPointer := makePointer(tt.path)
getRequest := pb.GetRequest{Path: tt.path}
data, err := proto.Marshal(getPointer.Pointer)
if err != nil {
log.Fatal("marshaling error: ", err)
}
byteData := data
ptr := &pb.Pointer{}
err = proto.Unmarshal(byteData, ptr)
assert.NoError(t, err)
getResponse := pb.GetResponse{Pointer: ptr, Nodes: []*pb.Node{}, Pba: &pb.OrderLimit{}}
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{client: gc}
gc.EXPECT().Get(gomock.Any(), &getRequest).Return(&getResponse, tt.err)
pointer, nodes, pba, err := pdb.Get(ctx, tt.path)
for _, v := range nodes {
if v != nil {
v.Type.DPanicOnInvalid("client test")
}
}
if err != nil {
assert.True(t, strings.Contains(err.Error(), tt.errString), errTag)
assert.Nil(t, pointer)
assert.Nil(t, nodes)
assert.Nil(t, pba)
} else {
assert.NotNil(t, pointer)
assert.NotNil(t, nodes)
assert.NotNil(t, pba)
assert.NoError(t, err, errTag)
}
}
}
func TestList(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
for i, tt := range []struct {
prefix string
startAfter string
endBefore string
recursive bool
limit int
metaFlags uint32
APIKey string
items []*pb.ListResponse_Item
more bool
err error
errString string
}{
{"", "", "", false, 0, meta.None, "",
[]*pb.ListResponse_Item{}, false, nil, ""},
{"", "", "", false, 0, meta.None, "",
[]*pb.ListResponse_Item{{}}, false, nil, ""},
{"", "", "", false, -1, meta.None, "",
[]*pb.ListResponse_Item{}, false, ErrUnauthenticated, unauthenticated},
{"prefix", "after", "before", false, 1, meta.None, "some key",
[]*pb.ListResponse_Item{
{Path: "a/b/c"},
},
true, nil, ""},
{"prefix", "after", "before", false, 1, meta.All, "some key",
[]*pb.ListResponse_Item{
{Path: "a/b/c", Pointer: &pb.Pointer{
SegmentSize: 1234,
CreationDate: ptypes.TimestampNow(),
ExpirationDate: ptypes.TimestampNow(),
}},
},
true, nil, ""},
{"some/prefix", "start/after", "end/before", true, 123, meta.Size, "some key",
[]*pb.ListResponse_Item{
{Path: "a/b/c", Pointer: &pb.Pointer{SegmentSize: 1234}},
{Path: "x/y", Pointer: &pb.Pointer{SegmentSize: 789}},
},
true, nil, ""},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, []byte(tt.APIKey))
errTag := fmt.Sprintf("Test case #%d", i)
listRequest := pb.ListRequest{
Prefix: tt.prefix,
StartAfter: tt.startAfter,
EndBefore: tt.endBefore,
Recursive: tt.recursive,
Limit: int32(tt.limit),
MetaFlags: tt.metaFlags,
}
listResponse := pb.ListResponse{Items: tt.items, More: tt.more}
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{client: gc}
gc.EXPECT().List(gomock.Any(), &listRequest).Return(&listResponse, tt.err)
items, more, err := pdb.List(ctx, tt.prefix, tt.startAfter, tt.endBefore, tt.recursive, tt.limit, tt.metaFlags)
if err != nil {
assert.EqualError(t, err, tt.errString, errTag)
assert.False(t, more)
assert.Nil(t, items)
} else {
assert.NoError(t, err, errTag)
assert.Equal(t, tt.more, more)
assert.NotNil(t, items)
assert.Equal(t, len(tt.items), len(items))
for i := 0; i < len(items); i++ {
assert.Equal(t, tt.items[i].GetPath(), items[i].Path)
assert.Equal(t, tt.items[i].GetPointer().GetSegmentSize(), items[i].Pointer.GetSegmentSize())
assert.Equal(t, tt.items[i].GetPointer().GetCreationDate(), items[i].Pointer.GetCreationDate())
assert.Equal(t, tt.items[i].GetPointer().GetExpirationDate(), items[i].Pointer.GetExpirationDate())
}
}
}
}
func TestDelete(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
for i, tt := range []struct {
APIKey []byte
path storj.Path
err error
errString string
}{
{[]byte("wrong key"), "file1/file2", ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), "", ErrNoFileGiven, noPathGiven},
{[]byte("wrong key"), "", ErrUnauthenticated, unauthenticated},
{[]byte(""), "", ErrUnauthenticated, unauthenticated},
{[]byte("abc123"), "file1/file2", nil, ""},
} {
ctx := context.Background()
ctx = auth.WithAPIKey(ctx, tt.APIKey)
deleteRequest := pb.DeleteRequest{Path: tt.path}
errTag := fmt.Sprintf("Test case #%d", i)
gc := NewMockPointerDBClient(ctrl)
pdb := PointerDB{client: gc}
gc.EXPECT().Delete(gomock.Any(), &deleteRequest).Return(nil, tt.err)
err := pdb.Delete(ctx, tt.path)
if err != nil {
assert.EqualError(t, err, tt.errString, errTag)
} else {
assert.NoError(t, err, errTag)
}
}
}

View File

@ -1,11 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package pdbclient
import (
"github.com/zeebo/errs"
)
// Error is the pdbclient error class
var Error = errs.Class("pointerdb client error")

View File

@ -1,114 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: storj.io/storj/pkg/pointerdb/pdbclient (interfaces: Client)
// Package mock_pointerdb is a generated GoMock package.
package mock_pointerdb
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
pb "storj.io/storj/pkg/pb"
pdbclient "storj.io/storj/pkg/pointerdb/pdbclient"
)
// MockClient is a mock of Client interface
type MockClient struct {
ctrl *gomock.Controller
recorder *MockClientMockRecorder
}
// MockClientMockRecorder is the mock recorder for MockClient
type MockClientMockRecorder struct {
mock *MockClient
}
// NewMockClient creates a new mock instance
func NewMockClient(ctrl *gomock.Controller) *MockClient {
mock := &MockClient{ctrl: ctrl}
mock.recorder = &MockClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockClient) EXPECT() *MockClientMockRecorder {
return m.recorder
}
// Delete mocks base method
func (m *MockClient) Delete(arg0 context.Context, arg1 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// Delete indicates an expected call of Delete
func (mr *MockClientMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), arg0, arg1)
}
// Get mocks base method
func (m *MockClient) Get(arg0 context.Context, arg1 string) (*pb.Pointer, []*pb.Node, *pb.OrderLimit, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Get", arg0, arg1)
ret0, _ := ret[0].(*pb.Pointer)
ret1, _ := ret[1].([]*pb.Node)
ret2, _ := ret[2].(*pb.OrderLimit)
ret3, _ := ret[3].(error)
return ret0, ret1, ret2, ret3
}
// Get indicates an expected call of Get
func (mr *MockClientMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0, arg1)
}
// List mocks base method
func (m *MockClient) List(arg0 context.Context, arg1, arg2, arg3 string, arg4 bool, arg5 int, arg6 uint32) ([]pdbclient.ListItem, bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", arg0, arg1, arg2, arg3, arg4, arg5, arg6)
ret0, _ := ret[0].([]pdbclient.ListItem)
ret1, _ := ret[1].(bool)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// List indicates an expected call of List
func (mr *MockClientMockRecorder) List(arg0, arg1, arg2, arg3, arg4, arg5, arg6 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List), arg0, arg1, arg2, arg3, arg4, arg5, arg6)
}
// PayerBandwidthAllocation mocks base method
func (m *MockClient) PayerBandwidthAllocation(arg0 context.Context, arg1 pb.BandwidthAction) (*pb.OrderLimit, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PayerBandwidthAllocation", arg0, arg1)
ret0, _ := ret[0].(*pb.OrderLimit)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// OrderLimit indicates an expected call of OrderLimit
func (mr *MockClientMockRecorder) PayerBandwidthAllocation(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PayerBandwidthAllocation", reflect.TypeOf((*MockClient)(nil).PayerBandwidthAllocation), arg0, arg1)
}
// Put mocks base method
func (m *MockClient) Put(arg0 context.Context, arg1 string, arg2 *pb.Pointer) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Put", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// Put indicates an expected call of Put
func (mr *MockClientMockRecorder) Put(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockClient)(nil).Put), arg0, arg1, arg2)
}

View File

@ -1,131 +0,0 @@
// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
// Code generated by MockGen. DO NOT EDIT.
// Source: storj.io/storj/pkg/pb (interfaces: PointerDBClient)
// Package pdbclient is a generated GoMock package.
package pdbclient
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
grpc "google.golang.org/grpc"
pb "storj.io/storj/pkg/pb"
)
// MockPointerDBClient is a mock of PointerDBClient interface
type MockPointerDBClient struct {
ctrl *gomock.Controller
recorder *MockPointerDBClientMockRecorder
}
// MockPointerDBClientMockRecorder is the mock recorder for MockPointerDBClient
type MockPointerDBClientMockRecorder struct {
mock *MockPointerDBClient
}
// NewMockPointerDBClient creates a new mock instance
func NewMockPointerDBClient(ctrl *gomock.Controller) *MockPointerDBClient {
mock := &MockPointerDBClient{ctrl: ctrl}
mock.recorder = &MockPointerDBClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockPointerDBClient) EXPECT() *MockPointerDBClientMockRecorder {
return m.recorder
}
// Delete mocks base method
func (m *MockPointerDBClient) Delete(arg0 context.Context, arg1 *pb.DeleteRequest, arg2 ...grpc.CallOption) (*pb.DeleteResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Delete", varargs...)
ret0, _ := ret[0].(*pb.DeleteResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Delete indicates an expected call of Delete
func (mr *MockPointerDBClientMockRecorder) Delete(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockPointerDBClient)(nil).Delete), varargs...)
}
// Get mocks base method
func (m *MockPointerDBClient) Get(arg0 context.Context, arg1 *pb.GetRequest, arg2 ...grpc.CallOption) (*pb.GetResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Get", varargs...)
ret0, _ := ret[0].(*pb.GetResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Get indicates an expected call of Get
func (mr *MockPointerDBClientMockRecorder) Get(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockPointerDBClient)(nil).Get), varargs...)
}
// List mocks base method
func (m *MockPointerDBClient) List(arg0 context.Context, arg1 *pb.ListRequest, arg2 ...grpc.CallOption) (*pb.ListResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "List", varargs...)
ret0, _ := ret[0].(*pb.ListResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// List indicates an expected call of List
func (mr *MockPointerDBClientMockRecorder) List(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockPointerDBClient)(nil).List), varargs...)
}
// PayerBandwidthAllocation mocks base method
func (m *MockPointerDBClient) PayerBandwidthAllocation(arg0 context.Context, arg1 *pb.PayerBandwidthAllocationRequest, arg2 ...grpc.CallOption) (*pb.PayerBandwidthAllocationResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "PayerBandwidthAllocation", varargs...)
ret0, _ := ret[0].(*pb.PayerBandwidthAllocationResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// OrderLimit indicates an expected call of OrderLimit
func (mr *MockPointerDBClientMockRecorder) PayerBandwidthAllocation(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PayerBandwidthAllocation", reflect.TypeOf((*MockPointerDBClient)(nil).PayerBandwidthAllocation), varargs...)
}
// Put mocks base method
func (m *MockPointerDBClient) Put(arg0 context.Context, arg1 *pb.PutRequest, arg2 ...grpc.CallOption) (*pb.PutResponse, error) {
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Put", varargs...)
ret0, _ := ret[0].(*pb.PutResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Put indicates an expected call of Put
func (mr *MockPointerDBClientMockRecorder) Put(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
varargs := append([]interface{}{arg0, arg1}, arg2...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockPointerDBClient)(nil).Put), varargs...)
}

View File

@ -16,7 +16,6 @@ import (
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/overlay"
"storj.io/storj/pkg/pb"
_ "storj.io/storj/pkg/pointerdb/auth" // ensures that we add api key flag to current executable
"storj.io/storj/pkg/storj"
"storj.io/storj/satellite/console"
"storj.io/storj/storage"

View File

@ -17,7 +17,6 @@ import (
"storj.io/storj/pkg/identity"
"storj.io/storj/pkg/metainfo/kvmetainfo"
"storj.io/storj/pkg/peertls/tlsopts"
"storj.io/storj/pkg/pointerdb/pdbclient"
"storj.io/storj/pkg/storage/buckets"
ecclient "storj.io/storj/pkg/storage/ec"
"storj.io/storj/pkg/storage/segments"
@ -50,11 +49,8 @@ type EncryptionConfig struct {
// ClientConfig is a configuration struct for the uplink that controls how
// to talk to the rest of the network.
type ClientConfig struct {
// TODO(jt): these should probably be the same
OverlayAddr string `help:"Address to contact overlay server through"`
PointerDBAddr string `help:"Address to contact pointerdb server through"`
APIKey string `help:"API Key (TODO: this needs to change to macaroons somehow)"`
APIKey string `default:"" help:"the api key to use for the satellite" noprefix:"true"`
SatelliteAddr string `default:"localhost:7778" help:"the address to use for the satellite" noprefix:"true"`
MaxInlineSize memory.Size `help:"max inline segment size in bytes" default:"4KiB"`
SegmentSize memory.Size `help:"the size of a segment in bytes" default:"64MiB"`
}
@ -84,27 +80,15 @@ func (c Config) GetMetainfo(ctx context.Context, identity *identity.FullIdentity
}
tc := transport.NewClient(tlsOpts)
if c.Client.OverlayAddr == "" || c.Client.PointerDBAddr == "" {
var errlist errs.Group
if c.Client.OverlayAddr == "" {
errlist.Add(errors.New("overlay address not specified"))
}
if c.Client.PointerDBAddr == "" {
errlist.Add(errors.New("pointerdb address not specified"))
}
return nil, nil, errlist.Err()
if c.Client.SatelliteAddr == "" {
return nil, nil, errors.New("satellite address not specified")
}
metainfo, err := metainfo.NewClient(ctx, tc, c.Client.PointerDBAddr, c.Client.APIKey)
metainfo, err := metainfo.NewClient(ctx, tc, c.Client.SatelliteAddr, c.Client.APIKey)
if err != nil {
return nil, nil, Error.New("failed to connect to metainfo service: %v", err)
}
pdb, err := pdbclient.NewClient(tc, c.Client.PointerDBAddr, c.Client.APIKey)
if err != nil {
return nil, nil, Error.New("failed to connect to pointer DB: %v", err)
}
ec := ecclient.NewClient(tc, c.RS.MaxBufferMem.Int())
fc, err := infectious.NewFEC(c.RS.MinThreshold, c.RS.MaxThreshold)
if err != nil {
@ -136,7 +120,7 @@ func (c Config) GetMetainfo(ctx context.Context, identity *identity.FullIdentity
buckets := buckets.NewStore(streams)
return kvmetainfo.New(buckets, streams, segments, pdb, key), streams, nil
return kvmetainfo.New(metainfo, buckets, streams, segments, key), streams, nil
}
// GetRedundancyScheme returns the configured redundancy scheme for new uploads