feat(go): advance validation sidecar toward production
This commit is contained in:
parent
6a06eba255
commit
f85c2e831a
92 changed files with 8881 additions and 91 deletions
111
go-agent/internal/eventlog/writer.go
Normal file
111
go-agent/internal/eventlog/writer.go
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// Package eventlog retains a bounded JSONL copy of emitted event envelopes.
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Writer appends records to Path and keeps one rotated segment at Path + ".1".
|
||||
type Writer struct {
|
||||
Path string
|
||||
MaxBytes int64
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// New preflights the parent directory and destination permissions. A service
|
||||
// can call it before signaling readiness so a broken retention path fails the
|
||||
// start instead of silently dropping records.
|
||||
func New(path string, maxBytes int64) (*Writer, error) {
|
||||
if path == "" {
|
||||
return nil, fmt.Errorf("event log path is required")
|
||||
}
|
||||
if maxBytes <= 0 {
|
||||
return nil, fmt.Errorf("event log max bytes must be positive")
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
|
||||
return nil, fmt.Errorf("create event log directory: %w", err)
|
||||
}
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open event log: %w", err)
|
||||
}
|
||||
if err := file.Chmod(0o600); err != nil {
|
||||
file.Close()
|
||||
return nil, fmt.Errorf("protect event log: %w", err)
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return nil, fmt.Errorf("close event log: %w", err)
|
||||
}
|
||||
return &Writer{Path: path, MaxBytes: maxBytes}, nil
|
||||
}
|
||||
|
||||
// Append writes exactly one JSON record. Alert and incident records are synced
|
||||
// before success is reported; routine status records rely on normal kernel
|
||||
// writeback to avoid forcing a disk flush every sweep.
|
||||
func (w *Writer) Append(record map[string]any) error {
|
||||
if w == nil {
|
||||
return fmt.Errorf("event log writer is required")
|
||||
}
|
||||
raw, err := json.Marshal(record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode event log record: %w", err)
|
||||
}
|
||||
raw = append(raw, '\n')
|
||||
if len(raw) > maxRecordBytes {
|
||||
return fmt.Errorf("event log record exceeds %d bytes", maxRecordBytes)
|
||||
}
|
||||
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if err := w.rotateBefore(int64(len(raw))); err != nil {
|
||||
return err
|
||||
}
|
||||
file, err := os.OpenFile(w.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("open event log: %w", err)
|
||||
}
|
||||
if err := file.Chmod(0o600); err != nil {
|
||||
file.Close()
|
||||
return fmt.Errorf("protect event log: %w", err)
|
||||
}
|
||||
if _, err := file.Write(raw); err != nil {
|
||||
file.Close()
|
||||
return fmt.Errorf("append event log: %w", err)
|
||||
}
|
||||
if eventType, _ := record["event_type"].(string); eventType != "status" {
|
||||
if err := file.Sync(); err != nil {
|
||||
file.Close()
|
||||
return fmt.Errorf("sync event log: %w", err)
|
||||
}
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return fmt.Errorf("close event log: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *Writer) rotateBefore(incoming int64) error {
|
||||
info, err := os.Stat(w.Path)
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("stat event log: %w", err)
|
||||
}
|
||||
// Always allow one record into an empty file, even when that individual
|
||||
// envelope is larger than the configured bound.
|
||||
if info.Size() == 0 || info.Size()+incoming <= w.MaxBytes {
|
||||
return nil
|
||||
}
|
||||
if err := os.Rename(w.Path, w.Path+".1"); err != nil {
|
||||
return fmt.Errorf("rotate event log: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue