Counters: forget the totals of objects that no longer exist
hc counters kept listing the objects of a configuration replaced since housecarld started (an import with --replace gives every object a new ID) beside the current ones. Disabled objects, built-ins and policies keep their totals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d813ce13db
commit
429c910df1
4 files changed
+61
-1
No files matched your search
@@ -42,6 +42,10 @@ All notable changes to Housecarl are documented here. The format follows
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- `hc counters` (`GET /counters`) kept listing the objects of a
|
||||||
|
configuration replaced since housecarld started (`hc import
|
||||||
|
--replace` gives every object a new ID), beside the current ones; the
|
||||||
|
totals of objects that no longer exist are now forgotten.
|
||||||
- Import: two base chains on one hook had their rules imported twice,
|
- Import: two base chains on one hook had their rules imported twice,
|
||||||
and the second chain's leading built-ins were moved above the first
|
and the second chain's leading built-ins were moved above the first
|
||||||
chain's rules; they now follow each other by priority (read in a
|
chain's rules; they now follow each other by priority (read in a
|
||||||
|
|||||||
+3
-1
@@ -154,7 +154,9 @@ table on its hook, so a drop in any of them wins.
|
|||||||
Every rule, NAT rule, built-in and policy has a counter. `hc rule list`,
|
Every rule, NAT rule, built-in and policy has a counter. `hc rule list`,
|
||||||
`hc forward list` and the web UI show packets; `hc counters` shows them
|
`hc forward list` and the web UI show packets; `hc counters` shows them
|
||||||
all, with bytes. They count from housecarld's start, and carry across
|
all, with bytes. They count from housecarld's start, and carry across
|
||||||
changes to the ruleset.
|
changes to the ruleset. A deleted object's counter goes with it; an
|
||||||
|
import with `--replace` gives every object a new ID, so its counters
|
||||||
|
start again. A disabled rule keeps its count for when it is enabled.
|
||||||
|
|
||||||
## Quotas
|
## Quotas
|
||||||
|
|
||||||
|
|||||||
@@ -556,9 +556,39 @@ func (f *Firewall) Apply(ctx context.Context, st *model.State) error {
|
|||||||
f.base[k] = b
|
f.base[k] = b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pruneCounters(f.base, st)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pruneCounters forgets the totals of objects st no longer has (deleted,
|
||||||
|
// or replaced by an import with new IDs), so they aren't listed beside
|
||||||
|
// the current ones. A disabled object keeps its totals for when it comes
|
||||||
|
// back; built-ins and policies are always kept.
|
||||||
|
func pruneCounters(base map[string]Counter, st *model.State) {
|
||||||
|
ids := map[string]bool{}
|
||||||
|
for _, r := range st.Rules {
|
||||||
|
ids[fmt.Sprintf("r%d", r.ID)] = true
|
||||||
|
}
|
||||||
|
for _, x := range st.Forwards {
|
||||||
|
ids[fmt.Sprintf("f%d", x.ID)] = true
|
||||||
|
}
|
||||||
|
for _, x := range st.SNATs {
|
||||||
|
ids[fmt.Sprintf("s%d", x.ID)] = true
|
||||||
|
}
|
||||||
|
for _, x := range st.Redirects {
|
||||||
|
ids[fmt.Sprintf("d%d", x.ID)] = true
|
||||||
|
}
|
||||||
|
for mark := range base {
|
||||||
|
obj, _, _ := strings.Cut(mark, "-") // f3-hairpin, f3-6 are f3's
|
||||||
|
if len(obj) < 2 || !strings.ContainsRune("rfsd", rune(obj[0])) || strings.Trim(obj[1:], "0123456789") != "" {
|
||||||
|
continue // b-established, p-input
|
||||||
|
}
|
||||||
|
if !ids[obj] {
|
||||||
|
delete(base, mark)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SaveBoot writes the ruleset of st for the boot unit, and the fallback
|
// SaveBoot writes the ruleset of st for the boot unit, and the fallback
|
||||||
// it loads if that fails (FallbackFile), atomically. The boot ruleset has
|
// it loads if that fails (FallbackFile), atomically. The boot ruleset has
|
||||||
// no flowtable: it loads before the network comes up, when its devices
|
// no flowtable: it loads before the network comes up, when its devices
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -437,3 +438,26 @@ func TestShadowInContainer(t *testing.T) {
|
|||||||
t.Errorf("the shadow fallback isn't recognised: %+v %v", tb, err)
|
t.Errorf("the shadow fallback isn't recognised: %+v %v", tb, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPruneCounters: the totals of objects gone (an import with --replace
|
||||||
|
// gives every object a new ID) are forgotten; built-ins, policies and
|
||||||
|
// disabled objects keep theirs.
|
||||||
|
func TestPruneCounters(t *testing.T) {
|
||||||
|
base := map[string]Counter{}
|
||||||
|
for _, m := range []string{"r1", "r2", "r3", "f4", "f4-hairpin", "f4-6", "f5-hairpin", "s6", "d7", "d8", "b-established", "p-input", "fallback-input"} {
|
||||||
|
base[m] = Counter{Packets: 1}
|
||||||
|
}
|
||||||
|
st := model.NewState()
|
||||||
|
st.Rules = []*model.Rule{{ID: 1}, {ID: 3, Disabled: true}}
|
||||||
|
st.Forwards = []*model.PortForward{{ID: 4}}
|
||||||
|
st.Redirects = []*model.Redirect{{ID: 8}}
|
||||||
|
pruneCounters(base, st)
|
||||||
|
var got []string
|
||||||
|
for m := range base {
|
||||||
|
got = append(got, m)
|
||||||
|
}
|
||||||
|
sort.Strings(got)
|
||||||
|
if strings.Join(got, " ") != "b-established d8 f4 f4-6 f4-hairpin fallback-input p-input r1 r3" {
|
||||||
|
t.Errorf("kept %v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user