Skip to content

Commit

Permalink
feat: SSAPI pass timestamps in Splunk query (BPS-283) (#1968)
Browse files Browse the repository at this point in the history
  • Loading branch information
Caleb-Hurshman authored Nov 18, 2024
2 parents a648d89 + add7da2 commit 421795d
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 10 deletions.
3 changes: 2 additions & 1 deletion receiver/splunksearchapireceiver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"fmt"
"io"
"net/http"
"net/url"

"go.opentelemetry.io/collector/component"
"go.uber.org/zap"
Expand Down Expand Up @@ -59,7 +60,7 @@ func newSplunkSearchAPIClient(ctx context.Context, settings component.TelemetryS
func (c defaultSplunkSearchAPIClient) CreateSearchJob(search string) (CreateJobResponse, error) {
endpoint := fmt.Sprintf("%s/services/search/jobs", c.endpoint)

reqBody := fmt.Sprintf(`search=%s`, search)
reqBody := fmt.Sprintf(`search=%s`, url.QueryEscape(search))
req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer([]byte(reqBody)))
if err != nil {
return CreateJobResponse{}, err
Expand Down
12 changes: 8 additions & 4 deletions receiver/splunksearchapireceiver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package splunksearchapireceiver

import (
"errors"
"fmt"
"strings"
"time"

Expand Down Expand Up @@ -65,7 +64,7 @@ func (cfg *Config) Validate() error {
return errors.New("missing query in search")
}

// query implicitly starts with "search" command
// query must start with "search" command
if !strings.HasPrefix(search.Query, "search ") {
return errNonStandaloneSearchQuery
}
Expand All @@ -74,8 +73,13 @@ func (cfg *Config) Validate() error {
return errNonStandaloneSearchQuery
}

if strings.Contains(search.Query, "earliest=") || strings.Contains(search.Query, "latest=") {
return fmt.Errorf("time query parameters must be configured using only the \"earliest_time\" and \"latest_time\" configuration parameters")
// ensure user query does not include time parameters
if strings.Contains(search.Query, "earliest=") ||
strings.Contains(search.Query, "latest=") ||
strings.Contains(search.Query, "starttime=") ||
strings.Contains(search.Query, "endtime=") ||
strings.Contains(search.Query, "timeformat=") {
return errors.New("time query parameters must be configured using only the 'earliest_time' and 'latest_time' configuration parameters")
}

if search.EarliestTime == "" {
Expand Down
15 changes: 15 additions & 0 deletions receiver/splunksearchapireceiver/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ func TestValidate(t *testing.T) {
},
errExpected: false,
},
{
desc: "Query with ealiest and latest time",
endpoint: "http://localhost:8089",
username: "user",
password: "password",
searches: []Search{
{
Query: "search index=_internal earliest=2024-10-30T04:00:00.000Z latest=2024-10-30T14:00:00.000Z",
EarliestTime: "2024-10-30T04:00:00.000Z",
LatestTime: "2024-10-30T14:00:00.000Z",
},
},
errExpected: true,
errText: "time query parameters must be configured using only the 'earliest_time' and 'latest_time' configuration parameters",
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion receiver/splunksearchapireceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var (
func createDefaultConfig() component.Config {
return &Config{
ClientConfig: confighttp.NewDefaultClientConfig(),
JobPollInterval: 10 * time.Second,
JobPollInterval: 5 * time.Second,
}
}

Expand Down
10 changes: 6 additions & 4 deletions receiver/splunksearchapireceiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ func (ssapir *splunksearchapireceiver) Shutdown(_ context.Context) error {
func (ssapir *splunksearchapireceiver) runQueries(ctx context.Context) error {
for _, search := range ssapir.config.Searches {
// create search in Splunk
ssapir.logger.Info("creating search", zap.String("query", search.Query))
searchID, err := ssapir.createSplunkSearch(search.Query)
searchID, err := ssapir.createSplunkSearch(search)
if err != nil {
ssapir.logger.Error("error creating search", zap.Error(err))
}
Expand Down Expand Up @@ -169,8 +168,11 @@ func (ssapir *splunksearchapireceiver) pollSearchCompletion(ctx context.Context,
}
}

func (ssapir *splunksearchapireceiver) createSplunkSearch(search string) (string, error) {
resp, err := ssapir.client.CreateSearchJob(search)
func (ssapir *splunksearchapireceiver) createSplunkSearch(search Search) (string, error) {
timeFormat := "%Y-%m-%dT%H:%M:%S"
searchQuery := fmt.Sprintf("%s starttime=\"%s\" endtime=\"%s\" timeformat=\"%s\"", search.Query, search.EarliestTime, search.LatestTime, timeFormat)
ssapir.logger.Info("creating search", zap.String("query", searchQuery))
resp, err := ssapir.client.CreateSearchJob(searchQuery)
if err != nil {
return "", err
}
Expand Down

0 comments on commit 421795d

Please sign in to comment.