2019-02-04 16:56:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information
|
|
|
|
|
|
|
|
package testplanet_test
|
|
|
|
|
|
|
|
import (
|
2019-06-21 17:41:39 +01:00
|
|
|
"bytes"
|
2019-04-25 09:17:26 +01:00
|
|
|
"context"
|
2020-03-30 10:08:02 +01:00
|
|
|
"errors"
|
2019-06-21 17:41:39 +01:00
|
|
|
"fmt"
|
2019-02-04 16:56:10 +00:00
|
|
|
"testing"
|
2019-04-25 09:17:26 +01:00
|
|
|
"time"
|
2019-02-04 16:56:10 +00:00
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
2020-02-21 08:10:51 +00:00
|
|
|
"github.com/zeebo/errs"
|
2023-02-17 16:06:24 +00:00
|
|
|
"go.uber.org/zap"
|
2019-04-10 18:35:43 +01:00
|
|
|
"golang.org/x/sync/errgroup"
|
2019-02-04 16:56:10 +00:00
|
|
|
|
2019-12-27 11:48:47 +00:00
|
|
|
"storj.io/common/memory"
|
|
|
|
"storj.io/common/pb"
|
|
|
|
"storj.io/common/peertls/extensions"
|
|
|
|
"storj.io/common/peertls/tlsopts"
|
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/testrand"
|
2021-04-23 14:13:51 +01:00
|
|
|
"storj.io/storj/private/revocation"
|
|
|
|
"storj.io/storj/private/server"
|
2019-11-14 19:46:15 +00:00
|
|
|
"storj.io/storj/private/testplanet"
|
2020-03-30 10:08:02 +01:00
|
|
|
"storj.io/uplink"
|
2021-04-30 14:41:20 +01:00
|
|
|
"storj.io/uplink/private/metaclient"
|
2019-02-04 16:56:10 +00:00
|
|
|
)
|
|
|
|
|
2019-06-21 17:41:39 +01:00
|
|
|
func TestUplinksParallel(t *testing.T) {
|
2019-09-20 08:45:04 +01:00
|
|
|
const uplinkCount = 2
|
2019-06-21 17:41:39 +01:00
|
|
|
const parallelCount = 2
|
|
|
|
|
2019-04-22 10:07:50 +01:00
|
|
|
testplanet.Run(t, testplanet.Config{
|
2019-06-21 17:41:39 +01:00
|
|
|
SatelliteCount: 1, StorageNodeCount: 6, UplinkCount: uplinkCount,
|
2019-04-22 10:07:50 +01:00
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
2019-06-21 17:41:39 +01:00
|
|
|
satellite := planet.Satellites[0]
|
2019-02-04 16:56:10 +00:00
|
|
|
|
2019-06-21 17:41:39 +01:00
|
|
|
var group errgroup.Group
|
|
|
|
for i := range planet.Uplinks {
|
|
|
|
uplink := planet.Uplinks[i]
|
2019-02-04 16:56:10 +00:00
|
|
|
|
2019-06-21 17:41:39 +01:00
|
|
|
for p := 0; p < parallelCount; p++ {
|
|
|
|
suffix := fmt.Sprintf("-%d-%d", i, p)
|
|
|
|
group.Go(func() error {
|
2019-06-26 11:38:51 +01:00
|
|
|
data := testrand.Bytes(memory.Size(100+testrand.Intn(500)) * memory.KiB)
|
2019-06-21 17:41:39 +01:00
|
|
|
|
2019-06-26 11:38:51 +01:00
|
|
|
err := uplink.Upload(ctx, satellite, "testbucket"+suffix, "test/path"+suffix, data)
|
2019-06-21 17:41:39 +01:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
downloaded, err := uplink.Download(ctx, satellite, "testbucket"+suffix, "test/path"+suffix)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !bytes.Equal(data, downloaded) {
|
|
|
|
return fmt.Errorf("upload != download data: %s", suffix)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err := group.Wait()
|
|
|
|
require.NoError(t, err)
|
2019-04-22 10:07:50 +01:00
|
|
|
})
|
|
|
|
}
|
2019-02-04 16:56:10 +00:00
|
|
|
|
2019-04-22 10:07:50 +01:00
|
|
|
func TestDownloadWithSomeNodesOffline(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 5, UplinkCount: 1,
|
2020-01-21 10:38:41 +00:00
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Satellite: testplanet.ReconfigureRS(2, 3, 4, 5),
|
|
|
|
},
|
2019-04-22 10:07:50 +01:00
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
// first, upload some remote data
|
|
|
|
ul := planet.Uplinks[0]
|
|
|
|
satellite := planet.Satellites[0]
|
2019-02-04 16:56:10 +00:00
|
|
|
|
2019-06-26 11:38:51 +01:00
|
|
|
testData := testrand.Bytes(memory.MiB)
|
2019-03-26 18:09:44 +00:00
|
|
|
|
2020-01-21 10:38:41 +00:00
|
|
|
err := ul.Upload(ctx, satellite, "testbucket", "test/path", testData)
|
2019-04-22 10:07:50 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-12-22 10:38:32 +00:00
|
|
|
// get a remote segment
|
2021-09-07 09:15:47 +01:00
|
|
|
segments, err := satellite.Metabase.DB.TestingAllSegments(ctx)
|
2019-04-10 13:10:08 +01:00
|
|
|
require.NoError(t, err)
|
2019-04-22 10:07:50 +01:00
|
|
|
|
|
|
|
// calculate how many storagenodes to kill
|
2020-12-22 10:38:32 +00:00
|
|
|
redundancy := segments[0].Redundancy
|
|
|
|
remotePieces := segments[0].Pieces
|
|
|
|
minReq := redundancy.RequiredShares
|
2019-04-22 10:07:50 +01:00
|
|
|
numPieces := len(remotePieces)
|
|
|
|
toKill := numPieces - int(minReq)
|
|
|
|
|
2020-05-07 09:23:40 +01:00
|
|
|
for _, piece := range remotePieces[:toKill] {
|
2020-12-22 10:38:32 +00:00
|
|
|
err := planet.StopNodeAndUpdate(ctx, planet.FindNode(piece.StorageNode))
|
2020-05-07 09:23:40 +01:00
|
|
|
require.NoError(t, err)
|
2019-04-10 13:10:08 +01:00
|
|
|
}
|
2019-03-26 18:09:44 +00:00
|
|
|
|
2019-09-19 19:37:31 +01:00
|
|
|
// confirm that we marked the correct number of storage nodes as offline
|
2019-11-15 22:43:06 +00:00
|
|
|
nodes, err := satellite.Overlay.Service.Reliable(ctx)
|
2019-09-19 19:37:31 +01:00
|
|
|
require.NoError(t, err)
|
2020-05-07 09:23:40 +01:00
|
|
|
require.Len(t, nodes, len(planet.StorageNodes)-toKill)
|
2019-03-26 18:09:44 +00:00
|
|
|
|
2019-04-22 10:07:50 +01:00
|
|
|
// we should be able to download data without any of the original nodes
|
|
|
|
newData, err := ul.Download(ctx, satellite, "testbucket", "test/path")
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, testData, newData)
|
|
|
|
})
|
2019-03-26 18:09:44 +00:00
|
|
|
}
|
2019-04-10 18:35:43 +01:00
|
|
|
|
2019-04-25 09:17:26 +01:00
|
|
|
type piecestoreMock struct {
|
|
|
|
}
|
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
func (mock *piecestoreMock) Upload(server pb.DRPCPiecestore_UploadStream) error {
|
2019-04-25 09:17:26 +01:00
|
|
|
return nil
|
|
|
|
}
|
2020-02-26 13:59:46 +00:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
func (mock *piecestoreMock) Download(server pb.DRPCPiecestore_DownloadStream) error {
|
2019-04-25 09:17:26 +01:00
|
|
|
timoutTicker := time.NewTicker(30 * time.Second)
|
|
|
|
defer timoutTicker.Stop()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-timoutTicker.C:
|
|
|
|
return nil
|
|
|
|
case <-server.Context().Done():
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (mock *piecestoreMock) Delete(ctx context.Context, delete *pb.PieceDeleteRequest) (_ *pb.PieceDeleteResponse, err error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-12-18 15:33:12 +00:00
|
|
|
|
|
|
|
func (mock *piecestoreMock) DeletePieces(ctx context.Context, delete *pb.DeletePiecesRequest) (_ *pb.DeletePiecesResponse, err error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2019-07-11 21:04:22 +01:00
|
|
|
func (mock *piecestoreMock) Retain(ctx context.Context, retain *pb.RetainRequest) (_ *pb.RetainResponse, err error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-11-20 16:28:49 +00:00
|
|
|
func (mock *piecestoreMock) RestoreTrash(context.Context, *pb.RestoreTrashRequest) (*pb.RestoreTrashResponse, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2022-12-14 20:04:41 +00:00
|
|
|
func (mock *piecestoreMock) Exists(context.Context, *pb.ExistsRequest) (*pb.ExistsResponse, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-04-25 09:17:26 +01:00
|
|
|
|
|
|
|
func TestDownloadFromUnresponsiveNode(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 5, UplinkCount: 1,
|
2020-01-21 10:38:41 +00:00
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Satellite: testplanet.ReconfigureRS(2, 3, 4, 5),
|
|
|
|
},
|
2019-04-25 09:17:26 +01:00
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
2019-06-26 11:38:51 +01:00
|
|
|
expectedData := testrand.Bytes(memory.MiB)
|
2019-04-25 09:17:26 +01:00
|
|
|
|
2020-01-21 10:38:41 +00:00
|
|
|
err := planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "test/path", expectedData)
|
2019-04-25 09:17:26 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-12-08 13:29:58 +00:00
|
|
|
// get a remote segment from metabase
|
2021-09-07 09:15:47 +01:00
|
|
|
segments, err := planet.Satellites[0].Metabase.DB.TestingAllSegments(ctx)
|
2020-12-08 13:29:58 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, segments, 1)
|
|
|
|
require.NotEmpty(t, segments[0].Pieces)
|
2019-04-25 09:17:26 +01:00
|
|
|
|
|
|
|
// choose used storage node and replace it with fake listener
|
2020-12-08 13:29:58 +00:00
|
|
|
storageNode := planet.FindNode(segments[0].Pieces[0].StorageNode)
|
2020-05-06 15:35:18 +01:00
|
|
|
require.NotNil(t, storageNode)
|
2019-04-25 09:17:26 +01:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
err = planet.StopPeer(storageNode)
|
|
|
|
require.NoError(t, err)
|
2019-08-20 16:04:17 +01:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
wl, err := planet.WriteWhitelist(storj.LatestIDVersion())
|
|
|
|
require.NoError(t, err)
|
|
|
|
tlscfg := tlsopts.Config{
|
|
|
|
RevocationDBURL: "bolt://" + ctx.File("fakestoragenode", "revocation.db"),
|
|
|
|
UsePeerCAWhitelist: true,
|
|
|
|
PeerCAWhitelistPath: wl,
|
|
|
|
PeerIDVersions: "*",
|
|
|
|
Extensions: extensions.Config{
|
|
|
|
Revocation: false,
|
|
|
|
WhitelistSignedLeaf: false,
|
|
|
|
},
|
|
|
|
}
|
2019-08-20 16:04:17 +01:00
|
|
|
|
2020-10-28 14:01:41 +00:00
|
|
|
revocationDB, err := revocation.OpenDBFromCfg(ctx, tlscfg)
|
2020-05-06 15:35:18 +01:00
|
|
|
require.NoError(t, err)
|
2019-04-25 09:17:26 +01:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
tlsOptions, err := tlsopts.NewOptions(storageNode.Identity, tlscfg, revocationDB)
|
|
|
|
require.NoError(t, err)
|
2020-02-21 08:10:51 +00:00
|
|
|
|
2021-01-28 23:24:35 +00:00
|
|
|
server, err := server.New(storageNode.Log.Named("mock-server"), tlsOptions, storageNode.Config.Server)
|
2020-05-06 15:35:18 +01:00
|
|
|
require.NoError(t, err)
|
2020-02-21 08:10:51 +00:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
err = pb.DRPCRegisterPiecestore(server.DRPC(), &piecestoreMock{})
|
|
|
|
require.NoError(t, err)
|
2023-02-02 22:43:57 +00:00
|
|
|
err = pb.DRPCRegisterReplaySafePiecestore(server.ReplaySafeDRPC(), &piecestoreMock{})
|
|
|
|
require.NoError(t, err)
|
2020-02-21 08:10:51 +00:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
defer ctx.Check(server.Close)
|
2020-02-21 08:10:51 +00:00
|
|
|
|
2020-05-06 15:35:18 +01:00
|
|
|
subctx, subcancel := context.WithCancel(ctx)
|
|
|
|
defer subcancel()
|
|
|
|
ctx.Go(func() error {
|
|
|
|
if err := server.Run(subctx); err != nil {
|
|
|
|
return errs.Wrap(err)
|
2019-04-25 09:17:26 +01:00
|
|
|
}
|
2020-05-06 15:35:18 +01:00
|
|
|
|
|
|
|
return errs.Wrap(revocationDB.Close())
|
|
|
|
})
|
2019-04-25 09:17:26 +01:00
|
|
|
|
|
|
|
data, err := planet.Uplinks[0].Download(ctx, planet.Satellites[0], "testbucket", "test/path")
|
|
|
|
assert.NoError(t, err)
|
|
|
|
|
|
|
|
assert.Equal(t, expectedData, data)
|
|
|
|
})
|
|
|
|
}
|
2019-09-05 13:25:30 +01:00
|
|
|
|
|
|
|
func TestDeleteWithOfflineStoragenode(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 6, UplinkCount: 1,
|
2020-05-26 09:05:43 +01:00
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Satellite: testplanet.MaxSegmentSize(1 * memory.MiB),
|
|
|
|
},
|
2019-09-05 13:25:30 +01:00
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
expectedData := testrand.Bytes(5 * memory.MiB)
|
|
|
|
|
2020-05-26 09:05:43 +01:00
|
|
|
err := planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "test-bucket", "test-file", expectedData)
|
2019-09-05 13:25:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
for _, node := range planet.StorageNodes {
|
|
|
|
err = planet.StopPeer(node)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2020-02-10 12:18:18 +00:00
|
|
|
err = planet.Uplinks[0].DeleteObject(ctx, planet.Satellites[0], "test-bucket", "test-file")
|
2019-12-16 19:03:20 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
_, err = planet.Uplinks[0].Download(ctx, planet.Satellites[0], "test-bucket", "test-file")
|
2019-09-05 13:25:30 +01:00
|
|
|
require.Error(t, err)
|
2020-03-30 10:08:02 +01:00
|
|
|
require.True(t, errors.Is(err, uplink.ErrObjectNotFound))
|
2019-09-05 13:25:30 +01:00
|
|
|
|
2019-09-19 17:19:29 +01:00
|
|
|
key := planet.Uplinks[0].APIKey[planet.Satellites[0].ID()]
|
|
|
|
metainfoClient, err := planet.Uplinks[0].DialMetainfo(ctx, planet.Satellites[0], key)
|
2019-09-05 13:25:30 +01:00
|
|
|
require.NoError(t, err)
|
|
|
|
defer ctx.Check(metainfoClient.Close)
|
|
|
|
|
2021-04-30 14:41:20 +01:00
|
|
|
objects, _, err := metainfoClient.ListObjects(ctx, metaclient.ListObjectsParams{
|
2019-09-05 13:25:30 +01:00
|
|
|
Bucket: []byte("test-bucket"),
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 0, len(objects))
|
|
|
|
})
|
|
|
|
}
|
2020-12-04 11:06:06 +00:00
|
|
|
|
|
|
|
func TestUplinkOpenProject(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
project, err := planet.Uplinks[0].OpenProject(ctx, planet.Satellites[0])
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer ctx.Check(project.Close)
|
|
|
|
|
|
|
|
_, err = project.EnsureBucket(ctx, "bucket-name")
|
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
}
|
2023-02-17 16:06:24 +00:00
|
|
|
|
|
|
|
func TestUplinkDifferentPathCipher(t *testing.T) {
|
|
|
|
testplanet.Run(t, testplanet.Config{
|
|
|
|
SatelliteCount: 1, UplinkCount: 1,
|
|
|
|
Reconfigure: testplanet.Reconfigure{
|
|
|
|
Uplink: func(log *zap.Logger, index int, config *testplanet.UplinkConfig) {
|
|
|
|
config.DefaultPathCipher = storj.EncNull
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
|
|
|
err := planet.Uplinks[0].Upload(ctx, planet.Satellites[0], "testbucket", "object-name", []byte("data"))
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
objects, err := planet.Satellites[0].Metabase.DB.TestingAllObjects(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, objects, 1)
|
|
|
|
|
|
|
|
require.EqualValues(t, "object-name", objects[0].ObjectKey)
|
|
|
|
})
|
|
|
|
}
|