m/p/logtree: add kmsg pipe

This allows ingesting Linux kernel (kmsg) logs into logtree with
the original metadata (timestamp, severity) preserved.

Change-Id: Ibb6e3a7a0ae4a008b8e9c98beccb3a95c067cb75
Reviewed-on: https://review.monogon.dev/c/monogon/+/2044
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/pkg/logtree/kmsg_test.go b/metropolis/pkg/logtree/kmsg_test.go
new file mode 100644
index 0000000..e2faf82
--- /dev/null
+++ b/metropolis/pkg/logtree/kmsg_test.go
@@ -0,0 +1,43 @@
+//go:build linux
+// +build linux
+
+package logtree
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestParseKmsg(t *testing.T) {
+	now := time.Unix(1691593045, 128027944)
+	nowMonotonic := time.Duration(1501096434537722)
+
+	for i, te := range []struct {
+		line string
+		want *LeveledPayload
+	}{
+		// Empty line
+		{"", nil},
+		// Unknown format
+		{"Not a valid line", nil},
+		// Normal entry
+		{"6,30962,1501094342185,-;test\n", &LeveledPayload{
+			messages:  []string{"test"},
+			timestamp: time.Date(2023, 8, 9, 14, 57, 23, 35675222, time.UTC),
+			severity:  INFO,
+		}},
+		// With metadata and different severity
+		{"4,30951,1486884175312,-;nvme nvme2: starting error recovery\n SUBSYSTEM=nvme\n DEVICE=c239:2\n", &LeveledPayload{
+			messages:  []string{"nvme nvme2: starting error recovery"},
+			timestamp: time.Date(2023, 8, 9, 11, 00, 32, 868802222, time.UTC),
+			severity:  WARNING,
+		}},
+	} {
+		got := parseKmsg(now, nowMonotonic, []byte(te.line))
+		if diff := cmp.Diff(te.want, got, cmp.AllowUnexported(LeveledPayload{})); diff != "" {
+			t.Errorf("%d: mismatch (-want +got):\n%s", i, diff)
+		}
+	}
+}