ingest: add status history support and drop status_at_collection
This commit is contained in:
@@ -278,8 +278,20 @@
|
||||
"firmware": "SN02",
|
||||
"interface": "SATA",
|
||||
"present": true,
|
||||
"status": "Critical",
|
||||
"error_description": "Error Code on GPU 0 [18:00.0] (S/N 1653925025827) = 020000190097 (unexpected device interrupts)"
|
||||
"status": "OK",
|
||||
"status_changed_at": "2026-02-10T15:22:00Z",
|
||||
"status_history": [
|
||||
{
|
||||
"status": "Critical",
|
||||
"changed_at": "2026-02-10T15:10:00Z",
|
||||
"details": "I/O timeout on NVMe queue 3"
|
||||
},
|
||||
{
|
||||
"status": "OK",
|
||||
"changed_at": "2026-02-10T15:22:00Z",
|
||||
"details": "Recovered after controller reset"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"pcie_devices": [
|
||||
|
||||
@@ -287,3 +287,88 @@ func TestIngestHardwareCreatesMachineInStock(t *testing.T) {
|
||||
t.Fatalf("expected stock machine with null project, got project=%v", projectID.String)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIngestHardwareStoresStatusHistoryInObservationDetails(t *testing.T) {
|
||||
dsn := os.Getenv("DATABASE_DSN")
|
||||
if dsn == "" {
|
||||
t.Skip("DATABASE_DSN not set")
|
||||
}
|
||||
|
||||
db, err := repository.Open(dsn)
|
||||
if err != nil {
|
||||
t.Fatalf("open db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := applyMigrations(db); err != nil {
|
||||
t.Fatalf("apply migrations: %v", err)
|
||||
}
|
||||
if err := cleanupRegistry(db); err != nil {
|
||||
t.Fatalf("cleanup: %v", err)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
RegisterIngestRoutes(mux, IngestDependencies{Service: ingest.NewService(db)})
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
payload := map[string]any{
|
||||
"target_host": "history-server",
|
||||
"collected_at": "2026-02-10T15:30:00Z",
|
||||
"hardware": map[string]any{
|
||||
"board": map[string]any{"serial_number": "HISTORY-001"},
|
||||
"storage": []map[string]any{
|
||||
{
|
||||
"slot": "Disk.Bay.0",
|
||||
"serial_number": "HISTORY-DISK-001",
|
||||
"present": true,
|
||||
"status": "OK",
|
||||
"status_changed_at": "2026-02-10T15:22:00Z",
|
||||
"status_history": []map[string]any{
|
||||
{
|
||||
"status": "Critical",
|
||||
"changed_at": "2026-02-10T15:10:00Z",
|
||||
},
|
||||
{
|
||||
"status": "OK",
|
||||
"changed_at": "2026-02-10T15:22:00Z",
|
||||
"details": "Recovered after controller reset",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal payload: %v", err)
|
||||
}
|
||||
|
||||
resp, err := http.Post(server.URL+"/ingest/hardware", "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatalf("post: %v", err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var historyStatus, statusChangedAt string
|
||||
row := db.QueryRow(`
|
||||
SELECT
|
||||
JSON_UNQUOTE(JSON_EXTRACT(details, '$.status_history[0].status')),
|
||||
JSON_UNQUOTE(JSON_EXTRACT(details, '$.status_changed_at'))
|
||||
FROM observations
|
||||
ORDER BY observed_at DESC, id DESC
|
||||
LIMIT 1
|
||||
`)
|
||||
if err := row.Scan(&historyStatus, &statusChangedAt); err != nil {
|
||||
t.Fatalf("details query: %v", err)
|
||||
}
|
||||
if historyStatus != "CRITICAL" {
|
||||
t.Fatalf("expected first status_history status CRITICAL, got %q", historyStatus)
|
||||
}
|
||||
if statusChangedAt != "2026-02-10T15:22:00Z" {
|
||||
t.Fatalf("expected status_changed_at=2026-02-10T15:22:00Z, got %q", statusChangedAt)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user