feat(go): advance validation sidecar toward production

This commit is contained in:
Luna 2026-07-22 01:52:19 -07:00
parent 6a06eba255
commit f85c2e831a
No known key found for this signature in database
92 changed files with 8881 additions and 91 deletions

View file

@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-3.0-or-later
package eventlog
import (
"bufio"
"encoding/json"
"fmt"
"os"
)
const maxRecordBytes = 1 << 20
// ReadRecent returns at most limit records in chronological order, reading the
// older rotated segment before the active file. Every returned row is valid
// JSON; corruption is reported instead of passed to an operator as evidence.
func ReadRecent(path string, limit int) ([][]byte, error) {
if path == "" {
return nil, fmt.Errorf("event log path is required")
}
if limit <= 0 {
return nil, fmt.Errorf("event limit must be positive")
}
buffer := recentBuffer{limit: limit}
if _, err := os.Stat(path + ".1"); err == nil {
if err := scanRecords(path+".1", buffer.add); err != nil {
return nil, err
}
} else if !os.IsNotExist(err) {
return nil, fmt.Errorf("stat rotated event log: %w", err)
}
if err := scanRecords(path, buffer.add); err != nil {
return nil, err
}
return buffer.values(), nil
}
func scanRecords(path string, consume func([]byte)) error {
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("open event log %s: %w", path, err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 64*1024), maxRecordBytes)
line := 0
for scanner.Scan() {
line++
raw := scanner.Bytes()
if len(raw) == 0 {
continue
}
if !json.Valid(raw) {
return fmt.Errorf("invalid JSON in %s at line %d", path, line)
}
consume(append([]byte(nil), raw...))
}
if err := scanner.Err(); err != nil {
return fmt.Errorf("read event log %s: %w", path, err)
}
return nil
}
type recentBuffer struct {
limit int
rows [][]byte
next int
}
func (b *recentBuffer) add(raw []byte) {
if len(b.rows) < b.limit {
b.rows = append(b.rows, raw)
return
}
b.rows[b.next] = raw
b.next = (b.next + 1) % b.limit
}
func (b *recentBuffer) values() [][]byte {
if len(b.rows) < b.limit || b.next == 0 {
return b.rows
}
result := make([][]byte, 0, len(b.rows))
result = append(result, b.rows[b.next:]...)
result = append(result, b.rows[:b.next]...)
return result
}