diff --git a/gateway/common/config.go b/gateway/common/config.go index 92f4bd4..fb2f8a4 100644 --- a/gateway/common/config.go +++ b/gateway/common/config.go @@ -24,6 +24,8 @@ const ( INSTRUMENT_ENABLED = "ENABLE_INSTRUMENT" LOG_LEVEL = "LOG_LEVEL" LOG_HTTP_RESPONSE = "LOG_HTTP_RESPONSE" + FLY_API_KEY = "FLY_API_KEY" + FLY_GATEWAY_URI = "FLY_GATEWAY_URI" ) // This may evolve to include config outside env, or use .env file for diff --git a/gateway/proxy/kitMetricsKitRouter.go b/gateway/proxy/kitMetricsKitRouter.go index a738118..2be9d0e 100644 --- a/gateway/proxy/kitMetricsKitRouter.go +++ b/gateway/proxy/kitMetricsKitRouter.go @@ -1,16 +1,66 @@ package proxy import ( + "compress/gzip" + "encoding/json" "fmt" "io" + log "log/slog" "net/http" + "strings" + + "porters/common" ) -func kitMetricsHandler(w http.ResponseWriter, r *http.Request, proxyToUrl string) { +type Machine struct { + ID string `json:"id"` + InstanceID string `json:"instance_id"` + Region string `json:"region"` +} + +func kitMetricsHandler(w http.ResponseWriter, r *http.Request, proxyToUrl, region string) { + flyApiKey := common.GetConfig(common.FLY_API_KEY) + // Fetch the machines from Fly.io + machines, err := fetchMachines(flyApiKey) + if err != nil { + http.Error(w, "Unable to retrieve machines from Fly.io", http.StatusInternalServerError) + return + } + + // Find the machine for the given region + machineID := "" + for _, machine := range machines { + if strings.EqualFold(machine.Region, region) { + machineID = machine.ID + break + } + } + + if machineID == "" { + http.Error(w, "Machine not found for the given region", http.StatusNotFound) + return + } + + log.Info("Retrieved Machine ID for gatewaykit", "Machine ID", machineID) + + // Construct the metrics URL kitMetricsUrl := fmt.Sprintf("%s/metrics", proxyToUrl) + log.Info("Calling metrics endpoint", "kitMetricsUrl", kitMetricsUrl) + + // Create a new HTTP request + req, err := http.NewRequest("GET", kitMetricsUrl, nil) + if err != nil { + http.Error(w, "Unable to create request", http.StatusInternalServerError) + return + } + + // Add the fly-force-instance-id header + req.Header.Set("fly-force-instance-id", machineID) + // Forward the request to the kit's /metrics endpoint - resp, err := http.Get(kitMetricsUrl) + client := &http.Client{} + resp, err := client.Do(req) if err != nil { http.Error(w, "Unable to retrieve kit metrics", http.StatusInternalServerError) return @@ -21,3 +71,44 @@ func kitMetricsHandler(w http.ResponseWriter, r *http.Request, proxyToUrl string w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) } + +func fetchMachines(authToken string) ([]Machine, error) { + flyGatewayMachines := common.GetConfig(common.FLY_GATEWAY_URI) + // Create a new request to fetch the machines + req, err := http.NewRequest("GET", flyGatewayMachines, nil) + if err != nil { + return nil, err + } + + // Set the Authorization header with the Bearer token + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", authToken)) + + // Perform the request + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + // Check if the response is gzipped and handle accordingly + var reader io.ReadCloser + if resp.Header.Get("Content-Encoding") == "gzip" { + reader, err = gzip.NewReader(resp.Body) + if err != nil { + return nil, err + } + defer reader.Close() + } else { + reader = resp.Body + } + + // Decode the JSON response into the machines slice + var machines []Machine + err = json.NewDecoder(reader).Decode(&machines) + if err != nil { + return nil, err + } + + return machines, nil +} diff --git a/gateway/proxy/mux.go b/gateway/proxy/mux.go index 25f70d0..7c6f5f4 100644 --- a/gateway/proxy/mux.go +++ b/gateway/proxy/mux.go @@ -11,9 +11,9 @@ import ( ) const ( - APP_PATH string = "appId" - PRODUCT_NAME = "product" - HEALTH = "health" + APP_PATH string = "appId" + PRODUCT_NAME = "product" + HEALTH = "health" ) func PluckAppId(req *http.Request) string { @@ -48,8 +48,9 @@ func addMetricsRoute(r *mux.Router) *mux.Router { // Since the Gateway Kit is on an internal private network, with only the Gateway having access to it, we proxy a gateway-kit/metrics endpoint to expose the data to POKTScan func addMetricsKitRoute(r *mux.Router, proxyToUrl string) *mux.Router { subrouter := r.PathPrefix("/gateway-kit/metrics").Subrouter() - subrouter.HandleFunc("", func(w http.ResponseWriter, r *http.Request) { - kitMetricsHandler(w, r, proxyToUrl) + subrouter.HandleFunc("/{region}", func(w http.ResponseWriter, r *http.Request) { + region := mux.Vars(r)["region"] + kitMetricsHandler(w, r, proxyToUrl, region) }) return subrouter }