From 1cefeb06002ab64df57896c861b11ab482df2c2d Mon Sep 17 00:00:00 2001
From: Jan Grewe <jan.grewe@flixbus.com>
Date: Thu, 7 Jun 2018 12:05:58 +0200
Subject: [PATCH] Use struct for request JSON, assume that there's always an
 error message if the result is null (according to docs)

---
 main.go | 45 ++++++++++++++++++++++++++-------------------
 1 file changed, 26 insertions(+), 19 deletions(-)

diff --git a/main.go b/main.go
index 17abf73..25a3a3a 100644
--- a/main.go
+++ b/main.go
@@ -33,10 +33,20 @@ var (
 	showVersion     = flag.Bool("version", false, "Print version information.")
 )
 
+type ApiRequest struct {
+	Object  string   `json:"object"`
+	Method  string   `json:"method"`
+	Params  []string `json:"params"`
+}
+
 type ApiResponse struct {
 	TGFlash string      `json:"tg_flash"`
 	Result  interface{} `json:"result"`
-	Error   interface{} `json:"error"`
+	Error   ApiError    `json:"error"`
+}
+
+type ApiError struct {
+	Message string `json:"message"`
 }
 
 type ResultObject struct {
@@ -61,13 +71,13 @@ func NewExporter(uri string) *Exporter {
 		URI: uri,
 		up: prometheus.NewDesc(
 			prometheus.BuildFQName(namespace, "", "up"),
-			"Could the Nexenta API be reached",
+			"Is the Nexenta API reachable",
 			nil,
 			nil),
 		scrapeFailures: prometheus.NewCounter(prometheus.CounterOpts{
 			Namespace: namespace,
 			Name:      "exporter_scrape_failures_total",
-			Help:      "Number of errors while scraping Nexenta API.",
+			Help:      "Number of errors while scraping the Nexenta API",
 		}),
 		volumeOnline: prometheus.NewDesc(
 			prometheus.BuildFQName(namespace, "volume", "online"),
@@ -92,7 +102,6 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
 func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
 	e.mutex.Lock() // To protect metrics from concurrent collects.
 	defer e.mutex.Unlock()
-	e.apiReachable = 0
 	err := e.collect(ch)
 	if err != nil {
 		log.Errorf("%v", err)
@@ -124,9 +133,7 @@ func (e *Exporter) collect(ch chan<- prometheus.Metric) error {
 
 func (e *Exporter) getVolumes(ch chan<- prometheus.Metric) ([]string, error) {
 
-	var reqJson = []byte(`{"object": "volume", "params": [""], "method": "get_names"}`)
-
-	apiResponse, err := e.queryApi(ch, reqJson); if err != nil { return nil, err }
+	apiResponse, err := e.queryApi(ch, "volume", "get_names", []string{""}); if err != nil { return nil, err }
 	apiResult, err := json.Marshal(apiResponse.Result); if err != nil { return nil, err }
 
 	var volumes []string
@@ -137,8 +144,7 @@ func (e *Exporter) getVolumes(ch chan<- prometheus.Metric) ([]string, error) {
 
 func (e *Exporter) getVolumeStatus(ch chan<- prometheus.Metric, volume string) error {
 
-	var reqJson = []byte(`{"object": "volume", "params": ["` + volume + `"], "method": "get_status"}`)
-	apiResponse, err := e.queryApi(ch, reqJson); if err != nil { return err }
+	apiResponse, err := e.queryApi(ch, "volume", "get_status", []string{volume}); if err != nil { return err }
 	apiResult, err := json.Marshal(apiResponse.Result); if err != nil { return err }
 
 	var result = new(ResultObject)
@@ -154,13 +160,20 @@ func (e *Exporter) getVolumeStatus(ch chan<- prometheus.Metric, volume string) e
 	return err
 }
 
-func (e *Exporter) queryApi(ch chan<- prometheus.Metric, reqJson []byte) (*ApiResponse, error) {
+func (e *Exporter) queryApi(ch chan<- prometheus.Metric, object string, method string, params []string) (*ApiResponse, error) {
+
+	reqObject := &ApiRequest{
+		Object: object,
+		Method: method,
+		Params: params,
+	}
 
+	e.apiReachable = 0
+	reqJson, _ := json.Marshal(reqObject)
 	resp, err := e.client.Post(e.URI, "application/json", bytes.NewBuffer(reqJson))
 	if err != nil {
 		return nil, fmt.Errorf("Error scraping Nexenta API: %v", err)
 	}
-
 	e.apiReachable = 1
 
 	data, err := ioutil.ReadAll(resp.Body)
@@ -171,7 +184,7 @@ func (e *Exporter) queryApi(ch chan<- prometheus.Metric, reqJson []byte) (*ApiRe
 		}
 		e.scrapeFailures.Inc()
 		e.scrapeFailures.Collect(ch)
-		return nil, fmt.Errorf("Status %s (%d) %s", resp.Status, resp.StatusCode, data)
+		return nil, fmt.Errorf("Request Error: Status %s %s", resp.Status, data)
 	}
 
 	//log.Infof("response: %s", data)
@@ -182,13 +195,7 @@ func (e *Exporter) queryApi(ch chan<- prometheus.Metric, reqJson []byte) (*ApiRe
 	if apiResponse.Result == nil {
 		e.scrapeFailures.Inc()
 		e.scrapeFailures.Collect(ch)
-		return nil, fmt.Errorf("Field 'result' in API response is null, API Error: %v", apiResponse.Error)
-	}
-
-	if apiResponse.Error != nil {
-		e.scrapeFailures.Inc()
-		e.scrapeFailures.Collect(ch)
-		return nil, fmt.Errorf("API Error: %v", apiResponse.Error)
+		return nil, fmt.Errorf("API Error: %v", apiResponse.Error.Message)
 	}
 
 	return apiResponse, nil
-- 
GitLab