Merge pull request from safing/feature/config-value-migration

Add value migrations to config options
This commit is contained in:
Daniel Hovie 2023-12-01 11:49:14 +01:00 committed by GitHub
commit 0607924762
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 3 deletions

View file

@ -147,7 +147,7 @@ func (mh *mainHandler) handle(w http.ResponseWriter, r *http.Request) error {
"default-src 'self'; "+
"connect-src https://*.safing.io 'self'; "+
"style-src 'self' 'unsafe-inline'; "+
"img-src 'self' data:",
"img-src 'self' data: blob:",
)
}

View file

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

View file

@ -35,6 +35,8 @@ optionsLoop:
if !ok {
continue
}
// migrate value
configValue = migrateValue(option, configValue)
// validate value
valueCache, err := validateValue(option, configValue)
if err != nil {

View file

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

View file

@ -5,6 +5,8 @@ import (
"fmt"
"math"
"reflect"
"github.com/safing/portbase/log"
)
type valueCache struct {
@ -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
@ -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: