storj/satellite/overlay/statdb_test.go
Márton Elek e2006d821c satellite/overlay: change Reliable and KnownReliable
as GetParticipatingNodes and GetNodes, respectively.

We now want these functions to include offline and suspended nodes as
well, so that we can force immediate repair when pieces are out of
placement or in excluded countries. With that change, the old names no
longer made sense.

Change-Id: Icbcbad43dbde0ca8cbc80a4d17a896bb89b078b7
2023-09-02 23:34:50 +00:00

286 lines
11 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package overlay_test
import (
"context"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/common/storj/location"
"storj.io/common/testcontext"
"storj.io/storj/satellite"
"storj.io/storj/satellite/nodeselection"
"storj.io/storj/satellite/overlay"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
)
func TestStatDB(t *testing.T) {
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
testDatabase(ctx, t, db.OverlayCache())
})
}
func testDatabase(ctx context.Context, t *testing.T, cache overlay.DB) {
for i, tt := range []struct {
nodeID storj.NodeID
unknownAuditSuspended bool
offlineSuspended bool
disqualified bool
offline bool
gracefullyExited bool
countryCode string
}{
{storj.NodeID{1}, false, false, false, false, false, "DE"}, // good
{storj.NodeID{2}, false, false, true, false, false, "DE"}, // disqualified
{storj.NodeID{3}, true, false, false, false, false, "DE"}, // unknown audit suspended
{storj.NodeID{4}, false, false, false, true, false, "DE"}, // offline
{storj.NodeID{5}, false, false, false, false, true, "DE"}, // gracefully exited
{storj.NodeID{6}, false, true, false, false, false, "DE"}, // offline suspended
{storj.NodeID{7}, false, false, false, false, false, "FR"}, // excluded country
{storj.NodeID{8}, false, false, false, false, false, ""}, // good
} {
addr := fmt.Sprintf("127.0.%d.0:8080", i)
lastNet := fmt.Sprintf("127.0.%d", i)
d := overlay.NodeCheckInInfo{
NodeID: tt.nodeID,
Address: &pb.NodeAddress{Address: addr},
LastIPPort: addr,
LastNet: lastNet,
Version: &pb.NodeVersion{Version: "v1.0.0"},
Capacity: &pb.NodeCapacity{},
IsUp: true,
CountryCode: location.ToCountryCode(tt.countryCode),
}
err := cache.UpdateCheckIn(ctx, d, time.Now().UTC(), overlay.NodeSelectionConfig{})
require.NoError(t, err)
if tt.unknownAuditSuspended {
err = cache.TestSuspendNodeUnknownAudit(ctx, tt.nodeID, time.Now())
require.NoError(t, err)
}
if tt.offlineSuspended {
err = cache.TestSuspendNodeOffline(ctx, tt.nodeID, time.Now())
require.NoError(t, err)
}
if tt.disqualified {
_, err = cache.DisqualifyNode(ctx, tt.nodeID, time.Now(), overlay.DisqualificationReasonUnknown)
require.NoError(t, err)
}
if tt.offline {
checkInInfo := getNodeInfo(tt.nodeID)
checkInInfo.CountryCode = location.ToCountryCode(tt.countryCode)
err = cache.UpdateCheckIn(ctx, checkInInfo, time.Now().Add(-2*time.Hour), overlay.NodeSelectionConfig{})
require.NoError(t, err)
}
if tt.gracefullyExited {
req := &overlay.ExitStatusRequest{
NodeID: tt.nodeID,
ExitInitiatedAt: time.Now(),
ExitLoopCompletedAt: time.Now(),
ExitFinishedAt: time.Now(),
}
_, err := cache.UpdateExitStatus(ctx, req)
require.NoError(t, err)
}
}
nodeIds := storj.NodeIDList{
storj.NodeID{1}, storj.NodeID{2},
storj.NodeID{3}, storj.NodeID{4},
storj.NodeID{5}, storj.NodeID{6},
storj.NodeID{7}, storj.NodeID{8},
storj.NodeID{9},
}
t.Run("GetNodes", func(t *testing.T) {
selectedNodes, err := cache.GetNodes(ctx, nodeIds, time.Hour, 0)
require.NoError(t, err)
require.Len(t, selectedNodes, len(nodeIds))
// disqualified/exited/unknown nodes should be returned as a zero-value SelectedNode in results
require.Zero(t, selectedNodes[1].ID) // #2 is disqualified
require.False(t, selectedNodes[1].Online)
require.Zero(t, selectedNodes[4].ID) // #5 gracefully exited
require.False(t, selectedNodes[4].Online)
require.Zero(t, selectedNodes[8].ID) // #9 is not in db
require.False(t, selectedNodes[8].Online)
require.Equal(t, nodeIds[0], selectedNodes[0].ID) // #1 is online
require.True(t, selectedNodes[0].Online)
require.Equal(t, "DE", selectedNodes[0].CountryCode.String())
require.Equal(t, nodeIds[2], selectedNodes[2].ID) // #3 is unknown-audit-suspended
require.True(t, selectedNodes[2].Online)
require.Equal(t, "DE", selectedNodes[2].CountryCode.String())
require.Equal(t, nodeIds[3], selectedNodes[3].ID) // #4 is offline
require.False(t, selectedNodes[3].Online)
require.Equal(t, "DE", selectedNodes[3].CountryCode.String())
require.Equal(t, nodeIds[5], selectedNodes[5].ID) // #6 is offline-suspended
require.True(t, selectedNodes[5].Online)
require.Equal(t, "DE", selectedNodes[5].CountryCode.String())
require.Equal(t, nodeIds[6], selectedNodes[6].ID) // #7 is in an excluded country
require.True(t, selectedNodes[6].Online)
require.Equal(t, "FR", selectedNodes[6].CountryCode.String())
require.Equal(t, nodeIds[7], selectedNodes[7].ID) // #8 is online but has no country code
require.True(t, selectedNodes[7].Online)
require.Equal(t, "", selectedNodes[7].CountryCode.String())
})
t.Run("GetParticipatingNodes", func(t *testing.T) {
allNodes, err := cache.GetParticipatingNodes(ctx, time.Hour, 0)
require.NoError(t, err)
expectOnline := func(t *testing.T, nodeList []nodeselection.SelectedNode, nodeID storj.NodeID, shouldBeOnline bool) {
for _, n := range nodeList {
if n.ID == nodeID {
if n.Online != shouldBeOnline {
require.Failf(t, "invalid Onlineness", "node %x was found in list, but Online=%v, whereas we expected Online=%v", n.ID[:], n.Online, shouldBeOnline)
}
return
}
}
require.Fail(t, "node not found in list", "node ID %x not found in list. list: %v", nodeID[:], nodeList)
}
expectOnline(t, allNodes, storj.NodeID{1}, true) // normal and online
expectOnline(t, allNodes, storj.NodeID{3}, true) // unknown audit suspended
expectOnline(t, allNodes, storj.NodeID{4}, false) // offline
expectOnline(t, allNodes, storj.NodeID{6}, true) // offline suspended
expectOnline(t, allNodes, storj.NodeID{7}, true) // excluded country
expectOnline(t, allNodes, storj.NodeID{8}, true) // normal and online, no country code
expectNotInList := func(t *testing.T, nodeList []nodeselection.SelectedNode, nodeID storj.NodeID) {
for index, n := range nodeList {
if n.ID == nodeID {
require.Failf(t, "not found in list", "node %x should not have been found in list, but it was found at index [%d].", nodeID[:], index)
}
}
}
expectNotInList(t, allNodes, storj.NodeID{2}) // disqualified
expectNotInList(t, allNodes, storj.NodeID{5}) // gracefully exited
expectNotInList(t, allNodes, storj.NodeID{9}) // not in db
require.Len(t, allNodes, 6)
})
t.Run("TestUpdateOperator", func(t *testing.T) {
nodeID := storj.NodeID{10}
addr := "127.0.1.0:8080"
lastNet := "127.0.1"
d := overlay.NodeCheckInInfo{
NodeID: nodeID,
Address: &pb.NodeAddress{Address: addr},
LastIPPort: addr,
LastNet: lastNet,
Version: &pb.NodeVersion{Version: "v1.0.0"},
Capacity: &pb.NodeCapacity{},
}
err := cache.UpdateCheckIn(ctx, d, time.Now().UTC(), overlay.NodeSelectionConfig{})
require.NoError(t, err)
update, err := cache.UpdateNodeInfo(ctx, nodeID, &overlay.InfoResponse{
Operator: &pb.NodeOperator{
Wallet: "0x1111111111111111111111111111111111111111",
Email: "abc123@mail.test",
WalletFeatures: []string{"wallet_features"},
},
})
require.NoError(t, err)
require.NotNil(t, update)
require.Equal(t, "0x1111111111111111111111111111111111111111", update.Operator.Wallet)
require.Equal(t, "abc123@mail.test", update.Operator.Email)
require.Equal(t, []string{"wallet_features"}, update.Operator.WalletFeatures)
found, err := cache.Get(ctx, nodeID)
require.NoError(t, err)
require.NotNil(t, found)
require.Equal(t, "0x1111111111111111111111111111111111111111", found.Operator.Wallet)
require.Equal(t, "abc123@mail.test", found.Operator.Email)
require.Equal(t, []string{"wallet_features"}, found.Operator.WalletFeatures)
updateEmail, err := cache.UpdateNodeInfo(ctx, nodeID, &overlay.InfoResponse{
Operator: &pb.NodeOperator{
Wallet: update.Operator.Wallet,
Email: "def456@mail.test",
WalletFeatures: update.Operator.WalletFeatures,
},
})
require.NoError(t, err)
assert.NotNil(t, updateEmail)
assert.Equal(t, "0x1111111111111111111111111111111111111111", updateEmail.Operator.Wallet)
assert.Equal(t, "def456@mail.test", updateEmail.Operator.Email)
assert.Equal(t, []string{"wallet_features"}, updateEmail.Operator.WalletFeatures)
updateWallet, err := cache.UpdateNodeInfo(ctx, nodeID, &overlay.InfoResponse{
Operator: &pb.NodeOperator{
Wallet: "0x2222222222222222222222222222222222222222",
Email: updateEmail.Operator.Email,
WalletFeatures: update.Operator.WalletFeatures,
},
})
require.NoError(t, err)
assert.NotNil(t, updateWallet)
assert.Equal(t, "0x2222222222222222222222222222222222222222", updateWallet.Operator.Wallet)
assert.Equal(t, "def456@mail.test", updateWallet.Operator.Email)
assert.Equal(t, []string{"wallet_features"}, updateWallet.Operator.WalletFeatures)
updateWalletFeatures, err := cache.UpdateNodeInfo(ctx, nodeID, &overlay.InfoResponse{
Operator: &pb.NodeOperator{
Wallet: updateWallet.Operator.Wallet,
Email: updateEmail.Operator.Email,
WalletFeatures: []string{"wallet_features_updated"},
},
})
require.NoError(t, err)
assert.NotNil(t, updateWalletFeatures)
assert.Equal(t, "0x2222222222222222222222222222222222222222", updateWalletFeatures.Operator.Wallet)
assert.Equal(t, "def456@mail.test", updateWalletFeatures.Operator.Email)
assert.Equal(t, []string{"wallet_features_updated"}, updateWalletFeatures.Operator.WalletFeatures)
})
// test UpdateCheckIn updates the reputation correctly when the node is offline/online
t.Run("UpdateCheckIn", func(t *testing.T) {
nodeID := storj.NodeID{1}
// get the existing node info that is stored in nodes table
_, err := cache.Get(ctx, nodeID)
require.NoError(t, err)
info := overlay.NodeCheckInInfo{
NodeID: nodeID,
Address: &pb.NodeAddress{
Address: "1.2.3.4",
},
IsUp: false,
Version: &pb.NodeVersion{
Version: "v0.0.0",
CommitHash: "",
Timestamp: time.Time{},
Release: false,
},
}
// update check-in when node is offline
err = cache.UpdateCheckIn(ctx, info, time.Now(), overlay.NodeSelectionConfig{})
require.NoError(t, err)
_, err = cache.Get(ctx, nodeID)
require.NoError(t, err)
info.IsUp = true
// update check-in when node is online
err = cache.UpdateCheckIn(ctx, info, time.Now(), overlay.NodeSelectionConfig{})
require.NoError(t, err)
_, err = cache.Get(ctx, nodeID)
require.NoError(t, err)
})
}