From 3c211a6debe2c137c39ff8bccecf4419104b1f1b Mon Sep 17 00:00:00 2001 From: Ali Alrahahleh Date: Tue, 24 May 2016 23:28:40 -0700 Subject: [PATCH] add graylog test and fix invalid JSON handling --- plugins/inputs/graylog/graylog.go | 26 ++-- plugins/inputs/graylog/graylog_test.go | 187 +++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 13 deletions(-) create mode 100644 plugins/inputs/graylog/graylog_test.go diff --git a/plugins/inputs/graylog/graylog.go b/plugins/inputs/graylog/graylog.go index 479f5229b..99644fb36 100644 --- a/plugins/inputs/graylog/graylog.go +++ b/plugins/inputs/graylog/graylog.go @@ -1,18 +1,18 @@ package graylog import ( - "bytes" - "encoding/json" "errors" "fmt" + "strconv" + "bytes" + "regexp" "io/ioutil" "net/http" "net/url" - "regexp" - "strconv" "strings" "sync" "time" + "encoding/json" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/internal" @@ -21,11 +21,11 @@ import ( ) type GrayLog struct { - Name string - Servers []string - TagKeys []string - Metrics []string - Headers map[string]string + Name string + Servers []string + TagKeys []string + Metrics []string + Headers map[string]string // Path to CA file SSLCA string `toml:"ssl_ca"` @@ -55,7 +55,7 @@ type HTTPClient interface { } type Messagebody struct { - Metrics []string `json:"metrics"` + Metrics []string `json:"metrics"` } type RealHTTPClient struct { @@ -202,7 +202,7 @@ func (h *GrayLog) gatherServer( return err } if err := json.Unmarshal([]byte(resp), &dat); err != nil { - panic(err) + return err } if rec, ok := dat["metrics"].([]interface{}); ok { for _, metric := range rec { @@ -222,10 +222,10 @@ func (h *GrayLog) gatherServer( for k, v := range metric.Fields() { re, _ := regexp.Compile(`metrics_([0-9]+)`) match := re.FindAllStringSubmatch(k, -1) - if match != nil { + if(match != nil) { i, _ := strconv.Atoi(match[0][1]) fields[name_list[i]] = v - } + } } fields["response_time"] = responseTime acc.AddFields(metric.Name(), fields, metric.Tags()) diff --git a/plugins/inputs/graylog/graylog_test.go b/plugins/inputs/graylog/graylog_test.go new file mode 100644 index 000000000..99d3ba84e --- /dev/null +++ b/plugins/inputs/graylog/graylog_test.go @@ -0,0 +1,187 @@ +package graylog + +import ( +// "fmt" + "io/ioutil" + "net/http" +// "net/http/httptest" + "strings" + "testing" + + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const validJSON = ` + { + "total": 2, + "metrics": [ + { + "full_name": "jvm.cl.loaded", + "metric": { + "value": 18910 + }, + "name": "loaded", + "type": "gauge" + }, + { + "full_name": "jvm.memory.pools.Metaspace.committed", + "metric": { + "value": 108040192 + }, + "name": "committed", + "type": "gauge" + } + ] + }` + + +var expectedFields = map[string]interface{}{ + "jvm.memory.pools.Metaspace.committed": float64(108040192), + "jvm.cl.loaded": float64(18910), +} + +const invalidJSON = "I don't think this is JSON" + +const empty = "" + +type mockHTTPClient struct { + responseBody string + statusCode int +} + +// Mock implementation of MakeRequest. Usually returns an http.Response with +// hard-coded responseBody and statusCode. However, if the request uses a +// nonstandard method, it uses status code 405 (method not allowed) +func (c *mockHTTPClient) MakeRequest(req *http.Request) (*http.Response, error) { + resp := http.Response{} + resp.StatusCode = c.statusCode + + // basic error checking on request method + allowedMethods := []string{"GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"} + methodValid := false + for _, method := range allowedMethods { + if req.Method == method { + methodValid = true + break + } + } + + if !methodValid { + resp.StatusCode = 405 // Method not allowed + } + + resp.Body = ioutil.NopCloser(strings.NewReader(c.responseBody)) + return &resp, nil +} + +func (c *mockHTTPClient) SetHTTPClient(_ *http.Client) { +} + +func (c *mockHTTPClient) HTTPClient() *http.Client { + return nil +} + +// Generates a pointer to an HttpJson object that uses a mock HTTP client. +// Parameters: +// response : Body of the response that the mock HTTP client should return +// statusCode: HTTP status code the mock HTTP client should return +// +// Returns: +// *HttpJson: Pointer to an HttpJson object that uses the generated mock HTTP client +func genMockGrayLog(response string, statusCode int) []*GrayLog { + return []*GrayLog{ + &GrayLog{ + client: &mockHTTPClient{responseBody: response, statusCode: statusCode}, + Servers: []string{ + "http://localhost:12900/system/metrics/multiple", + }, + Name: "my_webapp", + Metrics: []string{ + "jvm.cl.loaded", + }, + Headers: map[string]string{ + "Content-Type" : "application/json", + "Accept" : "application/json", + "Authorization" : "Basic DESfdsfffoffo", + }, + }, + &GrayLog{ + client: &mockHTTPClient{responseBody: response, statusCode: statusCode}, + Servers: []string{ + "http://server2:12900/system/metrics/multiple", + }, + Name: "other_webapp", + Metrics: []string{ + "jvm.memory.pools.Metaspace.committed", + }, + Headers: map[string]string{ + "Content-Type" : "application/json", + "Accept" : "application/json", + "Authorization" : "Basic DESfdsfffoffo", + }, + TagKeys: []string{ + "role", + "build", + }, + }, + } +} + +// Test that the proper values are ignored or collected +func TestNormalResponse(t *testing.T) { + graylog := genMockGrayLog(validJSON, 200) + + for _, service := range graylog { + var acc testutil.Accumulator + err := service.Gather(&acc) + require.NoError(t, err) + assert.Equal(t, 3, acc.NFields()) + // Set responsetime + for _, p := range acc.Metrics { + p.Fields["response_time"] = 1.0 + } + + for _, srv := range service.Servers { + tags := map[string]string{"server": srv} + mname := "graylog_" + service.Name + expectedFields["response_time"] = 1.0 + acc.AssertContainsTaggedFields(t, mname, expectedFields, tags) + } + } +} + + +// Test response to HTTP 500 +func TestHttpJson500(t *testing.T) { + graylog := genMockGrayLog(validJSON, 500) + + var acc testutil.Accumulator + err := graylog[0].Gather(&acc) + + assert.NotNil(t, err) + assert.Equal(t, 0, acc.NFields()) +} + +// Test response to malformed JSON +func TestHttpJsonBadJson(t *testing.T) { + graylog := genMockGrayLog(invalidJSON, 200) + + var acc testutil.Accumulator + err := graylog[0].Gather(&acc) + + assert.NotNil(t, err) + assert.Equal(t, 0, acc.NFields()) +} + +// Test response to empty string as response objectgT +func TestHttpJsonEmptyResponse(t *testing.T) { + graylog := genMockGrayLog(empty, 200) + + var acc testutil.Accumulator + err := graylog[0].Gather(&acc) + + assert.NotNil(t, err) + assert.Equal(t, 0, acc.NFields()) +}