satellite/overlay: fix placement selection config parsing
When we do `satellite run api --placement '...'`, the placement rules are not parsed well. The problem is based on `viper.AllSettings()`, and the main logic is sg. like this (from a new unit test): ``` r := ConfigurablePlacementRule{} err := r.Set(p) require.NoError(t, err) serialized := r.String() r2 := ConfigurablePlacementRule{} err = r2.Set(serialized) require.NoError(t, err) require.Equal(t, p, r2.String()) ``` All settings evaluates the placement rules in `ConfigurablePlacementRules` and stores the string representation. The problem is that we don't have proper `String()` implementation (it prints out the structs instead of the original definition. There are two main solutions for this problem: 1. We can fix the `String()`. When we parse a placement rule, the `String()` method should print out the original definition 2. We can switch to use pure string as configuration parameter, and parse the rules only when required. I feel that 1 is error prone, we can do it (and in this patch I added a lot of `String()` implementations, but it's hard to be sure that our `String()` logic is inline with the parsing logic. Therefore I decided to make the configuration value of the placements a string (or a wrapper around string). That's the main reason why this patch seems to be big, as I updated all the usages. But the main part is in beginning of the `placement.go` (configuration parsing is not a pflag.Value implementation any more, but a separated step). And `filter.go`, (a few more String implementation for filters. https://github.com/storj/storj/issues/6248 Change-Id: I47c762d3514342b76a2e85683b1c891502a0756a
This commit is contained in:
parent
950672ca6c
commit
98921f9faa
@ -94,7 +94,12 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
|
||||
|
||||
dialer := rpc.NewDefaultDialer(tlsOptions)
|
||||
|
||||
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -104,7 +109,7 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
|
||||
signing.SignerFromFullIdentity(identity),
|
||||
overlayService,
|
||||
orders.NewNoopDB(),
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Orders,
|
||||
)
|
||||
if err != nil {
|
||||
@ -126,7 +131,7 @@ func cmdRepairSegment(cmd *cobra.Command, args []string) (err error) {
|
||||
overlayService,
|
||||
nil, // TODO add noop version
|
||||
ecRepairer,
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Checker.RepairOverrides,
|
||||
config.Repairer,
|
||||
)
|
||||
|
@ -203,12 +203,12 @@ func verifySegments(cmd *cobra.Command, args []string) error {
|
||||
dialer := rpc.NewDefaultDialer(tlsOptions)
|
||||
|
||||
// setup dependencies for verification
|
||||
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), overlay.NewPlacementRules().CreateFilters, "", "", satelliteCfg.Overlay)
|
||||
overlayService, err := overlay.NewService(log.Named("overlay"), db.OverlayCache(), db.NodeEvents(), overlay.NewPlacementDefinitions().CreateFilters, "", "", satelliteCfg.Overlay)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
||||
ordersService, err := orders.NewService(log.Named("orders"), signing.SignerFromFullIdentity(identity), overlayService, orders.NewNoopDB(), overlay.NewPlacementRules().CreateFilters, satelliteCfg.Orders)
|
||||
ordersService, err := orders.NewService(log.Named("orders"), signing.SignerFromFullIdentity(identity), overlayService, orders.NewNoopDB(), overlay.NewPlacementDefinitions().CreateFilters, satelliteCfg.Orders)
|
||||
if err != nil {
|
||||
return Error.Wrap(err)
|
||||
}
|
||||
|
@ -281,10 +281,15 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
})
|
||||
}
|
||||
|
||||
placements, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
{ // setup overlay
|
||||
peer.Overlay.DB = peer.DB.OverlayCache()
|
||||
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.Overlay.DB, peer.DB.NodeEvents(), config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.Overlay.DB, peer.DB.NodeEvents(), placements.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, peer.Close())
|
||||
}
|
||||
@ -374,6 +379,11 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
peer.OIDC.Service = oidc.NewService(db.OIDC())
|
||||
}
|
||||
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
{ // setup orders
|
||||
peer.Orders.DB = rollupsWriteCache
|
||||
peer.Orders.Chore = orders.NewChore(log.Named("orders:chore"), rollupsWriteCache, config.Orders)
|
||||
@ -390,7 +400,7 @@ func NewAPI(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
signing.SignerFromFullIdentity(peer.Identity),
|
||||
peer.Overlay.Service,
|
||||
peer.Orders.DB,
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Orders,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -139,9 +139,14 @@ func NewAuditor(log *zap.Logger, full *identity.FullIdentity,
|
||||
peer.Dialer = rpc.NewDefaultDialer(tlsOptions)
|
||||
}
|
||||
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
{ // setup overlay
|
||||
var err error
|
||||
peer.Overlay, err = overlay.NewService(log.Named("overlay"), overlayCache, nodeEvents, config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
peer.Overlay, err = overlay.NewService(log.Named("overlay"), overlayCache, nodeEvents, placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, peer.Close())
|
||||
}
|
||||
@ -174,7 +179,12 @@ func NewAuditor(log *zap.Logger, full *identity.FullIdentity,
|
||||
}
|
||||
|
||||
{ // setup orders
|
||||
var err error
|
||||
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.Orders.Service, err = orders.NewService(
|
||||
log.Named("orders"),
|
||||
signing.SignerFromFullIdentity(peer.Identity),
|
||||
@ -183,7 +193,7 @@ func NewAuditor(log *zap.Logger, full *identity.FullIdentity,
|
||||
// PUT and GET actions which are not used by
|
||||
// auditor so we can set noop implementation.
|
||||
orders.NewNoopDB(),
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Orders,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -248,8 +248,14 @@ func New(log *zap.Logger, full *identity.FullIdentity, db DB,
|
||||
}
|
||||
|
||||
{ // setup overlay
|
||||
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.Overlay.DB = peer.DB.OverlayCache()
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.Overlay.DB, peer.DB.NodeEvents(), config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.Overlay.DB, peer.DB.NodeEvents(), placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, peer.Close())
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ func TestAllInOne(t *testing.T) {
|
||||
log.Named("repair:checker"),
|
||||
satellite.DB.RepairQueue(),
|
||||
satellite.Overlay.Service,
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
satellite.Config.Checker,
|
||||
),
|
||||
})
|
||||
|
@ -303,16 +303,14 @@ func TestBucketCreationWithDefaultPlacement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetBucketLocation(t *testing.T) {
|
||||
placementRules := overlay.NewPlacementRules()
|
||||
err := placementRules.AddPlacementFromString(fmt.Sprintf(`40:annotated(annotated(country("PL"),annotation("%s","Poland")),annotation("%s","%s"))`,
|
||||
nodeselection.Location, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF))
|
||||
require.NoError(t, err)
|
||||
|
||||
testplanet.Run(t, testplanet.Config{
|
||||
SatelliteCount: 1, StorageNodeCount: 0, UplinkCount: 1,
|
||||
Reconfigure: testplanet.Reconfigure{
|
||||
Satellite: func(log *zap.Logger, index int, config *satellite.Config) {
|
||||
config.Placement = *placementRules
|
||||
config.Placement = overlay.ConfigurablePlacementRule{
|
||||
PlacementRules: fmt.Sprintf(`40:annotated(annotated(country("PL"),annotation("%s","Poland")),annotation("%s","%s"))`,
|
||||
nodeselection.Location, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF),
|
||||
}
|
||||
},
|
||||
},
|
||||
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||
|
@ -4,6 +4,12 @@
|
||||
package nodeselection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/common/storj"
|
||||
"storj.io/common/storj/location"
|
||||
)
|
||||
@ -38,6 +44,10 @@ func (a Annotation) GetAnnotation(name string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (a Annotation) String() string {
|
||||
return fmt.Sprintf(`annotation("%s","%s")`, a.Key, a.Value)
|
||||
}
|
||||
|
||||
var _ NodeFilterWithAnnotation = Annotation{}
|
||||
|
||||
// AnnotatedNodeFilter is just a NodeFilter with additional annotations.
|
||||
@ -64,6 +74,14 @@ func (a AnnotatedNodeFilter) Match(node *SelectedNode) bool {
|
||||
return a.Filter.Match(node)
|
||||
}
|
||||
|
||||
func (a AnnotatedNodeFilter) String() string {
|
||||
var annotationStr []string
|
||||
for _, annotation := range a.Annotations {
|
||||
annotationStr = append(annotationStr, annotation.String())
|
||||
}
|
||||
return fmt.Sprintf("%s && %s", a.Filter, strings.Join(annotationStr, " && "))
|
||||
}
|
||||
|
||||
// WithAnnotation adds annotations to a NodeFilter.
|
||||
func WithAnnotation(filter NodeFilter, name string, value string) NodeFilterWithAnnotation {
|
||||
return AnnotatedNodeFilter{
|
||||
@ -124,6 +142,15 @@ func (n NodeFilters) WithExcludedIDs(ds []storj.NodeID) NodeFilters {
|
||||
return append(n, ExcludedIDs(ds))
|
||||
}
|
||||
|
||||
func (n NodeFilters) String() string {
|
||||
var res []string
|
||||
for _, filter := range n {
|
||||
res = append(res, fmt.Sprintf("%s", filter))
|
||||
}
|
||||
sort.Strings(res)
|
||||
return strings.Join(res, " && ")
|
||||
}
|
||||
|
||||
// GetAnnotation implements NodeFilterWithAnnotation.
|
||||
func (n NodeFilters) GetAnnotation(name string) string {
|
||||
for _, filter := range n {
|
||||
@ -145,17 +172,68 @@ type CountryFilter struct {
|
||||
}
|
||||
|
||||
// NewCountryFilter creates a new CountryFilter.
|
||||
func NewCountryFilter(permit location.Set) NodeFilter {
|
||||
func NewCountryFilter(permit location.Set) *CountryFilter {
|
||||
return &CountryFilter{
|
||||
permit: permit,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCountryFilterFromString parses country definitions like 'hu','!hu','*','none' and creates a CountryFilter.
|
||||
func NewCountryFilterFromString(countries []string) (*CountryFilter, error) {
|
||||
var set location.Set
|
||||
for _, country := range countries {
|
||||
apply := func(modified location.Set, code ...location.CountryCode) location.Set {
|
||||
return modified.With(code...)
|
||||
}
|
||||
if country[0] == '!' {
|
||||
apply = func(modified location.Set, code ...location.CountryCode) location.Set {
|
||||
return modified.Without(code...)
|
||||
}
|
||||
country = country[1:]
|
||||
}
|
||||
switch strings.ToLower(country) {
|
||||
case "all", "*", "any":
|
||||
set = location.NewFullSet()
|
||||
case "none":
|
||||
set = apply(set, location.None)
|
||||
case "eu":
|
||||
set = apply(set, EuCountries...)
|
||||
case "eea":
|
||||
set = apply(set, EuCountries...)
|
||||
set = apply(set, EeaCountriesWithoutEu...)
|
||||
default:
|
||||
code := location.ToCountryCode(country)
|
||||
if code == location.None {
|
||||
return nil, errs.New("invalid country code %q", code)
|
||||
}
|
||||
set = apply(set, code)
|
||||
}
|
||||
}
|
||||
return NewCountryFilter(set), nil
|
||||
}
|
||||
|
||||
// Match implements NodeFilter interface.
|
||||
func (p *CountryFilter) Match(node *SelectedNode) bool {
|
||||
return p.permit.Contains(node.CountryCode)
|
||||
}
|
||||
|
||||
func (p *CountryFilter) String() string {
|
||||
var included, excluded []string
|
||||
for country, iso := range location.CountryISOCode {
|
||||
if p.permit.Contains(location.CountryCode(country)) {
|
||||
included = append(included, iso)
|
||||
} else {
|
||||
excluded = append(excluded, "!"+iso)
|
||||
}
|
||||
}
|
||||
if len(excluded) < len(included) {
|
||||
sort.Strings(excluded)
|
||||
return fmt.Sprintf(`country("*","%s")`, strings.Join(excluded, `","`))
|
||||
}
|
||||
sort.Strings(included)
|
||||
return fmt.Sprintf(`country("%s")`, strings.Join(included, `","`))
|
||||
}
|
||||
|
||||
var _ NodeFilter = &CountryFilter{}
|
||||
|
||||
// ExcludedNetworks will exclude nodes with specified networks.
|
||||
@ -234,6 +312,10 @@ func (t TagFilter) Match(node *SelectedNode) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t TagFilter) String() string {
|
||||
return fmt.Sprintf(`tag("%s","%s","%s")`, t.signer, t.name, string(t.value))
|
||||
}
|
||||
|
||||
var _ NodeFilter = TagFilter{}
|
||||
|
||||
// ExcludeFilter excludes only the matched nodes.
|
||||
@ -246,6 +328,10 @@ func (e ExcludeFilter) Match(node *SelectedNode) bool {
|
||||
return !e.matchToExclude.Match(node)
|
||||
}
|
||||
|
||||
func (e ExcludeFilter) String() string {
|
||||
return fmt.Sprintf("exclude(%s)", e.matchToExclude)
|
||||
}
|
||||
|
||||
// NewExcludeFilter creates filter, nodes matching the given filter will be excluded.
|
||||
func NewExcludeFilter(filter NodeFilter) ExcludeFilter {
|
||||
return ExcludeFilter{
|
||||
|
@ -5,6 +5,7 @@ package nodeselection
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -67,6 +68,12 @@ func TestAnnotations(t *testing.T) {
|
||||
Value: "bar",
|
||||
}
|
||||
require.Equal(t, "bar", k.GetAnnotation("foo"))
|
||||
|
||||
// annotation can be used as pure filters
|
||||
l := Annotation{Key: "foo", Value: "bar"}
|
||||
require.True(t, l.Match(&SelectedNode{}))
|
||||
|
||||
require.Equal(t, `annotation("foo","bar")`, l.String())
|
||||
}
|
||||
|
||||
func TestCriteria_Geofencing(t *testing.T) {
|
||||
@ -120,6 +127,69 @@ func TestCriteria_Geofencing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCountryFilter_FromString(t *testing.T) {
|
||||
cases := []struct {
|
||||
definition []string
|
||||
canonical string
|
||||
mustIncluded []location.CountryCode
|
||||
mustNotIncluded []location.CountryCode
|
||||
}{
|
||||
{
|
||||
definition: []string{"HU"},
|
||||
canonical: `country("HU")`,
|
||||
mustIncluded: []location.CountryCode{location.Hungary},
|
||||
mustNotIncluded: []location.CountryCode{location.Germany, location.UnitedStates},
|
||||
},
|
||||
{
|
||||
definition: []string{"EU"},
|
||||
canonical: "country(\"AT\",\"BE\",\"BG\",\"CY\",\"CZ\",\"DE\",\"DK\",\"EE\",\"ES\",\"FI\",\"FR\",\"GR\",\"HR\",\"HU\",\"IE\",\"IT\",\"LT\",\"LU\",\"LV\",\"MT\",\"NL\",\"PL\",\"PT\",\"RO\",\"SE\",\"SI\",\"SK\")",
|
||||
mustIncluded: []location.CountryCode{location.Hungary, location.Germany, location.Austria},
|
||||
mustNotIncluded: []location.CountryCode{location.Iceland, location.UnitedStates},
|
||||
},
|
||||
{
|
||||
definition: []string{"EEA"},
|
||||
canonical: "country(\"AT\",\"BE\",\"BG\",\"CY\",\"CZ\",\"DE\",\"DK\",\"EE\",\"ES\",\"FI\",\"FR\",\"GR\",\"HR\",\"HU\",\"IE\",\"IS\",\"IT\",\"LI\",\"LT\",\"LU\",\"LV\",\"MT\",\"NL\",\"NO\",\"PL\",\"PT\",\"RO\",\"SE\",\"SI\",\"SK\")",
|
||||
mustIncluded: []location.CountryCode{location.Hungary, location.Germany, location.Austria, location.Iceland},
|
||||
mustNotIncluded: []location.CountryCode{location.UnitedStates},
|
||||
},
|
||||
{
|
||||
definition: []string{"EU", "US"},
|
||||
canonical: "country(\"AT\",\"BE\",\"BG\",\"CY\",\"CZ\",\"DE\",\"DK\",\"EE\",\"ES\",\"FI\",\"FR\",\"GR\",\"HR\",\"HU\",\"IE\",\"IT\",\"LT\",\"LU\",\"LV\",\"MT\",\"NL\",\"PL\",\"PT\",\"RO\",\"SE\",\"SI\",\"SK\",\"US\")",
|
||||
mustIncluded: []location.CountryCode{location.Hungary, location.Germany, location.UnitedStates},
|
||||
mustNotIncluded: []location.CountryCode{location.Russia},
|
||||
},
|
||||
{
|
||||
definition: []string{"NONE"},
|
||||
canonical: "country(\"\")",
|
||||
mustIncluded: []location.CountryCode{},
|
||||
mustNotIncluded: []location.CountryCode{location.Germany, location.UnitedStates, location.Hungary},
|
||||
},
|
||||
{
|
||||
definition: []string{"*", "!RU", "!BY"},
|
||||
canonical: "country(\"*\",\"!BY\",\"!RU\")",
|
||||
mustIncluded: []location.CountryCode{location.Hungary},
|
||||
mustNotIncluded: []location.CountryCode{location.Russia, location.Belarus},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(strings.Join(tc.definition, "_"), func(t *testing.T) {
|
||||
filter, err := NewCountryFilterFromString(tc.definition)
|
||||
require.NoError(t, err)
|
||||
for _, c := range tc.mustIncluded {
|
||||
require.True(t, filter.Match(&SelectedNode{
|
||||
CountryCode: c,
|
||||
}), "Country %s should be included", c.String())
|
||||
}
|
||||
for _, c := range tc.mustNotIncluded {
|
||||
require.False(t, filter.Match(&SelectedNode{
|
||||
CountryCode: c,
|
||||
}), "Country %s shouldn't be included", c.String())
|
||||
}
|
||||
require.Equal(t, tc.canonical, filter.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkNodeFilterFullTable checks performances of rule evaluation on ALL storage nodes.
|
||||
func BenchmarkNodeFilterFullTable(b *testing.B) {
|
||||
filters := NodeFilters{}
|
||||
|
@ -57,7 +57,7 @@ func TestGetOrderLimits(t *testing.T) {
|
||||
Return(nodes, nil).AnyTimes()
|
||||
|
||||
service, err := orders.NewService(zaptest.NewLogger(t), k, overlayService, orders.NewNoopDB(),
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
orders.Config{
|
||||
EncryptionKeys: orders.EncryptionKeys{
|
||||
Default: orders.EncryptionKey{
|
||||
|
@ -362,7 +362,7 @@ func BenchmarkNodeSelection(b *testing.B) {
|
||||
}
|
||||
})
|
||||
|
||||
service, err := overlay.NewService(zap.NewNop(), overlaydb, db.NodeEvents(), overlay.NewPlacementRules().CreateFilters, "", "", overlay.Config{
|
||||
service, err := overlay.NewService(zap.NewNop(), overlaydb, db.NodeEvents(), overlay.NewPlacementDefinitions().CreateFilters, "", "", overlay.Config{
|
||||
Node: nodeSelectionConfig,
|
||||
NodeSelectionCache: overlay.UploadSelectionCacheConfig{
|
||||
Staleness: time.Hour,
|
||||
|
@ -31,7 +31,7 @@ func TestDownloadSelectionCacheState_Refresh(t *testing.T) {
|
||||
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||
cache, err := overlay.NewDownloadSelectionCache(zap.NewNop(),
|
||||
db.OverlayCache(),
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
downloadSelectionCacheConfig,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@ -64,7 +64,7 @@ func TestDownloadSelectionCacheState_GetNodeIPs(t *testing.T) {
|
||||
satellitedbtest.Run(t, func(ctx *testcontext.Context, t *testing.T, db satellite.DB) {
|
||||
cache, err := overlay.NewDownloadSelectionCache(zap.NewNop(),
|
||||
db.OverlayCache(),
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
downloadSelectionCacheConfig,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@ -116,7 +116,7 @@ func TestDownloadSelectionCache_GetNodes(t *testing.T) {
|
||||
// create new cache and select nodes
|
||||
cache, err := overlay.NewDownloadSelectionCache(zap.NewNop(),
|
||||
db.OverlayCache(),
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
overlay.DownloadSelectionCacheConfig{
|
||||
Staleness: time.Hour,
|
||||
OnlineWindow: time.Hour,
|
||||
|
@ -5,7 +5,6 @@ package overlay
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -21,52 +20,52 @@ import (
|
||||
// PlacementRules can crate filter based on the placement identifier.
|
||||
type PlacementRules func(constraint storj.PlacementConstraint) (filter nodeselection.NodeFilter)
|
||||
|
||||
// ConfigurablePlacementRule can include the placement definitions for each known identifier.
|
||||
type ConfigurablePlacementRule struct {
|
||||
// PlacementDefinitions can include the placement definitions for each known identifier.
|
||||
type PlacementDefinitions struct {
|
||||
placements map[storj.PlacementConstraint]nodeselection.NodeFilter
|
||||
}
|
||||
|
||||
// ConfigurablePlacementRule is a string configuration includes all placement rules in the form of id1:def1,id2:def2...
|
||||
type ConfigurablePlacementRule struct {
|
||||
PlacementRules string
|
||||
}
|
||||
|
||||
// String implements pflag.Value.
|
||||
func (d *ConfigurablePlacementRule) String() string {
|
||||
parts := []string{}
|
||||
for id, filter := range d.placements {
|
||||
// we can hide the internal rules...
|
||||
if id > 9 {
|
||||
// TODO: we need proper String implementation for all the used filters
|
||||
parts = append(parts, fmt.Sprintf("%d:%s", id, filter))
|
||||
}
|
||||
}
|
||||
return strings.Join(parts, ";")
|
||||
func (c *ConfigurablePlacementRule) String() string {
|
||||
return c.PlacementRules
|
||||
}
|
||||
|
||||
// Set implements pflag.Value.
|
||||
func (d *ConfigurablePlacementRule) Set(s string) error {
|
||||
if d.placements == nil {
|
||||
d.placements = map[storj.PlacementConstraint]nodeselection.NodeFilter{
|
||||
storj.EveryCountry: nodeselection.AnyFilter{},
|
||||
}
|
||||
}
|
||||
d.AddLegacyStaticRules()
|
||||
return d.AddPlacementFromString(s)
|
||||
func (c *ConfigurablePlacementRule) Set(s string) error {
|
||||
c.PlacementRules = s
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type implements pflag.Value.
|
||||
func (d *ConfigurablePlacementRule) Type() string {
|
||||
return "placement-rule"
|
||||
func (c *ConfigurablePlacementRule) Type() string {
|
||||
return "configurable-placement-rule"
|
||||
}
|
||||
|
||||
// Parse creates the PlacementDefinitions from the string rules.
|
||||
func (c ConfigurablePlacementRule) Parse() (*PlacementDefinitions, error) {
|
||||
d := NewPlacementDefinitions()
|
||||
d.AddLegacyStaticRules()
|
||||
err := d.AddPlacementFromString(c.PlacementRules)
|
||||
return d, err
|
||||
}
|
||||
|
||||
var _ pflag.Value = &ConfigurablePlacementRule{}
|
||||
|
||||
// NewPlacementRules creates a fully initialized NewPlacementRules.
|
||||
func NewPlacementRules() *ConfigurablePlacementRule {
|
||||
return &ConfigurablePlacementRule{
|
||||
// NewPlacementDefinitions creates a fully initialized NewPlacementDefinitions.
|
||||
func NewPlacementDefinitions() *PlacementDefinitions {
|
||||
return &PlacementDefinitions{
|
||||
placements: map[storj.PlacementConstraint]nodeselection.NodeFilter{
|
||||
storj.EveryCountry: nodeselection.AnyFilter{}},
|
||||
}
|
||||
}
|
||||
|
||||
// AddLegacyStaticRules initializes all the placement rules defined earlier in static golang code.
|
||||
func (d *ConfigurablePlacementRule) AddLegacyStaticRules() {
|
||||
func (d *PlacementDefinitions) AddLegacyStaticRules() {
|
||||
d.placements[storj.EEA] = nodeselection.NodeFilters{nodeselection.NewCountryFilter(location.NewSet(nodeselection.EeaCountriesWithoutEu...).With(nodeselection.EuCountries...))}
|
||||
d.placements[storj.EU] = nodeselection.NodeFilters{nodeselection.NewCountryFilter(location.NewSet(nodeselection.EuCountries...))}
|
||||
d.placements[storj.US] = nodeselection.NodeFilters{nodeselection.NewCountryFilter(location.NewSet(location.UnitedStates))}
|
||||
@ -75,54 +74,25 @@ func (d *ConfigurablePlacementRule) AddLegacyStaticRules() {
|
||||
}
|
||||
|
||||
// AddPlacementRule registers a new placement.
|
||||
func (d *ConfigurablePlacementRule) AddPlacementRule(id storj.PlacementConstraint, filter nodeselection.NodeFilter) {
|
||||
func (d *PlacementDefinitions) AddPlacementRule(id storj.PlacementConstraint, filter nodeselection.NodeFilter) {
|
||||
d.placements[id] = filter
|
||||
}
|
||||
|
||||
type stringNotMatch string
|
||||
|
||||
// AddPlacementFromString parses placement definition form string representations from id:definition;id:definition;...
|
||||
func (d *ConfigurablePlacementRule) AddPlacementFromString(definitions string) error {
|
||||
func (d *PlacementDefinitions) AddPlacementFromString(definitions string) error {
|
||||
env := map[any]any{
|
||||
"country": func(countries ...string) (nodeselection.NodeFilters, error) {
|
||||
var set location.Set
|
||||
for _, country := range countries {
|
||||
apply := func(modified location.Set, code ...location.CountryCode) location.Set {
|
||||
return modified.With(code...)
|
||||
}
|
||||
if country[0] == '!' {
|
||||
apply = func(modified location.Set, code ...location.CountryCode) location.Set {
|
||||
return modified.Without(code...)
|
||||
}
|
||||
country = country[1:]
|
||||
}
|
||||
switch strings.ToLower(country) {
|
||||
case "all", "*", "any":
|
||||
set = location.NewFullSet()
|
||||
case "none":
|
||||
set = apply(set, location.None)
|
||||
case "eu":
|
||||
set = apply(set, nodeselection.EuCountries...)
|
||||
case "eea":
|
||||
set = apply(set, nodeselection.EuCountries...)
|
||||
set = apply(set, nodeselection.EeaCountriesWithoutEu...)
|
||||
default:
|
||||
code := location.ToCountryCode(country)
|
||||
if code == location.None {
|
||||
return nil, errs.New("invalid country code %q", code)
|
||||
}
|
||||
set = apply(set, code)
|
||||
}
|
||||
}
|
||||
return nodeselection.NodeFilters{nodeselection.NewCountryFilter(set)}, nil
|
||||
"country": func(countries ...string) (nodeselection.NodeFilter, error) {
|
||||
return nodeselection.NewCountryFilterFromString(countries)
|
||||
},
|
||||
"placement": func(ix int64) nodeselection.NodeFilter {
|
||||
return d.placements[storj.PlacementConstraint(ix)]
|
||||
},
|
||||
"all": func(filters ...nodeselection.NodeFilters) (nodeselection.NodeFilters, error) {
|
||||
"all": func(filters ...nodeselection.NodeFilter) (nodeselection.NodeFilters, error) {
|
||||
res := nodeselection.NodeFilters{}
|
||||
for _, filter := range filters {
|
||||
res = append(res, filter...)
|
||||
res = append(res, filter)
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
@ -204,7 +174,7 @@ func (d *ConfigurablePlacementRule) AddPlacementFromString(definitions string) e
|
||||
}
|
||||
|
||||
// CreateFilters implements PlacementCondition.
|
||||
func (d *ConfigurablePlacementRule) CreateFilters(constraint storj.PlacementConstraint) (filter nodeselection.NodeFilter) {
|
||||
func (d *PlacementDefinitions) CreateFilters(constraint storj.PlacementConstraint) (filter nodeselection.NodeFilter) {
|
||||
if filters, found := d.placements[constraint]; found {
|
||||
return filters
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ func TestPlacementFromString(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("invalid country-code", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`1:country("ZZZZ")`)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("country tests", func(t *testing.T) {
|
||||
countryTest := func(placementDef string, shouldBeIncluded []location.CountryCode, shouldBeExcluded []location.CountryCode) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString("11:" + placementDef)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -53,15 +53,23 @@ func TestPlacementFromString(t *testing.T) {
|
||||
|
||||
t.Run("tag rule", func(t *testing.T) {
|
||||
tagged := func(key string, value string) nodeselection.NodeTags {
|
||||
return nodeselection.NodeTags{
|
||||
{
|
||||
Signer: signer,
|
||||
Name: key,
|
||||
Value: []byte(value),
|
||||
},
|
||||
return nodeselection.NodeTags{{
|
||||
Signer: signer,
|
||||
Name: key,
|
||||
Value: []byte(value),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:tag("12whfK1EDvHJtajBiAUeajQLYcWqxcQmdYQU5zX5cCf6bAxfgu4","foo","bar")`)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
require.NotNil(t, filters)
|
||||
require.True(t, filters.Match(&nodeselection.SelectedNode{
|
||||
Tags: tagged("foo", "bar"),
|
||||
}))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
placement string
|
||||
@ -122,7 +130,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(tc.placement)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -138,7 +146,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("placement reuse", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`1:tag("12whfK1EDvHJtajBiAUeajQLYcWqxcQmdYQU5zX5cCf6bAxfgu4","foo","bar");2:exclude(placement(1))`)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -173,7 +181,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
`11:all(country("GB"),tag("12whfK1EDvHJtajBiAUeajQLYcWqxcQmdYQU5zX5cCf6bAxfgu4","foo","bar"))`,
|
||||
`11:country("GB") && tag("12whfK1EDvHJtajBiAUeajQLYcWqxcQmdYQU5zX5cCf6bAxfgu4","foo","bar")`,
|
||||
} {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(syntax)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -203,14 +211,14 @@ func TestPlacementFromString(t *testing.T) {
|
||||
}))
|
||||
}
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString("10:1 && 2")
|
||||
require.Error(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("multi rule", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:country("GB");12:country("DE")`)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -222,6 +230,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
require.False(t, filters.Match(&nodeselection.SelectedNode{
|
||||
CountryCode: location.Germany,
|
||||
}))
|
||||
require.Equal(t, `country("GB")`, fmt.Sprintf("%s", filters))
|
||||
|
||||
filters = p.placements[storj.PlacementConstraint(12)]
|
||||
require.NotNil(t, filters)
|
||||
@ -237,7 +246,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
t.Run("annotation usage", func(t *testing.T) {
|
||||
t.Run("normal", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:annotated(country("GB"),annotation("autoExcludeSubnet","off"))`)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -249,7 +258,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
})
|
||||
t.Run("with &&", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:country("GB") && annotation("foo","bar") && annotation("bar","foo")`)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -263,7 +272,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
})
|
||||
t.Run("chained", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:annotated(annotated(country("GB"),annotation("foo","bar")),annotation("bar","foo"))`)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -276,7 +285,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
require.Equal(t, "", nodeselection.GetAnnotation(filters, "kossuth"))
|
||||
})
|
||||
t.Run("location", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
s := fmt.Sprintf(`11:annotated(annotated(country("GB"),annotation("%s","test-location")),annotation("%s","%s"))`, nodeselection.Location, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF)
|
||||
require.NoError(t, p.AddPlacementFromString(s))
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -290,7 +299,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("exclude", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
err := p.AddPlacementFromString(`11:exclude(country("GB"))`)
|
||||
require.NoError(t, err)
|
||||
filters := p.placements[storj.PlacementConstraint(11)]
|
||||
@ -303,7 +312,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("legacy geofencing rules", func(t *testing.T) {
|
||||
p := NewPlacementRules()
|
||||
p := NewPlacementDefinitions()
|
||||
p.AddLegacyStaticRules()
|
||||
|
||||
t.Run("nr", func(t *testing.T) {
|
||||
@ -336,7 +345,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
t.Run("full example", func(t *testing.T) {
|
||||
// this is a realistic configuration, compatible with legacy rules + using one node tag for specific placement
|
||||
|
||||
rules1 := NewPlacementRules()
|
||||
rules1 := NewPlacementDefinitions()
|
||||
err := rules1.AddPlacementFromString(`
|
||||
10:tag("12whfK1EDvHJtajBiAUeajQLYcWqxcQmdYQU5zX5cCf6bAxfgu4","selected",notEmpty());
|
||||
11:placement(10) && annotation("autoExcludeSubnet","off") && annotation("location","do-not-use");
|
||||
@ -350,7 +359,7 @@ func TestPlacementFromString(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// for countries, it should be the same as above
|
||||
rules2 := NewPlacementRules()
|
||||
rules2 := NewPlacementDefinitions()
|
||||
rules2.AddLegacyStaticRules()
|
||||
|
||||
testCountries := []location.CountryCode{
|
||||
@ -434,5 +443,27 @@ func TestPlacementFromString(t *testing.T) {
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestStringSerialization(t *testing.T) {
|
||||
placements := []string{
|
||||
`"10:country("GB")`,
|
||||
}
|
||||
for _, p := range placements {
|
||||
// this flow is very similar to the logic of our flag parsing,
|
||||
// where viper first parses the value, but later write it out to a string when viper.AllSettings() is called
|
||||
// the string representation should be parseable, and have the same information.
|
||||
|
||||
r := ConfigurablePlacementRule{}
|
||||
err := r.Set(p)
|
||||
require.NoError(t, err)
|
||||
serialized := r.String()
|
||||
|
||||
r2 := ConfigurablePlacementRule{}
|
||||
err = r2.Set(serialized)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, p, r2.String())
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -638,7 +638,7 @@ func runServiceWithDB(ctx *testcontext.Context, log *zap.Logger, reputable int,
|
||||
db.reputable = append(db.reputable, &node)
|
||||
}
|
||||
}
|
||||
service, _ := overlay.NewService(log, db, nil, overlay.NewPlacementRules().CreateFilters, "", "", config)
|
||||
service, _ := overlay.NewService(log, db, nil, overlay.NewPlacementDefinitions().CreateFilters, "", "", config)
|
||||
serviceCtx, cancel := context.WithCancel(ctx)
|
||||
ctx.Go(func() error {
|
||||
return service.Run(serviceCtx)
|
||||
|
@ -73,7 +73,7 @@ func testCache(ctx *testcontext.Context, t *testing.T, store overlay.DB, nodeEve
|
||||
|
||||
serviceCtx, serviceCancel := context.WithCancel(ctx)
|
||||
defer serviceCancel()
|
||||
service, err := overlay.NewService(zaptest.NewLogger(t), store, nodeEvents, overlay.NewPlacementRules().CreateFilters, "", "", serviceConfig)
|
||||
service, err := overlay.NewService(zaptest.NewLogger(t), store, nodeEvents, overlay.NewPlacementDefinitions().CreateFilters, "", "", serviceConfig)
|
||||
require.NoError(t, err)
|
||||
ctx.Go(func() error { return service.Run(serviceCtx) })
|
||||
defer ctx.Check(service.Close)
|
||||
|
@ -65,7 +65,7 @@ func TestRefresh(t *testing.T) {
|
||||
lowStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -162,7 +162,7 @@ func TestRefreshConcurrent(t *testing.T) {
|
||||
highStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -189,7 +189,7 @@ func TestRefreshConcurrent(t *testing.T) {
|
||||
lowStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
ctx.Go(func() error { return cache.Run(cacheCtx) })
|
||||
@ -214,7 +214,7 @@ func TestSelectNodes(t *testing.T) {
|
||||
DistinctIP: true,
|
||||
MinimumDiskSpace: 100 * memory.MiB,
|
||||
}
|
||||
placementRules := overlay.NewPlacementRules()
|
||||
placementRules := overlay.NewPlacementDefinitions()
|
||||
placementRules.AddPlacementRule(storj.PlacementConstraint(5), nodeselection.NodeFilters{}.WithCountryFilter(location.NewSet(location.Germany)))
|
||||
placementRules.AddPlacementRule(storj.PlacementConstraint(6), nodeselection.WithAnnotation(nodeselection.NodeFilters{}.WithCountryFilter(location.NewSet(location.Germany)), nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF))
|
||||
|
||||
@ -389,7 +389,7 @@ func TestGetNodesConcurrent(t *testing.T) {
|
||||
highStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -436,7 +436,7 @@ func TestGetNodesConcurrent(t *testing.T) {
|
||||
lowStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -528,7 +528,7 @@ func TestGetNodesDistinct(t *testing.T) {
|
||||
highStaleness,
|
||||
config,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -569,7 +569,7 @@ func TestGetNodesDistinct(t *testing.T) {
|
||||
highStaleness,
|
||||
config,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -594,7 +594,7 @@ func TestGetNodesError(t *testing.T) {
|
||||
highStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -623,7 +623,7 @@ func TestNewNodeFraction(t *testing.T) {
|
||||
lowStaleness,
|
||||
nodeSelectionConfig,
|
||||
nodeselection.NodeFilters{},
|
||||
overlay.NewPlacementRules().CreateFilters,
|
||||
overlay.NewPlacementDefinitions().CreateFilters,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -674,7 +674,7 @@ func BenchmarkGetNodes(b *testing.B) {
|
||||
defer cancel()
|
||||
log, err := zap.NewDevelopment()
|
||||
require.NoError(b, err)
|
||||
placement := overlay.NewPlacementRules()
|
||||
placement := overlay.NewPlacementDefinitions()
|
||||
placement.AddLegacyStaticRules()
|
||||
defaultFilter := nodeselection.NodeFilters{}
|
||||
|
||||
|
@ -139,7 +139,12 @@ func NewRangedLoop(log *zap.Logger, db DB, metabaseDB *metabase.DB, config *Conf
|
||||
}
|
||||
|
||||
{ // setup overlay
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.DB.OverlayCache(), peer.DB.NodeEvents(), config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.Overlay.Service, err = overlay.NewService(peer.Log.Named("overlay"), peer.DB.OverlayCache(), peer.DB.NodeEvents(), placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, peer.Close())
|
||||
}
|
||||
@ -151,6 +156,11 @@ func NewRangedLoop(log *zap.Logger, db DB, metabaseDB *metabase.DB, config *Conf
|
||||
}
|
||||
|
||||
{ // setup repair
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(config.Checker.RepairExcludedCountryCodes) == 0 {
|
||||
config.Checker.RepairExcludedCountryCodes = config.Overlay.RepairExcludedCountryCodes
|
||||
}
|
||||
@ -159,7 +169,7 @@ func NewRangedLoop(log *zap.Logger, db DB, metabaseDB *metabase.DB, config *Conf
|
||||
peer.Log.Named("repair:checker"),
|
||||
peer.DB.RepairQueue(),
|
||||
peer.Overlay.Service,
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Checker,
|
||||
)
|
||||
}
|
||||
|
@ -557,7 +557,7 @@ func BenchmarkRemoteSegment(b *testing.B) {
|
||||
}
|
||||
|
||||
observer := checker.NewObserver(zap.NewNop(), planet.Satellites[0].DB.RepairQueue(),
|
||||
planet.Satellites[0].Auditor.Overlay, overlay.NewPlacementRules().CreateFilters, planet.Satellites[0].Config.Checker)
|
||||
planet.Satellites[0].Auditor.Overlay, overlay.NewPlacementDefinitions().CreateFilters, planet.Satellites[0].Config.Checker)
|
||||
segments, err := planet.Satellites[0].Metabase.DB.TestingAllSegments(ctx)
|
||||
require.NoError(b, err)
|
||||
|
||||
@ -658,7 +658,7 @@ func TestObserver_PlacementCheck(t *testing.T) {
|
||||
}
|
||||
|
||||
// confirm that some pieces are out of placement
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, overlay.NewPlacementRules().CreateFilters(segments[0].Placement))
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, overlay.NewPlacementDefinitions().CreateFilters(segments[0].Placement))
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
|
||||
|
@ -52,7 +52,7 @@ func TestObserverForkProcess(t *testing.T) {
|
||||
statsCollector: make(map[string]*observerRSStats),
|
||||
nodesCache: &ReliabilityCache{
|
||||
staleness: time.Hour,
|
||||
placementRules: overlay.NewPlacementRules().CreateFilters,
|
||||
placementRules: overlay.NewPlacementDefinitions().CreateFilters,
|
||||
},
|
||||
}
|
||||
|
||||
@ -144,11 +144,13 @@ func TestObserverForkProcess(t *testing.T) {
|
||||
|
||||
placements := overlay.ConfigurablePlacementRule{}
|
||||
require.NoError(t, placements.Set(fmt.Sprintf(`10:annotated(country("DE"),annotation("%s","%s"))`, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF)))
|
||||
o.nodesCache.placementRules = placements.CreateFilters
|
||||
parsed, err := placements.Parse()
|
||||
require.NoError(t, err)
|
||||
o.nodesCache.placementRules = parsed.CreateFilters
|
||||
|
||||
q := queue.MockRepairQueue{}
|
||||
fork := createFork(o, &q)
|
||||
err := fork.process(ctx, &rangedloop.Segment{
|
||||
err = fork.process(ctx, &rangedloop.Segment{
|
||||
Placement: 10,
|
||||
Pieces: createPieces(nodes, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
|
||||
Redundancy: storj.RedundancyScheme{
|
||||
|
@ -29,7 +29,7 @@ func TestReliabilityCache_Concurrent(t *testing.T) {
|
||||
ctx := testcontext.New(t)
|
||||
defer ctx.Cleanup()
|
||||
|
||||
overlayCache, err := overlay.NewService(zap.NewNop(), fakeOverlayDB{}, fakeNodeEvents{}, overlay.NewPlacementRules().CreateFilters, "", "", overlay.Config{
|
||||
overlayCache, err := overlay.NewService(zap.NewNop(), fakeOverlayDB{}, fakeNodeEvents{}, overlay.NewPlacementDefinitions().CreateFilters, "", "", overlay.Config{
|
||||
NodeSelectionCache: overlay.UploadSelectionCacheConfig{
|
||||
Staleness: 2 * time.Nanosecond,
|
||||
},
|
||||
@ -40,7 +40,7 @@ func TestReliabilityCache_Concurrent(t *testing.T) {
|
||||
ctx.Go(func() error { return overlayCache.Run(cacheCtx) })
|
||||
defer ctx.Check(overlayCache.Close)
|
||||
|
||||
cache := checker.NewReliabilityCache(overlayCache, time.Millisecond, overlay.NewPlacementRules().CreateFilters, []string{})
|
||||
cache := checker.NewReliabilityCache(overlayCache, time.Millisecond, overlay.NewPlacementDefinitions().CreateFilters, []string{})
|
||||
var group errgroup.Group
|
||||
for i := 0; i < 10; i++ {
|
||||
group.Go(func() error {
|
||||
@ -82,7 +82,7 @@ func TestReliabilityCache_OutOfPlacementPieces(t *testing.T) {
|
||||
overlayService := planet.Satellites[0].Overlay.Service
|
||||
config := planet.Satellites[0].Config.Checker
|
||||
|
||||
rules := overlay.NewPlacementRules()
|
||||
rules := overlay.NewPlacementDefinitions()
|
||||
rules.AddLegacyStaticRules()
|
||||
cache := checker.NewReliabilityCache(overlayService, config.ReliabilityCacheStaleness, rules.CreateFilters, []string{})
|
||||
|
||||
|
@ -110,7 +110,11 @@ func TestSegmentRepairPlacement(t *testing.T) {
|
||||
}
|
||||
|
||||
// confirm that some pieces are out of placement
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, planet.Satellites[0].Config.Placement.CreateFilters)
|
||||
|
||||
placement, err := planet.Satellites[0].Config.Placement.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, placement.CreateFilters)
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
|
||||
@ -129,7 +133,7 @@ func TestSegmentRepairPlacement(t *testing.T) {
|
||||
require.NotNil(t, segments[0].RepairedAt)
|
||||
require.Len(t, segments[0].Pieces, tc.piecesAfterRepair)
|
||||
|
||||
ok, err = allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, planet.Satellites[0].Config.Placement.CreateFilters)
|
||||
ok, err = allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, placement.CreateFilters)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
@ -236,8 +240,11 @@ func TestSegmentRepairWithNodeTags(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, segments, 1)
|
||||
|
||||
placement, err := planet.Satellites[0].Config.Placement.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, storj.PlacementConstraint(10), segments[0].Placement)
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, planet.Satellites[0].Config.Placement.CreateFilters)
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, placement.CreateFilters)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
|
||||
@ -347,8 +354,11 @@ func TestSegmentRepairPlacementAndClumped(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
placement, err := planet.Satellites[0].Config.Placement.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
// confirm that some pieces are out of placement
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, planet.Satellites[0].Config.Placement.CreateFilters)
|
||||
ok, err := allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, placement.CreateFilters)
|
||||
require.NoError(t, err)
|
||||
require.False(t, ok)
|
||||
|
||||
@ -367,7 +377,7 @@ func TestSegmentRepairPlacementAndClumped(t *testing.T) {
|
||||
require.NotNil(t, segments[0].RepairedAt)
|
||||
require.Len(t, segments[0].Pieces, 4)
|
||||
|
||||
ok, err = allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, planet.Satellites[0].Config.Placement.CreateFilters)
|
||||
ok, err = allPiecesInPlacement(ctx, planet.Satellites[0].Overlay.Service, segments[0].Pieces, segments[0].Placement, placement.CreateFilters)
|
||||
require.NoError(t, err)
|
||||
require.True(t, ok)
|
||||
})
|
||||
|
@ -45,7 +45,7 @@ func TestClassify(t *testing.T) {
|
||||
c := &overlay.ConfigurablePlacementRule{}
|
||||
require.NoError(t, c.Set(""))
|
||||
s := SegmentRepairer{
|
||||
placementRules: c.CreateFilters,
|
||||
placementRules: overlay.NewPlacementDefinitions().CreateFilters,
|
||||
}
|
||||
pieces := createPieces(selectedNodes, 0, 1, 2, 3, 4)
|
||||
result, err := s.classifySegmentPiecesWithNodes(ctx, metabase.Segment{Pieces: pieces}, allNodeIDs(pieces), selectedNodes)
|
||||
@ -69,8 +69,11 @@ func TestClassify(t *testing.T) {
|
||||
|
||||
})
|
||||
|
||||
c := &overlay.ConfigurablePlacementRule{}
|
||||
require.NoError(t, c.Set("10:country(\"GB\")"))
|
||||
c, err := overlay.ConfigurablePlacementRule{
|
||||
PlacementRules: `10:country("GB")`,
|
||||
}.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
s := SegmentRepairer{
|
||||
placementRules: c.CreateFilters,
|
||||
doPlacementCheck: true,
|
||||
@ -96,8 +99,11 @@ func TestClassify(t *testing.T) {
|
||||
node.CountryCode = location.Germany
|
||||
})
|
||||
|
||||
c := &overlay.ConfigurablePlacementRule{}
|
||||
require.NoError(t, c.Set("10:country(\"GB\")"))
|
||||
c, err := overlay.ConfigurablePlacementRule{
|
||||
PlacementRules: `10:country("GB")`,
|
||||
}.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
s := SegmentRepairer{
|
||||
placementRules: c.CreateFilters,
|
||||
doPlacementCheck: true,
|
||||
@ -124,7 +130,7 @@ func TestClassify(t *testing.T) {
|
||||
node.LastNet = fmt.Sprintf("127.0.%d.0", ix/2)
|
||||
})
|
||||
|
||||
c := overlay.NewPlacementRules()
|
||||
c := overlay.NewPlacementDefinitions()
|
||||
s := SegmentRepairer{
|
||||
placementRules: c.CreateFilters,
|
||||
doDeclumping: true,
|
||||
@ -154,8 +160,10 @@ func TestClassify(t *testing.T) {
|
||||
node.CountryCode = location.UnitedKingdom
|
||||
})
|
||||
|
||||
c := overlay.NewPlacementRules()
|
||||
require.NoError(t, c.Set(fmt.Sprintf(`10:annotated(country("GB"),annotation("%s","%s"))`, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF)))
|
||||
c, err := overlay.ConfigurablePlacementRule{
|
||||
PlacementRules: fmt.Sprintf(`10:annotated(country("GB"),annotation("%s","%s"))`, nodeselection.AutoExcludeSubnet, nodeselection.AutoExcludeSubnetOFF),
|
||||
}.Parse()
|
||||
require.NoError(t, err)
|
||||
|
||||
s := SegmentRepairer{
|
||||
placementRules: c.CreateFilters,
|
||||
|
@ -140,8 +140,12 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
|
||||
}
|
||||
|
||||
{ // setup overlay
|
||||
var err error
|
||||
peer.Overlay, err = overlay.NewService(log.Named("overlay"), overlayCache, nodeEvents, config.Placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.Overlay, err = overlay.NewService(log.Named("overlay"), overlayCache, nodeEvents, placement.CreateFilters, config.Console.ExternalAddress, config.Console.SatelliteName, config.Overlay)
|
||||
if err != nil {
|
||||
return nil, errs.Combine(err, peer.Close())
|
||||
}
|
||||
@ -174,7 +178,11 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
|
||||
}
|
||||
|
||||
{ // setup orders
|
||||
var err error
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.Orders.Service, err = orders.NewService(
|
||||
log.Named("orders"),
|
||||
signing.SignerFromFullIdentity(peer.Identity),
|
||||
@ -183,7 +191,7 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
|
||||
// PUT and GET actions which are not used by
|
||||
// repairer so we can set noop implementation.
|
||||
orders.NewNoopDB(),
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Orders,
|
||||
)
|
||||
if err != nil {
|
||||
@ -203,6 +211,11 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
|
||||
}
|
||||
|
||||
{ // setup repairer
|
||||
placement, err := config.Placement.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
peer.EcRepairer = repairer.NewECRepairer(
|
||||
log.Named("ec-repair"),
|
||||
peer.Dialer,
|
||||
@ -222,7 +235,7 @@ func NewRepairer(log *zap.Logger, full *identity.FullIdentity,
|
||||
peer.Overlay,
|
||||
peer.Audit.Reporter,
|
||||
peer.EcRepairer,
|
||||
config.Placement.CreateFilters,
|
||||
placement.CreateFilters,
|
||||
config.Checker.RepairOverrides,
|
||||
config.Repairer,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user