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:
Márton Elek 2023-09-06 11:40:22 +02:00 committed by Storj Robot
parent 950672ca6c
commit 98921f9faa
24 changed files with 379 additions and 150 deletions

View File

@ -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,
)

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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())
}

View File

@ -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,
),
})

View File

@ -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) {

View File

@ -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{

View File

@ -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{}

View File

@ -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{

View File

@ -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,

View File

@ -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,

View File

@ -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
}

View File

@ -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())
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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{}

View File

@ -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,
)
}

View File

@ -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)

View File

@ -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{

View File

@ -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{})

View File

@ -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)
})

View File

@ -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,

View File

@ -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,
)