mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-03 20:53:09 +00:00
seed DiskFillByType defaults and normalize on load
Seeds AlertConfig.DiskFillByType in defaultAlertConfig() with the three forward-compatible keys (nvme 92/87, sata 90/85, hdd 85/80). Adds NormalizeDiskFillByType to lowercase keys on load, seed defaults when the map is nil, and reset non-positive trigger/clear values to the default for that key. NormalizeAgentDefaults invokes the new helper so it runs through the existing UpdateConfig normalization chain. Round trip tests cover nil seed, customized survival, negative reset, and mixed case normalization, plus a defaultAlertConfig seed assertion. Only the "nvme" key is consulted by the host evaluation today (the inference helper only matches /dev/nvme*); "sata" and "hdd" are forward-compatible placeholders for when the agent protocol carries hardware type explicitly.
This commit is contained in:
@@ -282,6 +282,61 @@ func NormalizeAgentDefaults(config *AlertConfig) {
|
||||
}
|
||||
}
|
||||
EnsureValidHysteresis(config.AgentDefaults.DiskTemperature, "agent.diskTemperature")
|
||||
|
||||
NormalizeDiskFillByType(config)
|
||||
}
|
||||
|
||||
// diskFillByTypeDefaults returns the canonical per-type fill-% defaults.
|
||||
// Keys are lowercase hardware type strings.
|
||||
func diskFillByTypeDefaults() map[string]HysteresisThreshold {
|
||||
return map[string]HysteresisThreshold{
|
||||
"nvme": {Trigger: 92, Clear: 87},
|
||||
"sata": {Trigger: 90, Clear: 85},
|
||||
"hdd": {Trigger: 85, Clear: 80},
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeDiskFillByType ensures AlertConfig.DiskFillByType is seeded with
|
||||
// lowercase nvme/sata/hdd defaults when nil, lowercases any existing keys,
|
||||
// and resets non-positive trigger or clear values to the default for that
|
||||
// key. Operator-customized positive values are preserved.
|
||||
func NormalizeDiskFillByType(config *AlertConfig) {
|
||||
defaults := diskFillByTypeDefaults()
|
||||
if config.DiskFillByType == nil {
|
||||
copyMap := make(map[string]HysteresisThreshold, len(defaults))
|
||||
for k, v := range defaults {
|
||||
copyMap[k] = v
|
||||
}
|
||||
config.DiskFillByType = copyMap
|
||||
return
|
||||
}
|
||||
|
||||
// Lowercase any non-lowercase keys, moving values into the canonical position.
|
||||
for key, value := range config.DiskFillByType {
|
||||
lower := strings.ToLower(strings.TrimSpace(key))
|
||||
if lower == key {
|
||||
continue
|
||||
}
|
||||
delete(config.DiskFillByType, key)
|
||||
if lower == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := config.DiskFillByType[lower]; !exists {
|
||||
config.DiskFillByType[lower] = value
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure all canonical keys are present and have positive trigger/clear values.
|
||||
for key, defaultVal := range defaults {
|
||||
current, ok := config.DiskFillByType[key]
|
||||
if !ok {
|
||||
config.DiskFillByType[key] = defaultVal
|
||||
continue
|
||||
}
|
||||
if current.Trigger <= 0 || current.Clear <= 0 {
|
||||
config.DiskFillByType[key] = defaultVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NormalizeGeneralSettings(config *AlertConfig) {
|
||||
|
||||
@@ -76,7 +76,12 @@ func defaultAlertConfig() AlertConfig {
|
||||
CPU: &HysteresisThreshold{Trigger: 80, Clear: 75},
|
||||
Memory: &HysteresisThreshold{Trigger: 85, Clear: 80},
|
||||
},
|
||||
StorageDefault: HysteresisThreshold{Trigger: 85, Clear: 80},
|
||||
StorageDefault: HysteresisThreshold{Trigger: 85, Clear: 80},
|
||||
DiskFillByType: map[string]HysteresisThreshold{
|
||||
"nvme": {Trigger: 92, Clear: 87},
|
||||
"sata": {Trigger: 90, Clear: 85},
|
||||
"hdd": {Trigger: 85, Clear: 80},
|
||||
},
|
||||
MinimumDelta: 2.0, // 2% minimum change
|
||||
SuppressionWindow: 5, // 5 minutes
|
||||
HysteresisMargin: 5.0, // 5% default margin
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package alerts
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
alertconfig "github.com/rcourtman/pulse-go-rewrite/internal/alerts/config"
|
||||
)
|
||||
|
||||
func TestNormalizeSnapshotDefaults(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -189,3 +193,111 @@ func TestBackupIgnoreVMID(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeDiskFillByType(t *testing.T) {
|
||||
t.Run("nil map seeds nvme/sata/hdd defaults", func(t *testing.T) {
|
||||
cfg := &AlertConfig{}
|
||||
alertconfig.NormalizeDiskFillByType(cfg)
|
||||
|
||||
if cfg.DiskFillByType == nil {
|
||||
t.Fatal("expected DiskFillByType to be seeded, got nil")
|
||||
}
|
||||
nvme, ok := cfg.DiskFillByType["nvme"]
|
||||
if !ok {
|
||||
t.Fatal("nvme key not seeded")
|
||||
}
|
||||
if nvme.Trigger != 92 || nvme.Clear != 87 {
|
||||
t.Fatalf("nvme = %+v, want {Trigger:92 Clear:87}", nvme)
|
||||
}
|
||||
sata, ok := cfg.DiskFillByType["sata"]
|
||||
if !ok {
|
||||
t.Fatal("sata key not seeded")
|
||||
}
|
||||
if sata.Trigger != 90 || sata.Clear != 85 {
|
||||
t.Fatalf("sata = %+v, want {Trigger:90 Clear:85}", sata)
|
||||
}
|
||||
hdd, ok := cfg.DiskFillByType["hdd"]
|
||||
if !ok {
|
||||
t.Fatal("hdd key not seeded")
|
||||
}
|
||||
if hdd.Trigger != 85 || hdd.Clear != 80 {
|
||||
t.Fatalf("hdd = %+v, want {Trigger:85 Clear:80}", hdd)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("operator customized values survive", func(t *testing.T) {
|
||||
cfg := &AlertConfig{
|
||||
DiskFillByType: map[string]HysteresisThreshold{
|
||||
"nvme": {Trigger: 95, Clear: 90},
|
||||
},
|
||||
}
|
||||
alertconfig.NormalizeDiskFillByType(cfg)
|
||||
|
||||
nvme := cfg.DiskFillByType["nvme"]
|
||||
if nvme.Trigger != 95 || nvme.Clear != 90 {
|
||||
t.Fatalf("nvme = %+v, want operator value {Trigger:95 Clear:90}", nvme)
|
||||
}
|
||||
// Missing keys still seeded.
|
||||
if sata, ok := cfg.DiskFillByType["sata"]; !ok || sata.Trigger != 90 || sata.Clear != 85 {
|
||||
t.Fatalf("sata = %+v ok=%v, want default {Trigger:90 Clear:85}", sata, ok)
|
||||
}
|
||||
if hdd, ok := cfg.DiskFillByType["hdd"]; !ok || hdd.Trigger != 85 || hdd.Clear != 80 {
|
||||
t.Fatalf("hdd = %+v ok=%v, want default {Trigger:85 Clear:80}", hdd, ok)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("negative trigger resets to default", func(t *testing.T) {
|
||||
cfg := &AlertConfig{
|
||||
DiskFillByType: map[string]HysteresisThreshold{
|
||||
"nvme": {Trigger: -1, Clear: 10},
|
||||
"sata": {Trigger: 88, Clear: -5},
|
||||
},
|
||||
}
|
||||
alertconfig.NormalizeDiskFillByType(cfg)
|
||||
|
||||
nvme := cfg.DiskFillByType["nvme"]
|
||||
if nvme.Trigger != 92 || nvme.Clear != 87 {
|
||||
t.Fatalf("nvme = %+v, want default reset {Trigger:92 Clear:87}", nvme)
|
||||
}
|
||||
sata := cfg.DiskFillByType["sata"]
|
||||
if sata.Trigger != 90 || sata.Clear != 85 {
|
||||
t.Fatalf("sata = %+v, want default reset {Trigger:90 Clear:85}", sata)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("mixed case keys lowercase to canonical", func(t *testing.T) {
|
||||
cfg := &AlertConfig{
|
||||
DiskFillByType: map[string]HysteresisThreshold{
|
||||
"NVMe": {Trigger: 93, Clear: 88},
|
||||
},
|
||||
}
|
||||
alertconfig.NormalizeDiskFillByType(cfg)
|
||||
|
||||
if _, exists := cfg.DiskFillByType["NVMe"]; exists {
|
||||
t.Fatalf("expected mixed-case key NVMe to be removed, map=%+v", cfg.DiskFillByType)
|
||||
}
|
||||
nvme, ok := cfg.DiskFillByType["nvme"]
|
||||
if !ok {
|
||||
t.Fatalf("expected lowercase nvme key, map=%+v", cfg.DiskFillByType)
|
||||
}
|
||||
if nvme.Trigger != 93 || nvme.Clear != 88 {
|
||||
t.Fatalf("nvme = %+v, want preserved operator value {Trigger:93 Clear:88}", nvme)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDefaultAlertConfigSeedsDiskFillByType(t *testing.T) {
|
||||
cfg := defaultAlertConfig()
|
||||
if cfg.DiskFillByType == nil {
|
||||
t.Fatal("expected defaultAlertConfig to seed DiskFillByType, got nil")
|
||||
}
|
||||
if nvme, ok := cfg.DiskFillByType["nvme"]; !ok || nvme.Trigger != 92 || nvme.Clear != 87 {
|
||||
t.Fatalf("nvme = %+v ok=%v, want {Trigger:92 Clear:87}", nvme, ok)
|
||||
}
|
||||
if sata, ok := cfg.DiskFillByType["sata"]; !ok || sata.Trigger != 90 || sata.Clear != 85 {
|
||||
t.Fatalf("sata = %+v ok=%v, want {Trigger:90 Clear:85}", sata, ok)
|
||||
}
|
||||
if hdd, ok := cfg.DiskFillByType["hdd"]; !ok || hdd.Trigger != 85 || hdd.Clear != 80 {
|
||||
t.Fatalf("hdd = %+v ok=%v, want {Trigger:85 Clear:80}", hdd, ok)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user