Skip to content

Commit

Permalink
Heartbeat support options for lightweight checks (#32987)
Browse files Browse the repository at this point in the history
Fixes #32457 by adding support for this HTTP verb

Co-authored-by: debadair <debadair@elastic.co>
Co-authored-by: Xiao, Lowry <fenxiao@ebay.com>
  • Loading branch information
3 people authored and chrisberkhout committed Jun 1, 2023
1 parent d2e4d5c commit 6a049d2
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 3 deletions.
4 changes: 2 additions & 2 deletions heartbeat/docs/monitors/monitor-http.asciidoc
Expand Up @@ -133,8 +133,8 @@ Example configuration:

Under `check.request`, specify these options:

*`method`*:: The HTTP method to use. Valid values are `"HEAD"`, `"GET"` and
`"POST"`.
*`method`*:: The HTTP method to use. Valid values are `"HEAD"`, `"GET"`, `"POST"` and
`"OPTIONS"`.
*`headers`*:: A dictionary of additional HTTP headers to send. By default heartbeat
will set the 'User-Agent' header to identify itself.
*`body`*:: Optional request body content.
Expand Down
2 changes: 1 addition & 1 deletion heartbeat/monitors/active/http/config.go
Expand Up @@ -137,7 +137,7 @@ func (r *responseConfig) Validate() error {
// Validate validates of the requestParameters object is valid or not
func (r *requestParameters) Validate() error {
switch strings.ToUpper(r.Method) {
case "HEAD", "GET", "POST":
case "HEAD", "GET", "POST", "OPTIONS":
default:
return fmt.Errorf("HTTP method '%v' not supported", r.Method)
}
Expand Down
3 changes: 3 additions & 0 deletions heartbeat/tests/system/config/heartbeat.yml.j2
Expand Up @@ -33,6 +33,9 @@ heartbeat.monitors:
{% endfor %}
{% endif -%}

{%- if monitor.check_request_method is defined %}
check.request.method: {{monitor.check_request_method}}
{% endif -%}

{%- if monitor.check_response_json is defined %}
check.response.json:
Expand Down
13 changes: 13 additions & 0 deletions heartbeat/tests/system/heartbeat.py
Expand Up @@ -26,7 +26,20 @@ def do_GET(self):

self.wfile.write(bytes(content, "utf-8"))

# set up a HTTPHandler that supports OPTIONS method as well
class HTTPHandlerEnabledOPTIONS(HTTPHandler):
def do_OPTIONS(self):
self.send_response(status_code)
self.send_header('Access-Control-Allow-Credentials', 'true')
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'HEAD, GET, POST, OPTIONS')
self.end_headers()

# initialize http server based on if it needs to support OPTIONS method
server = http.server.HTTPServer(('localhost', 0), HTTPHandler)
# setup enable_options_method as False if it's not set
if kwargs.get("enable_options_method", False):
server = http.server.HTTPServer(('localhost', 0), HTTPHandlerEnabledOPTIONS)

thread = threading.Thread(target=server.serve_forever)
thread.start()
Expand Down
47 changes: 47 additions & 0 deletions heartbeat/tests/system/test_monitor.py
Expand Up @@ -67,6 +67,44 @@ def test_http_with_hosts_config(self, status_code):
raise unittest.SkipTest
self.assert_fields_are_documented(output[0])

@parameterized.expand([
# enable_options_method
(lambda enable_options: True, 200),
(lambda enable_options: False, 501),
])
def test_http_check_with_options_method(self, enable_options, status_code):
"""
Test http server if it supports OPTIONS method check
"""
# get enable_options value from parameterized decorator
enable_options = enable_options(enable_options)
status_code = int(status_code)
server = self.start_server("hello world", status_code,
enable_options_method=enable_options)

self.render_http_config_with_options_method(
["localhost:{}".format(server.server_port)])

proc = self.start_beat()
self.wait_until(lambda: self.log_contains("heartbeat is running"))

self.wait_until(
lambda: self.output_has(lines=1))

proc.check_kill_and_wait()

server.shutdown()
output = self.read_output()
assert status_code == output[0]["http.response.status_code"]
if enable_options:
# make sure OPTIONS is in the allowed methods from the response
assert -1 != output[0]["http.response.headers.Access-Control-Allow-Methods"].find("OPTIONS")

if os.name == "nt":
# Currently skipped on Windows as fields.yml not generated
raise unittest.SkipTest
self.assert_fields_are_documented(output[0])

def test_http_delayed(self):
"""
Ensure that the HTTP monitor consumes the whole body.
Expand Down Expand Up @@ -143,3 +181,12 @@ def render_http_config_with_hosts(self, urls):
"hosts": urls,
}]
)

def render_http_config_with_options_method(self, urls):
self.render_config_template(
monitors=[{
"type": "http",
"hosts": urls,
"check_request_method": "OPTIONS",
}]
)

0 comments on commit 6a049d2

Please sign in to comment.