From 7d372099f9b3fd4e449648616056db6d3e58b684 Mon Sep 17 00:00:00 2001 From: timn Date: Fri, 17 Mar 2023 14:08:31 -0400 Subject: [PATCH 1/3] Align hec receiver errors messages to splunk enterprise --- receiver/splunkhecreceiver/receiver.go | 28 +++++++--- receiver/splunkhecreceiver/receiver_test.go | 58 ++++++++++++++++++--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/receiver/splunkhecreceiver/receiver.go b/receiver/splunkhecreceiver/receiver.go index 75b0dd13bd63..a37f46aa2ba9 100644 --- a/receiver/splunkhecreceiver/receiver.go +++ b/receiver/splunkhecreceiver/receiver.go @@ -45,12 +45,16 @@ const ( responseOK = "OK" responseInvalidMethod = `Only "POST" method is supported` responseInvalidEncoding = `"Content-Encoding" must be "gzip" or empty` + responseInvalidDataFormat = `{"text":"Invalid data format","code":6}` + responseErrEventRequired = `{"text":"Event field is required","code":12}` + responseErrEventBlank = `{"text":"Event field cannot be blank","code":13}` responseErrGzipReader = "Error on gzip body" responseErrUnmarshalBody = "Failed to unmarshal message body" responseErrInternalServerError = "Internal Server Error" responseErrUnsupportedMetricEvent = "Unsupported metric event" responseErrUnsupportedLogEvent = "Unsupported log event" responseErrHandlingIndexedFields = `{"text":"Error in handling indexed fields","code":15,"invalid-event-number":%d}` + responseNoData = `{"text":"No data","code":5}` // Centralizing some HTTP and related string constants. gzipEncoding = "gzip" httpContentEncodingHeader = "Content-Encoding" @@ -64,13 +68,17 @@ var ( errInvalidEncoding = errors.New("invalid encoding") okRespBody = initJSONResponse(responseOK) - invalidMethodRespBody = initJSONResponse(responseInvalidMethod) + eventRequiredRespBody = initJSONResponse(responseErrEventRequired) + eventBlankRespBody = initJSONResponse(responseErrEventBlank) invalidEncodingRespBody = initJSONResponse(responseInvalidEncoding) + invalidFormatRespBody = initJSONResponse(responseInvalidDataFormat) + invalidMethodRespBody = initJSONResponse(responseInvalidMethod) errGzipReaderRespBody = initJSONResponse(responseErrGzipReader) errUnmarshalBodyRespBody = initJSONResponse(responseErrUnmarshalBody) errInternalServerError = initJSONResponse(responseErrInternalServerError) errUnsupportedMetricEvent = initJSONResponse(responseErrUnsupportedMetricEvent) errUnsupportedLogEvent = initJSONResponse(responseErrUnsupportedLogEvent) + noDataRespBody = initJSONResponse(responseNoData) ) // splunkReceiver implements the receiver.Metrics for Splunk HEC metric protocol. @@ -245,7 +253,7 @@ func (r *splunkReceiver) handleRawReq(resp http.ResponseWriter, req *http.Reques } if req.ContentLength == 0 { - r.obsrecv.EndLogsOp(ctx, typeStr, 0, nil) + r.failRequest(ctx, resp, http.StatusBadRequest, noDataRespBody, 0, nil) return } @@ -311,9 +319,7 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) } if req.ContentLength == 0 { - if _, err := resp.Write(okRespBody); err != nil { - r.failRequest(ctx, resp, http.StatusInternalServerError, errInternalServerError, 0, err) - } + r.failRequest(ctx, resp, http.StatusBadRequest, noDataRespBody, 0, nil) return } @@ -325,7 +331,17 @@ func (r *splunkReceiver) handleReq(resp http.ResponseWriter, req *http.Request) var msg splunk.Event err := dec.Decode(&msg) if err != nil { - r.failRequest(ctx, resp, http.StatusBadRequest, errUnmarshalBodyRespBody, len(events), err) + r.failRequest(ctx, resp, http.StatusBadRequest, invalidFormatRespBody, len(events), err) + return + } + + if msg.Event == nil { + r.failRequest(ctx, resp, http.StatusBadRequest, eventRequiredRespBody, len(events), nil) + return + } + + if msg.Event == "" { + r.failRequest(ctx, resp, http.StatusBadRequest, eventBlankRespBody, len(events), nil) return } diff --git a/receiver/splunkhecreceiver/receiver_test.go b/receiver/splunkhecreceiver/receiver_test.go index 8469bd85f6f0..1c1db2ec2eea 100644 --- a/receiver/splunkhecreceiver/receiver_test.go +++ b/receiver/splunkhecreceiver/receiver_test.go @@ -190,7 +190,9 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { { name: "incorrect_content_type", req: func() *http.Request { - req := httptest.NewRequest("POST", "http://localhost/foo", nil) + msgBytes, err := json.Marshal(splunkMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) req.Header.Set("Content-Type", "application/not-json") return req }(), @@ -234,7 +236,7 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { }(), assertResponse: func(t *testing.T, status int, body string) { assert.Equal(t, http.StatusBadRequest, status) - assert.Equal(t, responseErrUnmarshalBody, body) + assert.Equal(t, responseInvalidDataFormat, body) }, }, { @@ -244,8 +246,51 @@ func Test_splunkhecReceiver_handleReq(t *testing.T) { return req }(), assertResponse: func(t *testing.T, status int, body string) { - assert.Equal(t, http.StatusOK, status) - assert.Equal(t, responseOK, body) + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseNoData, body) + }, + }, + { + name: "invalid_data_format", + req: func() *http.Request { + msgBytes, err := json.Marshal(`{"foo":"bar"}`) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseInvalidDataFormat, body) + }, + }, + { + name: "event_required_error", + req: func() *http.Request { + nilEventMsg := buildSplunkHecMsg(currentTime, 3) + nilEventMsg.Event = nil + msgBytes, err := json.Marshal(nilEventMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseErrEventRequired, body) + }, + }, + { + name: "event_cannot_be_blank_error", + req: func() *http.Request { + blankEventMsg := buildSplunkHecMsg(currentTime, 3) + blankEventMsg.Event = "" + msgBytes, err := json.Marshal(blankEventMsg) + require.NoError(t, err) + req := httptest.NewRequest("POST", "http://localhost/foo", bytes.NewReader(msgBytes)) + return req + }(), + assertResponse: func(t *testing.T, status int, body string) { + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseErrEventBlank, body) }, }, { @@ -864,7 +909,7 @@ func Test_splunkhecReceiver_handleRawReq(t *testing.T) { { name: "incorrect_content_type", req: func() *http.Request { - req := httptest.NewRequest("POST", "http://localhost/foo", nil) + req := httptest.NewRequest("POST", "http://localhost/foo", strings.NewReader("foo\nbar")) req.Header.Set("Content-Type", "application/not-json") return req }(), @@ -891,7 +936,8 @@ func Test_splunkhecReceiver_handleRawReq(t *testing.T) { return req }(), assertResponse: func(t *testing.T, status int, body string) { - assert.Equal(t, http.StatusOK, status) + assert.Equal(t, http.StatusBadRequest, status) + assert.Equal(t, responseNoData, body) }, }, From a4e4824897fc51644a079ce78b9b2a126dd13ef6 Mon Sep 17 00:00:00 2001 From: timn Date: Fri, 17 Mar 2023 14:56:10 -0400 Subject: [PATCH 2/3] include chlog --- .chloggen/splunkhecreceiver-align-error-message.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .chloggen/splunkhecreceiver-align-error-message.yaml diff --git a/.chloggen/splunkhecreceiver-align-error-message.yaml b/.chloggen/splunkhecreceiver-align-error-message.yaml new file mode 100644 index 000000000000..293c8345b820 --- /dev/null +++ b/.chloggen/splunkhecreceiver-align-error-message.yaml @@ -0,0 +1,11 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: splunkhecreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: align error message with splunk enterprise to include No Data, Invalid Data Format, Event field is required, and Event field cannot be blank + +# One or more tracking issues related to the change +issues: [19219] From b5de04976c1335b216a5eb88f2df4a2e24e9cc01 Mon Sep 17 00:00:00 2001 From: timn Date: Fri, 17 Mar 2023 15:57:42 -0400 Subject: [PATCH 3/3] revert removing obsrecv in raw --- receiver/splunkhecreceiver/receiver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/receiver/splunkhecreceiver/receiver.go b/receiver/splunkhecreceiver/receiver.go index a37f46aa2ba9..b9752346da3a 100644 --- a/receiver/splunkhecreceiver/receiver.go +++ b/receiver/splunkhecreceiver/receiver.go @@ -253,6 +253,7 @@ func (r *splunkReceiver) handleRawReq(resp http.ResponseWriter, req *http.Reques } if req.ContentLength == 0 { + r.obsrecv.EndLogsOp(ctx, typeStr, 0, nil) r.failRequest(ctx, resp, http.StatusBadRequest, noDataRespBody, 0, nil) return }