// Command fakejarl answers the part of Jarl's API that Housecarl's Jarl // sets read (GET /zones, GET /zones/{id}/records), from a JSON file read // again on each request, so the e2e test can change records: // // {"zones": [{"id": 1, "name": "example.com.", "view": "internal", "type": "primary"}], // "records": {"1": [{"name": "nas.example.com.", "type": "A", "data": "10.0.0.5"}]}} // // Usage: fakejarl -listen 127.0.0.1:8053 -data jarl.json -token SECRET package main import ( "encoding/json" "flag" "log" "net/http" "os" "strings" ) type data struct { Zones []map[string]any `json:"zones"` Records map[string][]map[string]any `json:"records"` } func main() { listen := flag.String("listen", "127.0.0.1:8053", "address to listen on") file := flag.String("data", "jarl.json", "the zones and records") token := flag.String("token", "", "the API token Housecarl must send") flag.Parse() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "Bearer "+*token { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(`{"error": {"code": "unauthorized", "message": "bad token"}}`)) return } b, err := os.ReadFile(*file) var d data if err == nil { err = json.Unmarshal(b, &d) } if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") p := strings.TrimPrefix(r.URL.Path, "/api/v1") switch { case p == "/zones": json.NewEncoder(w).Encode(d.Zones) case strings.HasPrefix(p, "/zones/") && strings.HasSuffix(p, "/records"): id := strings.TrimSuffix(strings.TrimPrefix(p, "/zones/"), "/records") recs := d.Records[id] if recs == nil { recs = []map[string]any{} } json.NewEncoder(w).Encode(recs) default: w.WriteHeader(http.StatusNotFound) w.Write([]byte(`{"error": {"code": "not_found", "message": "not found"}}`)) } }) log.Fatal(http.ListenAndServe(*listen, nil)) }