diff options
| -rw-r--r-- | internal/daemon/upload.go | 23 | ||||
| -rw-r--r-- | internal/goprecords/aggregate_test.go | 12 | ||||
| -rw-r--r-- | internal/goprecords/db_test.go | 8 | ||||
| -rw-r--r-- | internal/goprecords/report.go | 10 | ||||
| -rw-r--r-- | internal/goprecords/report_test.go | 62 | ||||
| -rw-r--r-- | internal/goprecords/types.go | 22 | ||||
| -rw-r--r-- | internal/goprecords/types_test.go | 24 |
7 files changed, 89 insertions, 72 deletions
diff --git a/internal/daemon/upload.go b/internal/daemon/upload.go index 71bffd0..832019e 100644 --- a/internal/daemon/upload.go +++ b/internal/daemon/upload.go @@ -14,12 +14,21 @@ import ( const maxUploadBytes = 8 << 20 -var uploadKinds = map[string]string{ - "txt": ".txt", - "cur.txt": ".cur.txt", - "records": ".records", - "os.txt": ".os.txt", - "cpuinfo.txt": ".cpuinfo.txt", +func uploadKindExtension(kind string) (ext string, ok bool) { + switch kind { + case "txt": + return ".txt", true + case "cur.txt": + return ".cur.txt", true + case "records": + return ".records", true + case "os.txt": + return ".os.txt", true + case "cpuinfo.txt": + return ".cpuinfo.txt", true + default: + return "", false + } } func uploadHandler(statsDir string, store *authkeys.Store) http.Handler { @@ -38,7 +47,7 @@ func serveUploadPut(w http.ResponseWriter, r *http.Request, statsDir string, sto http.Error(w, "bad path", http.StatusBadRequest) return } - ext, ok := uploadKinds[kind] + ext, ok := uploadKindExtension(kind) if !ok { http.Error(w, "unknown file kind", http.StatusBadRequest) return diff --git a/internal/goprecords/aggregate_test.go b/internal/goprecords/aggregate_test.go index c7e75b4..d675b4f 100644 --- a/internal/goprecords/aggregate_test.go +++ b/internal/goprecords/aggregate_test.go @@ -26,7 +26,7 @@ func TestNewAggregatorMatchesDirFS(t *testing.T) { if len(a.Host) != 1 || len(b.Host) != 1 { t.Fatalf("hosts: a=%d b=%d", len(a.Host), len(b.Host)) } - if a.Host["h1"].Boots != b.Host["h1"].Boots || a.Host["h1"].Uptime != b.Host["h1"].Uptime { + if a.Host["h1"].Stats.Boots != b.Host["h1"].Stats.Boots || a.Host["h1"].Stats.Uptime != b.Host["h1"].Stats.Uptime { t.Fatalf("mismatch: %#v vs %#v", a.Host["h1"], b.Host["h1"]) } } @@ -53,7 +53,7 @@ func TestAggregateMapFS(t *testing.T) { t.Fatalf("Aggregate: %v", err) } h := aggs.Host["box"] - if h == nil || h.Boots != 2 || h.LastKernel != "Linux 5.11.0-test" { + if h == nil || h.Stats.Boots != 2 || h.LastKernel != "Linux 5.11.0-test" { t.Fatalf("host box: %#v", h) } } @@ -109,10 +109,10 @@ func TestAggregateFixturesContent(t *testing.T) { // Check a specific host if host, ok := aggregates.Host["earth"]; ok { - if host.Boots == 0 { + if host.Stats.Boots == 0 { t.Error("expected non-zero boots for earth") } - if host.Uptime == 0 { + if host.Stats.Uptime == 0 { t.Error("expected non-zero uptime for earth") } if host.LastKernel == "" { @@ -200,8 +200,8 @@ func TestProcessRecordsFile(t *testing.T) { t.Fatalf("failed to process records: %v", err) } - if aggs.Host["test"].Boots != 2 { - t.Errorf("expected 2 boots, got %d", aggs.Host["test"].Boots) + if aggs.Host["test"].Stats.Boots != 2 { + t.Errorf("expected 2 boots, got %d", aggs.Host["test"].Stats.Boots) } } diff --git a/internal/goprecords/db_test.go b/internal/goprecords/db_test.go index 3d6b788..44ee2f1 100644 --- a/internal/goprecords/db_test.go +++ b/internal/goprecords/db_test.go @@ -53,11 +53,11 @@ func TestLoadAggregates(t *testing.T) { } if host, ok := aggs.Host["host1"]; ok { - if host.Boots != 2 { - t.Errorf("expected 2 boots, got %d", host.Boots) + if host.Stats.Boots != 2 { + t.Errorf("expected 2 boots, got %d", host.Stats.Boots) } - if host.Uptime != 3000 { - t.Errorf("expected uptime 3000, got %d", host.Uptime) + if host.Stats.Uptime != 3000 { + t.Errorf("expected uptime 3000, got %d", host.Stats.Uptime) } if host.LastKernel != "Linux 5.11" { t.Errorf("LastKernel = %q, want %q (latest boot_time row)", host.LastKernel, "Linux 5.11") diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go index 11c809a..a512407 100644 --- a/internal/goprecords/report.go +++ b/internal/goprecords/report.go @@ -14,17 +14,17 @@ type metricExtractor struct { } var uptimeMetricExtractor = metricExtractor{ - hostSortKey: func(h *HostAggregate) uint64 { return h.Uptime }, + hostSortKey: func(h *HostAggregate) uint64 { return h.Stats.Uptime }, aggSortKey: func(a *Aggregate) uint64 { return a.Uptime }, - hostHuman: func(h *HostAggregate) string { return formatDuration(h.Uptime) }, + hostHuman: func(h *HostAggregate) string { return formatDuration(h.Stats.Uptime) }, aggHuman: func(a *Aggregate) string { return formatDuration(a.Uptime) }, } var metricExtractors = map[Metric]metricExtractor{ MetricBoots: { - hostSortKey: func(h *HostAggregate) uint64 { return h.Boots }, + hostSortKey: func(h *HostAggregate) uint64 { return h.Stats.Boots }, aggSortKey: func(a *Aggregate) uint64 { return a.Boots }, - hostHuman: func(h *HostAggregate) string { return formatInt(h.Boots) }, + hostHuman: func(h *HostAggregate) string { return formatInt(h.Stats.Boots) }, aggHuman: func(a *Aggregate) string { return formatInt(a.Boots) }, }, MetricUptime: uptimeMetricExtractor, @@ -215,7 +215,7 @@ func (r reportBuilder) buildHostTable() ([]tableRow, bool) { } rows = append(rows, tableRow{ Pos: fmt.Sprintf("%d.", i+1), - Name: active + h.Name, + Name: active + h.Stats.Name, Value: r.humanStrHost(h), LastKernel: h.LastKernel, }) diff --git a/internal/goprecords/report_test.go b/internal/goprecords/report_test.go index a227528..1469942 100644 --- a/internal/goprecords/report_test.go +++ b/internal/goprecords/report_test.go @@ -72,10 +72,10 @@ func TestReportWithData(t *testing.T) { // Add a host hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg reporter := NewReporter(aggs, CategoryHost, 20, MetricUptime, FormatPlaintext, 1) @@ -101,10 +101,10 @@ func TestReportHTML(t *testing.T) { } hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg reporter := NewReporter(aggs, CategoryHost, 20, MetricUptime, FormatHTML, 2) @@ -130,10 +130,10 @@ func TestReportMarkdown(t *testing.T) { } hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg reporter := NewReporter(aggs, CategoryHost, 20, MetricUptime, FormatMarkdown, 2) @@ -156,10 +156,10 @@ func TestReportGemtext(t *testing.T) { } hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg reporter := NewReporter(aggs, CategoryHost, 20, MetricUptime, FormatGemtext, 2) @@ -175,21 +175,21 @@ func TestReportGemtext(t *testing.T) { func TestExtractorForUnknownMetricFallsBackToUptime(t *testing.T) { h := NewHostAggregate("h", "k") - h.Uptime = 999 - h.Boots = 1 + h.Stats.Uptime = 999 + h.Stats.Boots = 1 a := NewAggregate("k") a.Uptime = 888 a.Boots = 2 unknown := Metric(255) ex := extractorFor(unknown) - if got := ex.hostSortKey(h); got != h.Uptime { - t.Errorf("host sort key: got %d want %d", got, h.Uptime) + if got := ex.hostSortKey(h); got != h.Stats.Uptime { + t.Errorf("host sort key: got %d want %d", got, h.Stats.Uptime) } if got := ex.aggSortKey(a); got != a.Uptime { t.Errorf("agg sort key: got %d want %d", got, a.Uptime) } - if got := ex.hostHuman(h); got != formatDuration(h.Uptime) { - t.Errorf("host human: got %q want %q", got, formatDuration(h.Uptime)) + if got := ex.hostHuman(h); got != formatDuration(h.Stats.Uptime) { + t.Errorf("host human: got %q want %q", got, formatDuration(h.Stats.Uptime)) } if got := ex.aggHuman(a); got != formatDuration(a.Uptime) { t.Errorf("agg human: got %q want %q", got, formatDuration(a.Uptime)) @@ -220,10 +220,10 @@ func TestReportMetrics(t *testing.T) { } hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg metrics := []Metric{MetricBoots, MetricUptime, MetricScore, MetricDowntime, MetricLifespan} @@ -274,7 +274,7 @@ func TestReportLimit(t *testing.T) { for i := 0; i < 10; i++ { host := hostName(i) hagg := NewHostAggregate(host, "Linux") - hagg.Uptime = uint64(86400000 * (10 - i)) + hagg.Stats.Uptime = uint64(86400000 * (10 - i)) aggs.Host[host] = hagg } @@ -361,10 +361,10 @@ func testAggregates() *Aggregates { KernelName: make(map[string]*Aggregate), } hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.Uptime = 86400000 - hagg.Boots = 10 - hagg.FirstBoot = 1000 - hagg.LastSeen = 86401000 + hagg.Stats.Uptime = 86400000 + hagg.Stats.Boots = 10 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 86401000 aggs.Host["host1"] = hagg kernel := NewAggregate("Linux 5.10") kernel.Uptime = 86400000 diff --git a/internal/goprecords/types.go b/internal/goprecords/types.go index 343f7e0..418d269 100644 --- a/internal/goprecords/types.go +++ b/internal/goprecords/types.go @@ -64,18 +64,26 @@ func NewAggregate(name string) *Aggregate { // HostAggregate adds last-kernel and lifespan/downtime for host reports. type HostAggregate struct { - Aggregate + Stats Aggregate LastKernel string } // NewHostAggregate constructs a HostAggregate. func NewHostAggregate(name, lastKernel string) *HostAggregate { return &HostAggregate{ - Aggregate: Aggregate{Name: name}, + Stats: Aggregate{Name: name}, LastKernel: lastKernel, } } +func (h *HostAggregate) AddRecord(uptimeSec, bootTime uint64) { + h.Stats.AddRecord(uptimeSec, bootTime) +} + +func (h *HostAggregate) IsActive(limitDays uint) bool { + return h.Stats.IsActive(limitDays) +} + // tableRow is one row in the report table. type tableRow struct { Pos string @@ -178,24 +186,24 @@ func (a *Aggregate) MetaScore() uint64 { // Lifespan returns last-seen minus first-boot. func (h *HostAggregate) Lifespan() uint64 { - if h.LastSeen < h.FirstBoot { + if h.Stats.LastSeen < h.Stats.FirstBoot { return 0 } - return h.LastSeen - h.FirstBoot + return h.Stats.LastSeen - h.Stats.FirstBoot } // Downtime returns lifespan minus uptime. func (h *HostAggregate) Downtime() uint64 { life := h.Lifespan() - if h.Uptime > life { + if h.Stats.Uptime > life { return 0 } - return life - h.Uptime + return life - h.Stats.Uptime } // MetaScore returns the host-specific score (includes downtime component). func (h *HostAggregate) MetaScore() uint64 { - return uint64(h.Downtime()/2000000) + h.Aggregate.MetaScore() + return uint64(h.Downtime()/2000000) + h.Stats.MetaScore() } // MetricDescription returns the description text for a metric. diff --git a/internal/goprecords/types_test.go b/internal/goprecords/types_test.go index 245cead..61ccbce 100644 --- a/internal/goprecords/types_test.go +++ b/internal/goprecords/types_test.go @@ -17,8 +17,8 @@ func TestNewAggregate(t *testing.T) { func TestNewHostAggregate(t *testing.T) { hagg := NewHostAggregate("testhost", "Linux 5.10.0") - if hagg.Name != "testhost" { - t.Errorf("got %q, want %q", hagg.Name, "testhost") + if hagg.Stats.Name != "testhost" { + t.Errorf("got %q, want %q", hagg.Stats.Name, "testhost") } if hagg.LastKernel != "Linux 5.10.0" { t.Errorf("got %q, want %q", hagg.LastKernel, "Linux 5.10.0") @@ -92,8 +92,8 @@ func TestMetaScore(t *testing.T) { func TestHostAggregateLifespan(t *testing.T) { hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.FirstBoot = 1000 - hagg.LastSeen = 5000 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 5000 lifespan := hagg.Lifespan() if lifespan != 4000 { @@ -103,9 +103,9 @@ func TestHostAggregateLifespan(t *testing.T) { func TestHostAggregateDowntime(t *testing.T) { hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.FirstBoot = 1000 - hagg.LastSeen = 5000 - hagg.Uptime = 3000 + hagg.Stats.FirstBoot = 1000 + hagg.Stats.LastSeen = 5000 + hagg.Stats.Uptime = 3000 downtime := hagg.Downtime() if downtime != 1000 { @@ -115,8 +115,8 @@ func TestHostAggregateDowntime(t *testing.T) { func TestHostAggregateLifespanUnderflow(t *testing.T) { hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.FirstBoot = 5000 - hagg.LastSeen = 1000 + hagg.Stats.FirstBoot = 5000 + hagg.Stats.LastSeen = 1000 if got := hagg.Lifespan(); got != 0 { t.Errorf("Lifespan() = %d, want 0 when LastSeen < FirstBoot", got) @@ -137,9 +137,9 @@ func TestHostAggregateDowntimeUnderflow(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hagg := NewHostAggregate("host1", "Linux 5.10") - hagg.FirstBoot = tt.firstBoot - hagg.LastSeen = tt.lastSeen - hagg.Uptime = tt.uptime + hagg.Stats.FirstBoot = tt.firstBoot + hagg.Stats.LastSeen = tt.lastSeen + hagg.Stats.Uptime = tt.uptime if got := hagg.Downtime(); got != 0 { t.Errorf("Downtime() = %d, want 0", got) } |
