Add support for interface field in http_response input plugin (#6006)
This commit is contained in:
parent
f8bef14860
commit
8d04cb76fd
|
@ -46,6 +46,9 @@ This input plugin checks HTTP/HTTPS connections.
|
||||||
## HTTP Request Headers (all values must be strings)
|
## HTTP Request Headers (all values must be strings)
|
||||||
# [inputs.http_response.headers]
|
# [inputs.http_response.headers]
|
||||||
# Host = "github.com"
|
# Host = "github.com"
|
||||||
|
|
||||||
|
## Interface to use when dialing an address
|
||||||
|
# interface = "eth0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Metrics:
|
### Metrics:
|
||||||
|
|
|
@ -31,6 +31,7 @@ type HTTPResponse struct {
|
||||||
Headers map[string]string
|
Headers map[string]string
|
||||||
FollowRedirects bool
|
FollowRedirects bool
|
||||||
ResponseStringMatch string
|
ResponseStringMatch string
|
||||||
|
Interface string
|
||||||
tls.ClientConfig
|
tls.ClientConfig
|
||||||
|
|
||||||
compiledStringMatch *regexp.Regexp
|
compiledStringMatch *regexp.Regexp
|
||||||
|
@ -82,6 +83,9 @@ var sampleConfig = `
|
||||||
## HTTP Request Headers (all values must be strings)
|
## HTTP Request Headers (all values must be strings)
|
||||||
# [inputs.http_response.headers]
|
# [inputs.http_response.headers]
|
||||||
# Host = "github.com"
|
# Host = "github.com"
|
||||||
|
|
||||||
|
## Interface to use when dialing an address
|
||||||
|
# interface = "eth0"
|
||||||
`
|
`
|
||||||
|
|
||||||
// SampleConfig returns the plugin SampleConfig
|
// SampleConfig returns the plugin SampleConfig
|
||||||
|
@ -108,16 +112,27 @@ func getProxyFunc(http_proxy string) func(*http.Request) (*url.URL, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateHttpClient creates an http client which will timeout at the specified
|
// createHttpClient creates an http client which will timeout at the specified
|
||||||
// timeout period and can follow redirects if specified
|
// timeout period and can follow redirects if specified
|
||||||
func (h *HTTPResponse) createHttpClient() (*http.Client, error) {
|
func (h *HTTPResponse) createHttpClient() (*http.Client, error) {
|
||||||
tlsCfg, err := h.ClientConfig.TLSConfig()
|
tlsCfg, err := h.ClientConfig.TLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialer := &net.Dialer{}
|
||||||
|
|
||||||
|
if h.Interface != "" {
|
||||||
|
dialer.LocalAddr, err = localAddress(h.Interface)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Proxy: getProxyFunc(h.HTTPProxy),
|
Proxy: getProxyFunc(h.HTTPProxy),
|
||||||
|
DialContext: dialer.DialContext,
|
||||||
DisableKeepAlives: true,
|
DisableKeepAlives: true,
|
||||||
TLSClientConfig: tlsCfg,
|
TLSClientConfig: tlsCfg,
|
||||||
},
|
},
|
||||||
|
@ -132,6 +147,27 @@ func (h *HTTPResponse) createHttpClient() (*http.Client, error) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func localAddress(interfaceName string) (net.Addr, error) {
|
||||||
|
i, err := net.InterfaceByName(interfaceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := i.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if naddr, ok := addr.(*net.IPNet); ok {
|
||||||
|
// leaving port set to zero to let kernel pick
|
||||||
|
return &net.TCPAddr{IP: naddr.IP}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("cannot create local address for interface %q", interfaceName)
|
||||||
|
}
|
||||||
|
|
||||||
func setResult(result_string string, fields map[string]interface{}, tags map[string]string) {
|
func setResult(result_string string, fields map[string]interface{}, tags map[string]string) {
|
||||||
result_codes := map[string]int{
|
result_codes := map[string]int{
|
||||||
"success": 0,
|
"success": 0,
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package http_response
|
package http_response
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -210,6 +212,67 @@ func TestFields(t *testing.T) {
|
||||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findInterface() (net.Interface, error) {
|
||||||
|
potential, _ := net.Interfaces()
|
||||||
|
|
||||||
|
for _, i := range potential {
|
||||||
|
// we are only interest in loopback interfaces which are up
|
||||||
|
if (i.Flags&net.FlagUp == 0) || (i.Flags&net.FlagLoopback == 0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrs, _ := i.Addrs(); len(addrs) > 0 {
|
||||||
|
// return interface if it has at least one unicast address
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.Interface{}, errors.New("cannot find suitable loopback interface")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterface(t *testing.T) {
|
||||||
|
var (
|
||||||
|
mux = setUpTestMux()
|
||||||
|
ts = httptest.NewServer(mux)
|
||||||
|
)
|
||||||
|
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
intf, err := findInterface()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
h := &HTTPResponse{
|
||||||
|
Address: ts.URL + "/good",
|
||||||
|
Body: "{ 'test': 'data'}",
|
||||||
|
Method: "GET",
|
||||||
|
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
||||||
|
Headers: map[string]string{
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
FollowRedirects: true,
|
||||||
|
Interface: intf.Name,
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = h.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedFields := map[string]interface{}{
|
||||||
|
"http_response_code": http.StatusOK,
|
||||||
|
"result_type": "success",
|
||||||
|
"result_code": 0,
|
||||||
|
"response_time": nil,
|
||||||
|
}
|
||||||
|
expectedTags := map[string]interface{}{
|
||||||
|
"server": nil,
|
||||||
|
"method": "GET",
|
||||||
|
"status_code": "200",
|
||||||
|
"result": "success",
|
||||||
|
}
|
||||||
|
absentFields := []string{"response_string_match"}
|
||||||
|
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRedirects(t *testing.T) {
|
func TestRedirects(t *testing.T) {
|
||||||
mux := setUpTestMux()
|
mux := setUpTestMux()
|
||||||
ts := httptest.NewServer(mux)
|
ts := httptest.NewServer(mux)
|
||||||
|
|
Loading…
Reference in New Issue