diff --git a/.env.example b/.env.example index 57e81c7..ef8e838 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,6 @@ UPDATE_INTERVAL_SECONDS=2592000 # PeeringDB info_type filter (should match PeeringDB values) PDB_INFO_TYPE=Educational/Research + +# minimum ASN entries for healthy /healthz +MIN_ASN_COUNT=10 diff --git a/README.md b/README.md index 5112f55..1cef1d6 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,13 @@ Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich --- +## Healthcheck + +- `GET /healthz` liefert `200`, wenn mindestens `MIN_ASN_COUNT` ASNs geladen sind +- Standard: `MIN_ASN_COUNT=10` (konfigurierbar via Env) + +--- + ## Einschränkungen - Die Erkennung ist **heuristisch** diff --git a/README_technical.md b/README_technical.md index 5112f55..1cef1d6 100644 --- a/README_technical.md +++ b/README_technical.md @@ -76,6 +76,13 @@ Der Service ist **nicht öffentlich exponiert** und kommuniziert ausschließlich --- +## Healthcheck + +- `GET /healthz` liefert `200`, wenn mindestens `MIN_ASN_COUNT` ASNs geladen sind +- Standard: `MIN_ASN_COUNT=10` (konfigurierbar via Env) + +--- + ## Einschränkungen - Die Erkennung ist **heuristisch** diff --git a/main.go b/main.go index bc7485d..4194708 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,8 @@ type server struct { nrenASNs map[uint]struct{} ready atomic.Bool versionTag string + minASN int + asnCount int } func loadASNSet(path string) (map[uint]struct{}, error) { @@ -119,6 +121,7 @@ func main() { asnListPath := getenv("ASN_LIST_PATH", "/data/nren_asns.txt") addr := getenv("ADDR", ":8080") version := getenv("VERSION_TAG", "asn-header-service") + minASN := getenvInt("MIN_ASN_COUNT", 10) db, err := maxminddb.Open(mmdbPath) if err != nil { @@ -130,13 +133,26 @@ func main() { if err != nil { log.Fatalf("failed to load asn list: %v", err) } + asnCount := len(set) - s := &server{db: db, nrenASNs: set, versionTag: version} + s := &server{ + db: db, + nrenASNs: set, + versionTag: version, + minASN: minASN, + asnCount: asnCount, + } s.ready.Store(true) mux := http.NewServeMux() mux.HandleFunc("/auth", s.authHandler) - mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }) + mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { + if s.asnCount < s.minASN { + w.WriteHeader(http.StatusServiceUnavailable) + return + } + w.WriteHeader(http.StatusOK) + }) srv := &http.Server{ Addr: addr, @@ -144,7 +160,7 @@ func main() { ReadHeaderTimeout: 2 * time.Second, } - log.Printf("listening on %s (asn_count=%d)", addr, len(set)) + log.Printf("listening on %s (asn_count=%d, min_asn=%d)", addr, asnCount, minASN) log.Fatal(srv.ListenAndServe()) } @@ -156,3 +172,14 @@ func getenv(k, def string) string { return v } +func getenvInt(k string, def int) int { + v := strings.TrimSpace(os.Getenv(k)) + if v == "" { + return def + } + parsed, err := strconv.Atoi(v) + if err != nil { + return def + } + return parsed +}