summaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/goprecords/aggregate.go49
-rw-r--r--internal/goprecords/db.go28
-rw-r--r--internal/goprecords/parse_test.go78
-rw-r--r--internal/goprecords/types.go44
4 files changed, 136 insertions, 63 deletions
diff --git a/internal/goprecords/aggregate.go b/internal/goprecords/aggregate.go
index ba446cb..e75d486 100644
--- a/internal/goprecords/aggregate.go
+++ b/internal/goprecords/aggregate.go
@@ -6,7 +6,6 @@ import (
"fmt"
"os"
"path/filepath"
- "strconv"
"strings"
)
@@ -82,35 +81,14 @@ func processRecordsFile(ctx context.Context, path, host string, out *Aggregates)
return ctx.Err()
default:
}
- line := strings.TrimSpace(sc.Text())
- if line == "" {
+ rec, ok := parseRecordLine(sc.Text())
+ if !ok {
continue
}
- parts := strings.SplitN(line, ":", 3)
- if len(parts) != 3 {
- continue
- }
- uptime, _ := strconv.ParseUint(parts[0], 10, 64)
- bootTime, _ := strconv.ParseUint(parts[1], 10, 64)
- osStr := parts[2]
- uname := osStr
- if i := strings.Index(osStr, " "); i > 0 {
- uname = osStr[:i]
- }
- osMajor := uname + " "
- rest := osStr
- if i := strings.Index(osStr, " "); i >= 0 {
- rest = osStr[i+1:]
- }
- if j := strings.Index(rest, "."); j >= 0 {
- osMajor += rest[:j] + "..."
- } else {
- osMajor += rest + "..."
- }
- out.Host[host].AddRecord(uptime, bootTime)
- getOrNewAggregate(out.Kernel, osStr).AddRecord(uptime, bootTime)
- getOrNewAggregate(out.KernelName, uname).AddRecord(uptime, bootTime)
- getOrNewAggregate(out.KernelMajor, osMajor).AddRecord(uptime, bootTime)
+ out.Host[host].AddRecord(rec.Uptime, rec.BootTime)
+ getOrNewAggregate(out.Kernel, rec.OS).AddRecord(rec.Uptime, rec.BootTime)
+ getOrNewAggregate(out.KernelName, rec.KernelName).AddRecord(rec.Uptime, rec.BootTime)
+ getOrNewAggregate(out.KernelMajor, rec.KernelMajor).AddRecord(rec.Uptime, rec.BootTime)
}
if err := sc.Err(); err != nil {
return fmt.Errorf("scan %s: %w", path, err)
@@ -137,18 +115,13 @@ func lastKernelFromFile(path string) (string, error) {
var lastOS string
sc := bufio.NewScanner(f)
for sc.Scan() {
- line := strings.TrimSpace(sc.Text())
- if line == "" {
- continue
- }
- parts := strings.SplitN(line, ":", 3)
- if len(parts) != 3 {
+ rec, ok := parseRecordLine(sc.Text())
+ if !ok {
continue
}
- bootTime, _ := strconv.ParseUint(parts[1], 10, 64)
- if bootTime >= maxBoot {
- maxBoot = bootTime
- lastOS = parts[2]
+ if rec.BootTime >= maxBoot {
+ maxBoot = rec.BootTime
+ lastOS = rec.OS
}
}
return lastOS, sc.Err()
diff --git a/internal/goprecords/db.go b/internal/goprecords/db.go
index 18fadf5..c11cdb0 100644
--- a/internal/goprecords/db.go
+++ b/internal/goprecords/db.go
@@ -7,7 +7,6 @@ import (
"fmt"
"os"
"path/filepath"
- "strconv"
"strings"
_ "modernc.org/sqlite"
@@ -96,32 +95,11 @@ func ImportFromDir(ctx context.Context, db *sql.DB, statsDir string) error {
}
sc := bufio.NewScanner(f)
for sc.Scan() {
- line := strings.TrimSpace(sc.Text())
- if line == "" {
+ rec, ok := parseRecordLine(sc.Text())
+ if !ok {
continue
}
- parts := strings.SplitN(line, ":", 3)
- if len(parts) != 3 {
- continue
- }
- uptimeSec, _ := strconv.ParseInt(parts[0], 10, 64)
- bootTime, _ := strconv.ParseInt(parts[1], 10, 64)
- osStr := parts[2]
- osKernelName := osStr
- if i := strings.Index(osStr, " "); i > 0 {
- osKernelName = osStr[:i]
- }
- osMajor := osKernelName + " "
- rest := osStr
- if i := strings.Index(osStr, " "); i >= 0 {
- rest = osStr[i+1:]
- }
- if j := strings.Index(rest, "."); j >= 0 {
- osMajor += rest[:j] + "..."
- } else {
- osMajor += rest + "..."
- }
- _, err := insert.ExecContext(ctx, host, uptimeSec, bootTime, osStr, osKernelName, osMajor)
+ _, err := insert.ExecContext(ctx, host, rec.Uptime, rec.BootTime, rec.OS, rec.KernelName, rec.KernelMajor)
if err != nil {
f.Close()
return fmt.Errorf("insert: %w", err)
diff --git a/internal/goprecords/parse_test.go b/internal/goprecords/parse_test.go
index 304b06e..700440d 100644
--- a/internal/goprecords/parse_test.go
+++ b/internal/goprecords/parse_test.go
@@ -51,6 +51,84 @@ func TestParseMetric(t *testing.T) {
}
}
+func TestParseRecordLine(t *testing.T) {
+ tests := []struct {
+ in string
+ want recordLine
+ ok bool
+ }{
+ {
+ "12345:1700000000:Linux 6.5.0-generic",
+ recordLine{
+ Uptime: 12345,
+ BootTime: 1700000000,
+ OS: "Linux 6.5.0-generic",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {
+ " 99:100:FreeBSD 14.0-RELEASE ",
+ recordLine{
+ Uptime: 99,
+ BootTime: 100,
+ OS: "FreeBSD 14.0-RELEASE",
+ KernelName: "FreeBSD",
+ KernelMajor: "FreeBSD 14...",
+ },
+ true,
+ },
+ {
+ "500:200:SingleToken",
+ recordLine{
+ Uptime: 500,
+ BootTime: 200,
+ OS: "SingleToken",
+ KernelName: "SingleToken",
+ KernelMajor: "SingleToken SingleToken...",
+ },
+ true,
+ },
+ {
+ "100:200:Linux 6.5.0:extra",
+ recordLine{
+ Uptime: 100,
+ BootTime: 200,
+ OS: "Linux 6.5.0:extra",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {
+ "abc:def:Linux 6.5.0",
+ recordLine{
+ Uptime: 0,
+ BootTime: 0,
+ OS: "Linux 6.5.0",
+ KernelName: "Linux",
+ KernelMajor: "Linux 6...",
+ },
+ true,
+ },
+ {"", recordLine{}, false},
+ {" ", recordLine{}, false},
+ {"only:two", recordLine{}, false},
+ {"no-colons-at-all", recordLine{}, false},
+ }
+ for _, tt := range tests {
+ got, ok := parseRecordLine(tt.in)
+ if ok != tt.ok {
+ t.Errorf("parseRecordLine(%q) ok=%v, want %v", tt.in, ok, tt.ok)
+ continue
+ }
+ if tt.ok && got != tt.want {
+ t.Errorf("parseRecordLine(%q) = %+v, want %+v", tt.in, got, tt.want)
+ }
+ }
+}
+
func TestParseOutputFormat(t *testing.T) {
tests := []struct {
in string
diff --git a/internal/goprecords/types.go b/internal/goprecords/types.go
index 05a68f8..ae8a059 100644
--- a/internal/goprecords/types.go
+++ b/internal/goprecords/types.go
@@ -75,6 +75,15 @@ func NewHostAggregate(name, lastKernel string) *HostAggregate {
}
}
+// recordLine holds the parsed fields from a single uptimed record line.
+type recordLine struct {
+ Uptime uint64
+ BootTime uint64
+ OS string
+ KernelName string
+ KernelMajor string
+}
+
// tableRow is one row in the report table.
type tableRow struct {
Pos string
@@ -250,6 +259,41 @@ func ParseOutputFormat(s string) (OutputFormat, error) {
}
}
+func parseRecordLine(line string) (recordLine, bool) {
+ line = strings.TrimSpace(line)
+ if line == "" {
+ return recordLine{}, false
+ }
+ parts := strings.SplitN(line, ":", 3)
+ if len(parts) != 3 {
+ return recordLine{}, false
+ }
+ uptime, _ := strconv.ParseUint(parts[0], 10, 64)
+ bootTime, _ := strconv.ParseUint(parts[1], 10, 64)
+ osStr := parts[2]
+ kernelName := osStr
+ if i := strings.Index(osStr, " "); i > 0 {
+ kernelName = osStr[:i]
+ }
+ kernelMajor := kernelName + " "
+ rest := osStr
+ if i := strings.Index(osStr, " "); i >= 0 {
+ rest = osStr[i+1:]
+ }
+ if j := strings.Index(rest, "."); j >= 0 {
+ kernelMajor += rest[:j] + "..."
+ } else {
+ kernelMajor += rest + "..."
+ }
+ return recordLine{
+ Uptime: uptime,
+ BootTime: bootTime,
+ OS: osStr,
+ KernelName: kernelName,
+ KernelMajor: kernelMajor,
+ }, true
+}
+
func wordWrap(s string, lineLimit int) string {
if lineLimit <= 0 || len(s) <= lineLimit {
return s