mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
Use checked Proxmox replication parsing
This commit is contained in:
parent
7950cfe0e2
commit
072ca71e7c
4 changed files with 52 additions and 19 deletions
|
|
@ -291,6 +291,11 @@ canonical physical-disk model and the Proxmox polling runtime in
|
|||
`internal/monitoring/monitor_pve.go` must evaluate disk alerts only after that
|
||||
merged disk view exists, so controller-backed disks do not lose health and
|
||||
endurance coverage between collection and alerting.
|
||||
That same Proxmox monitoring boundary also owns checked response parsing for
|
||||
polymorphic numeric fields. Shared client parsers such as
|
||||
`pkg/proxmox/replication.go` must use the package's checked integer conversion
|
||||
helpers instead of direct casts, so malformed or oversized Proxmox values do
|
||||
not overflow into monitoring state.
|
||||
|
||||
Backup polling and recovery guest identity assembly now derive workload node,
|
||||
name, and type context from canonical `ReadState` instead of from
|
||||
|
|
|
|||
|
|
@ -126,6 +126,13 @@ func floatToIntTrunc(v float64) (int, error) {
|
|||
return int(v), nil
|
||||
}
|
||||
|
||||
func floatToIntRounded(v float64) (int, error) {
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
return 0, fmt.Errorf("non-finite float %v cannot be converted to int", v)
|
||||
}
|
||||
return floatToIntTrunc(math.Round(v))
|
||||
}
|
||||
|
||||
func looksLikeNumericLiteral(raw string) bool {
|
||||
hasDigit := false
|
||||
var prev rune
|
||||
|
|
|
|||
|
|
@ -368,33 +368,39 @@ func intFromAny(value interface{}) (int, bool) {
|
|||
case int32:
|
||||
return int(v), true
|
||||
case int64:
|
||||
return int(v), true
|
||||
parsed, err := int64ToInt(v)
|
||||
return parsed, err == nil
|
||||
case uint:
|
||||
return int(v), true
|
||||
parsed, err := uint64ToInt(uint64(v))
|
||||
return parsed, err == nil
|
||||
case uint8:
|
||||
return int(v), true
|
||||
case uint16:
|
||||
return int(v), true
|
||||
case uint32:
|
||||
return int(v), true
|
||||
parsed, err := uint64ToInt(uint64(v))
|
||||
return parsed, err == nil
|
||||
case uint64:
|
||||
return int(v), true
|
||||
parsed, err := uint64ToInt(v)
|
||||
return parsed, err == nil
|
||||
case float32:
|
||||
if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
|
||||
return 0, false
|
||||
}
|
||||
return int(math.Round(float64(v))), true
|
||||
parsed, err := floatToIntRounded(float64(v))
|
||||
return parsed, err == nil
|
||||
case float64:
|
||||
if math.IsNaN(v) || math.IsInf(v, 0) {
|
||||
parsed, err := floatToIntRounded(v)
|
||||
return parsed, err == nil
|
||||
case json.Number:
|
||||
s := strings.TrimSpace(v.String())
|
||||
if i, err := v.Int64(); err == nil {
|
||||
parsed, err := int64ToInt(i)
|
||||
return parsed, err == nil
|
||||
}
|
||||
if !looksLikeFloatLiteral(s) {
|
||||
return 0, false
|
||||
}
|
||||
return int(math.Round(v)), true
|
||||
case json.Number:
|
||||
if i, err := v.Int64(); err == nil {
|
||||
return int(i), true
|
||||
}
|
||||
if f, err := v.Float64(); err == nil && !math.IsNaN(f) && !math.IsInf(f, 0) {
|
||||
return int(math.Round(f)), true
|
||||
if f, err := v.Float64(); err == nil {
|
||||
parsed, err := floatToIntRounded(f)
|
||||
return parsed, err == nil
|
||||
}
|
||||
case string:
|
||||
s := strings.TrimSpace(v)
|
||||
|
|
@ -402,15 +408,24 @@ func intFromAny(value interface{}) (int, bool) {
|
|||
return 0, false
|
||||
}
|
||||
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
|
||||
return int(i), true
|
||||
parsed, err := int64ToInt(i)
|
||||
return parsed, err == nil
|
||||
}
|
||||
if f, err := strconv.ParseFloat(s, 64); err == nil && !math.IsNaN(f) && !math.IsInf(f, 0) {
|
||||
return int(math.Round(f)), true
|
||||
if !looksLikeFloatLiteral(s) {
|
||||
return 0, false
|
||||
}
|
||||
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||
parsed, err := floatToIntRounded(f)
|
||||
return parsed, err == nil
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func looksLikeFloatLiteral(s string) bool {
|
||||
return strings.ContainsAny(s, ".eE")
|
||||
}
|
||||
|
||||
func boolFromAny(value interface{}) (bool, bool) {
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ func TestIntFromAny(t *testing.T) {
|
|||
{"uint16", uint16(65535), 65535, true},
|
||||
{"uint32", uint32(42), 42, true},
|
||||
{"uint64", uint64(42), 42, true},
|
||||
{"uint64 overflow", ^uint64(0), 0, false},
|
||||
|
||||
// float types (rounded)
|
||||
{"float32 integer", float32(42.0), 42, true},
|
||||
|
|
@ -107,16 +108,21 @@ func TestIntFromAny(t *testing.T) {
|
|||
{"float64 NaN", math.NaN(), 0, false},
|
||||
{"float64 +Inf", math.Inf(1), 0, false},
|
||||
{"float64 -Inf", math.Inf(-1), 0, false},
|
||||
{"float64 overflow", math.MaxFloat64, 0, false},
|
||||
|
||||
// json.Number
|
||||
{"json.Number int", json.Number("42"), 42, true},
|
||||
{"json.Number float", json.Number("42.6"), 43, true},
|
||||
{"json.Number scientific float", json.Number("4.2e1"), 42, true},
|
||||
{"json.Number oversized int", json.Number("9223372036854775808"), 0, false},
|
||||
{"json.Number invalid", json.Number("abc"), 0, false},
|
||||
|
||||
// string
|
||||
{"string int", "42", 42, true},
|
||||
{"string negative", "-42", -42, true},
|
||||
{"string float", "42.6", 43, true},
|
||||
{"string scientific float", "4.2e1", 42, true},
|
||||
{"string oversized int", "9223372036854775808", 0, false},
|
||||
{"string empty", "", 0, false},
|
||||
{"string whitespace", " 42 ", 42, true},
|
||||
{"string invalid", "abc", 0, false},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue