func()

in agent/stats/envoy_prometheus_stats.go [66:162]


func (envoyPrometheusStatsHandler *EnvoyPrometheusStatsHandler) HandleStats(resWriter http.ResponseWriter, request *http.Request) {
	if !envoyPrometheusStatsHandler.Limiter.Allow() {
		http.Error(resWriter, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
		return
	}
	// Validate the query parameters in the request before passing it to call Envoy stats endpoint.
	// Extract queryStringToEnvoy from request and generate the stats url sent to Envoy stats endpoint.
	//
	// Note that we always prepend the ENVOY_PROMETHEUS_QUERY_STRING to enforce the prometheus format.
	// See https://www.envoyproxy.io/docs/envoy/latest/operations/admin#get--stats?format=prometheus
	if queryParams, err := validateGetStatsRequest(request); err != nil {
		http.Error(resWriter, err.Error(), http.StatusBadRequest)
		return
	} else {
		envoyPrometheusStatsHandler.QueryParameters = *queryParams
	}

	// If the Delta exists, we would just return Delta directly with no further operation needed.
	if envoyPrometheusStatsHandler.QueryParameters.Has(deltaQueryKey) && envoyPrometheusStatsHandler.Snapshotter.Delta != nil {
		resWriter.WriteHeader(http.StatusOK)
		err := writeMetricsToResponse(resWriter, envoyPrometheusStatsHandler.Snapshotter.Delta)
		if err != nil {
			log.Errorf("error while writing response: %s", err)
		}
		return
	}

	// If Delta is not yet computed, most likely it is too early and we don't yet have two snapshots to compute the
	// delta. In this case we will just return the stats as it is because that is essentially the delta
	// (current stats - 0).
	//
	// Start building the request to Envoy Admin Interface for the stats.
	queryStringToEnvoy := constructQueryString(envoyPrometheusStatsHandler.QueryParameters)

	envoyStatsUrl := getEnvoyStatsUrl(&envoyPrometheusStatsHandler.AgentConfig, queryStringToEnvoy)
	log.Debugf("Full URL to query Envoy Stats Endpoint: %s", envoyStatsUrl)
	// Building the client for Envoy server
	httpClient, err := client.CreateRetryableHttpClientForEnvoyServer(envoyPrometheusStatsHandler.AgentConfig)
	httpClient.HTTPClient.Timeout = EnvoyStatsClientHttpTimeout
	if err != nil {
		http.Error(resWriter, err.Error(), http.StatusInternalServerError)
		return
	}
	statsRequest, err := client.CreateRetryableAgentRequest(http.MethodGet, envoyStatsUrl, nil)
	if err != nil {
		http.Error(resWriter, err.Error(), http.StatusInternalServerError)
		return
	}

	// Start a timer to record the response time observed from client side.
	start := time.Now()
	// Make the request to Envoy stats endpoint
	statsResponse, err := httpClient.Do(statsRequest)
	duration := time.Since(start)
	log.Debugf("Stats request took: %vms", duration.Milliseconds())

	if err != nil {
		log.Errorf("Call to fetch stats from Envoy admin failed: %s", err)
		http.Error(resWriter, "Failed to fetch stats from Envoy", http.StatusInternalServerError)
		return
	}

	if statsResponse.StatusCode != http.StatusOK {
		log.Errorf("Envoy stats response status code not OK: %v", statsResponse.Status)
		http.Error(resWriter, "Failed to fetch stats from Envoy", http.StatusInternalServerError)
		return
	}

	defer statsResponse.Body.Close()
	responseBody, err := ioutil.ReadAll(statsResponse.Body)
	if err != nil {
		log.Errorf("Failed to read stats response retreived from Envoy admin: %v", err)
		http.Error(resWriter, "Failed to fetch stats from Envoy", http.StatusInternalServerError)
		return
	}
	// Directly write the response if there is no filter query
	if !envoyPrometheusStatsHandler.QueryParameters.Has(filterQueryKey) {
		resWriter.WriteHeader(http.StatusOK)
		_, err := resWriter.Write(responseBody)
		if err != nil {
			log.Errorf("error while writing response: %s", err)
		}
		return
	}

	// Filter the stats
	filteredMetricFamilies, err := processPrometheusStats(responseBody)
	if err != nil {
		http.Error(resWriter, fmt.Sprintf("error processing prometheus stats: %s", err), http.StatusInternalServerError)
		return
	}
	resWriter.WriteHeader(http.StatusOK)
	err = writeMetricsToResponse(resWriter, filteredMetricFamilies)
	if err != nil {
		log.Errorf("error while writing response: %s", err)
	}
}