Implement Config Manager

This commit is contained in:
Samuel Lorch 2023-04-01 18:16:25 +02:00
parent f6bcffd8df
commit f4cdd809cd
9 changed files with 219 additions and 0 deletions

41
internal/config/apply.go Normal file
View file

@ -0,0 +1,41 @@
package config
import (
"fmt"
"golang.org/x/exp/slog"
"nfsense.net/nfsense/internal/definitions"
)
// ApplyPendingChanges Takes all pending Changes and Tries to Apply them using the Registered Apply Functions.
// In Case of error it Attempts to Revert to the Current Config
func (m *ConfigManager) ApplyPendingChanges() error {
slog.Info("Applying Pending Changes...")
for _, fn := range m.applyFunctions {
err := fn(*m.pendingConfig)
if err != nil {
slog.Error("Applying Pending Changes", err)
err2 := revertToCurrent(m)
if err2 != nil {
slog.Error("Reverting Error", err2)
return fmt.Errorf("Apply Error %w; Reverting Error %w", err, err2)
}
return err
}
}
return nil
}
func revertToCurrent(m *ConfigManager) error {
for _, fn := range m.applyFunctions {
err := fn(*m.currentConfig)
if err != nil {
return err
}
}
return nil
}
func (m *ConfigManager) RegisterApplyFunction(fn func(definitions.Config) error) {
m.applyFunctions = append(m.applyFunctions, fn)
}

11
internal/config/diff.go Normal file
View file

@ -0,0 +1,11 @@
package config
import "github.com/r3labs/diff/v3"
func (m *ConfigManager) AreChangesPending() bool {
return diff.Changed(m.currentConfig, m.pendingConfig)
}
func (m *ConfigManager) GetPendingChangelog() (diff.Changelog, error) {
return diff.Diff(m.currentConfig, m.pendingConfig)
}

View file

@ -0,0 +1,6 @@
package config
func (m *ConfigManager) DiscardPendingConfig() error {
m.pendingConfig = m.currentConfig.Clone()
return nil
}

11
internal/config/get.go Normal file
View file

@ -0,0 +1,11 @@
package config
import "nfsense.net/nfsense/internal/definitions"
func (m *ConfigManager) GetCurrentConfig() definitions.Config {
return *m.currentConfig.Clone()
}
func (m *ConfigManager) GetPendingConfig() definitions.Config {
return *m.pendingConfig.Clone()
}

57
internal/config/load.go Normal file
View file

@ -0,0 +1,57 @@
package config
import (
"encoding/json"
"fmt"
"os"
"nfsense.net/nfsense/internal/definitions"
)
func (m *ConfigManager) LoadCurrentConfigFromDisk() error {
var config definitions.Config
configFile, err := os.Open(m.currentConfigFilePath)
if err != nil {
return fmt.Errorf("opening Config File %w", err)
}
defer configFile.Close()
jsonParser := json.NewDecoder(configFile)
jsonParser.DisallowUnknownFields()
err = jsonParser.Decode(&config)
if err != nil {
return fmt.Errorf("decoding Config File %w", err)
}
err = definitions.ValidateConfig(&config)
if err != nil {
return fmt.Errorf("validating Config: %w", err)
}
m.currentConfig = &config
return nil
}
func (m *ConfigManager) LoadPendingConfigFromDisk() error {
var config definitions.Config
configFile, err := os.Open(m.pendingConfigFilePath)
if err != nil {
return fmt.Errorf("opening Config File %w", err)
}
defer configFile.Close()
jsonParser := json.NewDecoder(configFile)
jsonParser.DisallowUnknownFields()
err = jsonParser.Decode(&config)
if err != nil {
return fmt.Errorf("decoding Config File %w", err)
}
err = definitions.ValidateConfig(&config)
if err != nil {
return fmt.Errorf("validating Config: %w", err)
}
m.pendingConfig = &config
return nil
}

View file

@ -0,0 +1,29 @@
package config
import (
"sync"
"nfsense.net/nfsense/internal/definitions"
)
type ConfigManager struct {
currentConfigFilePath string
pendingConfigFilePath string
currentConfig *definitions.Config
pendingConfig *definitions.Config
transactionMutex sync.Mutex
applyFunctions []func(definitions.Config) error
}
func CreateConfigManager() *ConfigManager {
manager := ConfigManager{
currentConfigFilePath: "config.json",
pendingConfigFilePath: "pending.json",
currentConfig: &definitions.Config{},
pendingConfig: &definitions.Config{},
}
return &manager
}

View file

@ -0,0 +1,55 @@
package config
import (
"fmt"
"sync"
"nfsense.net/nfsense/internal/definitions"
)
type ConfigTransaction struct {
finished bool
mutex sync.Mutex
configManager *ConfigManager
changes *definitions.Config
}
func (m *ConfigManager) StartTransaction() (*ConfigTransaction, *definitions.Config) {
m.transactionMutex.Lock()
confCopy := m.pendingConfig.Clone()
return &ConfigTransaction{
configManager: m,
changes: confCopy,
}, confCopy
}
func (t *ConfigTransaction) Commit() error {
t.mutex.Lock()
defer t.mutex.Unlock()
if t.finished {
return fmt.Errorf("transaction already finished")
}
t.finished = true
defer t.configManager.transactionMutex.Unlock()
err := definitions.ValidateConfig(t.changes)
if err != nil {
return fmt.Errorf("validating Config before Apply: %w", err)
}
t.configManager.pendingConfig = t.changes.Clone()
return nil
}
func (t *ConfigTransaction) Discard() {
t.mutex.Lock()
defer t.mutex.Unlock()
if !t.finished {
t.finished = true
t.configManager.transactionMutex.Unlock()
}
}