Improve config import and export utils

This commit is contained in:
Daniel 2023-09-22 15:17:16 +02:00
parent 01b03aa936
commit 4451b6985c
7 changed files with 109 additions and 45 deletions

View file

@ -482,7 +482,6 @@ func (e *Endpoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Check for handler error.
if err != nil {
// if statusProvider, ok := err.(HTTPStatusProvider); ok {
var statusProvider HTTPStatusProvider
if errors.As(err, &statusProvider) {
http.Error(w, err.Error(), statusProvider.HTTPStatus())

View file

@ -14,7 +14,7 @@ func parseAndReplaceConfig(jsonData string) error {
return err
}
validationErrors := replaceConfig(m)
validationErrors, _ := ReplaceConfig(m)
if len(validationErrors) > 0 {
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
}
@ -27,7 +27,7 @@ func parseAndReplaceDefaultConfig(jsonData string) error {
return err
}
validationErrors := replaceDefaultConfig(m)
validationErrors, _ := ReplaceDefaultConfig(m)
if len(validationErrors) > 0 {
return fmt.Errorf("%d errors, first: %w", len(validationErrors), validationErrors[0])
}

View file

@ -69,7 +69,7 @@ func start() error {
err = loadConfig(false)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return err
return fmt.Errorf("failed to load config file: %w", err)
}
return nil
}

View file

@ -325,6 +325,29 @@ func (option *Option) IsSetByUser() bool {
return option.activeValue != nil
}
// UserValue returns the value set by the user or nil if the value has not
// been changed from the default.
func (option *Option) UserValue() any {
option.Lock()
defer option.Unlock()
if option.activeValue == nil {
return nil
}
return option.activeValue.getData(option)
}
// ValidateValue checks if the given value is valid for the option.
func (option *Option) ValidateValue(value any) error {
option.Lock()
defer option.Unlock()
if _, err := validateValue(option, value); err != nil {
return err
}
return nil
}
// Export expors an option to a Record.
func (option *Option) Export() (record.Record, error) {
option.Lock()

View file

@ -45,7 +45,7 @@ func loadConfig(requireValidConfig bool) error {
return err
}
validationErrors := replaceConfig(newValues)
validationErrors, _ := ReplaceConfig(newValues)
if requireValidConfig && len(validationErrors) > 0 {
return fmt.Errorf("encountered %d validation errors during config loading", len(validationErrors))
}

View file

@ -37,70 +37,112 @@ func signalChanges() {
module.TriggerEvent(ChangeEvent, nil)
}
// replaceConfig sets the (prioritized) user defined config.
func replaceConfig(newValues map[string]interface{}) []*ValidationError {
var validationErrors []*ValidationError
// ValidateConfig validates the given configuration and returns all validation
// errors as well as whether the given configuration contains unknown keys.
func ValidateConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool, containsUnknown bool) {
// RLock the options because we are not adding or removing
// options from the registration but rather only update the
// options value which is guarded by the option's lock itself
// options from the registration but rather only checking the
// options value which is guarded by the option's lock itself.
optionsLock.RLock()
defer optionsLock.RUnlock()
var checked int
for key, option := range options {
newValue, ok := newValues[key]
option.Lock()
option.activeValue = nil
if ok {
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeValue = valueCache
} else {
validationErrors = append(validationErrors, err)
}
}
checked++
handleOptionUpdate(option, true)
option.Unlock()
func() {
option.Lock()
defer option.Unlock()
_, err := validateValue(option, newValue)
if err != nil {
validationErrors = append(validationErrors, err)
}
if option.RequiresRestart {
requiresRestart = true
}
}()
}
}
signalChanges()
return validationErrors
return validationErrors, requiresRestart, checked < len(newValues)
}
// replaceDefaultConfig sets the (fallback) default config.
func replaceDefaultConfig(newValues map[string]interface{}) []*ValidationError {
var validationErrors []*ValidationError
// ReplaceConfig sets the (prioritized) user defined config.
func ReplaceConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool) {
// RLock the options because we are not adding or removing
// options from the registration but rather only update the
// options value which is guarded by the option's lock itself
// options value which is guarded by the option's lock itself.
optionsLock.RLock()
defer optionsLock.RUnlock()
for key, option := range options {
newValue, ok := newValues[key]
option.Lock()
option.activeDefaultValue = nil
if ok {
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeDefaultValue = valueCache
} else {
validationErrors = append(validationErrors, err)
func() {
option.Lock()
defer option.Unlock()
option.activeValue = nil
if ok {
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeValue = valueCache
} else {
validationErrors = append(validationErrors, err)
}
}
}
handleOptionUpdate(option, true)
option.Unlock()
handleOptionUpdate(option, true)
if option.RequiresRestart {
requiresRestart = true
}
}()
}
signalChanges()
return validationErrors
return validationErrors, requiresRestart
}
// ReplaceDefaultConfig sets the (fallback) default config.
func ReplaceDefaultConfig(newValues map[string]interface{}) (validationErrors []*ValidationError, requiresRestart bool) {
// RLock the options because we are not adding or removing
// options from the registration but rather only update the
// options value which is guarded by the option's lock itself.
optionsLock.RLock()
defer optionsLock.RUnlock()
for key, option := range options {
newValue, ok := newValues[key]
func() {
option.Lock()
defer option.Unlock()
option.activeDefaultValue = nil
if ok {
valueCache, err := validateValue(option, newValue)
if err == nil {
option.activeDefaultValue = valueCache
} else {
validationErrors = append(validationErrors, err)
}
}
handleOptionUpdate(option, true)
if option.RequiresRestart {
requiresRestart = true
}
}()
}
signalChanges()
return validationErrors, requiresRestart
}
// SetConfigOption sets a single value in the (prioritized) user defined config.

View file

@ -24,7 +24,7 @@ func TestLayersGetters(t *testing.T) { //nolint:paralleltest
t.Fatal(err)
}
validationErrors := replaceConfig(mapData)
validationErrors, _ := ReplaceConfig(mapData)
if len(validationErrors) > 0 {
t.Fatalf("%d errors, first: %s", len(validationErrors), validationErrors[0].Error())
}