Skip to content

Commit

Permalink
satellite/overlay: Add ability to exclude country codes on upload
Browse files Browse the repository at this point in the history
Create global config to specify a list of country codes that should be
excluded from node selection during uploads.

This exclusion is not implemented when the upload selection cache is
disabled.

Change-Id: Ic41e8b4f18857a11045668eac23107da99668a72
  • Loading branch information
mobyvb authored and littleskunk committed Mar 8, 2022
1 parent d4a6524 commit 3bc2dcc
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 13 deletions.
15 changes: 12 additions & 3 deletions satellite/nodeselection/uploadselection/criteria.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ package uploadselection

import (
"storj.io/common/storj"
"storj.io/common/storj/location"
)

// Criteria to filter nodes.
type Criteria struct {
ExcludeNodeIDs []storj.NodeID
AutoExcludeSubnets map[string]struct{} // initialize it with empty map to keep only one node per subnet.
Placement storj.PlacementConstraint
ExcludeNodeIDs []storj.NodeID
AutoExcludeSubnets map[string]struct{} // initialize it with empty map to keep only one node per subnet.
Placement storj.PlacementConstraint
ExcludedCountryCodes []location.CountryCode
}

// MatchInclude returns with true if node is selected.
Expand All @@ -30,6 +32,13 @@ func (c *Criteria) MatchInclude(node *Node) bool {
}
c.AutoExcludeSubnets[node.LastNet] = struct{}{}
}

for _, code := range c.ExcludedCountryCodes {
if node.CountryCode == code {
return false
}
}

return true
}

Expand Down
16 changes: 11 additions & 5 deletions satellite/nodeselection/uploadselection/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/zeebo/errs"

"storj.io/common/storj"
"storj.io/common/storj/location"
)

// ErrNotEnoughNodes is when selecting nodes failed with the given parameters.
Expand Down Expand Up @@ -83,11 +84,12 @@ func NewState(reputableNodes, newNodes []*Node) *State {

// Request contains arguments for State.Request.
type Request struct {
Count int
NewFraction float64
Distinct bool
ExcludedIDs []storj.NodeID
Placement storj.PlacementConstraint
Count int
NewFraction float64
Distinct bool
ExcludedIDs []storj.NodeID
Placement storj.PlacementConstraint
ExcludedCountryCodes []string
}

// Select selects requestedCount nodes where there will be newFraction nodes.
Expand All @@ -111,6 +113,10 @@ func (state *State) Select(ctx context.Context, request Request) (_ []*Node, err
criteria.ExcludeNodeIDs = request.ExcludedIDs
}

for _, code := range request.ExcludedCountryCodes {
criteria.ExcludedCountryCodes = append(criteria.ExcludedCountryCodes, location.ToCountryCode(code))
}

criteria.Placement = request.Placement

if request.Distinct {
Expand Down
2 changes: 2 additions & 0 deletions satellite/overlay/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ type NodeSelectionConfig struct {
MinimumDiskSpace memory.Size `help:"how much disk space a node at minimum must have to be selected for upload" default:"500.00MB" testDefault:"100.00MB"`

AsOfSystemTime AsOfSystemTimeConfig

UploadExcludedCountryCodes []string `help:"list of country codes to exclude from node selection for uploads" default:"" testDefault:"FR,BE"`
}

// GeoIPConfig is a configuration struct that helps configure the GeoIP lookup features on the satellite.
Expand Down
1 change: 1 addition & 0 deletions satellite/overlay/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ func (service *Service) FindStorageNodesForUpload(ctx context.Context, req FindS
req.AsOfSystemInterval = service.config.Node.AsOfSystemTime.DefaultInterval
}

// TODO excluding country codes on upload if cache is disabled is not implemented
if service.config.NodeSelectionCache.Disabled {
return service.FindStorageNodesWithPreferences(ctx, req, &service.config.Node)
}
Expand Down
11 changes: 6 additions & 5 deletions satellite/overlay/uploadselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ func (cache *UploadSelectionCache) GetNodes(ctx context.Context, req FindStorage
}

selected, err := state.Select(ctx, uploadselection.Request{
Count: req.RequestedCount,
NewFraction: cache.selectionConfig.NewNodeFraction,
Distinct: cache.selectionConfig.DistinctIP,
ExcludedIDs: req.ExcludedIDs,
Placement: req.Placement,
Count: req.RequestedCount,
NewFraction: cache.selectionConfig.NewNodeFraction,
Distinct: cache.selectionConfig.DistinctIP,
ExcludedIDs: req.ExcludedIDs,
Placement: req.Placement,
ExcludedCountryCodes: cache.selectionConfig.UploadExcludedCountryCodes,
})
if uploadselection.ErrNotEnoughNodes.Has(err) {
err = ErrNotEnoughNodes.Wrap(err)
Expand Down
23 changes: 23 additions & 0 deletions satellite/overlay/uploadselection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"storj.io/common/sync2"
"storj.io/common/testcontext"
"storj.io/common/testrand"
"storj.io/storj/private/testplanet"
"storj.io/storj/satellite"
"storj.io/storj/satellite/overlay"
"storj.io/storj/satellite/satellitedb/satellitedbtest"
Expand Down Expand Up @@ -226,6 +227,28 @@ func TestGetNodes(t *testing.T) {
})
}

func TestGetNodesExcludeCountryCodes(t *testing.T) {
testplanet.Run(t, testplanet.Config{
SatelliteCount: 1, StorageNodeCount: 2, UplinkCount: 0,
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
err := planet.Satellites[0].Overlay.Service.TestNodeCountryCode(ctx, planet.StorageNodes[0].ID(), "FR")
require.NoError(t, err)

cache := planet.Satellites[0].Overlay.Service.UploadSelectionCache

// confirm cache.GetNodes returns the correct nodes
selectedNodes, err := cache.GetNodes(ctx, overlay.FindStorageNodesRequest{RequestedCount: 2})
// we only expect one node to be returned, even though we requested two, so there will be an error
require.Error(t, err)

_, new := cache.Size()
require.Equal(t, 2, new)
require.Equal(t, 1, len(selectedNodes))
// the node that was returned should be the one that does not have the "FR" country code
require.Equal(t, planet.StorageNodes[1].ID(), selectedNodes[0].ID)
})
}

func TestGetNodesConcurrent(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
Expand Down
3 changes: 3 additions & 0 deletions scripts/testdata/satellite-config.yaml.lock
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,9 @@ identity.key-path: /root/.local/share/storj/identity/satellite/identity.key
# the amount of time without seeing a node before its considered offline
# overlay.node.online-window: 4h0m0s

# list of country codes to exclude from node selection for uploads
# overlay.node.upload-excluded-country-codes: '[]'

# list of country codes to exclude nodes from target repair selection
# overlay.repair-excluded-country-codes: '[]'

Expand Down

0 comments on commit 3bc2dcc

Please sign in to comment.