Skip to content

Commit

Permalink
Add value migrations to config options
Browse files Browse the repository at this point in the history
  • Loading branch information
dhaavi committed Nov 24, 2023
1 parent 83b7095 commit 3afd500
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 2 deletions.
7 changes: 7 additions & 0 deletions config/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ type PossibleValue struct {
// Format: <vendor/package>:<scope>:<identifier> //.
type Annotations map[string]interface{}

// MigrationFunc is a function that migrates a config option value.
type MigrationFunc func(option *Option, value any) any

// Well known annotations defined by this package.
const (
// DisplayHintAnnotation provides a hint for the user
Expand Down Expand Up @@ -259,6 +262,9 @@ type Option struct {
// Annotations is considered mutable and setting/reading annotation keys
// must be performed while the option is locked.
Annotations Annotations
// Migrations holds migration functions that are given the raw option value
// before any validation is run. The returned value is then used.
Migrations []MigrationFunc `json:"-"`

activeValue *valueCache // runtime value (loaded from config file or set by user)
activeDefaultValue *valueCache // runtime default value (may be set internally)
Expand Down Expand Up @@ -361,6 +367,7 @@ func (option *Option) ValidateValue(value any) error {
option.Lock()
defer option.Unlock()

value = migrateValue(option, value)
if _, err := validateValue(option, value); err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions config/perspective.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ optionsLoop:
if !ok {
continue
}
// migrate value
configValue = migrateValue(option, configValue)
// validate value
valueCache, err := validateValue(option, configValue)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions config/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func ValidateConfig(newValues map[string]interface{}) (validationErrors []*Valid
option.Lock()
defer option.Unlock()

newValue = migrateValue(option, newValue)
_, err := validateValue(option, newValue)
if err != nil {
validationErrors = append(validationErrors, err)
Expand Down Expand Up @@ -88,6 +89,7 @@ func ReplaceConfig(newValues map[string]interface{}) (validationErrors []*Valida

option.activeValue = nil
if ok {
newValue = migrateValue(option, newValue)
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeValue = valueCache
Expand Down Expand Up @@ -125,6 +127,7 @@ func ReplaceDefaultConfig(newValues map[string]interface{}) (validationErrors []

option.activeDefaultValue = nil
if ok {
newValue = migrateValue(option, newValue)
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeDefaultValue = valueCache
Expand Down Expand Up @@ -160,6 +163,7 @@ func setConfigOption(key string, value any, push bool) (err error) {
if value == nil {
option.activeValue = nil
} else {
value = migrateValue(option, value)
valueCache, vErr := validateValue(option, value)
if vErr == nil {
option.activeValue = valueCache
Expand Down Expand Up @@ -201,6 +205,7 @@ func setDefaultConfigOption(key string, value interface{}, push bool) (err error
if value == nil {
option.activeDefaultValue = nil
} else {
value = migrateValue(option, value)
valueCache, vErr := validateValue(option, value)
if vErr == nil {
option.activeDefaultValue = valueCache
Expand Down
16 changes: 14 additions & 2 deletions config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"math"
"reflect"

"github.com/safing/portbase/log"
)

type valueCache struct {
Expand Down Expand Up @@ -64,6 +66,18 @@ func isAllowedPossibleValue(opt *Option, value interface{}) error {
return errors.New("value is not allowed")
}

// migrateValue runs all value migrations.
func migrateValue(option *Option, value any) any {
for _, migration := range option.Migrations {
newValue := migration(option, value)
if newValue != value {
log.Debugf("config: migrated %s value from %v to %v", option.Key, value, newValue)
}
value = newValue
}
return value
}

// validateValue ensures that value matches the expected type of option.
// It does not create a copy of the value!
func validateValue(option *Option, value interface{}) (*valueCache, *ValidationError) { //nolint:gocyclo
Expand All @@ -76,8 +90,6 @@ func validateValue(option *Option, value interface{}) (*valueCache, *ValidationE
}
}

reflect.TypeOf(value).ConvertibleTo(reflect.TypeOf(""))

var validated *valueCache
switch v := value.(type) {
case string:
Expand Down

0 comments on commit 3afd500

Please sign in to comment.