2026-03-31 14:52:50 -03:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"log"
|
|
|
|
|
"net/http"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strconv"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"metis/pkg/sentinel"
|
|
|
|
|
)
|
|
|
|
|
|
2026-04-11 00:17:10 -03:00
|
|
|
var fatalf = log.Fatalf
|
|
|
|
|
|
2026-03-31 14:52:50 -03:00
|
|
|
func main() {
|
|
|
|
|
interval := time.Duration(getenvInt("METIS_SENTINEL_INTERVAL_SEC", 300)) * time.Second
|
|
|
|
|
pushURL := os.Getenv("METIS_SENTINEL_PUSH_URL")
|
|
|
|
|
runOnce := os.Getenv("METIS_SENTINEL_RUN_ONCE") == "1"
|
|
|
|
|
|
|
|
|
|
for {
|
|
|
|
|
snap := sentinel.Collect()
|
|
|
|
|
enc := json.NewEncoder(os.Stdout)
|
|
|
|
|
enc.SetIndent("", " ")
|
|
|
|
|
if err := enc.Encode(snap); err != nil {
|
2026-04-11 00:17:10 -03:00
|
|
|
fatalf("encode: %v", err)
|
2026-03-31 14:52:50 -03:00
|
|
|
}
|
|
|
|
|
if out := os.Getenv("METIS_SENTINEL_OUT"); out != "" {
|
|
|
|
|
writeHistory(out, snap)
|
|
|
|
|
}
|
|
|
|
|
if pushURL != "" {
|
|
|
|
|
if err := pushSnapshot(pushURL, snap); err != nil {
|
|
|
|
|
log.Printf("push snapshot failed: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if runOnce || pushURL == "" {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(interval)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeHistory(path string, snap *sentinel.Snapshot) {
|
|
|
|
|
if path == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
ts := time.Now().UTC().Format("20060102T150405Z")
|
|
|
|
|
b, _ := json.MarshalIndent(snap, "", " ")
|
|
|
|
|
_ = os.WriteFile(filepath.Join(path, "snapshot-"+ts+".json"), b, 0o644)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func pushSnapshot(url string, snap *sentinel.Snapshot) error {
|
|
|
|
|
payload := map[string]any{
|
|
|
|
|
"node": snap.Hostname,
|
|
|
|
|
"collected_at": time.Now().UTC(),
|
|
|
|
|
"snapshot": snap,
|
|
|
|
|
}
|
|
|
|
|
body, err := json.Marshal(payload)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
if resp.StatusCode >= 300 {
|
|
|
|
|
return fmt.Errorf("push snapshot: %s", resp.Status)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getenvInt(key string, fallback int) int {
|
|
|
|
|
if raw := os.Getenv(key); raw != "" {
|
|
|
|
|
if value, err := strconv.Atoi(raw); err == nil {
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return fallback
|
|
|
|
|
}
|