mirror of
https://github.com/safing/portbase
synced 2025-04-25 19:49:09 +00:00
Merge pull request #194 from safing/feature/config-value-migration
Add value migrations to config options
This commit is contained in:
commit
0607924762
5 changed files with 29 additions and 3 deletions
|
@ -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:",
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ optionsLoop:
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
// migrate value
|
||||
configValue = migrateValue(option, configValue)
|
||||
// validate value
|
||||
valueCache, err := validateValue(option, configValue)
|
||||
if err != nil {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue