/
client.go
185 lines (158 loc) · 5.06 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package splunkenterprisereceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/splunkenterprisereceiver"
import (
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"go.opentelemetry.io/collector/component"
)
// Indexer type "enum". Included in context sent from scraper functions
const (
typeIdx = "IDX"
typeSh = "SH"
typeCm = "CM"
)
var (
errCtxMissingEndpointType = errors.New("context was passed without the endpoint type included")
errEndpointTypeNotFound = errors.New("requested client is not configured and could not be found in splunkEntClient")
errNoClientFound = errors.New("no client corresponding to the endpoint type was found")
)
// Type wrapper for accessing context value
type endpointType string
// Wrapper around splunkClientMap to avoid awkward reference/dereference stuff that arises when using maps in golang
type splunkEntClient struct {
clients splunkClientMap
}
// The splunkEntClient is made up of a number of splunkClients defined for each configured endpoint
type splunkClientMap map[any]splunkClient
// The client does not carry the endpoint that is configured with it and golang does not support mixed
// type arrays so this struct contains the pair: the client configured for the endpoint and the endpoint
// itself
type splunkClient struct {
client *http.Client
endpoint *url.URL
}
func newSplunkEntClient(ctx context.Context, cfg *Config, h component.Host, s component.TelemetrySettings) (*splunkEntClient, error) {
var err error
var e *url.URL
var c *http.Client
clientMap := make(splunkClientMap)
// if the endpoint is defined, put it in the endpoints map for later use
// we already checked that url.Parse does not fail in cfg.Validate()
if cfg.IdxEndpoint.Endpoint != "" {
e, _ = url.Parse(cfg.IdxEndpoint.Endpoint)
c, err = cfg.IdxEndpoint.ToClient(ctx, h, s)
if err != nil {
return nil, err
}
clientMap[typeIdx] = splunkClient{
client: c,
endpoint: e,
}
}
if cfg.SHEndpoint.Endpoint != "" {
e, _ = url.Parse(cfg.SHEndpoint.Endpoint)
c, err = cfg.SHEndpoint.ToClient(ctx, h, s)
if err != nil {
return nil, err
}
clientMap[typeSh] = splunkClient{
client: c,
endpoint: e,
}
}
if cfg.CMEndpoint.Endpoint != "" {
e, _ = url.Parse(cfg.CMEndpoint.Endpoint)
c, err = cfg.CMEndpoint.ToClient(ctx, h, s)
if err != nil {
return nil, err
}
clientMap[typeCm] = splunkClient{
client: c,
endpoint: e,
}
}
return &splunkEntClient{clients: clientMap}, nil
}
// For running ad hoc searches only
func (c *splunkEntClient) createRequest(ctx context.Context, sr *searchResponse) (req *http.Request, err error) {
// get endpoint type from the context
eptType := ctx.Value(endpointType("type"))
if eptType == nil {
return nil, errCtxMissingEndpointType
}
// Running searches via Splunk's REST API is a two step process: First you submit the job to run
// this returns a jobid which is then used in the second part to retrieve the search results
if sr.Jobid == nil {
var u string
path := "/services/search/jobs/"
if e, ok := c.clients[eptType]; ok {
u, err = url.JoinPath(e.endpoint.String(), path)
if err != nil {
return nil, err
}
} else {
return nil, errNoClientFound
}
// reader for the response data
data := strings.NewReader(sr.search)
// return the build request, ready to be run by makeRequest
req, err = http.NewRequestWithContext(ctx, http.MethodPost, u, data)
if err != nil {
return nil, err
}
return req, nil
}
path := fmt.Sprintf("/services/search/jobs/%s/results", *sr.Jobid)
url, _ := url.JoinPath(c.clients[eptType].endpoint.String(), path)
req, err = http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, err
}
return req, nil
}
// forms an *http.Request for use with Splunk built-in API's (like introspection).
func (c *splunkEntClient) createAPIRequest(ctx context.Context, apiEndpoint string) (req *http.Request, err error) {
var u string
// get endpoint type from the context
eptType := ctx.Value(endpointType("type"))
if eptType == nil {
return nil, errCtxMissingEndpointType
}
if e, ok := c.clients[eptType]; ok {
u = e.endpoint.String() + apiEndpoint
} else {
return nil, errNoClientFound
}
req, err = http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return nil, err
}
return req, nil
}
// Perform a request.
func (c *splunkEntClient) makeRequest(req *http.Request) (*http.Response, error) {
// get endpoint type from the context
eptType := req.Context().Value(endpointType("type"))
if eptType == nil {
return nil, errCtxMissingEndpointType
}
if sc, ok := c.clients[eptType]; ok {
res, err := sc.client.Do(req)
if err != nil {
return nil, err
}
return res, nil
}
return nil, errEndpointTypeNotFound
}
// Check if the splunkEntClient contains a configured endpoint for the type of scraper
// Returns true if an entry exists, false if not.
func (c *splunkEntClient) isConfigured(v string) bool {
_, ok := c.clients[v]
return ok
}