From edc17b3da33c134c8085e4025e572b9b2d557366 Mon Sep 17 00:00:00 2001
From: Pablo Mercado <pablo.mercado@elastic.co>
Date: Thu, 30 Jan 2020 17:13:25 +0100
Subject: [PATCH] [Metricbeat]Log prometheus errors instead of parsing families
 (#15712)

* log prometheus errors instead of parsing
---
 CHANGELOG.next.asciidoc                         |  2 +-
 metricbeat/helper/prometheus/prometheus.go      | 14 +++++++++++++-
 metricbeat/helper/prometheus/prometheus_test.go | 10 ++++++----
 3 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc
index dcfe3f5abff1..a87560bf6a7a 100644
--- a/CHANGELOG.next.asciidoc
+++ b/CHANGELOG.next.asciidoc
@@ -50,7 +50,6 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
 - Update replicaset group to apps/v1 {pull}15854[15802]
 - Fix issue where default go logger is not discarded when either * or stdout is selected. {issue}10251[10251] {pull}15708[15708]
 
-
 *Auditbeat*
 
 - system/socket: Fixed compatibility issue with kernel 5.x. {pull}15771[15771]
@@ -72,6 +71,7 @@ https://github.com/elastic/beats/compare/v7.0.0-alpha2...master[Check the HEAD d
 
 - Add dedot for tags in ec2 metricset and cloudwatch metricset. {issue}15843[15843] {pull}15844[15844]
 - Use RFC3339 format for timestamps collected using the SQL module. {pull}15847[15847]
+- Avoid parsing errors returned from prometheus endpoints. {pull}15712[15712]
 - Change lookup_fields from metricset.host to service.address {pull}15883[15883]
 - Add dedot for cloudwatch metric name. {issue}15916[15916] {pull}15917[15917]
 
diff --git a/metricbeat/helper/prometheus/prometheus.go b/metricbeat/helper/prometheus/prometheus.go
index 69c959e0b3a9..6709e6c93eff 100644
--- a/metricbeat/helper/prometheus/prometheus.go
+++ b/metricbeat/helper/prometheus/prometheus.go
@@ -20,6 +20,7 @@ package prometheus
 import (
 	"fmt"
 	"io"
+	"io/ioutil"
 	"net/http"
 
 	"github.com/pkg/errors"
@@ -27,6 +28,7 @@ import (
 	"github.com/prometheus/common/expfmt"
 
 	"github.com/elastic/beats/libbeat/common"
+	"github.com/elastic/beats/libbeat/logp"
 	"github.com/elastic/beats/metricbeat/helper"
 	"github.com/elastic/beats/metricbeat/mb"
 )
@@ -43,6 +45,7 @@ type Prometheus interface {
 
 type prometheus struct {
 	httpfetcher
+	logger *logp.Logger
 }
 
 type httpfetcher interface {
@@ -52,10 +55,11 @@ type httpfetcher interface {
 // NewPrometheusClient creates new prometheus helper
 func NewPrometheusClient(base mb.BaseMetricSet) (Prometheus, error) {
 	http, err := helper.NewHTTP(base)
+
 	if err != nil {
 		return nil, err
 	}
-	return &prometheus{http}, nil
+	return &prometheus{http, base.Logger()}, nil
 }
 
 // GetFamilies requests metric families from prometheus endpoint and returns them
@@ -66,6 +70,14 @@ func (p *prometheus) GetFamilies() ([]*dto.MetricFamily, error) {
 	}
 	defer resp.Body.Close()
 
+	if resp.StatusCode > 399 {
+		bodyBytes, err := ioutil.ReadAll(resp.Body)
+		if err == nil {
+			p.logger.Debug("error received from prometheus endpoint: ", string(bodyBytes))
+		}
+		return nil, fmt.Errorf("unexpected status code %d from server", resp.StatusCode)
+	}
+
 	format := expfmt.ResponseFormat(resp.Header)
 	if format == "" {
 		return nil, fmt.Errorf("Invalid format for response of response")
diff --git a/metricbeat/helper/prometheus/prometheus_test.go b/metricbeat/helper/prometheus/prometheus_test.go
index a3ec53443520..20715b67750d 100644
--- a/metricbeat/helper/prometheus/prometheus_test.go
+++ b/metricbeat/helper/prometheus/prometheus_test.go
@@ -27,6 +27,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/elastic/beats/libbeat/common"
+	"github.com/elastic/beats/libbeat/logp"
 	mbtest "github.com/elastic/beats/metricbeat/mb/testing"
 )
 
@@ -185,14 +186,15 @@ var _ = httpfetcher(&mockFetcher{})
 // returns the mockFetcher.Response contents
 func (m mockFetcher) FetchResponse() (*http.Response, error) {
 	return &http.Response{
-		Header: make(http.Header),
-		Body:   ioutil.NopCloser(bytes.NewReader([]byte(m.response))),
+		StatusCode: 200,
+		Header:     make(http.Header),
+		Body:       ioutil.NopCloser(bytes.NewReader([]byte(m.response))),
 	}, nil
 }
 
 func TestPrometheus(t *testing.T) {
 
-	p := &prometheus{mockFetcher{response: promMetrics}}
+	p := &prometheus{mockFetcher{response: promMetrics}, logp.NewLogger("test")}
 
 	tests := []struct {
 		mapping  *MetricsMapping
@@ -933,7 +935,7 @@ func TestPrometheusKeyLabels(t *testing.T) {
 
 	for _, tc := range testCases {
 		r := &mbtest.CapturingReporterV2{}
-		p := &prometheus{mockFetcher{response: tc.prometheusResponse}}
+		p := &prometheus{mockFetcher{response: tc.prometheusResponse}, logp.NewLogger("test")}
 		p.ReportProcessedMetrics(tc.mapping, r)
 		if !assert.Nil(t, r.GetErrors(),
 			"error reporting/processing metrics, at %q", tc.testName) {