From 9bdcc415bcc9c64752845d2574fb1aac7532f57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rton=20Elek?= Date: Fri, 29 Oct 2021 14:29:14 +0200 Subject: [PATCH] satellite/nodeselection: add geofencing constraints to the node selection criteria Closes https://github.com/storj/storj/issues/4242 Change-Id: Ieda59a4f37c673e4e81abb4c89c09daf3199bbc7 --- go.mod | 2 +- go.sum | 4 +- .../nodeselection/uploadselection/criteria.go | 10 +++- .../uploadselection/criteria_test.go | 57 +++++++++++++++++++ .../nodeselection/uploadselection/node.go | 13 +++-- .../nodeselection/uploadselection/state.go | 3 + testsuite/go.mod | 2 +- testsuite/go.sum | 4 +- 8 files changed, 83 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 46873e8e6..6e1cedbb4 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e gopkg.in/segmentio/analytics-go.v3 v3.1.0 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c - storj.io/common v0.0.0-20211102144601-401a79f0706a + storj.io/common v0.0.0-20211108092228-14e900b161d9 storj.io/drpc v0.0.26 storj.io/monkit-jaeger v0.0.0-20210426161729-debb1cbcbbd7 storj.io/private v0.0.0-20211029202355-a7eae71c382a diff --git a/go.sum b/go.sum index 774cd7d60..c353d4ba4 100644 --- a/go.sum +++ b/go.sum @@ -880,8 +880,8 @@ sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3 storj.io/common v0.0.0-20200424175742-65ac59022f4f/go.mod h1:pZyXiIE7bGETIRXtfs0nICqMwp7PM8HqnDuyUeldNA0= storj.io/common v0.0.0-20210805073808-8e0feb09e92a/go.mod h1:mhZYWpTojKsACxWE66RfXNz19zbyr/uEDVWHJH8dHog= storj.io/common v0.0.0-20211019072056-34a5992b4856/go.mod h1:objobGrIWQwhmTSpSm6Y7ykd40wZjB7CezNfic5YLKg= -storj.io/common v0.0.0-20211102144601-401a79f0706a h1:6AF6LcFbZ8PtQX6omT6npU8Yhh1g7OG2emZKuLgS5+o= -storj.io/common v0.0.0-20211102144601-401a79f0706a/go.mod h1:a2Kw7Uipu929OFANfWKLHRoD0JfhgssikEvimd6hbSQ= +storj.io/common v0.0.0-20211108092228-14e900b161d9 h1:PxSH22djpdRU1wHS45QE6Yy9oSsBnl1frp2tURKOV1k= +storj.io/common v0.0.0-20211108092228-14e900b161d9/go.mod h1:a2Kw7Uipu929OFANfWKLHRoD0JfhgssikEvimd6hbSQ= storj.io/drpc v0.0.11/go.mod h1:TiFc2obNjL9/3isMW1Rpxjy8V9uE0B2HMeMFGiiI7Iw= storj.io/drpc v0.0.24/go.mod h1:ofQUDPQbbIymRDKE0tms48k8bLP5Y+dsI9CbXGv3gko= storj.io/drpc v0.0.26 h1:T6jJzjby7QUa/2XHR1qMxTCENpDHEw4/o+kfDfZQqQI= diff --git a/satellite/nodeselection/uploadselection/criteria.go b/satellite/nodeselection/uploadselection/criteria.go index 45ab19681..218f9692a 100644 --- a/satellite/nodeselection/uploadselection/criteria.go +++ b/satellite/nodeselection/uploadselection/criteria.go @@ -3,12 +3,15 @@ package uploadselection -import "storj.io/common/storj" +import ( + "storj.io/common/storj" +) // 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 } // MatchInclude returns with true if node is selected. @@ -16,6 +19,11 @@ func (c *Criteria) MatchInclude(node *Node) bool { if ContainsID(c.ExcludeNodeIDs, node.ID) { return false } + + if !c.Placement.AllowedCountry(node.CountryCode) { + return false + } + if c.AutoExcludeSubnets != nil { if _, excluded := c.AutoExcludeSubnets[node.LastNet]; excluded { return false diff --git a/satellite/nodeselection/uploadselection/criteria_test.go b/satellite/nodeselection/uploadselection/criteria_test.go index f382bb755..0d5320a6a 100644 --- a/satellite/nodeselection/uploadselection/criteria_test.go +++ b/satellite/nodeselection/uploadselection/criteria_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "storj.io/common/storj" + "storj.io/common/storj/location" "storj.io/common/testrand" ) @@ -81,3 +82,59 @@ func TestCriteria_NodeIDAndSubnet(t *testing.T) { })) } + +func TestCriteria_Geofencing(t *testing.T) { + eu := Criteria{ + Placement: storj.EU, + } + + us := Criteria{ + Placement: storj.US, + } + + cases := []struct { + name string + country location.CountryCode + criteria Criteria + expected bool + }{ + { + name: "US matches US selector", + country: location.UnitedStates, + criteria: us, + expected: true, + }, + { + name: "Germany is EU", + country: location.Germany, + criteria: eu, + expected: true, + }, + { + name: "US is not eu", + country: location.UnitedStates, + criteria: eu, + expected: false, + }, + { + name: "Empty country doesn't match region", + country: location.CountryCode(0), + criteria: eu, + expected: false, + }, + { + name: "Empty country doesn't match country", + country: location.CountryCode(0), + criteria: us, + expected: false, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + assert.Equal(t, c.expected, c.criteria.MatchInclude(&Node{ + CountryCode: c.country, + })) + }) + } +} diff --git a/satellite/nodeselection/uploadselection/node.go b/satellite/nodeselection/uploadselection/node.go index 68d9193bd..05a4cbf05 100644 --- a/satellite/nodeselection/uploadselection/node.go +++ b/satellite/nodeselection/uploadselection/node.go @@ -5,20 +5,23 @@ package uploadselection import ( "storj.io/common/storj" + "storj.io/common/storj/location" ) // Node defines necessary information for node-selection. type Node struct { storj.NodeURL - LastNet string - LastIPPort string + LastNet string + LastIPPort string + CountryCode location.CountryCode } // Clone returns a deep clone of the selected node. func (node *Node) Clone() *Node { return &Node{ - NodeURL: node.NodeURL, - LastNet: node.LastNet, - LastIPPort: node.LastIPPort, + NodeURL: node.NodeURL, + LastNet: node.LastNet, + LastIPPort: node.LastIPPort, + CountryCode: node.CountryCode, } } diff --git a/satellite/nodeselection/uploadselection/state.go b/satellite/nodeselection/uploadselection/state.go index 3cf000939..42f859a20 100644 --- a/satellite/nodeselection/uploadselection/state.go +++ b/satellite/nodeselection/uploadselection/state.go @@ -87,6 +87,7 @@ type Request struct { NewFraction float64 Distinct bool ExcludedIDs []storj.NodeID + Placement storj.PlacementConstraint } // Select selects requestedCount nodes where there will be newFraction nodes. @@ -110,6 +111,8 @@ func (state *State) Select(ctx context.Context, request Request) (_ []*Node, err criteria.ExcludeNodeIDs = request.ExcludedIDs } + criteria.Placement = request.Placement + if request.Distinct { criteria.AutoExcludeSubnets = make(map[string]struct{}) for _, id := range request.ExcludedIDs { diff --git a/testsuite/go.mod b/testsuite/go.mod index 53b93efc5..b6031b1ac 100644 --- a/testsuite/go.mod +++ b/testsuite/go.mod @@ -10,7 +10,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 go.uber.org/zap v1.17.0 - storj.io/common v0.0.0-20211102144601-401a79f0706a + storj.io/common v0.0.0-20211108092228-14e900b161d9 storj.io/gateway-mt v1.14.4-0.20211015103214-01eddbc864fb storj.io/private v0.0.0-20211029202355-a7eae71c382a storj.io/storj v0.12.1-0.20211102170500-1de8a695e84a diff --git a/testsuite/go.sum b/testsuite/go.sum index 3a75ba769..a414fc4ba 100644 --- a/testsuite/go.sum +++ b/testsuite/go.sum @@ -1396,8 +1396,8 @@ storj.io/common v0.0.0-20210805073808-8e0feb09e92a/go.mod h1:mhZYWpTojKsACxWE66R storj.io/common v0.0.0-20210916151047-6aaeb34bb916/go.mod h1:objobGrIWQwhmTSpSm6Y7ykd40wZjB7CezNfic5YLKg= storj.io/common v0.0.0-20211006105453-d3fff091f9d2/go.mod h1:objobGrIWQwhmTSpSm6Y7ykd40wZjB7CezNfic5YLKg= storj.io/common v0.0.0-20211019072056-34a5992b4856/go.mod h1:objobGrIWQwhmTSpSm6Y7ykd40wZjB7CezNfic5YLKg= -storj.io/common v0.0.0-20211102144601-401a79f0706a h1:6AF6LcFbZ8PtQX6omT6npU8Yhh1g7OG2emZKuLgS5+o= -storj.io/common v0.0.0-20211102144601-401a79f0706a/go.mod h1:a2Kw7Uipu929OFANfWKLHRoD0JfhgssikEvimd6hbSQ= +storj.io/common v0.0.0-20211108092228-14e900b161d9 h1:PxSH22djpdRU1wHS45QE6Yy9oSsBnl1frp2tURKOV1k= +storj.io/common v0.0.0-20211108092228-14e900b161d9/go.mod h1:a2Kw7Uipu929OFANfWKLHRoD0JfhgssikEvimd6hbSQ= storj.io/dotworld v0.0.0-20210324183515-0d11aeccd840/go.mod h1:KU9YvEgRrMMiWLvH8pzn1UkoCoxggKIPvQxmNdx7aXQ= storj.io/drpc v0.0.11/go.mod h1:TiFc2obNjL9/3isMW1Rpxjy8V9uE0B2HMeMFGiiI7Iw= storj.io/drpc v0.0.24/go.mod h1:ofQUDPQbbIymRDKE0tms48k8bLP5Y+dsI9CbXGv3gko=