master
go 155 lines 3.39 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package httpcheck
4
5 import (
6 "context"
7 _ "embed"
8 "errors"
9 "fmt"
10 "net/http"
11 "regexp"
12 "time"
13
14 "github.com/netdata/netdata/go/plugins/pkg/confopt"
15 "github.com/netdata/netdata/go/plugins/pkg/web"
16 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
17 )
18
19 //go:embed "config_schema.json"
20 var configSchema string
21
22 func init() {
23 collectorapi.Register("httpcheck", collectorapi.Creator{
24 JobConfigSchema: configSchema,
25 Defaults: collectorapi.Defaults{
26 UpdateEvery: 5,
27 },
28 Create: func() collectorapi.CollectorV1 { return New() },
29 Config: func() any { return &Config{} },
30 })
31 }
32
33 func New() *Collector {
34 return &Collector{
35 Config: Config{
36 HTTPConfig: web.HTTPConfig{
37 ClientConfig: web.ClientConfig{
38 Timeout: confopt.Duration(time.Second),
39 },
40 },
41 AcceptedStatuses: []int{200},
42 },
43
44 acceptedStatuses: make(map[int]bool),
45 }
46 }
47
48 type (
49 Config struct {
50 Vnode string `yaml:"vnode,omitempty" json:"vnode"`
51 UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
52 web.HTTPConfig `yaml:",inline" json:""`
53 AcceptedStatuses []int `yaml:"status_accepted" json:"status_accepted"`
54 ResponseMatch string `yaml:"response_match,omitempty" json:"response_match"`
55 CookieFile string `yaml:"cookie_file,omitempty" json:"cookie_file"`
56 HeaderMatch []headerMatchConfig `yaml:"header_match,omitempty" json:"header_match"`
57 }
58 headerMatchConfig struct {
59 Exclude bool `yaml:"exclude" json:"exclude"`
60 Key string `yaml:"key" json:"key"`
61 Value string `yaml:"value" json:"value"`
62 }
63 )
64
65 type Collector struct {
66 collectorapi.Base
67 Config `yaml:",inline" json:""`
68
69 charts *collectorapi.Charts
70
71 httpClient *http.Client
72
73 acceptedStatuses map[int]bool
74 reResponse *regexp.Regexp
75 headerMatch []headerMatch
76 cookieFileModTime time.Time
77
78 metrics metrics
79 }
80
81 func (c *Collector) Configuration() any {
82 return c.Config
83 }
84
85 func (c *Collector) Init(context.Context) error {
86 if err := c.validateConfig(); err != nil {
87 return fmt.Errorf("config validation: %v", err)
88 }
89
90 c.charts = c.initCharts()
91
92 httpClient, err := c.initHTTPClient()
93 if err != nil {
94 return fmt.Errorf("init HTTP client: %v", err)
95 }
96 c.httpClient = httpClient
97
98 re, err := c.initResponseMatchRegexp()
99 if err != nil {
100 return fmt.Errorf("init response match regexp: %v", err)
101 }
102 c.reResponse = re
103
104 hm, err := c.initHeaderMatch()
105 if err != nil {
106 return fmt.Errorf("init header match: %v", err)
107 }
108 c.headerMatch = hm
109
110 for _, v := range c.AcceptedStatuses {
111 c.acceptedStatuses[v] = true
112 }
113
114 c.Debugf("using URL %s", c.URL)
115 c.Debugf("using HTTP timeout %s", c.Timeout.Duration())
116 c.Debugf("using accepted HTTPConfig statuses %v", c.AcceptedStatuses)
117 if c.reResponse != nil {
118 c.Debugf("using response match regexp %s", c.reResponse)
119 }
120
121 return nil
122 }
123
124 func (c *Collector) Check(context.Context) error {
125 mx, err := c.collect()
126 if err != nil {
127 return err
128 }
129 if len(mx) == 0 {
130 return errors.New("no metrics collected")
131 }
132 return nil
133 }
134
135 func (c *Collector) Charts() *collectorapi.Charts {
136 return c.charts
137 }
138
139 func (c *Collector) Collect(context.Context) map[string]int64 {
140 mx, err := c.collect()
141 if err != nil {
142 c.Error(err)
143 }
144
145 if len(mx) == 0 {
146 return nil
147 }
148 return mx
149 }
150
151 func (c *Collector) Cleanup(context.Context) {
152 if c.httpClient != nil {
153 c.httpClient.CloseIdleConnections()
154 }
155 }