diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index e08d48214147..cd2fa3c834ef 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -40,6 +40,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Setting period for counter cache for Prometheus remote_write at least to 60sec {pull}38553[38553] - Remove fallback to the node limit for the `kubernetes.pod.cpu.usage.limit.pct` and `kubernetes.pod.memory.usage.limit.pct` metrics calculation - Fixed a bug where `event.duration` could be missing from an event on Windows systems due to low-resolution clock. {pull}44440[44440] +- Add check for http error codes in the Metricbeat's Prometheus query submodule {pull}44493[44493] *Osquerybeat* diff --git a/metricbeat/module/prometheus/query/query.go b/metricbeat/module/prometheus/query/query.go index 788ca9d50ac9..19f5778068e9 100644 --- a/metricbeat/module/prometheus/query/query.go +++ b/metricbeat/module/prometheus/query/query.go @@ -95,6 +95,12 @@ func (m *MetricSet) Fetch(reporter mb.ReporterV2) error { return err } + if response.StatusCode > 399 { + m.Logger().Debugf("error received from prometheus endpoint %v: %v", url, string(body)) + reporter.Error(fmt.Errorf("unexpected status code %d from %v", response.StatusCode, url)) + continue + } + events, parseErr := parseResponse(body, pathConfig) if parseErr != nil { reporter.Error(fmt.Errorf("error parsing response from %v: %w", url, parseErr)) diff --git a/metricbeat/module/prometheus/query/query_test.go b/metricbeat/module/prometheus/query/query_test.go index fe85f195a53b..dd89759609c9 100644 --- a/metricbeat/module/prometheus/query/query_test.go +++ b/metricbeat/module/prometheus/query/query_test.go @@ -22,6 +22,8 @@ import ( "net/http/httptest" "os" "path/filepath" + "strconv" + "strings" "testing" mbtest "github.com/elastic/beats/v7/metricbeat/mb/testing" @@ -214,3 +216,42 @@ func TestQueryFetchEventContentString(t *testing.T) { t.Logf("%s/%s event: %+v", metricSet.Module().Name(), metricSet.Name(), e.Fields.StringToPrint()) } } + +func TestHTTPErrorCodeHandling(t *testing.T) { + statusCode := 400 + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(statusCode) + _, _ = w.Write([]byte("Client sent an HTTP request to an HTTPS server.")) + })) + defer server.Close() + + config := map[string]interface{}{ + "module": "prometheus", + "metricsets": []string{"query"}, + "hosts": []string{server.URL}, + // queries do not have an actual role here since all http responses are mocked + "queries": []mapstr.M{ + mapstr.M{ + "name": "string", + "path": "/api/v1/query", + "params": mapstr.M{ + "query": "some", + }, + }, + }, + } + reporter := &mbtest.CapturingReporterV2{} + + metricSet := mbtest.NewReportingMetricSetV2Error(t, config) + _ = metricSet.Fetch(reporter) + + errs := reporter.GetErrors() + if len(errs) != 1 { + t.Fatalf("Expected 1 error, had %d: %v\n", len(errs), errs) + } + + if !strings.Contains(errs[0].Error(), strconv.Itoa(statusCode)) { + t.Fatalf("Expected error to contain HTTP response code %d, got error: %s\n", statusCode, errs[0]) + } +}