go.d move doing http req logic to web (#18546)
Ilya Mashchenko committed
Sep 14, 2024 at 21:43 UTC
e37e1b801e8ccbcddbecd8d4cb5314e37357e7d7
71 files changed
+651
-1389
src/go/plugin/go.d/agent/module/job.go
+3
-3
@@ -227,14 +227,14 @@ func (j *Job) AutoDetection() (err error) {
227
}
228
229
if err = j.init(); err != nil {
230
- j.Error("init failed")
230
+ j.Errorf("init failed: %v", err)
231
j.Unmute()
232
j.disableAutoDetection()
233
return err
234
}
235
236
if err = j.check(); err != nil {
237
- j.Error("check failed")
237
+ j.Errorf("check failed: %v", err)
238
j.Unmute()
239
return err
240
}
@@ -243,7 +243,7 @@ func (j *Job) AutoDetection() (err error) {
243
j.Info("check success")
244
245
if err = j.postCheck(); err != nil {
246
- j.Error("postCheck failed")
246
+ j.Errorf("postCheck failed: %v", err)
247
j.disableAutoDetection()
248
return err
249
}
src/go/plugin/go.d/modules/activemq/apiclient.go
+8
-50
@@ -6,8 +6,6 @@ import (
6
"encoding/xml"
7
"fmt"
8
"net/http"
9
- "net/url"
10
- "path"
9
10
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
@@ -59,71 +57,31 @@ type apiClient struct {
57
}
58
59
func (a *apiClient) getQueues() (*queues, error) {
62
- req, err := a.createRequest(fmt.Sprintf(pathStats, a.webadmin, keyQueues))
60
+ req, err := web.NewHTTPRequestWithPath(a.request, fmt.Sprintf(pathStats, a.webadmin, keyQueues))
61
if err != nil {
64
- return nil, fmt.Errorf("error on creating request '%s' : %v", a.request.URL, err)
65
- }
66
-
67
- resp, err := a.doRequestOK(req)
68
-
69
- defer web.CloseBody(resp)
70
-
71
- if err != nil {
72
- return nil, err
62
+ return nil, fmt.Errorf("failed to create HTTP request '%s': %v", a.request.URL, err)
63
}
64
65
var queues queues
66
77
- if err := xml.NewDecoder(resp.Body).Decode(&queues); err != nil {
78
- return nil, fmt.Errorf("error on decoding resp from %s : %s", req.URL, err)
67
+ if err := web.DoHTTP(a.httpClient).RequestXML(req, &queues); err != nil {
68
+ return nil, err
69
}
70
71
return &queues, nil
72
}
73
74
func (a *apiClient) getTopics() (*topics, error) {
85
- req, err := a.createRequest(fmt.Sprintf(pathStats, a.webadmin, keyTopics))
75
+ req, err := web.NewHTTPRequestWithPath(a.request, fmt.Sprintf(pathStats, a.webadmin, keyTopics))
76
if err != nil {
87
- return nil, fmt.Errorf("error on creating request '%s' : %v", a.request.URL, err)
88
- }
89
-
90
- resp, err := a.doRequestOK(req)
91
-
92
- defer web.CloseBody(resp)
93
-
94
- if err != nil {
95
- return nil, err
77
+ return nil, fmt.Errorf("failed to create HTTP request '%s': %v", a.request.URL, err)
78
}
79
80
var topics topics
81
100
- if err := xml.NewDecoder(resp.Body).Decode(&topics); err != nil {
101
- return nil, fmt.Errorf("error on decoding resp from %s : %s", req.URL, err)
82
+ if err := web.DoHTTP(a.httpClient).RequestXML(req, &topics); err != nil {
83
+ return nil, err
84
}
85
86
return &topics, nil
87
}
106
-
107
-func (a *apiClient) doRequestOK(req *http.Request) (*http.Response, error) {
108
- resp, err := a.httpClient.Do(req)
109
- if err != nil {
110
- return resp, fmt.Errorf("error on request to %s : %v", req.URL, err)
111
- }
112
-
113
- if resp.StatusCode != http.StatusOK {
114
- return resp, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
115
- }
116
-
117
- return resp, err
118
-}
119
-
120
-func (a *apiClient) createRequest(urlPath string) (*http.Request, error) {
121
- req := a.request.Copy()
122
- u, err := url.Parse(req.URL)
123
- if err != nil {
124
- return nil, err
125
- }
126
- u.Path = path.Join(u.Path, urlPath)
127
- req.URL = u.String()
128
- return web.NewHTTPRequest(req)
129
-}
src/go/plugin/go.d/modules/apache/collect.go
+10
-10
@@ -6,7 +6,6 @@ import (
6
"bufio"
7
"fmt"
8
"io"
9
- "net/http"
9
"strconv"
10
"strings"
11
@@ -36,18 +35,19 @@ func (a *Apache) scrapeStatus() (*serverStatus, error) {
35
return nil, err
36
}
37
39
- resp, err := a.httpClient.Do(req)
40
- if err != nil {
41
- return nil, fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
42
- }
43
-
44
- defer web.CloseBody(resp)
38
+ var stats *serverStatus
39
+ var perr error
40
46
- if resp.StatusCode != http.StatusOK {
47
- return nil, fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
41
+ if err := web.DoHTTP(a.httpClient).Request(req, func(body io.Reader) error {
42
+ if stats, perr = parseResponse(body); perr != nil {
43
+ return perr
44
+ }
45
+ return nil
46
+ }); err != nil {
47
+ return nil, err
48
}
49
50
- return parseResponse(resp.Body)
50
+ return stats, nil
51
}
52
53
func parseResponse(r io.Reader) (*serverStatus, error) {
src/go/plugin/go.d/modules/bind/json_client.go
+6
-27
@@ -3,11 +3,8 @@
3
package bind
4
5
import (
6
- "encoding/json"
6
"fmt"
7
"net/http"
9
- "net/url"
10
- "path"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
10
)
@@ -42,34 +39,16 @@ type jsonClient struct {
39
}
40
41
func (c jsonClient) serverStats() (*serverStats, error) {
45
- req := c.request.Copy()
46
- u, err := url.Parse(req.URL)
42
+ req, err := web.NewHTTPRequestWithPath(c.request, "/server")
43
if err != nil {
48
- return nil, fmt.Errorf("error on parsing URL: %v", err)
44
+ return nil, fmt.Errorf("failed to create HTTP request: %v", err)
45
}
46
51
- u.Path = path.Join(u.Path, "/server")
52
- req.URL = u.String()
47
+ var stats jsonServerStats
48
54
- httpReq, err := web.NewHTTPRequest(req)
55
- if err != nil {
56
- return nil, fmt.Errorf("error on creating HTTP request: %v", err)
57
- }
58
-
59
- resp, err := c.httpClient.Do(httpReq)
60
- if err != nil {
61
- return nil, fmt.Errorf("error on request : %v", err)
49
+ if err := web.DoHTTP(c.httpClient).RequestJSON(req, &stats); err != nil {
50
+ return nil, err
51
}
52
64
- defer web.CloseBody(resp)
65
-
66
- if resp.StatusCode != http.StatusOK {
67
- return nil, fmt.Errorf("%s returned HTTP status %d", httpReq.URL, resp.StatusCode)
68
- }
69
-
70
- stats := &jsonServerStats{}
71
- if err = json.NewDecoder(resp.Body).Decode(stats); err != nil {
72
- return nil, fmt.Errorf("error on decoding response from %s : %v", httpReq.URL, err)
73
- }
74
- return stats, nil
53
+ return &stats, nil
54
}
src/go/plugin/go.d/modules/bind/xml3_client.go
+5
-26
@@ -3,11 +3,8 @@
3
package bind
4
5
import (
6
- "encoding/xml"
6
"fmt"
7
"net/http"
9
- "net/url"
10
- "path"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
10
)
@@ -44,35 +41,17 @@ type xml3Client struct {
41
}
42
43
func (c xml3Client) serverStats() (*serverStats, error) {
47
- req := c.request.Copy()
48
- u, err := url.Parse(req.URL)
44
+ req, err := web.NewHTTPRequestWithPath(c.request, "/server")
45
if err != nil {
50
- return nil, fmt.Errorf("error on parsing URL: %v", err)
46
+ return nil, fmt.Errorf("failed to create HTTP request: %v", err)
47
}
48
53
- u.Path = path.Join(u.Path, "/server")
54
- req.URL = u.String()
49
+ var stats xml3Stats
50
56
- httpReq, err := web.NewHTTPRequest(req)
57
- if err != nil {
58
- return nil, fmt.Errorf("error on creating HTTP request: %v", err)
59
- }
60
-
61
- resp, err := c.httpClient.Do(httpReq)
62
- if err != nil {
63
- return nil, fmt.Errorf("error on request : %v", err)
51
+ if err := web.DoHTTP(c.httpClient).RequestXML(req, &stats); err != nil {
52
+ return nil, err
53
}
54
66
- defer web.CloseBody(resp)
67
-
68
- if resp.StatusCode != http.StatusOK {
69
- return nil, fmt.Errorf("%s returned HTTP status %d", httpReq.URL, resp.StatusCode)
70
- }
71
-
72
- stats := xml3Stats{}
73
- if err = xml.NewDecoder(resp.Body).Decode(&stats); err != nil {
74
- return nil, fmt.Errorf("error on decoding response from %s : %v", httpReq.URL, err)
75
- }
55
return convertXML(stats), nil
56
}
57
src/go/plugin/go.d/modules/clickhouse/collect.go
+4
-13
@@ -40,19 +40,10 @@ func (c *ClickHouse) collect() (map[string]int64, error) {
40
return mx, nil
41
}
42
43
-func (c *ClickHouse) doOKDecodeCSV(req *http.Request, assign func(column, value string, lineEnd bool)) error {
44
- resp, err := c.httpClient.Do(req)
45
- if err != nil {
46
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
47
- }
48
-
49
- defer web.CloseBody(resp)
50
-
51
- if resp.StatusCode != http.StatusOK {
52
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
53
- }
54
-
55
- return readCSVResponseData(resp.Body, assign)
43
+func (c *ClickHouse) doHTTP(req *http.Request, assign func(column, value string, lineEnd bool)) error {
44
+ return web.DoHTTP(c.httpClient).Request(req, func(body io.Reader) error {
45
+ return readCSVResponseData(body, assign)
46
+ })
47
}
48
49
func readCSVResponseData(reader io.Reader, assign func(column, value string, lineEnd bool)) error {
src/go/plugin/go.d/modules/clickhouse/collect_system_async_metrics.go
+1
-1
@@ -35,7 +35,7 @@ func (c *ClickHouse) collectSystemAsyncMetrics(mx map[string]int64) error {
35
var metric string
36
var n int
37
38
- err := c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
38
+ err := c.doHTTP(req, func(column, value string, lineEnd bool) {
39
switch column {
40
case "metric":
41
metric = value
src/go/plugin/go.d/modules/clickhouse/collect_system_disks.go
+1
-1
@@ -42,7 +42,7 @@ func (c *ClickHouse) collectSystemDisks(mx map[string]int64) error {
42
43
var name string
44
45
- err := c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
45
+ err := c.doHTTP(req, func(column, value string, lineEnd bool) {
46
switch column {
47
case "name":
48
name = value
src/go/plugin/go.d/modules/clickhouse/collect_system_events.go
+1
-1
@@ -25,7 +25,7 @@ func (c *ClickHouse) collectSystemEvents(mx map[string]int64) error {
25
var event string
26
var n int
27
28
- err := c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
28
+ err := c.doHTTP(req, func(column, value string, lineEnd bool) {
29
switch column {
30
case "event":
31
event = value
src/go/plugin/go.d/modules/clickhouse/collect_system_metrics.go
+1
-1
@@ -25,7 +25,7 @@ func (c *ClickHouse) collectSystemMetrics(mx map[string]int64) error {
25
var metric string
26
var n int
27
28
- err := c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
28
+ err := c.doHTTP(req, func(column, value string, lineEnd bool) {
29
switch column {
30
case "metric":
31
metric = value
src/go/plugin/go.d/modules/clickhouse/collect_system_parts.go
+1
-1
@@ -51,7 +51,7 @@ func (c *ClickHouse) collectSystemParts(mx map[string]int64) error {
51
52
var database, table string
53
54
- err := c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
54
+ err := c.doHTTP(req, func(column, value string, lineEnd bool) {
55
switch column {
56
case "database":
57
database = value
src/go/plugin/go.d/modules/clickhouse/collect_system_processes.go
+1
-1
@@ -19,7 +19,7 @@ func (c *ClickHouse) collectLongestRunningQueryTime(mx map[string]int64) error {
19
req, _ := web.NewHTTPRequest(c.RequestConfig)
20
req.URL.RawQuery = makeURLQuery(queryLongestQueryTime)
21
22
- return c.doOKDecodeCSV(req, func(column, value string, lineEnd bool) {
22
+ return c.doHTTP(req, func(column, value string, lineEnd bool) {
23
if column == "value" {
24
if v, err := strconv.ParseFloat(value, 64); err == nil {
25
mx["LongestRunningQueryTime"] = int64(v * precision)
src/go/plugin/go.d/modules/consul/collect.go
+10
-24
@@ -3,9 +3,9 @@
3
package consul
4
5
import (
6
- "encoding/json"
6
"fmt"
7
"net/http"
8
+ "slices"
9
10
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
@@ -67,37 +67,23 @@ func (c *Consul) isServer() bool {
67
return c.cfg.Config.Server
68
}
69
70
-func (c *Consul) doOKDecode(urlPath string, in interface{}, statusCodes ...int) error {
70
+func (c *Consul) client(statusCodes ...int) *web.Client {
71
+ return web.DoHTTP(c.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
72
+ return slices.Contains(statusCodes, resp.StatusCode), nil
73
+ })
74
+}
75
+
76
+func (c *Consul) createRequest(urlPath string) (*http.Request, error) {
77
req, err := web.NewHTTPRequestWithPath(c.RequestConfig, urlPath)
78
if err != nil {
73
- return fmt.Errorf("error on creating request: %v", err)
79
+ return nil, fmt.Errorf("failed to create '%s' request: %w", urlPath, err)
80
}
81
82
if c.ACLToken != "" {
83
req.Header.Set("X-Consul-Token", c.ACLToken)
84
}
85
80
- resp, err := c.httpClient.Do(req)
81
- if err != nil {
82
- return fmt.Errorf("error on request to %s : %v", req.URL, err)
83
- }
84
-
85
- defer web.CloseBody(resp)
86
-
87
- codes := map[int]bool{http.StatusOK: true}
88
- for _, v := range statusCodes {
89
- codes[v] = true
90
- }
91
-
92
- if !codes[resp.StatusCode] {
93
- return fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
94
- }
95
-
96
- if err = json.NewDecoder(resp.Body).Decode(&in); err != nil {
97
- return fmt.Errorf("error on decoding response from %s : %v", req.URL, err)
98
- }
99
-
100
- return nil
86
+ return req, nil
87
}
88
89
func boolToInt(v bool) int64 {
src/go/plugin/go.d/modules/consul/collect_autopilot.go
+6
-1
@@ -25,11 +25,16 @@ type autopilotHealth struct {
25
}
26
27
func (c *Consul) collectAutopilotHealth(mx map[string]int64) error {
28
+ req, err := c.createRequest(urlPathOperationAutopilotHealth)
29
+ if err != nil {
30
+ return err
31
+ }
32
+
33
var health autopilotHealth
34
35
// The HTTP status code will indicate the health of the cluster: 200 is healthy, 429 is unhealthy.
36
// https://github.com/hashicorp/consul/blob/c7ef04c5979dbc311ff3c67b7bf3028a93e8b0f1/agent/operator_endpoint.go#L325
32
- if err := c.doOKDecode(urlPathOperationAutopilotHealth, &health, http.StatusTooManyRequests); err != nil {
37
+ if err := c.client(http.StatusTooManyRequests).RequestJSON(req, &health); err != nil {
38
return err
39
}
40
src/go/plugin/go.d/modules/consul/collect_checks.go
+6
-1
@@ -18,9 +18,14 @@ type agentCheck struct {
18
}
19
20
func (c *Consul) collectChecks(mx map[string]int64) error {
21
+ req, err := c.createRequest(urlPathAgentChecks)
22
+ if err != nil {
23
+ return err
24
+ }
25
+
26
var checks map[string]*agentCheck
27
23
- if err := c.doOKDecode(urlPathAgentChecks, &checks); err != nil {
28
+ if err := c.client().RequestJSON(req, &checks); err != nil {
29
return err
30
}
31
src/go/plugin/go.d/modules/consul/collect_config.go
+6
-1
@@ -46,9 +46,14 @@ type consulConfig struct {
46
}
47
48
func (c *Consul) collectConfiguration() error {
49
+ req, err := c.createRequest(urlPathAgentSelf)
50
+ if err != nil {
51
+ return err
52
+ }
53
+
54
var cfg consulConfig
55
51
- if err := c.doOKDecode(urlPathAgentSelf, &cfg); err != nil {
56
+ if err := c.client().RequestJSON(req, &cfg); err != nil {
57
return err
58
}
59
src/go/plugin/go.d/modules/consul/collect_net_rtt.go
+6
-1
@@ -23,9 +23,14 @@ type nodeCoordinates struct {
23
}
24
25
func (c *Consul) collectNetworkRTT(mx map[string]int64) error {
26
+ req, err := c.createRequest(urlPathCoordinateNodes)
27
+ if err != nil {
28
+ return err
29
+ }
30
+
31
var coords []nodeCoordinates
32
28
- if err := c.doOKDecode(urlPathCoordinateNodes, &coords); err != nil {
33
+ if err := c.client().RequestJSON(req, &coords); err != nil {
34
return err
35
}
36
src/go/plugin/go.d/modules/couchbase/collect.go
+2
-21
@@ -3,9 +3,7 @@
3
package couchbase
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
"net/url"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -118,28 +116,11 @@ func (cb *Couchbase) scrapeCouchbase() (*cbMetrics, error) {
116
req.URL.RawQuery = url.Values{"skipMap": []string{"true"}}.Encode()
117
118
ms := &cbMetrics{}
121
- if err := cb.doOKDecode(req, &ms.BucketsBasicStats); err != nil {
119
+ if err := web.DoHTTP(cb.httpClient).RequestJSON(req, &ms.BucketsBasicStats); err != nil {
120
return nil, err
121
}
124
- return ms, nil
125
-}
126
-
127
-func (cb *Couchbase) doOKDecode(req *http.Request, in interface{}) error {
128
- resp, err := cb.httpClient.Do(req)
129
- if err != nil {
130
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
131
- }
122
133
- defer web.CloseBody(resp)
134
-
135
- if resp.StatusCode != http.StatusOK {
136
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
137
- }
138
-
139
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
140
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
141
- }
142
- return nil
123
+ return ms, nil
124
}
125
126
func indexDimID(name, metric string) string {
src/go/plugin/go.d/modules/couchdb/collect.go
+19
-23
@@ -123,10 +123,12 @@ func (cdb *CouchDB) scrapeNodeStats(ms *cdbMetrics) {
123
req, _ := web.NewHTTPRequestWithPath(cdb.RequestConfig, fmt.Sprintf(urlPathOverviewStats, cdb.Config.Node))
124
125
var stats cdbNodeStats
126
- if err := cdb.doOKDecode(req, &stats); err != nil {
126
+
127
+ if err := cdb.client().RequestJSON(req, &stats); err != nil {
128
cdb.Warning(err)
129
return
130
}
131
+
132
ms.NodeStats = &stats
133
}
134
@@ -134,10 +136,12 @@ func (cdb *CouchDB) scrapeSystemStats(ms *cdbMetrics) {
136
req, _ := web.NewHTTPRequestWithPath(cdb.RequestConfig, fmt.Sprintf(urlPathSystemStats, cdb.Config.Node))
137
138
var stats cdbNodeSystem
137
- if err := cdb.doOKDecode(req, &stats); err != nil {
139
+
140
+ if err := cdb.client().RequestJSON(req, &stats); err != nil {
141
cdb.Warning(err)
142
return
143
}
144
+
145
ms.NodeSystem = &stats
146
}
147
@@ -145,10 +149,12 @@ func (cdb *CouchDB) scrapeActiveTasks(ms *cdbMetrics) {
149
req, _ := web.NewHTTPRequestWithPath(cdb.RequestConfig, urlPathActiveTasks)
150
151
var stats []cdbActiveTask
148
- if err := cdb.doOKDecode(req, &stats); err != nil {
152
+
153
+ if err := cdb.client().RequestJSON(req, &stats); err != nil {
154
cdb.Warning(err)
155
return
156
}
157
+
158
ms.ActiveTasks = stats
159
}
160
@@ -170,10 +176,12 @@ func (cdb *CouchDB) scrapeDBStats(ms *cdbMetrics) {
176
req.Body = io.NopCloser(bytes.NewReader(body))
177
178
var stats []cdbDBStats
173
- if err := cdb.doOKDecode(req, &stats); err != nil {
179
+
180
+ if err := cdb.client().RequestJSON(req, &stats); err != nil {
181
cdb.Warning(err)
182
return
183
}
184
+
185
ms.DBStats = stats
186
}
187
@@ -196,7 +204,8 @@ func (cdb *CouchDB) pingCouchDB() error {
204
req, _ := web.NewHTTPRequest(cdb.RequestConfig)
205
206
var info struct{ Couchdb string }
199
- if err := cdb.doOKDecode(req, &info); err != nil {
207
+
208
+ if err := cdb.client().RequestJSON(req, &info); err != nil {
209
return err
210
}
211
@@ -207,30 +216,17 @@ func (cdb *CouchDB) pingCouchDB() error {
216
return nil
217
}
218
210
-func (cdb *CouchDB) doOKDecode(req *http.Request, in interface{}) error {
211
- resp, err := cdb.httpClient.Do(req)
212
- if err != nil {
213
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
214
- }
215
-
216
- defer web.CloseBody(resp)
217
-
218
- if resp.StatusCode != http.StatusOK {
219
+func (cdb *CouchDB) client() *web.Client {
220
+ return web.DoHTTP(cdb.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
221
var msg struct {
222
Error string `json:"error"`
223
Reason string `json:"reason"`
224
}
225
if err := json.NewDecoder(resp.Body).Decode(&msg); err == nil && msg.Error != "" {
224
- return fmt.Errorf("'%s' returned HTTP status code: %d (err '%s', reason '%s')",
225
- req.URL, resp.StatusCode, msg.Error, msg.Reason)
226
+ return false, fmt.Errorf("error '%s', reason '%s'", msg.Error, msg.Reason)
227
}
227
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
228
- }
229
-
230
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
231
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
232
- }
233
- return nil
228
+ return false, nil
229
+ })
230
}
231
232
func merge(dst, src map[string]int64, prefix string) {
src/go/plugin/go.d/modules/dnsdist/collect.go
+3
-25
@@ -3,9 +3,6 @@
3
package dnsdist
4
5
import (
6
- "encoding/json"
7
- "fmt"
8
- "net/http"
6
"net/url"
7
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
@@ -41,29 +38,10 @@ func (d *DNSdist) scrapeStatistics() (*statisticMetrics, error) {
38
}
39
req.URL.RawQuery = url.Values{"command": []string{"stats"}}.Encode()
40
44
- var statistics statisticMetrics
45
- if err := d.doOKDecode(req, &statistics); err != nil {
41
+ var stats statisticMetrics
42
+ if err := web.DoHTTP(d.httpClient).RequestJSON(req, &stats); err != nil {
43
return nil, err
44
}
45
49
- return &statistics, nil
50
-}
51
-
52
-func (d *DNSdist) doOKDecode(req *http.Request, in interface{}) error {
53
- resp, err := d.httpClient.Do(req)
54
- if err != nil {
55
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
56
- }
57
-
58
- defer web.CloseBody(resp)
59
-
60
- if resp.StatusCode != http.StatusOK {
61
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
62
- }
63
-
64
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
65
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
66
- }
67
-
68
- return nil
46
+ return &stats, nil
47
}
src/go/plugin/go.d/modules/dockerhub/apiclient.go
+2
-21
@@ -3,7 +3,6 @@
3
package dockerhub
4
5
import (
6
- "encoding/json"
6
"fmt"
7
"net/http"
8
"net/url"
@@ -37,29 +36,11 @@ func (a apiClient) getRepository(repoName string) (*repository, error) {
36
}
37
38
var repo repository
40
- if err := a.doOKDecode(req, &repo); err != nil {
39
+ if err := web.DoHTTP(a.httpClient).RequestJSON(req, &repo); err != nil {
40
return nil, err
41
}
43
- return &repo, nil
44
-}
45
-
46
-func (a apiClient) doOKDecode(req *http.Request, in any) error {
47
- resp, err := a.httpClient.Do(req)
48
- if err != nil {
49
- return fmt.Errorf("error on request: %v", err)
50
- }
51
-
52
- defer web.CloseBody(resp)
42
54
- if resp.StatusCode != http.StatusOK {
55
- return fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
56
- }
57
-
58
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
59
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
60
- }
61
-
62
- return nil
43
+ return &repo, nil
44
}
45
46
func (a apiClient) createRequest(urlPath string) (*http.Request, error) {
src/go/plugin/go.d/modules/elasticsearch/collect.go
+9
-35
@@ -3,11 +3,10 @@
3
package elasticsearch
4
5
import (
6
- "encoding/json"
6
"errors"
7
"fmt"
8
"math"
10
- "net/http"
9
+ "slices"
10
"strconv"
11
"strings"
12
"sync"
@@ -167,7 +166,7 @@ func (es *Elasticsearch) scrapeNodesStats(ms *esMetrics) {
166
req, _ := web.NewHTTPRequestWithPath(es.RequestConfig, p)
167
168
var stats esNodesStats
170
- if err := es.doOKDecode(req, &stats); err != nil {
169
+ if err := web.DoHTTP(es.httpClient).RequestJSON(req, &stats); err != nil {
170
es.Warning(err)
171
return
172
}
@@ -179,7 +178,7 @@ func (es *Elasticsearch) scrapeClusterHealth(ms *esMetrics) {
178
req, _ := web.NewHTTPRequestWithPath(es.RequestConfig, urlPathClusterHealth)
179
180
var health esClusterHealth
182
- if err := es.doOKDecode(req, &health); err != nil {
181
+ if err := web.DoHTTP(es.httpClient).RequestJSON(req, &health); err != nil {
182
es.Warning(err)
183
return
184
}
@@ -191,7 +190,7 @@ func (es *Elasticsearch) scrapeClusterStats(ms *esMetrics) {
190
req, _ := web.NewHTTPRequestWithPath(es.RequestConfig, urlPathClusterStats)
191
192
var stats esClusterStats
194
- if err := es.doOKDecode(req, &stats); err != nil {
193
+ if err := web.DoHTTP(es.httpClient).RequestJSON(req, &stats); err != nil {
194
es.Warning(err)
195
return
196
}
@@ -204,7 +203,7 @@ func (es *Elasticsearch) scrapeLocalIndicesStats(ms *esMetrics) {
203
req.URL.RawQuery = "local=true&format=json"
204
205
var stats []esIndexStats
207
- if err := es.doOKDecode(req, &stats); err != nil {
206
+ if err := web.DoHTTP(es.httpClient).RequestJSON(req, &stats); err != nil {
207
es.Warning(err)
208
return
209
}
@@ -218,8 +217,7 @@ func (es *Elasticsearch) getClusterName() (string, error) {
217
var info struct {
218
ClusterName string `json:"cluster_name"`
219
}
221
-
222
- if err := es.doOKDecode(req, &info); err != nil {
220
+ if err := web.DoHTTP(es.httpClient).RequestJSON(req, &info); err != nil {
221
return "", err
222
}
223
@@ -230,24 +228,6 @@ func (es *Elasticsearch) getClusterName() (string, error) {
228
return info.ClusterName, nil
229
}
230
233
-func (es *Elasticsearch) doOKDecode(req *http.Request, in interface{}) error {
234
- resp, err := es.httpClient.Do(req)
235
- if err != nil {
236
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
237
- }
238
-
239
- defer web.CloseBody(resp)
240
-
241
- if resp.StatusCode != http.StatusOK {
242
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
243
- }
244
-
245
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
246
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
247
- }
248
- return nil
249
-}
250
-
231
func convertIndexStoreSizeToBytes(size string) int64 {
232
var num float64
233
switch {
@@ -282,15 +262,9 @@ func boolToInt(v bool) int64 {
262
}
263
264
func removeSystemIndices(indices []esIndexStats) []esIndexStats {
285
- var i int
286
- for _, index := range indices {
287
- if strings.HasPrefix(index.Index, ".") {
288
- continue
289
- }
290
- indices[i] = index
291
- i++
292
- }
293
- return indices[:i]
265
+ return slices.DeleteFunc(indices, func(stats esIndexStats) bool {
266
+ return strings.HasPrefix(stats.Index, ".")
267
+ })
268
}
269
270
func merge(dst, src map[string]int64, prefix string) {
src/go/plugin/go.d/modules/fluentd/apiclient.go
+2
-22
@@ -3,7 +3,6 @@
3
package fluentd
4
5
import (
6
- "encoding/json"
6
"fmt"
7
"net/http"
8
"net/url"
@@ -55,32 +54,13 @@ func (a apiClient) getPluginsInfo() (*pluginsInfo, error) {
54
}
55
56
var info pluginsInfo
58
- if err := a.doOKDecode(req, &info); err != nil {
59
- return nil, err
57
+ if err := web.DoHTTP(a.httpClient).RequestJSON(req, &info); err != nil {
58
+ return nil, fmt.Errorf("error on decoding request : %v", err)
59
}
60
61
return &info, nil
62
}
63
65
-func (a apiClient) doOKDecode(req *http.Request, in any) error {
66
- resp, err := a.httpClient.Do(req)
67
- if err != nil {
68
- return fmt.Errorf("error on request: %v", err)
69
- }
70
-
71
- defer web.CloseBody(resp)
72
-
73
- if resp.StatusCode != http.StatusOK {
74
- return fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
75
- }
76
-
77
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
78
- return fmt.Errorf("error on decoding response from %s : %v", req.URL, err)
79
- }
80
-
81
- return nil
82
-}
83
-
64
func (a apiClient) createRequest(urlPath string) (*http.Request, error) {
65
req := a.request.Copy()
66
u, err := url.Parse(req.URL)
src/go/plugin/go.d/modules/hdfs/client.go
deleted
-61
@@ -1,61 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package hdfs
4
-
5
-import (
6
- "encoding/json"
7
- "fmt"
8
- "net/http"
9
-
10
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
-)
12
-
13
-func newClient(httpClient *http.Client, request web.RequestConfig) *client {
14
- return &client{
15
- httpClient: httpClient,
16
- request: request,
17
- }
18
-}
19
-
20
-type client struct {
21
- httpClient *http.Client
22
- request web.RequestConfig
23
-}
24
-
25
-func (c *client) do() (*http.Response, error) {
26
- req, err := web.NewHTTPRequest(c.request)
27
- if err != nil {
28
- return nil, fmt.Errorf("error on creating http request to %s : %v", c.request.URL, err)
29
- }
30
-
31
- // req.Header.Add("Accept-Encoding", "gzip")
32
- // req.Header.Set("User-Agent", "netdata/go.d.plugin")
33
-
34
- return c.httpClient.Do(req)
35
-}
36
-
37
-func (c *client) doOK() (*http.Response, error) {
38
- resp, err := c.do()
39
- if err != nil {
40
- return nil, err
41
- }
42
-
43
- if resp.StatusCode != http.StatusOK {
44
- return resp, fmt.Errorf("%s returned %d", c.request.URL, resp.StatusCode)
45
- }
46
- return resp, nil
47
-}
48
-
49
-func (c *client) doOKWithDecodeJSON(dst interface{}) error {
50
- resp, err := c.doOK()
51
- defer web.CloseBody(resp)
52
- if err != nil {
53
- return err
54
- }
55
-
56
- err = json.NewDecoder(resp.Body).Decode(dst)
57
- if err != nil {
58
- return fmt.Errorf("error on decoding response from %s : %v", c.request.URL, err)
59
- }
60
- return nil
61
-}
src/go/plugin/go.d/modules/hdfs/collect.go
+20
-18
@@ -9,12 +9,17 @@ import (
9
"strings"
10
11
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
12
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
13
)
14
15
func (h *HDFS) collect() (map[string]int64, error) {
15
- var raw rawJMX
16
- err := h.client.doOKWithDecodeJSON(&raw)
16
+ req, err := web.NewHTTPRequest(h.RequestConfig)
17
if err != nil {
18
+ return nil, fmt.Errorf("failed to create HTTP request: %v", err)
19
+ }
20
+
21
+ var raw rawJMX
22
+ if err := web.DoHTTP(h.httpClient).RequestJSON(req, &raw); err != nil {
23
return nil, err
24
}
25
@@ -28,9 +33,13 @@ func (h *HDFS) collect() (map[string]int64, error) {
33
}
34
35
func (h *HDFS) determineNodeType() (nodeType, error) {
31
- var raw rawJMX
32
- err := h.client.doOKWithDecodeJSON(&raw)
36
+ req, err := web.NewHTTPRequest(h.RequestConfig)
37
if err != nil {
38
+ return "", fmt.Errorf("failed to create HTTP request: %v", err)
39
+ }
40
+
41
+ var raw rawJMX
42
+ if err := web.DoHTTP(h.httpClient).RequestJSON(req, &raw); err != nil {
43
return "", err
44
}
45
@@ -69,40 +78,33 @@ func (h *HDFS) collectRawJMX(raw rawJMX) *metrics {
78
}
79
80
func (h *HDFS) collectNameNode(mx *metrics, raw rawJMX) {
72
- err := h.collectJVM(mx, raw)
73
- if err != nil {
81
+ if err := h.collectJVM(mx, raw); err != nil {
82
h.Debugf("error on collecting jvm : %v", err)
83
}
84
77
- err = h.collectRPCActivity(mx, raw)
78
- if err != nil {
85
+ if err := h.collectRPCActivity(mx, raw); err != nil {
86
h.Debugf("error on collecting rpc activity : %v", err)
87
}
88
82
- err = h.collectFSNameSystem(mx, raw)
83
- if err != nil {
89
+ if err := h.collectFSNameSystem(mx, raw); err != nil {
90
h.Debugf("error on collecting fs name system : %v", err)
91
}
92
}
93
94
func (h *HDFS) collectDataNode(mx *metrics, raw rawJMX) {
89
- err := h.collectJVM(mx, raw)
90
- if err != nil {
95
+ if err := h.collectJVM(mx, raw); err != nil {
96
h.Debugf("error on collecting jvm : %v", err)
97
}
98
94
- err = h.collectRPCActivity(mx, raw)
95
- if err != nil {
99
+ if err := h.collectRPCActivity(mx, raw); err != nil {
100
h.Debugf("error on collecting rpc activity : %v", err)
101
}
102
99
- err = h.collectFSDatasetState(mx, raw)
100
- if err != nil {
103
+ if err := h.collectFSDatasetState(mx, raw); err != nil {
104
h.Debugf("error on collecting fs dataset state : %v", err)
105
}
106
104
- err = h.collectDataNodeActivity(mx, raw)
105
- if err != nil {
107
+ if err := h.collectDataNodeActivity(mx, raw); err != nil {
108
h.Debugf("error on collecting datanode activity state : %v", err)
109
}
110
}
src/go/plugin/go.d/modules/hdfs/hdfs.go
+13
-17
@@ -5,6 +5,8 @@ package hdfs
5
import (
6
_ "embed"
7
"errors"
8
+ "fmt"
9
+ "net/http"
10
"time"
11
12
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -50,7 +52,7 @@ type (
52
module.Base
53
Config `yaml:",inline" json:""`
54
53
- client *client
55
+ httpClient *http.Client
56
57
nodeType
58
}
@@ -67,17 +69,15 @@ func (h *HDFS) Configuration() any {
69
}
70
71
func (h *HDFS) Init() error {
70
- if err := h.validateConfig(); err != nil {
71
- h.Errorf("config validation: %v", err)
72
- return err
72
+ if h.URL == "" {
73
+ return errors.New("URL is required but not set")
74
}
75
75
- cl, err := h.createClient()
76
+ httpClient, err := web.NewHTTPClient(h.ClientConfig)
77
if err != nil {
77
- h.Errorf("error on creating client : %v", err)
78
- return err
78
+ return fmt.Errorf("failed to create HTTP client: %v", err)
79
}
80
- h.client = cl
80
+ h.httpClient = httpClient
81
82
return nil
83
}
@@ -85,19 +85,19 @@ func (h *HDFS) Init() error {
85
func (h *HDFS) Check() error {
86
typ, err := h.determineNodeType()
87
if err != nil {
88
- h.Errorf("error on node type determination : %v", err)
89
- return err
88
+ return fmt.Errorf("error on node type determination : %v", err)
89
}
90
h.nodeType = typ
91
92
mx, err := h.collect()
93
if err != nil {
95
- h.Error(err)
94
return err
95
}
96
+
97
if len(mx) == 0 {
98
return errors.New("no metrics collected")
99
}
100
+
101
return nil
102
}
103
@@ -114,12 +114,8 @@ func (h *HDFS) Charts() *Charts {
114
115
func (h *HDFS) Collect() map[string]int64 {
116
mx, err := h.collect()
117
-
117
if err != nil {
118
h.Error(err)
120
- }
121
-
122
- if len(mx) == 0 {
119
return nil
120
}
121
@@ -127,7 +123,7 @@ func (h *HDFS) Collect() map[string]int64 {
123
}
124
125
func (h *HDFS) Cleanup() {
130
- if h.client != nil && h.client.httpClient != nil {
131
- h.client.httpClient.CloseIdleConnections()
126
+ if h.httpClient != nil {
127
+ h.httpClient.CloseIdleConnections()
128
}
129
}
src/go/plugin/go.d/modules/hdfs/init.go
deleted
-25
@@ -1,25 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package hdfs
4
-
5
-import (
6
- "errors"
7
-
8
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
9
-)
10
-
11
-func (h *HDFS) validateConfig() error {
12
- if h.URL == "" {
13
- return errors.New("url not set")
14
- }
15
- return nil
16
-}
17
-
18
-func (h *HDFS) createClient() (*client, error) {
19
- httpClient, err := web.NewHTTPClient(h.ClientConfig)
20
- if err != nil {
21
- return nil, err
22
- }
23
-
24
- return newClient(httpClient, h.RequestConfig), nil
25
-}
src/go/plugin/go.d/modules/icecast/collect.go
+1
-22
@@ -3,9 +3,7 @@
3
package icecast
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
9
)
@@ -73,28 +71,9 @@ func (ic *Icecast) queryServerStats() (*serverStats, error) {
71
}
72
73
var stats serverStats
76
-
77
- if err := ic.doOKDecode(req, &stats); err != nil {
74
+ if err := web.DoHTTP(ic.httpClient).RequestJSON(req, &stats); err != nil {
75
return nil, err
76
}
77
78
return &stats, nil
79
}
83
-
84
-func (ic *Icecast) doOKDecode(req *http.Request, in interface{}) error {
85
- resp, err := ic.httpClient.Do(req)
86
- if err != nil {
87
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
88
- }
89
-
90
- defer web.CloseBody(resp)
91
-
92
- if resp.StatusCode != http.StatusOK {
93
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
94
- }
95
-
96
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
97
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
98
- }
99
- return nil
100
-}
src/go/plugin/go.d/modules/ipfs/collect.go
+4
-24
@@ -3,9 +3,7 @@
3
package ipfs
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
9
)
@@ -130,7 +128,7 @@ func (ip *IPFS) queryStatsBandwidth() (*ipfsStatsBw, error) {
128
}
129
130
var stats ipfsStatsBw
133
- if err := ip.doOKDecode(req, &stats); err != nil {
131
+ if err := web.DoHTTP(ip.httpClient).RequestJSON(req, &stats); err != nil {
132
return nil, err
133
}
134
@@ -148,7 +146,7 @@ func (ip *IPFS) querySwarmPeers() (*ipfsSwarmPeers, error) {
146
}
147
148
var stats ipfsSwarmPeers
151
- if err := ip.doOKDecode(req, &stats); err != nil {
149
+ if err := web.DoHTTP(ip.httpClient).RequestJSON(req, &stats); err != nil {
150
return nil, err
151
}
152
@@ -162,7 +160,7 @@ func (ip *IPFS) queryStatsRepo() (*ipfsStatsRepo, error) {
160
}
161
162
var stats ipfsStatsRepo
165
- if err := ip.doOKDecode(req, &stats); err != nil {
163
+ if err := web.DoHTTP(ip.httpClient).RequestJSON(req, &stats); err != nil {
164
return nil, err
165
}
166
@@ -176,27 +174,9 @@ func (ip *IPFS) queryPinLs() (*ipfsPinsLs, error) {
174
}
175
176
var stats ipfsPinsLs
179
- if err := ip.doOKDecode(req, &stats); err != nil {
177
+ if err := web.DoHTTP(ip.httpClient).RequestJSON(req, &stats); err != nil {
178
return nil, err
179
}
180
181
return &stats, nil
182
}
185
-
186
-func (ip *IPFS) doOKDecode(req *http.Request, in interface{}) error {
187
- resp, err := ip.httpClient.Do(req)
188
- if err != nil {
189
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
190
- }
191
-
192
- defer web.CloseBody(resp)
193
-
194
- if resp.StatusCode != http.StatusOK {
195
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
196
- }
197
-
198
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
199
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
200
- }
201
- return nil
202
-}
src/go/plugin/go.d/modules/k8s_state/cluster_meta.go
+20
-26
@@ -3,11 +3,14 @@
3
package k8s_state
4
5
import (
6
+ "errors"
7
"fmt"
8
"io"
9
"net/http"
10
"time"
11
12
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
13
+
14
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15
)
16
@@ -21,7 +24,7 @@ func (ks *KubeState) getKubeClusterID() string {
24
}
25
26
func (ks *KubeState) getKubeClusterName() string {
24
- client := http.Client{Timeout: time.Second}
27
+ client := &http.Client{Timeout: time.Second}
28
n, err := getGKEKubeClusterName(client)
29
if err != nil {
30
ks.Debugf("error on getting GKE cluster name: %v", err)
@@ -29,7 +32,7 @@ func (ks *KubeState) getKubeClusterName() string {
32
return n
33
}
34
32
-func getGKEKubeClusterName(client http.Client) (string, error) {
35
+func getGKEKubeClusterName(client *http.Client) (string, error) {
36
id, err := doMetaGKEHTTPReq(client, "http://metadata/computeMetadata/v1/project/project-id")
37
if err != nil {
38
return "", err
@@ -46,39 +49,30 @@ func getGKEKubeClusterName(client http.Client) (string, error) {
49
return fmt.Sprintf("gke_%s_%s_%s", id, loc, name), nil
50
}
51
49
-func doMetaGKEHTTPReq(client http.Client, url string) (string, error) {
52
+func doMetaGKEHTTPReq(client *http.Client, url string) (string, error) {
53
req, err := http.NewRequest(http.MethodGet, url, nil)
54
if err != nil {
55
return "", err
56
}
57
58
req.Header.Add("Metadata-Flavor", "Google")
56
- resp, err := client.Do(req)
57
- if err != nil {
58
- return "", err
59
- }
60
- defer closeHTTPRespBody(resp)
59
62
- if resp.StatusCode != http.StatusOK {
63
- return "", fmt.Errorf("'%s' returned HTTP status code %d", url, resp.StatusCode)
64
- }
60
+ var resp string
61
66
- bs, err := io.ReadAll(resp.Body)
67
- if err != nil {
68
- return "", err
69
- }
70
-
71
- s := string(bs)
72
- if s == "" {
73
- return "", fmt.Errorf("an empty response from '%s'", url)
74
- }
62
+ if err := web.DoHTTP(client).Request(req, func(body io.Reader) error {
63
+ bs, rerr := io.ReadAll(body)
64
+ if rerr != nil {
65
+ return rerr
66
+ }
67
76
- return s, nil
77
-}
68
+ if resp = string(bs); len(resp) == 0 {
69
+ return errors.New("empty response")
70
+ }
71
79
-func closeHTTPRespBody(resp *http.Response) {
80
- if resp != nil && resp.Body != nil {
81
- _, _ = io.Copy(io.Discard, resp.Body)
82
- _ = resp.Body.Close()
72
+ return nil
73
+ }); err != nil {
74
+ return "", err
75
}
76
+
77
+ return resp, nil
78
}
src/go/plugin/go.d/modules/lighttpd/collect.go
+14
-7
@@ -4,22 +4,29 @@ package lighttpd
4
5
import (
6
"fmt"
7
+ "io"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
12
13
func (l *Lighttpd) collect() (map[string]int64, error) {
12
- status, err := l.apiClient.getServerStatus()
13
-
14
+ req, err := web.NewHTTPRequest(l.RequestConfig)
15
if err != nil {
15
- return nil, err
16
+ return nil, fmt.Errorf("failed to create HTTP request: %v", err)
17
}
18
18
- mx := stm.ToMap(status)
19
+ var status *serverStatus
20
+ var perr error
21
20
- if len(mx) == 0 {
21
- return nil, fmt.Errorf("nothing was collected from %s", l.URL)
22
+ if err := web.DoHTTP(l.httpClient).Request(req, func(body io.Reader) error {
23
+ if status, perr = parseResponse(body); perr != nil {
24
+ return perr
25
+ }
26
+ return nil
27
+ }); err != nil {
28
+ return nil, err
29
}
30
24
- return mx, nil
31
+ return stm.ToMap(status), nil
32
}
src/go/plugin/go.d/modules/lighttpd/init.go
deleted
-29
@@ -1,29 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package lighttpd
4
-
5
-import (
6
- "errors"
7
- "fmt"
8
- "strings"
9
-
10
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
-)
12
-
13
-func (l *Lighttpd) validateConfig() error {
14
- if l.URL == "" {
15
- return errors.New("url not set")
16
- }
17
- if !strings.HasSuffix(l.URL, "?auto") {
18
- return fmt.Errorf("bad URL '%s', should ends in '?auto'", l.URL)
19
- }
20
- return nil
21
-}
22
-
23
-func (l *Lighttpd) initApiClient() (*apiClient, error) {
24
- client, err := web.NewHTTPClient(l.ClientConfig)
25
- if err != nil {
26
- return nil, err
27
- }
28
- return newAPIClient(client, l.RequestConfig), nil
29
-}
src/go/plugin/go.d/modules/lighttpd/lighttpd.go
+22
-14
@@ -5,6 +5,9 @@ package lighttpd
5
import (
6
_ "embed"
7
"errors"
8
+ "fmt"
9
+ "net/http"
10
+ "strings"
11
"time"
12
13
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -33,7 +36,9 @@ func New() *Lighttpd {
36
Timeout: confopt.Duration(time.Second * 2),
37
},
38
},
36
- }}
39
+ },
40
+ charts: charts.Copy(),
41
+ }
42
}
43
44
type Config struct {
@@ -45,7 +50,9 @@ type Lighttpd struct {
50
module.Base
51
Config `yaml:",inline" json:""`
52
48
- apiClient *apiClient
53
+ charts *module.Charts
54
+
55
+ httpClient *http.Client
56
}
57
58
func (l *Lighttpd) Configuration() any {
@@ -53,17 +60,18 @@ func (l *Lighttpd) Configuration() any {
60
}
61
62
func (l *Lighttpd) Init() error {
56
- if err := l.validateConfig(); err != nil {
57
- l.Errorf("config validation: %v", err)
58
- return err
63
+ if l.URL == "" {
64
+ return errors.New("URL is required but not set")
65
+ }
66
+ if !strings.HasSuffix(l.URL, "?auto") {
67
+ return fmt.Errorf("bad URL '%s', should ends in '?auto'", l.URL)
68
}
69
61
- client, err := l.initApiClient()
70
+ httpClient, err := web.NewHTTPClient(l.ClientConfig)
71
if err != nil {
63
- l.Error(err)
64
- return err
72
+ return fmt.Errorf("failed to create http client: %v", err)
73
}
66
- l.apiClient = client
74
+ l.httpClient = httpClient
75
76
l.Debugf("using URL %s", l.URL)
77
l.Debugf("using timeout: %s", l.Timeout.Duration())
@@ -74,22 +82,22 @@ func (l *Lighttpd) Init() error {
82
func (l *Lighttpd) Check() error {
83
mx, err := l.collect()
84
if err != nil {
77
- l.Error(err)
85
return err
86
}
87
+
88
if len(mx) == 0 {
89
return errors.New("no metrics collected")
90
}
91
+
92
return nil
93
}
94
95
func (l *Lighttpd) Charts() *Charts {
87
- return charts.Copy()
96
+ return l.charts
97
}
98
99
func (l *Lighttpd) Collect() map[string]int64 {
100
mx, err := l.collect()
92
-
101
if err != nil {
102
l.Error(err)
103
return nil
@@ -99,7 +107,7 @@ func (l *Lighttpd) Collect() map[string]int64 {
107
}
108
109
func (l *Lighttpd) Cleanup() {
102
- if l.apiClient != nil && l.apiClient.httpClient != nil {
103
- l.apiClient.httpClient.CloseIdleConnections()
110
+ if l.httpClient != nil {
111
+ l.httpClient.CloseIdleConnections()
112
}
113
}
src/go/plugin/go.d/modules/lighttpd/lighttpd_test.go
-1
@@ -41,7 +41,6 @@ func TestLighttpd_Init(t *testing.T) {
41
job := New()
42
43
require.NoError(t, job.Init())
44
- assert.NotNil(t, job.apiClient)
44
}
45
46
func TestLighttpd_InitNG(t *testing.T) {
src/go/plugin/go.d/modules/lighttpd/metrics.go
deleted
-33
@@ -1,33 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package lighttpd
4
-
5
-type (
6
- serverStatus struct {
7
- Total struct {
8
- Accesses *int64 `stm:"accesses"`
9
- KBytes *int64 `stm:"kBytes"`
10
- } `stm:"total"`
11
- Servers struct {
12
- Busy *int64 `stm:"busy_servers"`
13
- Idle *int64 `stm:"idle_servers"`
14
- } `stm:""`
15
- Uptime *int64 `stm:"uptime"`
16
- Scoreboard *scoreboard `stm:"scoreboard"`
17
- }
18
- scoreboard struct {
19
- Waiting int64 `stm:"waiting"`
20
- Open int64 `stm:"open"`
21
- Close int64 `stm:"close"`
22
- HardError int64 `stm:"hard_error"`
23
- KeepAlive int64 `stm:"keepalive"`
24
- Read int64 `stm:"read"`
25
- ReadPost int64 `stm:"read_post"`
26
- Write int64 `stm:"write"`
27
- HandleRequest int64 `stm:"handle_request"`
28
- RequestStart int64 `stm:"request_start"`
29
- RequestEnd int64 `stm:"request_end"`
30
- ResponseStart int64 `stm:"response_start"`
31
- ResponseEnd int64 `stm:"response_end"`
32
- }
33
-)
src/go/plugin/go.d/modules/lighttpd/status.go
renamed
+27
-44
@@ -6,11 +6,8 @@ import (
6
"bufio"
7
"fmt"
8
"io"
9
- "net/http"
9
"strconv"
10
"strings"
12
-
13
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
12
13
const (
@@ -25,49 +22,35 @@ const (
22
scoreBoard = "Scoreboard"
23
)
24
28
-func newAPIClient(client *http.Client, request web.RequestConfig) *apiClient {
29
- return &apiClient{httpClient: client, request: request}
30
-}
31
-
32
-type apiClient struct {
33
- httpClient *http.Client
34
- request web.RequestConfig
35
-}
36
-
37
-func (a apiClient) getServerStatus() (*serverStatus, error) {
38
- req, err := web.NewHTTPRequest(a.request)
39
-
40
- if err != nil {
41
- return nil, fmt.Errorf("error on creating request : %v", err)
25
+type (
26
+ serverStatus struct {
27
+ Total struct {
28
+ Accesses *int64 `stm:"accesses"`
29
+ KBytes *int64 `stm:"kBytes"`
30
+ } `stm:"total"`
31
+ Servers struct {
32
+ Busy *int64 `stm:"busy_servers"`
33
+ Idle *int64 `stm:"idle_servers"`
34
+ } `stm:""`
35
+ Uptime *int64 `stm:"uptime"`
36
+ Scoreboard *scoreboard `stm:"scoreboard"`
37
}
43
-
44
- resp, err := a.doRequestOK(req)
45
-
46
- defer web.CloseBody(resp)
47
-
48
- if err != nil {
49
- return nil, err
38
+ scoreboard struct {
39
+ Waiting int64 `stm:"waiting"`
40
+ Open int64 `stm:"open"`
41
+ Close int64 `stm:"close"`
42
+ HardError int64 `stm:"hard_error"`
43
+ KeepAlive int64 `stm:"keepalive"`
44
+ Read int64 `stm:"read"`
45
+ ReadPost int64 `stm:"read_post"`
46
+ Write int64 `stm:"write"`
47
+ HandleRequest int64 `stm:"handle_request"`
48
+ RequestStart int64 `stm:"request_start"`
49
+ RequestEnd int64 `stm:"request_end"`
50
+ ResponseStart int64 `stm:"response_start"`
51
+ ResponseEnd int64 `stm:"response_end"`
52
}
51
-
52
- status, err := parseResponse(resp.Body)
53
-
54
- if err != nil {
55
- return nil, fmt.Errorf("error on parsing response from %s : %v", req.URL, err)
56
- }
57
-
58
- return status, nil
59
-}
60
-
61
-func (a apiClient) doRequestOK(req *http.Request) (*http.Response, error) {
62
- resp, err := a.httpClient.Do(req)
63
- if err != nil {
64
- return nil, fmt.Errorf("error on request : %v", err)
65
- }
66
- if resp.StatusCode != http.StatusOK {
67
- return resp, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
68
- }
69
- return resp, nil
70
-}
53
+)
54
55
func parseResponse(r io.Reader) (*serverStatus, error) {
56
s := bufio.NewScanner(r)
src/go/plugin/go.d/modules/logstash/collect.go
+2
-31
@@ -3,10 +3,7 @@
3
package logstash
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "io"
9
- "net/http"
7
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
@@ -47,40 +44,14 @@ func (l *Logstash) updateCharts(pipelines map[string]pipelineStats) {
44
func (l *Logstash) queryNodeStats() (*nodeStats, error) {
45
req, err := web.NewHTTPRequestWithPath(l.RequestConfig, urlPathNodeStatsAPI)
46
if err != nil {
50
- return nil, err
47
+ return nil, fmt.Errorf("failed to create HTTP request: %w", err)
48
}
49
50
var stats nodeStats
51
55
- if err := l.doWithDecode(&stats, req); err != nil {
52
+ if err := web.DoHTTP(l.httpClient).RequestJSON(req, &stats); err != nil {
53
return nil, err
54
}
55
56
return &stats, nil
57
}
61
-
62
-func (l *Logstash) doWithDecode(dst interface{}, req *http.Request) error {
63
- l.Debugf("executing %s '%s'", req.Method, req.URL)
64
-
65
- resp, err := l.httpClient.Do(req)
66
- if err != nil {
67
- return err
68
- }
69
-
70
- defer web.CloseBody(resp)
71
-
72
- if resp.StatusCode != http.StatusOK {
73
- return fmt.Errorf("%s returned %d status code (%s)", req.URL, resp.StatusCode, resp.Status)
74
- }
75
-
76
- content, err := io.ReadAll(resp.Body)
77
- if err != nil {
78
- return fmt.Errorf("error on reading response from %s : %v", req.URL, err)
79
- }
80
-
81
- if err := json.Unmarshal(content, dst); err != nil {
82
- return fmt.Errorf("error on parsing response from %s : %v", req.URL, err)
83
- }
84
-
85
- return nil
86
-}
src/go/plugin/go.d/modules/monit/collect.go
+3
-24
@@ -6,7 +6,6 @@ import (
6
"encoding/xml"
7
"errors"
8
"fmt"
9
- "net/http"
9
"net/url"
10
11
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
@@ -80,31 +79,11 @@ func (m *Monit) fetchStatus() (*monitStatus, error) {
79
req.URL.RawQuery = urlQueryStatus
80
81
var status monitStatus
83
- if err := m.doOKDecode(req, &status); err != nil {
82
+ if err := web.DoHTTP(m.httpClient).RequestXML(req, &status, func(d *xml.Decoder) {
83
+ d.CharsetReader = charset.NewReaderLabel
84
+ }); err != nil {
85
return nil, err
86
}
87
88
return &status, nil
89
}
89
-
90
-func (m *Monit) doOKDecode(req *http.Request, in interface{}) error {
91
- resp, err := m.httpClient.Do(req)
92
- if err != nil {
93
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
94
- }
95
-
96
- defer web.CloseBody(resp)
97
-
98
- if resp.StatusCode != http.StatusOK {
99
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
100
- }
101
-
102
- dec := xml.NewDecoder(resp.Body)
103
- dec.CharsetReader = charset.NewReaderLabel
104
-
105
- if err := dec.Decode(in); err != nil {
106
- return fmt.Errorf("error on decoding XML response from '%s': %v", req.URL, err)
107
- }
108
-
109
- return nil
110
-}
src/go/plugin/go.d/modules/nginx/collect.go
+17
-2
@@ -3,13 +3,28 @@
3
package nginx
4
5
import (
6
+ "fmt"
7
+ "io"
8
+
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
12
13
func (n *Nginx) collect() (map[string]int64, error) {
10
- status, err := n.apiClient.getStubStatus()
11
-
14
+ req, err := web.NewHTTPRequest(n.RequestConfig)
15
if err != nil {
16
+ return nil, fmt.Errorf("failed to create HTTP request to '%s': %w'", n.URL, err)
17
+ }
18
+
19
+ var status *stubStatus
20
+ var perr error
21
+
22
+ if err := web.DoHTTP(n.httpClient).Request(req, func(body io.Reader) error {
23
+ if status, perr = parseStubStatus(body); perr != nil {
24
+ return perr
25
+ }
26
+ return nil
27
+ }); err != nil {
28
return nil, err
29
}
30
src/go/plugin/go.d/modules/nginx/metrics.go
deleted
-34
@@ -1,34 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package nginx
4
-
5
-type stubStatus struct {
6
- Connections struct {
7
- // The current number of active client connections including Waiting connections.
8
- Active int64 `stm:"active"`
9
-
10
- // The total number of accepted client connections.
11
- Accepts int64 `stm:"accepts"`
12
-
13
- // The total number of handled connections.
14
- // Generally, the parameter value is the same as accepts unless some resource limits have been reached.
15
- Handled int64 `stm:"handled"`
16
-
17
- // The current number of connections where nginx is reading the request header.
18
- Reading int64 `stm:"reading"`
19
-
20
- // The current number of connections where nginx is writing the response back to the client.
21
- Writing int64 `stm:"writing"`
22
-
23
- // The current number of idle client connections waiting for a request.
24
- Waiting int64 `stm:"waiting"`
25
- } `stm:""`
26
- Requests struct {
27
- // The total number of client requests.
28
- Total int64 `stm:"requests"`
29
-
30
- // Note: tengine specific
31
- // The total requests' response time, which is in millisecond
32
- Time *int64 `stm:"request_time"`
33
- } `stm:""`
34
-}
src/go/plugin/go.d/modules/nginx/nginx.go
+17
-13
@@ -5,6 +5,8 @@ package nginx
5
import (
6
_ "embed"
7
"errors"
8
+ "fmt"
9
+ "net/http"
10
"time"
11
12
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -34,7 +36,9 @@ func New() *Nginx {
36
Timeout: confopt.Duration(time.Second * 1),
37
},
38
},
37
- }}
39
+ },
40
+ charts: charts.Copy(),
41
+ }
42
}
43
44
type Config struct {
@@ -46,7 +50,9 @@ type Nginx struct {
50
module.Base
51
Config `yaml:",inline" json:""`
52
49
- apiClient *apiClient
53
+ charts *module.Charts
54
+
55
+ httpClient *http.Client
56
}
57
58
func (n *Nginx) Configuration() any {
@@ -55,17 +61,14 @@ func (n *Nginx) Configuration() any {
61
62
func (n *Nginx) Init() error {
63
if n.URL == "" {
58
- n.Error("URL not set")
59
- return errors.New("url not set")
64
+ return errors.New("nginx URL required but not set")
65
}
66
62
- client, err := web.NewHTTPClient(n.ClientConfig)
67
+ httpClient, err := web.NewHTTPClient(n.ClientConfig)
68
if err != nil {
64
- n.Error(err)
65
- return err
69
+ return fmt.Errorf("failed initializing http client: %w", err)
70
}
67
-
68
- n.apiClient = newAPIClient(client, n.RequestConfig)
71
+ n.httpClient = httpClient
72
73
n.Debugf("using URL %s", n.URL)
74
n.Debugf("using timeout: %s", n.Timeout)
@@ -79,15 +82,16 @@ func (n *Nginx) Check() error {
82
n.Error(err)
83
return err
84
}
85
+
86
if len(mx) == 0 {
87
return errors.New("no metrics collected")
84
-
88
}
89
+
90
return nil
91
}
92
93
func (n *Nginx) Charts() *Charts {
90
- return charts.Copy()
94
+ return n.charts
95
}
96
97
func (n *Nginx) Collect() map[string]int64 {
@@ -101,7 +105,7 @@ func (n *Nginx) Collect() map[string]int64 {
105
}
106
107
func (n *Nginx) Cleanup() {
104
- if n.apiClient != nil && n.apiClient.httpClient != nil {
105
- n.apiClient.httpClient.CloseIdleConnections()
108
+ if n.httpClient != nil {
109
+ n.httpClient.CloseIdleConnections()
110
}
111
}
src/go/plugin/go.d/modules/nginx/nginx_test.go
-1
@@ -45,7 +45,6 @@ func TestNginx_Init(t *testing.T) {
45
job := New()
46
47
require.NoError(t, job.Init())
48
- assert.NotNil(t, job.apiClient)
48
}
49
50
func TestNginx_Check(t *testing.T) {
src/go/plugin/go.d/modules/nginx/status.go
renamed
+23
-37
@@ -6,12 +6,9 @@ import (
6
"bufio"
7
"fmt"
8
"io"
9
- "net/http"
9
"regexp"
10
"strconv"
11
"strings"
13
-
14
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
12
)
13
14
const (
@@ -49,46 +46,35 @@ var (
46
reStatus = regexp.MustCompile(`^Active connections: ([0-9]+)\n[^\d]+([0-9]+) ([0-9]+) ([0-9]+) ?([0-9]+)?\nReading: ([0-9]+) Writing: ([0-9]+) Waiting: ([0-9]+)`)
47
)
48
52
-func newAPIClient(client *http.Client, request web.RequestConfig) *apiClient {
53
- return &apiClient{httpClient: client, request: request}
54
-}
49
+type stubStatus struct {
50
+ Connections struct {
51
+ // The current number of active client connections including Waiting connections.
52
+ Active int64 `stm:"active"`
53
56
-type apiClient struct {
57
- httpClient *http.Client
58
- request web.RequestConfig
59
-}
54
+ // The total number of accepted client connections.
55
+ Accepts int64 `stm:"accepts"`
56
61
-func (a apiClient) getStubStatus() (*stubStatus, error) {
62
- req, err := web.NewHTTPRequest(a.request)
63
- if err != nil {
64
- return nil, fmt.Errorf("error on creating request : %v", err)
65
- }
57
+ // The total number of handled connections.
58
+ // Generally, the parameter value is the same as accepts unless some resource limits have been reached.
59
+ Handled int64 `stm:"handled"`
60
67
- resp, err := a.doRequestOK(req)
68
- defer web.CloseBody(resp)
69
- if err != nil {
70
- return nil, err
71
- }
61
+ // The current number of connections where nginx is reading the request header.
62
+ Reading int64 `stm:"reading"`
63
73
- status, err := parseStubStatus(resp.Body)
74
- if err != nil {
75
- return nil, fmt.Errorf("error on parsing response : %v", err)
76
- }
77
-
78
- return status, nil
79
-}
64
+ // The current number of connections where nginx is writing the response back to the client.
65
+ Writing int64 `stm:"writing"`
66
81
-func (a apiClient) doRequestOK(req *http.Request) (*http.Response, error) {
82
- resp, err := a.httpClient.Do(req)
83
- if err != nil {
84
- return resp, fmt.Errorf("error on request : %v", err)
85
- }
86
-
87
- if resp.StatusCode != http.StatusOK {
88
- return resp, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
89
- }
67
+ // The current number of idle client connections waiting for a request.
68
+ Waiting int64 `stm:"waiting"`
69
+ } `stm:""`
70
+ Requests struct {
71
+ // The total number of client requests.
72
+ Total int64 `stm:"requests"`
73
91
- return resp, err
74
+ // Note: tengine specific
75
+ // The total requests' response time, which is in millisecond
76
+ Time *int64 `stm:"request_time"`
77
+ } `stm:""`
78
}
79
80
func parseStubStatus(r io.Reader) (*stubStatus, error) {
src/go/plugin/go.d/modules/nginxplus/nginx_http_api_query.go
+23
-40
@@ -3,10 +3,8 @@
3
package nginxplus
4
5
import (
6
- "encoding/json"
6
"errors"
7
"fmt"
9
- "io"
8
"net/http"
9
"sync"
10
@@ -49,7 +47,7 @@ func (n *NginxPlus) queryAPIVersion() (int64, error) {
47
req, _ := web.NewHTTPRequestWithPath(n.RequestConfig, urlPathAPIVersions)
48
49
var versions nginxAPIVersions
52
- if err := n.doWithDecode(&versions, req); err != nil {
50
+ if err := n.doHTTP(req, &versions); err != nil {
51
return 0, err
52
}
53
@@ -64,7 +62,7 @@ func (n *NginxPlus) queryAvailableEndpoints() error {
62
req, _ := web.NewHTTPRequestWithPath(n.RequestConfig, fmt.Sprintf(urlPathAPIEndpointsRoot, n.apiVersion))
63
64
var endpoints []string
67
- if err := n.doWithDecode(&endpoints, req); err != nil {
65
+ if err := n.doHTTP(req, &endpoints); err != nil {
66
return err
67
}
68
@@ -91,7 +89,7 @@ func (n *NginxPlus) queryAvailableEndpoints() error {
89
endpoints = endpoints[:0]
90
req, _ = web.NewHTTPRequestWithPath(n.RequestConfig, fmt.Sprintf(urlPathAPIEndpointsHTTP, n.apiVersion))
91
94
- if err := n.doWithDecode(&endpoints, req); err != nil {
92
+ if err := n.doHTTP(req, &endpoints); err != nil {
93
return err
94
}
95
@@ -116,7 +114,7 @@ func (n *NginxPlus) queryAvailableEndpoints() error {
114
endpoints = endpoints[:0]
115
req, _ = web.NewHTTPRequestWithPath(n.RequestConfig, fmt.Sprintf(urlPathAPIEndpointsStream, n.apiVersion))
116
119
- if err := n.doWithDecode(&endpoints, req); err != nil {
117
+ if err := n.doHTTP(req, &endpoints); err != nil {
118
return err
119
}
120
@@ -171,7 +169,7 @@ func (n *NginxPlus) queryNginxInfo(ms *nginxMetrics) {
169
170
var v nginxInfo
171
174
- if err := n.doWithDecode(&v, req); err != nil {
172
+ if err := n.doHTTP(req, &v); err != nil {
173
n.endpoints.nginx = !errors.Is(err, errPathNotFound)
174
n.Warning(err)
175
return
@@ -185,7 +183,7 @@ func (n *NginxPlus) queryConnections(ms *nginxMetrics) {
183
184
var v nginxConnections
185
188
- if err := n.doWithDecode(&v, req); err != nil {
186
+ if err := n.doHTTP(req, &v); err != nil {
187
n.endpoints.connections = !errors.Is(err, errPathNotFound)
188
n.Warning(err)
189
return
@@ -199,7 +197,7 @@ func (n *NginxPlus) querySSL(ms *nginxMetrics) {
197
198
var v nginxSSL
199
202
- if err := n.doWithDecode(&v, req); err != nil {
200
+ if err := n.doHTTP(req, &v); err != nil {
201
n.endpoints.ssl = !errors.Is(err, errPathNotFound)
202
n.Warning(err)
203
return
@@ -213,7 +211,7 @@ func (n *NginxPlus) queryHTTPRequests(ms *nginxMetrics) {
211
212
var v nginxHTTPRequests
213
216
- if err := n.doWithDecode(&v, req); err != nil {
214
+ if err := n.doHTTP(req, &v); err != nil {
215
n.endpoints.httpRequest = !errors.Is(err, errPathNotFound)
216
n.Warning(err)
217
return
@@ -227,7 +225,7 @@ func (n *NginxPlus) queryHTTPServerZones(ms *nginxMetrics) {
225
226
var v nginxHTTPServerZones
227
230
- if err := n.doWithDecode(&v, req); err != nil {
228
+ if err := n.doHTTP(req, &v); err != nil {
229
n.endpoints.httpServerZones = !errors.Is(err, errPathNotFound)
230
n.Warning(err)
231
return
@@ -241,7 +239,7 @@ func (n *NginxPlus) queryHTTPLocationZones(ms *nginxMetrics) {
239
240
var v nginxHTTPLocationZones
241
244
- if err := n.doWithDecode(&v, req); err != nil {
242
+ if err := n.doHTTP(req, &v); err != nil {
243
n.endpoints.httpLocationZones = !errors.Is(err, errPathNotFound)
244
n.Warning(err)
245
return
@@ -255,7 +253,7 @@ func (n *NginxPlus) queryHTTPUpstreams(ms *nginxMetrics) {
253
254
var v nginxHTTPUpstreams
255
258
- if err := n.doWithDecode(&v, req); err != nil {
256
+ if err := n.doHTTP(req, &v); err != nil {
257
n.endpoints.httpUpstreams = !errors.Is(err, errPathNotFound)
258
n.Warning(err)
259
return
@@ -269,7 +267,7 @@ func (n *NginxPlus) queryHTTPCaches(ms *nginxMetrics) {
267
268
var v nginxHTTPCaches
269
272
- if err := n.doWithDecode(&v, req); err != nil {
270
+ if err := n.doHTTP(req, &v); err != nil {
271
n.endpoints.httpCaches = !errors.Is(err, errPathNotFound)
272
n.Warning(err)
273
return
@@ -283,7 +281,7 @@ func (n *NginxPlus) queryStreamServerZones(ms *nginxMetrics) {
281
282
var v nginxStreamServerZones
283
286
- if err := n.doWithDecode(&v, req); err != nil {
284
+ if err := n.doHTTP(req, &v); err != nil {
285
n.endpoints.streamServerZones = !errors.Is(err, errPathNotFound)
286
n.Warning(err)
287
return
@@ -297,7 +295,7 @@ func (n *NginxPlus) queryStreamUpstreams(ms *nginxMetrics) {
295
296
var v nginxStreamUpstreams
297
300
- if err := n.doWithDecode(&v, req); err != nil {
298
+ if err := n.doHTTP(req, &v); err != nil {
299
n.endpoints.streamUpstreams = !errors.Is(err, errPathNotFound)
300
n.Warning(err)
301
return
@@ -311,7 +309,7 @@ func (n *NginxPlus) queryResolvers(ms *nginxMetrics) {
309
310
var v nginxResolvers
311
314
- if err := n.doWithDecode(&v, req); err != nil {
312
+ if err := n.doHTTP(req, &v); err != nil {
313
n.endpoints.resolvers = !errors.Is(err, errPathNotFound)
314
n.Warning(err)
315
return
@@ -324,32 +322,17 @@ var (
322
errPathNotFound = errors.New("path not found")
323
)
324
327
-func (n *NginxPlus) doWithDecode(dst interface{}, req *http.Request) error {
325
+func (n *NginxPlus) doHTTP(req *http.Request, dst any) error {
326
n.Debugf("executing %s '%s'", req.Method, req.URL)
329
- resp, err := n.httpClient.Do(req)
330
- if err != nil {
331
- return err
332
- }
333
-
334
- defer web.CloseBody(resp)
335
-
336
- if resp.StatusCode == http.StatusNotFound {
337
- return fmt.Errorf("%s returned %d status code (%w)", req.URL, resp.StatusCode, errPathNotFound)
338
- }
339
- if resp.StatusCode != http.StatusOK {
340
- return fmt.Errorf("%s returned %d status code (%s)", req.URL, resp.StatusCode, resp.Status)
341
- }
327
343
- content, err := io.ReadAll(resp.Body)
344
- if err != nil {
345
- return fmt.Errorf("error on reading response from %s : %v", req.URL, err)
346
- }
347
-
348
- if err := json.Unmarshal(content, dst); err != nil {
349
- return fmt.Errorf("error on parsing response from %s : %v", req.URL, err)
350
- }
328
+ cl := web.DoHTTP(n.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
329
+ if resp.StatusCode == http.StatusNotFound {
330
+ return false, errPathNotFound
331
+ }
332
+ return false, nil
333
+ })
334
352
- return nil
335
+ return cl.RequestJSON(req, dst)
336
}
337
338
func (n *nginxMetrics) empty() bool {
src/go/plugin/go.d/modules/nginxvts/collect.go
+2
-24
@@ -3,10 +3,6 @@
3
package nginxvts
4
5
import (
6
- "encoding/json"
7
- "fmt"
8
- "net/http"
9
-
6
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
7
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
8
)
@@ -47,28 +43,10 @@ func (vts *NginxVTS) scapeVTS() (*vtsMetrics, error) {
43
req, _ := web.NewHTTPRequest(vts.RequestConfig)
44
45
var total vtsMetrics
50
-
51
- if err := vts.doOKDecode(req, &total); err != nil {
46
+ if err := web.DoHTTP(vts.httpClient).RequestJSON(req, &total); err != nil {
47
vts.Warning(err)
48
return nil, err
49
}
55
- return &total, nil
56
-}
57
-
58
-func (vts *NginxVTS) doOKDecode(req *http.Request, in interface{}) error {
59
- resp, err := vts.httpClient.Do(req)
60
- if err != nil {
61
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
62
- }
63
-
64
- defer web.CloseBody(resp)
65
-
66
- if resp.StatusCode != http.StatusOK {
67
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
68
- }
50
70
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
71
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
72
- }
73
- return nil
51
+ return &total, nil
52
}
src/go/plugin/go.d/modules/phpdaemon/client.go
deleted
-70
@@ -1,70 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package phpdaemon
4
-
5
-import (
6
- "encoding/json"
7
- "fmt"
8
- "io"
9
- "net/http"
10
-
11
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
12
-)
13
-
14
-type decodeFunc func(dst interface{}, reader io.Reader) error
15
-
16
-func decodeJson(dst interface{}, reader io.Reader) error { return json.NewDecoder(reader).Decode(dst) }
17
-
18
-func newAPIClient(httpClient *http.Client, request web.RequestConfig) *client {
19
- return &client{
20
- httpClient: httpClient,
21
- request: request,
22
- }
23
-}
24
-
25
-type client struct {
26
- httpClient *http.Client
27
- request web.RequestConfig
28
-}
29
-
30
-func (c *client) queryFullStatus() (*FullStatus, error) {
31
- var status FullStatus
32
- err := c.doWithDecode(&status, decodeJson, c.request)
33
- if err != nil {
34
- return nil, err
35
- }
36
-
37
- return &status, nil
38
-}
39
-
40
-func (c *client) doWithDecode(dst interface{}, decode decodeFunc, request web.RequestConfig) error {
41
- req, err := web.NewHTTPRequest(request)
42
- if err != nil {
43
- return fmt.Errorf("error on creating http request to %s : %v", request.URL, err)
44
- }
45
-
46
- resp, err := c.doOK(req)
47
- defer web.CloseBody(resp)
48
- if err != nil {
49
- return err
50
- }
51
-
52
- if err = decode(dst, resp.Body); err != nil {
53
- return fmt.Errorf("error on parsing response from %s : %v", req.URL, err)
54
- }
55
-
56
- return nil
57
-}
58
-
59
-func (c *client) doOK(req *http.Request) (*http.Response, error) {
60
- resp, err := c.httpClient.Do(req)
61
- if err != nil {
62
- return resp, fmt.Errorf("error on request : %v", err)
63
- }
64
-
65
- if resp.StatusCode != http.StatusOK {
66
- return resp, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
67
- }
68
-
69
- return resp, err
70
-}
src/go/plugin/go.d/modules/phpdaemon/collect.go
+48
-5
@@ -2,18 +2,61 @@
2
3
package phpdaemon
4
5
-import "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
5
+import (
6
+ "fmt"
7
7
-func (p *PHPDaemon) collect() (map[string]int64, error) {
8
- s, err := p.client.queryFullStatus()
8
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
9
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
10
+)
11
+
12
+// https://github.com/kakserpom/phpdaemon/blob/master/PHPDaemon/Core/Daemon.php
13
+// see getStateOfWorkers()
14
+
15
+type fullStatus struct {
16
+ // Alive is sum of Idle, Busy and Reloading
17
+ Alive int64 `json:"alive" stm:"alive"`
18
+ Shutdown int64 `json:"shutdown" stm:"shutdown"`
19
+
20
+ // Idle that the worker is not in the middle of execution valuable callback (e.g. request) at this moment of time.
21
+ // It does not mean that worker not have any pending operations.
22
+ // Idle is sum of Preinit, Init and Initialized.
23
+ Idle int64 `json:"idle" stm:"idle"`
24
+ // Busy means that the worker is in the middle of execution valuable callback.
25
+ Busy int64 `json:"busy" stm:"busy"`
26
+ Reloading int64 `json:"reloading" stm:"reloading"`
27
+
28
+ Preinit int64 `json:"preinit" stm:"preinit"`
29
+ // Init means that worker is starting right now.
30
+ Init int64 `json:"init" stm:"init"`
31
+ // Initialized means that the worker is in Idle state.
32
+ Initialized int64 `json:"initialized" stm:"initialized"`
33
+
34
+ Uptime *int64 `json:"uptime" stm:"uptime"`
35
+}
36
37
+func (p *PHPDaemon) collect() (map[string]int64, error) {
38
+ req, err := web.NewHTTPRequest(p.RequestConfig)
39
if err != nil {
40
+ return nil, fmt.Errorf("failed to create HTTP request to '%s': %w", p.URL, err)
41
+ }
42
+
43
+ var st fullStatus
44
+
45
+ if err := web.DoHTTP(p.httpClient).RequestJSON(req, &st); err != nil {
46
return nil, err
47
}
48
49
// https://github.com/kakserpom/phpdaemon/blob/master/PHPDaemon/Core/Daemon.php
50
// see getStateOfWorkers()
16
- s.Initialized = s.Idle - (s.Init + s.Preinit)
51
+ st.Initialized = st.Idle - (st.Init + st.Preinit)
52
+
53
+ mx := stm.ToMap(st)
54
+
55
+ p.once.Do(func() {
56
+ if _, ok := mx["uptime"]; ok {
57
+ _ = p.charts.Add(uptimeChart.Copy())
58
+ }
59
+ })
60
18
- return stm.ToMap(s), nil
61
+ return mx, nil
62
}
src/go/plugin/go.d/modules/phpdaemon/init.go
deleted
-27
@@ -1,27 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package phpdaemon
4
-
5
-import (
6
- "errors"
7
-
8
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
9
-)
10
-
11
-func (p *PHPDaemon) validateConfig() error {
12
- if p.URL == "" {
13
- return errors.New("url not set")
14
- }
15
- if _, err := web.NewHTTPRequest(p.RequestConfig); err != nil {
16
- return err
17
- }
18
- return nil
19
-}
20
-
21
-func (p *PHPDaemon) initClient() (*client, error) {
22
- httpClient, err := web.NewHTTPClient(p.ClientConfig)
23
- if err != nil {
24
- return nil, err
25
- }
26
- return newAPIClient(httpClient, p.RequestConfig), nil
27
-}
src/go/plugin/go.d/modules/phpdaemon/metrics.go
deleted
-33
@@ -1,33 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package phpdaemon
4
-
5
-// https://github.com/kakserpom/phpdaemon/blob/master/PHPDaemon/Core/Daemon.php
6
-// see getStateOfWorkers()
7
-
8
-// WorkerState represents phpdaemon worker state.
9
-type WorkerState struct {
10
- // Alive is sum of Idle, Busy and Reloading
11
- Alive int64 `stm:"alive"`
12
- Shutdown int64 `stm:"shutdown"`
13
-
14
- // Idle that the worker is not in the middle of execution valuable callback (e.g. request) at this moment of time.
15
- // It does not mean that worker not have any pending operations.
16
- // Idle is sum of Preinit, Init and Initialized.
17
- Idle int64 `stm:"idle"`
18
- // Busy means that the worker is in the middle of execution valuable callback.
19
- Busy int64 `stm:"busy"`
20
- Reloading int64 `stm:"reloading"`
21
-
22
- Preinit int64 `stm:"preinit"`
23
- // Init means that worker is starting right now.
24
- Init int64 `stm:"init"`
25
- // Initialized means that the worker is in Idle state.
26
- Initialized int64 `stm:"initialized"`
27
-}
28
-
29
-// FullStatus FullStatus.
30
-type FullStatus struct {
31
- WorkerState `stm:""`
32
- Uptime *int64 `stm:"uptime"`
33
-}
src/go/plugin/go.d/modules/phpdaemon/phpdaemon.go
+13
-16
@@ -5,6 +5,9 @@ package phpdaemon
5
import (
6
_ "embed"
7
"errors"
8
+ "fmt"
9
+ "net/http"
10
+ "sync"
11
"time"
12
13
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -49,8 +52,9 @@ type PHPDaemon struct {
52
Config `yaml:",inline" json:""`
53
54
charts *Charts
55
+ once sync.Once
56
53
- client *client
57
+ httpClient *http.Client
58
}
59
60
func (p *PHPDaemon) Configuration() any {
@@ -58,17 +62,15 @@ func (p *PHPDaemon) Configuration() any {
62
}
63
64
func (p *PHPDaemon) Init() error {
61
- if err := p.validateConfig(); err != nil {
62
- p.Error(err)
63
- return err
65
+ if p.URL == "" {
66
+ return errors.New("phpDaemon URL is required but not set")
67
}
68
66
- c, err := p.initClient()
69
+ httpClient, err := web.NewHTTPClient(p.ClientConfig)
70
if err != nil {
68
- p.Error(err)
69
- return err
71
+ return fmt.Errorf("failed to initialize http client: %w", err)
72
}
71
- p.client = c
73
+ p.httpClient = httpClient
74
75
p.Debugf("using URL %s", p.URL)
76
p.Debugf("using timeout: %s", p.Timeout)
@@ -79,17 +81,13 @@ func (p *PHPDaemon) Init() error {
81
func (p *PHPDaemon) Check() error {
82
mx, err := p.collect()
83
if err != nil {
82
- p.Error(err)
84
return err
85
}
86
+
87
if len(mx) == 0 {
88
return errors.New("no metrics collected")
89
}
90
89
- if _, ok := mx["uptime"]; ok {
90
- _ = p.charts.Add(uptimeChart.Copy())
91
- }
92
-
91
return nil
92
}
93
@@ -99,7 +97,6 @@ func (p *PHPDaemon) Charts() *Charts {
97
98
func (p *PHPDaemon) Collect() map[string]int64 {
99
mx, err := p.collect()
102
-
100
if err != nil {
101
p.Error(err)
102
return nil
@@ -109,7 +106,7 @@ func (p *PHPDaemon) Collect() map[string]int64 {
106
}
107
108
func (p *PHPDaemon) Cleanup() {
112
- if p.client != nil && p.client.httpClient != nil {
113
- p.client.httpClient.CloseIdleConnections()
109
+ if p.httpClient != nil {
110
+ p.httpClient.CloseIdleConnections()
111
}
112
}
src/go/plugin/go.d/modules/phpdaemon/phpdaemon_test.go
-1
@@ -39,7 +39,6 @@ func TestPHPDaemon_Init(t *testing.T) {
39
job := New()
40
41
require.NoError(t, job.Init())
42
- assert.NotNil(t, job.client)
42
}
43
44
func TestPHPDaemon_Check(t *testing.T) {
src/go/plugin/go.d/modules/phpfpm/client.go
+6
-16
@@ -77,25 +77,15 @@ func newHTTPClient(c *http.Client, r web.RequestConfig) (*httpClient, error) {
77
func (c *httpClient) getStatus() (*status, error) {
78
req, err := web.NewHTTPRequest(c.req)
79
if err != nil {
80
- return nil, fmt.Errorf("error on creating HTTP request: %v", err)
81
- }
82
-
83
- resp, err := c.client.Do(req)
84
- if err != nil {
85
- return nil, fmt.Errorf("error on HTTP request to '%s': %v", req.URL, err)
86
- }
87
- defer func() {
88
- _, _ = io.Copy(io.Discard, resp.Body)
89
- _ = resp.Body.Close()
90
- }()
91
-
92
- if resp.StatusCode != http.StatusOK {
93
- return nil, fmt.Errorf("%s returned HTTP status %d", req.URL, resp.StatusCode)
80
+ return nil, fmt.Errorf("failed to create HTTP request: %v", err)
81
}
82
83
st := &status{}
97
- if err := c.dec(resp.Body, st); err != nil {
98
- return nil, fmt.Errorf("error parsing HTTP response from '%s': %v", req.URL, err)
84
+
85
+ if err := web.DoHTTP(c.client).Request(req, func(body io.Reader) error {
86
+ return c.dec(body, st)
87
+ }); err != nil {
88
+ return nil, err
89
}
90
91
return st, nil
src/go/plugin/go.d/modules/pihole/collect.go
+20
-28
@@ -4,6 +4,7 @@ package pihole
4
5
import (
6
"encoding/json"
7
+ "errors"
8
"fmt"
9
"io"
10
"net/http"
@@ -143,7 +144,7 @@ func (p *Pihole) querySummary(pmx *piholeMetrics) {
144
}.Encode()
145
146
var v summaryRawMetrics
146
- if err = p.doWithDecode(&v, req); err != nil {
147
+ if err = p.doHTTP(req, &v); err != nil {
148
p.Error(err)
149
return
150
}
@@ -164,7 +165,7 @@ func (p *Pihole) queryQueryTypes(pmx *piholeMetrics) {
165
}.Encode()
166
167
var v queryTypesMetrics
167
- err = p.doWithDecode(&v, req)
168
+ err = p.doHTTP(req, &v)
169
if err != nil {
170
p.Error(err)
171
return
@@ -186,7 +187,7 @@ func (p *Pihole) queryForwardedDestinations(pmx *piholeMetrics) {
187
}.Encode()
188
189
var v forwardDestinations
189
- err = p.doWithDecode(&v, req)
190
+ err = p.doHTTP(req, &v)
191
if err != nil {
192
p.Error(err)
193
return
@@ -207,7 +208,7 @@ func (p *Pihole) queryAPIVersion() (int, error) {
208
}.Encode()
209
210
var v piholeAPIVersion
210
- err = p.doWithDecode(&v, req)
211
+ err = p.doHTTP(req, &v)
212
if err != nil {
213
return 0, err
214
}
@@ -215,33 +216,24 @@ func (p *Pihole) queryAPIVersion() (int, error) {
216
return v.Version, nil
217
}
218
218
-func (p *Pihole) doWithDecode(dst interface{}, req *http.Request) error {
219
- resp, err := p.httpClient.Do(req)
220
- if err != nil {
221
- return err
222
- }
223
-
224
- defer web.CloseBody(resp)
225
-
226
- if resp.StatusCode != http.StatusOK {
227
- return fmt.Errorf("%s returned %d status code", req.URL, resp.StatusCode)
228
- }
229
-
230
- content, err := io.ReadAll(resp.Body)
231
- if err != nil {
232
- return fmt.Errorf("error on reading response from %s : %v", req.URL, err)
233
- }
219
+func (p *Pihole) doHTTP(req *http.Request, dst any) error {
220
+ return web.DoHTTP(p.httpClient).Request(req, func(body io.Reader) error {
221
+ content, err := io.ReadAll(body)
222
+ if err != nil {
223
+ return fmt.Errorf("failed to read response: %v", err)
224
+ }
225
235
- // empty array if unauthorized query or wrong query
236
- if isEmptyArray(content) {
237
- return fmt.Errorf("unauthorized access to %s", req.URL)
238
- }
226
+ // empty array if unauthorized query or wrong query
227
+ if isEmptyArray(content) {
228
+ return errors.New("unauthorized access")
229
+ }
230
240
- if err := json.Unmarshal(content, dst); err != nil {
241
- return fmt.Errorf("error on parsing response from %s : %v", req.URL, err)
242
- }
231
+ if err := json.Unmarshal(content, dst); err != nil {
232
+ return fmt.Errorf("failed to decode JSON response: %v", err)
233
+ }
234
244
- return nil
235
+ return nil
236
+ })
237
}
238
239
func isEmptyArray(data []byte) bool {
src/go/plugin/go.d/modules/powerdns/collect.go
+3
-24
@@ -3,10 +3,7 @@
3
package powerdns
4
5
import (
6
- "encoding/json"
6
"errors"
8
- "fmt"
9
- "net/http"
7
"strconv"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
@@ -66,28 +63,10 @@ func (ns *AuthoritativeNS) collectStatistics(collected map[string]int64, statist
63
func (ns *AuthoritativeNS) scrapeStatistics() ([]statisticMetric, error) {
64
req, _ := web.NewHTTPRequestWithPath(ns.RequestConfig, urlPathLocalStatistics)
65
69
- var statistics statisticMetrics
70
- if err := ns.doOKDecode(req, &statistics); err != nil {
66
+ var stats statisticMetrics
67
+ if err := web.DoHTTP(ns.httpClient).RequestJSON(req, &stats); err != nil {
68
return nil, err
69
}
70
74
- return statistics, nil
75
-}
76
-
77
-func (ns *AuthoritativeNS) doOKDecode(req *http.Request, in interface{}) error {
78
- resp, err := ns.httpClient.Do(req)
79
- if err != nil {
80
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
81
- }
82
-
83
- defer web.CloseBody(resp)
84
-
85
- if resp.StatusCode != http.StatusOK {
86
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
87
- }
88
-
89
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
90
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
91
- }
92
- return nil
71
+ return stats, nil
72
}
src/go/plugin/go.d/modules/powerdns_recursor/collect.go
+3
-24
@@ -3,10 +3,7 @@
3
package powerdns_recursor
4
5
import (
6
- "encoding/json"
6
"errors"
8
- "fmt"
9
- "net/http"
7
"strconv"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
@@ -66,28 +63,10 @@ func (r *Recursor) collectStatistics(collected map[string]int64, statistics stat
63
func (r *Recursor) scrapeStatistics() ([]statisticMetric, error) {
64
req, _ := web.NewHTTPRequestWithPath(r.RequestConfig, urlPathLocalStatistics)
65
69
- var statistics statisticMetrics
70
- if err := r.doOKDecode(req, &statistics); err != nil {
66
+ var stats statisticMetrics
67
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
68
return nil, err
69
}
70
74
- return statistics, nil
75
-}
76
-
77
-func (r *Recursor) doOKDecode(req *http.Request, in interface{}) error {
78
- resp, err := r.httpClient.Do(req)
79
- if err != nil {
80
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
81
- }
82
-
83
- defer web.CloseBody(resp)
84
-
85
- if resp.StatusCode != http.StatusOK {
86
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
87
- }
88
-
89
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
90
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
91
- }
92
- return nil
71
+ return stats, nil
72
}
src/go/plugin/go.d/modules/puppet/collect.go
+1
-21
@@ -3,9 +3,7 @@
3
package puppet
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
"net/url"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
@@ -38,7 +36,7 @@ func (p *Puppet) queryStatsService() (*statusServiceResponse, error) {
36
req.URL.RawQuery = urlQueryStatusService
37
38
var stats statusServiceResponse
41
- if err := p.doOKDecode(req, &stats); err != nil {
39
+ if err := web.DoHTTP(p.httpClient).RequestJSON(req, &stats); err != nil {
40
return nil, err
41
}
42
@@ -48,21 +46,3 @@ func (p *Puppet) queryStatsService() (*statusServiceResponse, error) {
46
47
return &stats, nil
48
}
51
-
52
-func (p *Puppet) doOKDecode(req *http.Request, in interface{}) error {
53
- resp, err := p.httpClient.Do(req)
54
- if err != nil {
55
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
56
- }
57
-
58
- defer web.CloseBody(resp)
59
-
60
- if resp.StatusCode != http.StatusOK {
61
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
62
- }
63
-
64
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
65
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
66
- }
67
- return nil
68
-}
src/go/plugin/go.d/modules/rabbitmq/collect.go
+24
-31
@@ -3,9 +3,7 @@
3
package rabbitmq
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
"path/filepath"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
@@ -43,8 +41,13 @@ func (r *RabbitMQ) collect() (map[string]int64, error) {
41
}
42
43
func (r *RabbitMQ) collectOverviewStats(mx map[string]int64) error {
44
+ req, err := web.NewHTTPRequestWithPath(r.RequestConfig, urlPathAPIOverview)
45
+ if err != nil {
46
+ return fmt.Errorf("failed to create overview stats request: %w", err)
47
+ }
48
+
49
var stats overviewStats
47
- if err := r.doOKDecode(urlPathAPIOverview, &stats); err != nil {
50
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
51
return err
52
}
53
@@ -64,8 +67,13 @@ func (r *RabbitMQ) collectNodeStats(mx map[string]int64) error {
67
return nil
68
}
69
70
+ req, err := web.NewHTTPRequestWithPath(r.RequestConfig, filepath.Join(urlPathAPINodes, r.nodeName))
71
+ if err != nil {
72
+ return fmt.Errorf("failed to create node stats request: %w", err)
73
+ }
74
+
75
var stats nodeStats
68
- if err := r.doOKDecode(filepath.Join(urlPathAPINodes, r.nodeName), &stats); err != nil {
76
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
77
return err
78
}
79
@@ -78,8 +86,13 @@ func (r *RabbitMQ) collectNodeStats(mx map[string]int64) error {
86
}
87
88
func (r *RabbitMQ) collectVhostsStats(mx map[string]int64) error {
89
+ req, err := web.NewHTTPRequestWithPath(r.RequestConfig, urlPathAPIVhosts)
90
+ if err != nil {
91
+ return fmt.Errorf("failed to create vhosts stats request: %w", err)
92
+ }
93
+
94
var stats []vhostStats
82
- if err := r.doOKDecode(urlPathAPIVhosts, &stats); err != nil {
95
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
96
return err
97
}
98
@@ -111,8 +124,13 @@ func (r *RabbitMQ) collectVhostsStats(mx map[string]int64) error {
124
}
125
126
func (r *RabbitMQ) collectQueuesStats(mx map[string]int64) error {
127
+ req, err := web.NewHTTPRequestWithPath(r.RequestConfig, urlPathAPIQueues)
128
+ if err != nil {
129
+ return fmt.Errorf("failed to create queues stats request: %w", err)
130
+ }
131
+
132
var stats []queueStats
115
- if err := r.doOKDecode(urlPathAPIQueues, &stats); err != nil {
133
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
134
return err
135
}
136
@@ -142,28 +160,3 @@ func (r *RabbitMQ) collectQueuesStats(mx map[string]int64) error {
160
161
return nil
162
}
145
-
146
-func (r *RabbitMQ) doOKDecode(urlPath string, in interface{}) error {
147
- req, err := web.NewHTTPRequestWithPath(r.RequestConfig, urlPath)
148
- if err != nil {
149
- return fmt.Errorf("error on creating request: %v", err)
150
- }
151
-
152
- r.Debugf("doing HTTPConfig %s to '%s'", req.Method, req.URL)
153
- resp, err := r.httpClient.Do(req)
154
- if err != nil {
155
- return fmt.Errorf("error on request to %s: %v", req.URL, err)
156
- }
157
-
158
- defer web.CloseBody(resp)
159
-
160
- if resp.StatusCode != http.StatusOK {
161
- return fmt.Errorf("%s returned HTTP status %d (%s)", req.URL, resp.StatusCode, resp.Status)
162
- }
163
-
164
- if err = json.NewDecoder(resp.Body).Decode(&in); err != nil {
165
- return fmt.Errorf("error on decoding response from %s: %v", req.URL, err)
166
- }
167
-
168
- return nil
169
-}
src/go/plugin/go.d/modules/riakkv/collect.go
+6
-22
@@ -3,9 +3,7 @@
3
package riakkv
4
5
import (
6
- "encoding/json"
6
"errors"
8
- "fmt"
7
"net/http"
8
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
@@ -36,32 +34,18 @@ func (r *RiakKv) getStats() (*riakStats, error) {
34
}
35
36
var stats riakStats
39
- if err := r.doOKDecode(req, &stats); err != nil {
37
+ if err := r.client().RequestJSON(req, &stats); err != nil {
38
return nil, err
39
}
40
41
return &stats, nil
42
}
43
46
-func (r *RiakKv) doOKDecode(req *http.Request, in interface{}) error {
47
- resp, err := r.httpClient.Do(req)
48
- if err != nil {
49
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
50
- }
51
-
52
- defer web.CloseBody(resp)
53
-
54
- if resp.StatusCode != http.StatusOK {
55
- msg := fmt.Sprintf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
44
+func (r *RiakKv) client() *web.Client {
45
+ return web.DoHTTP(r.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
46
if resp.StatusCode == http.StatusNotFound {
57
- msg = fmt.Sprintf("%s (riak_kv_stat is not enabled)", msg)
47
+ return false, errors.New("riak_kv_stat is not enabled)")
48
}
59
- return errors.New(msg)
60
- }
61
-
62
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
63
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
64
- }
65
-
66
- return nil
49
+ return false, nil
50
+ })
51
}
src/go/plugin/go.d/modules/rspamd/collect.go
+1
-21
@@ -3,9 +3,7 @@
3
package rspamd
4
5
import (
6
- "encoding/json"
6
"fmt"
8
- "net/http"
7
8
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
@@ -55,7 +53,7 @@ func (r *Rspamd) queryRspamdStats() (*rspamdStats, error) {
53
}
54
55
var stats rspamdStats
58
- if err := r.doOKDecode(req, &stats); err != nil {
56
+ if err := web.DoHTTP(r.httpClient).RequestJSON(req, &stats); err != nil {
57
return nil, err
58
}
59
@@ -65,21 +63,3 @@ func (r *Rspamd) queryRspamdStats() (*rspamdStats, error) {
63
64
return &stats, nil
65
}
68
-
69
-func (r *Rspamd) doOKDecode(req *http.Request, in interface{}) error {
70
- resp, err := r.httpClient.Do(req)
71
- if err != nil {
72
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
73
- }
74
-
75
- defer web.CloseBody(resp)
76
-
77
- if resp.StatusCode != http.StatusOK {
78
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
79
- }
80
-
81
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
82
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
83
- }
84
- return nil
85
-}
src/go/plugin/go.d/modules/squid/collect.go
+7
-27
@@ -6,7 +6,6 @@ import (
6
"bufio"
7
"fmt"
8
"io"
9
- "net/http"
9
"strconv"
10
"strings"
11
@@ -44,10 +43,10 @@ func (s *Squid) collect() (map[string]int64, error) {
43
func (s *Squid) collectCounters(mx map[string]int64) error {
44
req, err := web.NewHTTPRequestWithPath(s.RequestConfig, urlPathServerStats)
45
if err != nil {
47
- return err
46
+ return fmt.Errorf("failed to create '%s' request: %w", urlPathServerStats, err)
47
}
48
50
- if err := s.doOK(req, func(body io.Reader) error {
49
+ return web.DoHTTP(s.httpClient).Request(req, func(body io.Reader) error {
50
sc := bufio.NewScanner(body)
51
52
for sc.Scan() {
@@ -70,29 +69,10 @@ func (s *Squid) collectCounters(mx map[string]int64) error {
69
70
mx[key] = v
71
}
73
- return nil
74
- }); err != nil {
75
- return err
76
- }
77
-
78
- if len(mx) == 0 {
79
- return fmt.Errorf("unexpected response from '%s': no metrics found", req.URL)
80
- }
81
-
82
- return nil
83
-}
84
-
85
-func (s *Squid) doOK(req *http.Request, parse func(body io.Reader) error) error {
86
- resp, err := s.httpClient.Do(req)
87
- if err != nil {
88
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
89
- }
72
91
- defer web.CloseBody(resp)
92
-
93
- if resp.StatusCode != http.StatusOK {
94
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
95
- }
96
-
97
- return parse(resp.Body)
73
+ if len(mx) == 0 {
74
+ return fmt.Errorf("unexpected response from '%s': no metrics found", req.URL)
75
+ }
76
+ return nil
77
+ })
78
}
src/go/plugin/go.d/modules/tengine/collect.go
+19
-1
@@ -3,20 +3,38 @@
3
package tengine
4
5
import (
6
+ "fmt"
7
+ "io"
8
+
9
"github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/stm"
10
+ "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
11
)
12
13
func (t *Tengine) collect() (map[string]int64, error) {
10
- status, err := t.apiClient.getStatus()
14
+ req, err := web.NewHTTPRequest(t.RequestConfig)
15
if err != nil {
16
+ return nil, fmt.Errorf("failed to create HTTP request: %w", err)
17
+ }
18
+
19
+ var status *tengineStatus
20
+ var perr error
21
+
22
+ if err := web.DoHTTP(t.httpClient).Request(req, func(body io.Reader) error {
23
+ if status, perr = parseStatus(body); perr != nil {
24
+ return perr
25
+ }
26
+ return nil
27
+ }); err != nil {
28
return nil, err
29
}
30
31
mx := make(map[string]int64)
32
+
33
for _, m := range *status {
34
for k, v := range stm.ToMap(m) {
35
mx[k] += v
36
}
37
}
38
+
39
return mx, nil
40
}
src/go/plugin/go.d/modules/tengine/metrics.go
deleted
-75
@@ -1,75 +0,0 @@
1
-// SPDX-License-Identifier: GPL-3.0-or-later
2
-
3
-package tengine
4
-
5
-/*
6
-http://tengine.taobao.org/document/http_reqstat.html
7
-
8
-bytes_in total number of bytes received from client
9
-bytes_out total number of bytes sent to client
10
-conn_total total number of accepted connections
11
-req_total total number of processed requests
12
-http_2xx total number of 2xx requests
13
-http_3xx total number of 3xx requests
14
-http_4xx total number of 4xx requests
15
-http_5xx total number of 5xx requests
16
-http_other_status total number of other requests
17
-rt accumulation or rt
18
-ups_req total number of requests calling for upstream
19
-ups_rt accumulation or upstream rt
20
-ups_tries total number of times calling for upstream
21
-http_200 total number of 200 requests
22
-http_206 total number of 206 requests
23
-http_302 total number of 302 requests
24
-http_304 total number of 304 requests
25
-http_403 total number of 403 requests
26
-http_404 total number of 404 requests
27
-http_416 total number of 416 requests
28
-http_499 total number of 499 requests
29
-http_500 total number of 500 requests
30
-http_502 total number of 502 requests
31
-http_503 total number of 503 requests
32
-http_504 total number of 504 requests
33
-http_508 total number of 508 requests
34
-http_other_detail_status total number of requests of other status codes
35
-http_ups_4xx total number of requests of upstream 4xx
36
-http_ups_5xx total number of requests of upstream 5xx
37
-*/
38
-
39
-type (
40
- tengineStatus []metric
41
-
42
- metric struct {
43
- Host string
44
- ServerAddress string
45
- BytesIn *int64 `stm:"bytes_in"`
46
- BytesOut *int64 `stm:"bytes_out"`
47
- ConnTotal *int64 `stm:"conn_total"`
48
- ReqTotal *int64 `stm:"req_total"`
49
- HTTP2xx *int64 `stm:"http_2xx"`
50
- HTTP3xx *int64 `stm:"http_3xx"`
51
- HTTP4xx *int64 `stm:"http_4xx"`
52
- HTTP5xx *int64 `stm:"http_5xx"`
53
- HTTPOtherStatus *int64 `stm:"http_other_status"`
54
- RT *int64 `stm:"rt"`
55
- UpsReq *int64 `stm:"ups_req"`
56
- UpsRT *int64 `stm:"ups_rt"`
57
- UpsTries *int64 `stm:"ups_tries"`
58
- HTTP200 *int64 `stm:"http_200"`
59
- HTTP206 *int64 `stm:"http_206"`
60
- HTTP302 *int64 `stm:"http_302"`
61
- HTTP304 *int64 `stm:"http_304"`
62
- HTTP403 *int64 `stm:"http_403"`
63
- HTTP404 *int64 `stm:"http_404"`
64
- HTTP416 *int64 `stm:"http_416"`
65
- HTTP499 *int64 `stm:"http_499"`
66
- HTTP500 *int64 `stm:"http_500"`
67
- HTTP502 *int64 `stm:"http_502"`
68
- HTTP503 *int64 `stm:"http_503"`
69
- HTTP504 *int64 `stm:"http_504"`
70
- HTTP508 *int64 `stm:"http_508"`
71
- HTTPOtherDetailStatus *int64 `stm:"http_other_detail_status"`
72
- HTTPUps4xx *int64 `stm:"http_ups_4xx"`
73
- HTTPUps5xx *int64 `stm:"http_ups_5xx"`
74
- }
75
-)
src/go/plugin/go.d/modules/tengine/status.go
renamed
+71
-42
@@ -6,11 +6,80 @@ import (
6
"bufio"
7
"fmt"
8
"io"
9
- "net/http"
9
"strconv"
10
"strings"
11
+)
12
+
13
+/*
14
+http://tengine.taobao.org/document/http_reqstat.html
15
+
16
+bytes_in total number of bytes received from client
17
+bytes_out total number of bytes sent to client
18
+conn_total total number of accepted connections
19
+req_total total number of processed requests
20
+http_2xx total number of 2xx requests
21
+http_3xx total number of 3xx requests
22
+http_4xx total number of 4xx requests
23
+http_5xx total number of 5xx requests
24
+http_other_status total number of other requests
25
+rt accumulation or rt
26
+ups_req total number of requests calling for upstream
27
+ups_rt accumulation or upstream rt
28
+ups_tries total number of times calling for upstream
29
+http_200 total number of 200 requests
30
+http_206 total number of 206 requests
31
+http_302 total number of 302 requests
32
+http_304 total number of 304 requests
33
+http_403 total number of 403 requests
34
+http_404 total number of 404 requests
35
+http_416 total number of 416 requests
36
+http_499 total number of 499 requests
37
+http_500 total number of 500 requests
38
+http_502 total number of 502 requests
39
+http_503 total number of 503 requests
40
+http_504 total number of 504 requests
41
+http_508 total number of 508 requests
42
+http_other_detail_status total number of requests of other status codes
43
+http_ups_4xx total number of requests of upstream 4xx
44
+http_ups_5xx total number of requests of upstream 5xx
45
+*/
46
+
47
+type (
48
+ tengineStatus []metric
49
13
- "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web"
50
+ metric struct {
51
+ Host string
52
+ ServerAddress string
53
+ BytesIn *int64 `stm:"bytes_in"`
54
+ BytesOut *int64 `stm:"bytes_out"`
55
+ ConnTotal *int64 `stm:"conn_total"`
56
+ ReqTotal *int64 `stm:"req_total"`
57
+ HTTP2xx *int64 `stm:"http_2xx"`
58
+ HTTP3xx *int64 `stm:"http_3xx"`
59
+ HTTP4xx *int64 `stm:"http_4xx"`
60
+ HTTP5xx *int64 `stm:"http_5xx"`
61
+ HTTPOtherStatus *int64 `stm:"http_other_status"`
62
+ RT *int64 `stm:"rt"`
63
+ UpsReq *int64 `stm:"ups_req"`
64
+ UpsRT *int64 `stm:"ups_rt"`
65
+ UpsTries *int64 `stm:"ups_tries"`
66
+ HTTP200 *int64 `stm:"http_200"`
67
+ HTTP206 *int64 `stm:"http_206"`
68
+ HTTP302 *int64 `stm:"http_302"`
69
+ HTTP304 *int64 `stm:"http_304"`
70
+ HTTP403 *int64 `stm:"http_403"`
71
+ HTTP404 *int64 `stm:"http_404"`
72
+ HTTP416 *int64 `stm:"http_416"`
73
+ HTTP499 *int64 `stm:"http_499"`
74
+ HTTP500 *int64 `stm:"http_500"`
75
+ HTTP502 *int64 `stm:"http_502"`
76
+ HTTP503 *int64 `stm:"http_503"`
77
+ HTTP504 *int64 `stm:"http_504"`
78
+ HTTP508 *int64 `stm:"http_508"`
79
+ HTTPOtherDetailStatus *int64 `stm:"http_other_detail_status"`
80
+ HTTPUps4xx *int64 `stm:"http_ups_4xx"`
81
+ HTTPUps5xx *int64 `stm:"http_ups_5xx"`
82
+ }
83
)
84
85
const (
@@ -77,46 +146,6 @@ var defaultLineFormat = []string{
146
httpUps5xx,
147
}
148
80
-func newAPIClient(client *http.Client, request web.RequestConfig) *apiClient {
81
- return &apiClient{httpClient: client, request: request}
82
-}
83
-
84
-type apiClient struct {
85
- httpClient *http.Client
86
- request web.RequestConfig
87
-}
88
-
89
-func (a apiClient) getStatus() (*tengineStatus, error) {
90
- req, err := web.NewHTTPRequest(a.request)
91
- if err != nil {
92
- return nil, fmt.Errorf("error on creating request : %v", err)
93
- }
94
-
95
- resp, err := a.doRequestOK(req)
96
- defer web.CloseBody(resp)
97
- if err != nil {
98
- return nil, err
99
- }
100
-
101
- status, err := parseStatus(resp.Body)
102
- if err != nil {
103
- return nil, fmt.Errorf("error on parsing response : %v", err)
104
- }
105
-
106
- return status, nil
107
-}
108
-
109
-func (a apiClient) doRequestOK(req *http.Request) (*http.Response, error) {
110
- resp, err := a.httpClient.Do(req)
111
- if err != nil {
112
- return nil, fmt.Errorf("error on request : %v", err)
113
- }
114
- if resp.StatusCode != http.StatusOK {
115
- return resp, fmt.Errorf("%s returned HTTPConfig code %d", req.URL, resp.StatusCode)
116
- }
117
- return resp, nil
118
-}
119
-
149
func parseStatus(r io.Reader) (*tengineStatus, error) {
150
var status tengineStatus
151
src/go/plugin/go.d/modules/tengine/tengine.go
+12
-7
@@ -5,6 +5,7 @@ package tengine
5
import (
6
_ "embed"
7
"errors"
8
+ "net/http"
9
"time"
10
11
"github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module"
@@ -50,7 +51,7 @@ type Tengine struct {
51
52
charts *module.Charts
53
53
- apiClient *apiClient
54
+ httpClient *http.Client
55
}
56
57
func (t *Tengine) Configuration() any {
@@ -63,13 +64,12 @@ func (t *Tengine) Init() error {
64
return errors.New("url not set")
65
}
66
66
- client, err := web.NewHTTPClient(t.ClientConfig)
67
+ httpClient, err := web.NewHTTPClient(t.ClientConfig)
68
if err != nil {
69
t.Errorf("error on creating http client : %v", err)
70
return err
71
}
71
-
72
- t.apiClient = newAPIClient(client, t.RequestConfig)
72
+ t.httpClient = httpClient
73
74
t.Debugf("using URL: %s", t.URL)
75
t.Debugf("using timeout: %s", t.Timeout)
@@ -83,9 +83,11 @@ func (t *Tengine) Check() error {
83
t.Error(err)
84
return err
85
}
86
+
87
if len(mx) == 0 {
88
return errors.New("no metrics collected")
89
}
90
+
91
return nil
92
}
93
@@ -95,17 +97,20 @@ func (t *Tengine) Charts() *module.Charts {
97
98
func (t *Tengine) Collect() map[string]int64 {
99
mx, err := t.collect()
98
-
100
if err != nil {
101
t.Error(err)
102
return nil
103
}
104
105
+ if len(mx) == 0 {
106
+ return nil
107
+ }
108
+
109
return mx
110
}
111
112
func (t *Tengine) Cleanup() {
108
- if t.apiClient != nil && t.apiClient.httpClient != nil {
109
- t.apiClient.httpClient.CloseIdleConnections()
113
+ if t.httpClient != nil {
114
+ t.httpClient.CloseIdleConnections()
115
}
116
}
src/go/plugin/go.d/modules/tengine/tengine_test.go
-1
@@ -43,7 +43,6 @@ func TestTengine_Init(t *testing.T) {
43
job := New()
44
45
require.NoError(t, job.Init())
46
- assert.NotNil(t, job.apiClient)
46
}
47
48
func TestTengine_Check(t *testing.T) {
src/go/plugin/go.d/modules/tomcat/collect.go
+1
-23
@@ -3,10 +3,7 @@
3
package tomcat
4
5
import (
6
- "encoding/xml"
6
"errors"
8
- "fmt"
9
- "net/http"
7
"net/url"
8
"strings"
9
@@ -95,28 +92,9 @@ func (t *Tomcat) queryServerStatus() (*serverStatusResponse, error) {
92
req.URL.RawQuery = urlQueryServerStatus
93
94
var status serverStatusResponse
98
-
99
- if err := t.doOKDecode(req, &status); err != nil {
95
+ if err := web.DoHTTP(t.httpClient).RequestXML(req, &status); err != nil {
96
return nil, err
97
}
98
99
return &status, nil
100
}
105
-
106
-func (t *Tomcat) doOKDecode(req *http.Request, in interface{}) error {
107
- resp, err := t.httpClient.Do(req)
108
- if err != nil {
109
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
110
- }
111
- defer web.CloseBody(resp)
112
-
113
- if resp.StatusCode != http.StatusOK {
114
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
115
- }
116
-
117
- if err := xml.NewDecoder(resp.Body).Decode(in); err != nil {
118
- return fmt.Errorf("error decoding XML response from '%s': %v", req.URL, err)
119
- }
120
-
121
- return nil
122
-}
src/go/plugin/go.d/modules/typesense/collect.go
+8
-25
@@ -59,7 +59,7 @@ func (ts *Typesense) collectHealth(mx map[string]int64) error {
59
}
60
61
var resp healthResponse
62
- if err := ts.doOKDecode(req, &resp); err != nil {
62
+ if err := ts.client().RequestJSON(req, &resp); err != nil {
63
return err
64
}
65
@@ -95,8 +95,8 @@ func (ts *Typesense) collectStats(mx map[string]int64) error {
95
req.Header.Set("X-TYPESENSE-API-KEY", ts.APIKey)
96
97
var resp statsResponse
98
- if err := ts.doOKDecode(req, &resp); err != nil {
99
- if !isStatusUnauthorized(err) {
98
+ if err := ts.client().RequestJSON(req, &resp); err != nil {
99
+ if !strings.Contains(err.Error(), "code: 401") {
100
return err
101
}
102
@@ -115,32 +115,15 @@ func (ts *Typesense) collectStats(mx map[string]int64) error {
115
return nil
116
}
117
118
-func (ts *Typesense) doOKDecode(req *http.Request, in interface{}) error {
119
- resp, err := ts.httpClient.Do(req)
120
- if err != nil {
121
- return fmt.Errorf("error on HTTP request '%s': %v", req.URL, err)
122
- }
123
-
124
- defer web.CloseBody(resp)
125
-
126
- if resp.StatusCode != http.StatusOK {
118
+func (ts *Typesense) client() *web.Client {
119
+ return web.DoHTTP(ts.httpClient).OnNokCode(func(resp *http.Response) (bool, error) {
120
// {"message": "Forbidden - a valid `x-typesense-api-key` header must be sent."}
121
var msg struct {
122
Msg string `json:"message"`
123
}
124
if err := json.NewDecoder(resp.Body).Decode(&msg); err == nil && msg.Msg != "" {
132
- return fmt.Errorf("'%s' returned HTTP status code: %d (msg: '%s')",
133
- req.URL, resp.StatusCode, msg.Msg)
125
+ return false, fmt.Errorf("msg: '%s'", msg.Msg)
126
}
135
- return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
136
- }
137
-
138
- if err := json.NewDecoder(resp.Body).Decode(in); err != nil {
139
- return fmt.Errorf("error on decoding response from '%s': %v", req.URL, err)
140
- }
141
- return nil
142
-}
143
-
144
-func isStatusUnauthorized(err error) bool {
145
- return strings.Contains(err.Error(), "code: 401")
127
+ return false, nil
128
+ })
129
}
src/go/plugin/go.d/pkg/prometheus/client.go
+1
-4
@@ -123,10 +123,7 @@ func (p *prometheus) fetch(w io.Writer) error {
123
return err
124
}
125
126
- defer func() {
127
- _, _ = io.Copy(io.Discard, resp.Body)
128
- _ = resp.Body.Close()
129
- }()
126
+ defer web.CloseBody(resp)
127
128
if resp.StatusCode != http.StatusOK {
129
return fmt.Errorf("server '%s' returned HTTP status code %d (%s)", req.URL, resp.StatusCode, resp.Status)
src/go/plugin/go.d/pkg/web/client.go
new
+86
@@ -0,0 +1,86 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+package web
4
+
5
+import (
6
+ "encoding/json"
7
+ "encoding/xml"
8
+ "fmt"
9
+ "io"
10
+ "net/http"
11
+)
12
+
13
+type Client struct {
14
+ httpClient *http.Client
15
+ onNokCode func(resp *http.Response) (bool, error)
16
+}
17
+
18
+func DoHTTP(cl *http.Client) *Client {
19
+ return &Client{
20
+ httpClient: cl,
21
+ }
22
+}
23
+
24
+func (c *Client) OnNokCode(fn func(resp *http.Response) (bool, error)) *Client {
25
+ c.onNokCode = fn
26
+ return c
27
+}
28
+
29
+func (c *Client) RequestJSON(req *http.Request, in any) error {
30
+ return c.Request(req, func(body io.Reader) error {
31
+ return json.NewDecoder(body).Decode(in)
32
+ })
33
+}
34
+
35
+func (c *Client) RequestXML(req *http.Request, in any, opts ...func(dec *xml.Decoder)) error {
36
+ return c.Request(req, func(body io.Reader) error {
37
+ dec := xml.NewDecoder(body)
38
+ for _, opt := range opts {
39
+ opt(dec)
40
+ }
41
+ return dec.Decode(in)
42
+ })
43
+}
44
+
45
+func (c *Client) Request(req *http.Request, parse func(body io.Reader) error) error {
46
+ resp, err := c.httpClient.Do(req)
47
+ if err != nil {
48
+ return fmt.Errorf("error on HTTP request to '%s': %w", req.URL, err)
49
+ }
50
+
51
+ defer CloseBody(resp)
52
+
53
+ if resp.StatusCode != http.StatusOK {
54
+ if err := c.handleNokCode(req, resp); err != nil {
55
+ return err
56
+ }
57
+ }
58
+
59
+ if parse != nil {
60
+ if err := parse(resp.Body); err != nil {
61
+ return fmt.Errorf("error on parsing response from '%s': %w", req.URL, err)
62
+ }
63
+ }
64
+
65
+ return nil
66
+}
67
+
68
+func (c *Client) handleNokCode(req *http.Request, resp *http.Response) error {
69
+ if c.onNokCode != nil {
70
+ handled, err := c.onNokCode(resp)
71
+ if err != nil {
72
+ return fmt.Errorf("'%s' returned HTTP status code: %d (%w)", req.URL, resp.StatusCode, err)
73
+ }
74
+ if handled {
75
+ return nil
76
+ }
77
+ }
78
+ return fmt.Errorf("'%s' returned HTTP status code: %d", req.URL, resp.StatusCode)
79
+}
80
+
81
+func CloseBody(resp *http.Response) {
82
+ if resp != nil && resp.Body != nil {
83
+ _, _ = io.Copy(io.Discard, resp.Body)
84
+ _ = resp.Body.Close()
85
+ }
86
+}
src/go/plugin/go.d/pkg/web/client_config.go
-8
@@ -5,7 +5,6 @@ package web
5
import (
6
"errors"
7
"fmt"
8
- "io"
8
"net"
9
"net/http"
10
"net/url"
@@ -66,13 +65,6 @@ func NewHTTPClient(cfg ClientConfig) (*http.Client, error) {
65
}, nil
66
}
67
69
-func CloseBody(resp *http.Response) {
70
- if resp != nil && resp.Body != nil {
71
- _, _ = io.Copy(io.Discard, resp.Body)
72
- _ = resp.Body.Close()
73
- }
74
-}
75
-
68
func redirectFunc(notFollowRedirect bool) func(req *http.Request, via []*http.Request) error {
69
if follow := !notFollowRedirect; follow {
70
return nil