From 837542102c30448e86915abdc7f443f5351cbf3b Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Fri, 14 Dec 2018 11:01:28 -0800 Subject: [PATCH 1/5] Add support for `body_path` in HTTP probe config. Fixes prometheus/blackbox_exporter#391. Signed-off-by: Emmanuel Gomez --- config/config.go | 35 +++++++++++++++++++++++------------ prober/http.go | 6 +++++- prober/http_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/config/config.go b/config/config.go index 2bd5d946..d65864c3 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,9 @@ package config import ( "errors" "fmt" + "io" "io/ioutil" + "os" "runtime" "sync" "time" @@ -52,18 +54,20 @@ type Module struct { type HTTPProbe struct { // Defaults to 2xx. - ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"` - ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` - IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` - IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` - NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` - FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` - FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` - Method string `yaml:"method,omitempty"` - Headers map[string]string `yaml:"headers,omitempty"` - FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` - FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` - Body string `yaml:"body,omitempty"` + ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"` + ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` + IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` + IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` + NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` + FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` + FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` + Method string `yaml:"method,omitempty"` + Headers map[string]string `yaml:"headers,omitempty"` + FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` + FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` + Body string `yaml:"body,omitempty"` + BodyPath string `yaml:"body_path,omitempty"` + BodyFile io.Reader HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` } @@ -135,6 +139,13 @@ func (s *HTTPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := s.HTTPClientConfig.Validate(); err != nil { return err } + if s.BodyPath != "" { + file, err := os.Open(s.BodyPath) + if err != nil { + return err + } + s.BodyFile = file + } return nil } diff --git a/prober/http.go b/prober/http.go index 542f3129..3054ad86 100644 --- a/prober/http.go +++ b/prober/http.go @@ -257,10 +257,14 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr var body io.Reader - // If a body is configured, add it to the request. + // If a body string is configured, add it to the request. if httpConfig.Body != "" { body = strings.NewReader(httpConfig.Body) } + // If a body path is configured, add it to the request. + if httpConfig.BodyFile != nil { + body = httpConfig.BodyFile + } request, err := http.NewRequest(httpConfig.Method, targetURL.String(), body) request.Host = origHost diff --git a/prober/http_test.go b/prober/http_test.go index a7deaa89..080ea0fa 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -164,6 +164,33 @@ func TestPost(t *testing.T) { } } +func TestPostBody(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusBadRequest) + } + })) + defer ts.Close() + + recorder := httptest.NewRecorder() + registry := prometheus.NewRegistry() + testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + result := ProbeHTTP(testCTX, ts.URL, + config.Module{ + Timeout: time.Second, + HTTP: config.HTTPProbe{ + IPProtocolFallback: true, + Method: "POST", + BodyPath: "./http.go", + }, + }, registry, log.NewNopLogger()) + body := recorder.Body.String() + if !result { + t.Fatalf("Post test failed unexpectedly, got %s", body) + } +} + func TestBasicAuth(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { })) From cf2bc45afa58f87df469f32a2b2b67f89991cc5c Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Wed, 19 Dec 2018 14:44:18 -0800 Subject: [PATCH 2/5] Rename `config.HTTPProbe.BodyPath`->`BodyFile`. Signed-off-by: Emmanuel Gomez --- config/config.go | 15 ++++++++------- prober/http.go | 10 ++++++++-- prober/http_test.go | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index d65864c3..aa5dc849 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,6 @@ package config import ( "errors" "fmt" - "io" "io/ioutil" "os" "runtime" @@ -66,9 +65,9 @@ type HTTPProbe struct { FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` Body string `yaml:"body,omitempty"` - BodyPath string `yaml:"body_path,omitempty"` - BodyFile io.Reader - HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` + BodyFile string `yaml:"body_file,omitempty"` + + HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` } type QueryResponse struct { @@ -139,12 +138,14 @@ func (s *HTTPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := s.HTTPClientConfig.Validate(); err != nil { return err } - if s.BodyPath != "" { - file, err := os.Open(s.BodyPath) + if s.Body != "" && s.BodyFile != "" { + return errors.New("Only one of `body` and `body_file` may be set") + } + if s.BodyFile != "" { + _, err := os.Open(s.BodyFile) if err != nil { return err } - s.BodyFile = file } return nil } diff --git a/prober/http.go b/prober/http.go index 3054ad86..a88358c3 100644 --- a/prober/http.go +++ b/prober/http.go @@ -24,6 +24,7 @@ import ( "net/http/cookiejar" "net/http/httptrace" "net/url" + "os" "regexp" "strconv" "strings" @@ -262,8 +263,13 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr body = strings.NewReader(httpConfig.Body) } // If a body path is configured, add it to the request. - if httpConfig.BodyFile != nil { - body = httpConfig.BodyFile + if httpConfig.BodyFile != "" { + file, err := os.Open(httpConfig.BodyFile) + if err != nil { + level.Error(logger).Log("msg", "Error opening body file", "err", err) + return + } + body = file } request, err := http.NewRequest(httpConfig.Method, targetURL.String(), body) diff --git a/prober/http_test.go b/prober/http_test.go index 080ea0fa..b1c08b7e 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -182,7 +182,7 @@ func TestPostBody(t *testing.T) { HTTP: config.HTTPProbe{ IPProtocolFallback: true, Method: "POST", - BodyPath: "./http.go", + BodyFile: "./http.go", }, }, registry, log.NewNopLogger()) body := recorder.Body.String() From 427d68b52775571df2a8dfadcb15c21b1cdf5765 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Wed, 19 Dec 2018 14:50:25 -0800 Subject: [PATCH 3/5] Adopt exclusive-or check style from prometheus/common. Signed-off-by: Emmanuel Gomez --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index aa5dc849..b9935470 100644 --- a/config/config.go +++ b/config/config.go @@ -138,8 +138,8 @@ func (s *HTTPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := s.HTTPClientConfig.Validate(); err != nil { return err } - if s.Body != "" && s.BodyFile != "" { - return errors.New("Only one of `body` and `body_file` may be set") + if len(s.Body) > 0 && len(s.BodyFile) > 0 { + return errors.New("at most one of body & body_file may be configured") } if s.BodyFile != "" { _, err := os.Open(s.BodyFile) From c97981f06c5824cdf55d957f4ee65f76c38a4399 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Tue, 8 Jan 2019 16:58:07 -0800 Subject: [PATCH 4/5] Close file bodies of HTTP probes. --- config/config.go | 3 ++- prober/http.go | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index b9935470..aac188cd 100644 --- a/config/config.go +++ b/config/config.go @@ -142,10 +142,11 @@ func (s *HTTPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { return errors.New("at most one of body & body_file may be configured") } if s.BodyFile != "" { - _, err := os.Open(s.BodyFile) + file, err := os.Open(s.BodyFile) if err != nil { return err } + defer file.Close() } return nil } diff --git a/prober/http.go b/prober/http.go index a88358c3..7f647aef 100644 --- a/prober/http.go +++ b/prober/http.go @@ -269,6 +269,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr level.Error(logger).Log("msg", "Error opening body file", "err", err) return } + defer file.Close() body = file } From b72856c379efbd483d2aa5cb3e02a826ef6a1192 Mon Sep 17 00:00:00 2001 From: Emmanuel Gomez Date: Fri, 11 Jan 2019 14:01:04 -0800 Subject: [PATCH 5/5] Use a test data file in TestPostBody. --- prober/http_test.go | 2 +- prober/testdata/http_body.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 prober/testdata/http_body.txt diff --git a/prober/http_test.go b/prober/http_test.go index b1c08b7e..d65037ec 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -182,7 +182,7 @@ func TestPostBody(t *testing.T) { HTTP: config.HTTPProbe{ IPProtocolFallback: true, Method: "POST", - BodyFile: "./http.go", + BodyFile: "./testdata/http_body.txt", }, }, registry, log.NewNopLogger()) body := recorder.Body.String() diff --git a/prober/testdata/http_body.txt b/prober/testdata/http_body.txt new file mode 100644 index 00000000..4bc5dd81 --- /dev/null +++ b/prober/testdata/http_body.txt @@ -0,0 +1 @@ +TEST HTTP BODY \ No newline at end of file