mirror of
https://github.com/speatzle/nfsense.git
synced 2025-05-11 02:48:21 +00:00
Implement Config Manager
This commit is contained in:
parent
f6bcffd8df
commit
f4cdd809cd
9 changed files with 219 additions and 0 deletions
3
go.mod
3
go.mod
|
@ -15,6 +15,9 @@ require (
|
||||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||||
github.com/klauspost/compress v1.10.3 // indirect
|
github.com/klauspost/compress v1.10.3 // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
|
github.com/r3labs/diff/v3 v3.0.1 // indirect
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
golang.org/x/crypto v0.5.0 // indirect
|
golang.org/x/crypto v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.4.0 // indirect
|
golang.org/x/sys v0.4.0 // indirect
|
||||||
golang.org/x/text v0.6.0 // indirect
|
golang.org/x/text v0.6.0 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -48,6 +48,8 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg=
|
||||||
|
github.com/r3labs/diff/v3 v3.0.1/go.mod h1:f1S9bourRbiM66NskseyUdo0fTmEE0qKrikYJX63dgo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
@ -56,6 +58,10 @@ github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||||
|
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
|
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s=
|
||||||
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc=
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||||
|
|
41
internal/config/apply.go
Normal file
41
internal/config/apply.go
Normal 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
11
internal/config/diff.go
Normal 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)
|
||||||
|
}
|
6
internal/config/discard.go
Normal file
6
internal/config/discard.go
Normal 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
11
internal/config/get.go
Normal 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
57
internal/config/load.go
Normal 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
|
||||||
|
}
|
29
internal/config/manager.go
Normal file
29
internal/config/manager.go
Normal 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
|
||||||
|
}
|
55
internal/config/transaction.go
Normal file
55
internal/config/transaction.go
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue