agent: support custom header and method for http checks (#3106)

This patch adds support for custom headers and
method for HTTP checks.

Fixes #2474
Fixes #2657
Fixes #3106
This commit is contained in:
Frank Schröder 2017-06-07 01:11:56 +02:00 committed by GitHub
parent ccfa5dbbac
commit e206ab5cb6
8 changed files with 277 additions and 216 deletions

View File

@ -67,17 +67,19 @@ type AgentCheckRegistration struct {
// AgentServiceCheck is used to define a node or service level check
type AgentServiceCheck struct {
Script string `json:",omitempty"`
DockerContainerID string `json:",omitempty"`
Shell string `json:",omitempty"` // Only supported for Docker.
Interval string `json:",omitempty"`
Timeout string `json:",omitempty"`
TTL string `json:",omitempty"`
HTTP string `json:",omitempty"`
TCP string `json:",omitempty"`
Status string `json:",omitempty"`
Notes string `json:",omitempty"`
TLSSkipVerify bool `json:",omitempty"`
Script string `json:",omitempty"`
DockerContainerID string `json:",omitempty"`
Shell string `json:",omitempty"` // Only supported for Docker.
Interval string `json:",omitempty"`
Timeout string `json:",omitempty"`
TTL string `json:",omitempty"`
HTTP string `json:",omitempty"`
Header map[string][]string `json:",omitempty"`
Method string `json:",omitempty"`
TCP string `json:",omitempty"`
Status string `json:",omitempty"`
Notes string `json:",omitempty"`
TLSSkipVerify bool `json:",omitempty"`
// In Consul 0.7 and later, checks that are associated with a service
// may also contain this optional DeregisterCriticalServiceAfter field,

View File

@ -1575,6 +1575,8 @@ func (a *Agent) AddCheck(check *structs.HealthCheck, chkType *CheckType, persist
Notify: &a.state,
CheckID: check.CheckID,
HTTP: chkType.HTTP,
Header: chkType.Header,
Method: chkType.Method,
Interval: chkType.Interval,
Timeout: chkType.Timeout,
Logger: a.logger,

View File

@ -57,6 +57,8 @@ type CheckType struct {
Script string
HTTP string
Header map[string][]string
Method string
TCP string
Interval time.Duration
DockerContainerID string
@ -347,6 +349,8 @@ type CheckHTTP struct {
Notify CheckNotifier
CheckID types.CheckID
HTTP string
Header map[string][]string
Method string
Interval time.Duration
Timeout time.Duration
Logger *log.Logger
@ -429,15 +433,31 @@ func (c *CheckHTTP) run() {
// check is invoked periodically to perform the HTTP check
func (c *CheckHTTP) check() {
req, err := http.NewRequest("GET", c.HTTP, nil)
method := c.Method
if method == "" {
method = "GET"
}
req, err := http.NewRequest(method, c.HTTP, nil)
if err != nil {
c.Logger.Printf("[WARN] agent: http request failed '%s': %s", c.HTTP, err)
c.Notify.UpdateCheck(c.CheckID, api.HealthCritical, err.Error())
return
}
req.Header.Set("User-Agent", UserAgent)
req.Header.Set("Accept", "text/plain, text/*, */*")
req.Header = http.Header(c.Header)
// this happens during testing but not in prod
if req.Header == nil {
req.Header = make(http.Header)
}
if req.Header.Get("User-Agent") == "" {
req.Header.Set("User-Agent", UserAgent)
}
if req.Header.Get("Accept") == "" {
req.Header.Set("Accept", "text/plain, text/*, */*")
}
resp, err := c.httpClient.Do(req)
if err != nil {

View File

@ -4,12 +4,14 @@ import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"reflect"
"strings"
"testing"
"time"
@ -28,7 +30,7 @@ func expectStatus(t *testing.T, script, status string) {
CheckID: types.CheckID("foo"),
Script: script,
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -71,7 +73,7 @@ func TestCheckMonitor_Timeout(t *testing.T) {
Script: "sleep 1 && exit 0",
Interval: 10 * time.Millisecond,
Timeout: 5 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -95,7 +97,7 @@ func TestCheckMonitor_RandomStagger(t *testing.T) {
CheckID: types.CheckID("foo"),
Script: "exit 0",
Interval: 25 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -120,7 +122,7 @@ func TestCheckMonitor_LimitOutput(t *testing.T) {
CheckID: types.CheckID("foo"),
Script: "od -N 81920 /dev/urandom",
Interval: 25 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -140,7 +142,7 @@ func TestCheckTTL(t *testing.T) {
Notify: notif,
CheckID: types.CheckID("foo"),
TTL: 100 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -178,126 +180,98 @@ func TestCheckTTL(t *testing.T) {
}
}
func mockHTTPServer(responseCode int) *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*CheckBufSize)
w.WriteHeader(responseCode)
w.Write(body)
return
})
func TestCheckHTTP(t *testing.T) {
t.Parallel()
return httptest.NewServer(mux)
}
tests := []struct {
desc string
code int
method string
header http.Header
status string
}{
// passing
{code: 200, status: api.HealthPassing},
{code: 201, status: api.HealthPassing},
{code: 250, status: api.HealthPassing},
{code: 299, status: api.HealthPassing},
func mockTLSHTTPServer(responseCode int) *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*CheckBufSize)
w.WriteHeader(responseCode)
w.Write(body)
return
})
// warning
{code: 429, status: api.HealthWarning},
return httptest.NewTLSServer(mux)
}
// critical
{code: 150, status: api.HealthCritical},
{code: 199, status: api.HealthCritical},
{code: 300, status: api.HealthCritical},
{code: 400, status: api.HealthCritical},
{code: 500, status: api.HealthCritical},
func expectHTTPStatus(t *testing.T, url string, status string) {
notif := mock.NewNotify()
check := &CheckHTTP{
Notify: notif,
CheckID: types.CheckID("foo"),
HTTP: url,
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
// custom method
{desc: "custom method GET", code: 200, method: "GET", status: api.HealthPassing},
{desc: "custom method POST", code: 200, method: "POST", status: api.HealthPassing},
{desc: "custom method abc", code: 200, method: "abc", status: api.HealthPassing},
// custom header
{desc: "custom header", code: 200, header: http.Header{"A": []string{"b", "c"}}, status: api.HealthPassing},
}
check.Start()
defer check.Stop()
retry.Run(t, func(r *retry.R) {
if got, want := notif.Updates("foo"), 2; got < want {
r.Fatalf("got %d updates want at least %d", got, want)
for _, tt := range tests {
desc := tt.desc
if desc == "" {
desc = fmt.Sprintf("code %d -> status %s", tt.code, tt.status)
}
if got, want := notif.State("foo"), status; got != want {
r.Fatalf("got state %q want %q", got, want)
}
// Allow slightly more data than CheckBufSize, for the header
if n := len(notif.Output("foo")); n > (CheckBufSize + 256) {
r.Fatalf("output too long: %d (%d-byte limit)", n, CheckBufSize)
}
})
}
t.Run(desc, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if tt.method != "" && tt.method != r.Method {
w.WriteHeader(999)
return
}
if len(tt.header) > 0 && !reflect.DeepEqual(tt.header, r.Header) {
w.WriteHeader(999)
return
}
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*CheckBufSize)
w.WriteHeader(tt.code)
w.Write(body)
}))
defer server.Close()
func TestCheckHTTPCritical(t *testing.T) {
t.Parallel()
// var server *httptest.Server
notif := mock.NewNotify()
check := &CheckHTTP{
Notify: notif,
CheckID: types.CheckID("foo"),
HTTP: server.URL,
Method: tt.method,
Header: tt.header,
Interval: 10 * time.Millisecond,
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
server := mockHTTPServer(150)
expectHTTPStatus(t, server.URL, api.HealthCritical)
server.Close()
// 2xx - 1
server = mockHTTPServer(199)
expectHTTPStatus(t, server.URL, api.HealthCritical)
server.Close()
// 2xx + 1
server = mockHTTPServer(300)
expectHTTPStatus(t, server.URL, api.HealthCritical)
server.Close()
server = mockHTTPServer(400)
expectHTTPStatus(t, server.URL, api.HealthCritical)
server.Close()
server = mockHTTPServer(500)
expectHTTPStatus(t, server.URL, api.HealthCritical)
server.Close()
}
func TestCheckHTTPPassing(t *testing.T) {
t.Parallel()
var server *httptest.Server
server = mockHTTPServer(200)
expectHTTPStatus(t, server.URL, api.HealthPassing)
server.Close()
server = mockHTTPServer(201)
expectHTTPStatus(t, server.URL, api.HealthPassing)
server.Close()
server = mockHTTPServer(250)
expectHTTPStatus(t, server.URL, api.HealthPassing)
server.Close()
server = mockHTTPServer(299)
expectHTTPStatus(t, server.URL, api.HealthPassing)
server.Close()
}
func TestCheckHTTPWarning(t *testing.T) {
t.Parallel()
server := mockHTTPServer(429)
expectHTTPStatus(t, server.URL, api.HealthWarning)
server.Close()
}
func mockSlowHTTPServer(responseCode int, sleep time.Duration) *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(sleep)
w.WriteHeader(responseCode)
return
})
return httptest.NewServer(mux)
retry.Run(t, func(r *retry.R) {
if got, want := notif.Updates("foo"), 2; got < want {
r.Fatalf("got %d updates want at least %d", got, want)
}
if got, want := notif.State("foo"), tt.status; got != want {
r.Fatalf("got state %q want %q", got, want)
}
// Allow slightly more data than CheckBufSize, for the header
if n := len(notif.Output("foo")); n > (CheckBufSize + 256) {
r.Fatalf("output too long: %d (%d-byte limit)", n, CheckBufSize)
}
})
})
}
}
func TestCheckHTTPTimeout(t *testing.T) {
t.Parallel()
server := mockSlowHTTPServer(200, 10*time.Millisecond)
timeout := 5 * time.Millisecond
server := httptest.NewServer(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
time.Sleep(2 * timeout)
}))
defer server.Close()
notif := mock.NewNotify()
@ -305,9 +279,9 @@ func TestCheckHTTPTimeout(t *testing.T) {
Notify: notif,
CheckID: types.CheckID("bar"),
HTTP: server.URL,
Timeout: 5 * time.Millisecond,
Timeout: timeout,
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
@ -328,7 +302,7 @@ func TestCheckHTTP_disablesKeepAlives(t *testing.T) {
CheckID: types.CheckID("foo"),
HTTP: "http://foo.bar/baz",
Interval: 10 * time.Second,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
@ -345,7 +319,7 @@ func TestCheckHTTP_TLSSkipVerify_defaultFalse(t *testing.T) {
CheckID: "foo",
HTTP: "https://foo.bar/baz",
Interval: 10 * time.Second,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
@ -356,6 +330,15 @@ func TestCheckHTTP_TLSSkipVerify_defaultFalse(t *testing.T) {
}
}
func mockTLSHTTPServer(code int) *httptest.Server {
return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Body larger than 4k limit
body := bytes.Repeat([]byte{'a'}, 2*CheckBufSize)
w.WriteHeader(code)
w.Write(body)
}))
}
func TestCheckHTTP_TLSSkipVerify_true_pass(t *testing.T) {
t.Parallel()
server := mockTLSHTTPServer(200)
@ -368,7 +351,7 @@ func TestCheckHTTP_TLSSkipVerify_true_pass(t *testing.T) {
CheckID: types.CheckID("skipverify_true"),
HTTP: server.URL,
Interval: 5 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
TLSSkipVerify: true,
}
@ -397,7 +380,7 @@ func TestCheckHTTP_TLSSkipVerify_true_fail(t *testing.T) {
CheckID: types.CheckID("skipverify_true"),
HTTP: server.URL,
Interval: 5 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
TLSSkipVerify: true,
}
check.Start()
@ -425,7 +408,7 @@ func TestCheckHTTP_TLSSkipVerify_false(t *testing.T) {
CheckID: types.CheckID("skipverify_false"),
HTTP: server.URL,
Interval: 100 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
TLSSkipVerify: false,
}
@ -472,7 +455,7 @@ func expectTCPStatus(t *testing.T, tcp string, status string) {
CheckID: types.CheckID("foo"),
TCP: tcp,
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
}
check.Start()
defer check.Stop()
@ -649,7 +632,7 @@ func expectDockerCheckStatus(t *testing.T, dockerClient DockerClient, status str
DockerContainerID: "54432bad1fc7",
Shell: "/bin/sh",
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
dockerClient: dockerClient,
}
check.Start()
@ -711,7 +694,7 @@ func TestDockerCheckDefaultToSh(t *testing.T) {
Script: "/health.sh",
DockerContainerID: "54432bad1fc7",
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
dockerClient: &fakeDockerClientWithNoErrors{},
}
check.Start()
@ -733,7 +716,7 @@ func TestDockerCheckUseShellFromEnv(t *testing.T) {
Script: "/health.sh",
DockerContainerID: "54432bad1fc7",
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
dockerClient: &fakeDockerClientWithNoErrors{},
}
check.Start()
@ -756,7 +739,7 @@ func TestDockerCheckTruncateOutput(t *testing.T) {
DockerContainerID: "54432bad1fc7",
Shell: "/bin/sh",
Interval: 10 * time.Millisecond,
Logger: log.New(os.Stderr, UniqueID(), log.LstdFlags),
Logger: log.New(ioutil.Discard, UniqueID(), log.LstdFlags),
dockerClient: &fakeDockerClientWithLongOutput{},
}
check.Start()
@ -768,5 +751,4 @@ func TestDockerCheckTruncateOutput(t *testing.T) {
if len(notif.Output("foo")) > CheckBufSize+100 {
t.Fatalf("output size is too long")
}
}

View File

@ -4,6 +4,7 @@ import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net"
@ -1413,83 +1414,97 @@ AFTER_FIX:
return &result, nil
}
func FixupCheckType(raw interface{}) error {
var ttlKey, intervalKey, timeoutKey string
const deregisterKey = "DeregisterCriticalServiceAfter"
var errInvalidHeaderFormat = errors.New("agent: invalid format of 'header' field")
// Handle decoding of time durations
func FixupCheckType(raw interface{}) error {
rawMap, ok := raw.(map[string]interface{})
if !ok {
return nil
}
parseDuration := func(v interface{}) (time.Duration, error) {
if v == nil {
return 0, nil
}
if d, ok := v.(time.Duration); ok {
return d, nil
}
s, ok := v.(string)
if !ok {
return 0, fmt.Errorf("invalid format")
}
d, err := time.ParseDuration(s)
if err != nil {
return 0, err
}
return d, nil
}
parseHeaderMap := func(v interface{}) (map[string][]string, error) {
if v == nil {
return nil, nil
}
vm, ok := v.(map[string]interface{})
if !ok {
return nil, errInvalidHeaderFormat
}
m := map[string][]string{}
for k, vv := range vm {
vs, ok := vv.([]interface{})
if !ok {
return nil, errInvalidHeaderFormat
}
for _, vs := range vs {
s, ok := vs.(string)
if !ok {
return nil, errInvalidHeaderFormat
}
m[k] = append(m[k], s)
}
}
return m, nil
}
replace := func(oldKey, newKey string, val interface{}) {
rawMap[newKey] = val
if oldKey != newKey {
delete(rawMap, oldKey)
}
}
for k, v := range rawMap {
switch strings.ToLower(k) {
case "ttl":
ttlKey = k
case "interval":
intervalKey = k
case "timeout":
timeoutKey = k
case "deregister_critical_service_after":
rawMap[deregisterKey] = v
delete(rawMap, k)
case "service_id":
rawMap["serviceid"] = v
delete(rawMap, k)
case "header":
h, err := parseHeaderMap(v)
if err != nil {
return fmt.Errorf("invalid %q: %s", k, err)
}
rawMap[k] = h
case "ttl", "interval", "timeout":
d, err := parseDuration(v)
if err != nil {
return fmt.Errorf("invalid %q: %v", k, err)
}
rawMap[k] = d
case "deregister_critical_service_after", "deregistercriticalserviceafter":
d, err := parseDuration(v)
if err != nil {
return fmt.Errorf("invalid %q: %v", k, err)
}
replace(k, "DeregisterCriticalServiceAfter", d)
case "docker_container_id":
rawMap["DockerContainerID"] = v
delete(rawMap, k)
replace(k, "DockerContainerID", v)
case "service_id":
replace(k, "ServiceID", v)
case "tls_skip_verify":
rawMap["TLSSkipVerify"] = v
delete(rawMap, k)
replace(k, "TLSSkipVerify", v)
}
}
if ttl, ok := rawMap[ttlKey]; ok {
ttlS, ok := ttl.(string)
if ok {
dur, err := time.ParseDuration(ttlS)
if err != nil {
return err
}
rawMap[ttlKey] = dur
}
}
if interval, ok := rawMap[intervalKey]; ok {
intervalS, ok := interval.(string)
if ok {
dur, err := time.ParseDuration(intervalS)
if err != nil {
return err
}
rawMap[intervalKey] = dur
}
}
if timeout, ok := rawMap[timeoutKey]; ok {
timeoutS, ok := timeout.(string)
if ok {
dur, err := time.ParseDuration(timeoutS)
if err != nil {
return err
}
rawMap[timeoutKey] = dur
}
}
if deregister, ok := rawMap[deregisterKey]; ok {
timeoutS, ok := deregister.(string)
if ok {
dur, err := time.ParseDuration(timeoutS)
if err != nil {
return err
}
rawMap[deregisterKey] = dur
}
}
return nil
}

View File

@ -724,6 +724,8 @@ func TestDecodeConfig(t *testing.T) {
"Notes": "j",
"Script": "k",
"HTTP": "l",
"Header": {"a":["b"], "c":["d", "e"]},
"Method": "x",
"TCP": "m",
"DockerContainerID": "n",
"Shell": "o",
@ -752,6 +754,8 @@ func TestDecodeConfig(t *testing.T) {
Notes: "j",
Script: "k",
HTTP: "l",
Header: map[string][]string{"a": []string{"b"}, "c": []string{"d", "e"}},
Method: "x",
TCP: "m",
DockerContainerID: "n",
Shell: "o",
@ -784,6 +788,8 @@ func TestDecodeConfig(t *testing.T) {
"Notes": "j",
"Script": "k",
"HTTP": "l",
"Header": {"a":["b"], "c":["d", "e"]},
"Method": "x",
"TCP": "m",
"DockerContainerID": "n",
"Shell": "o",
@ -800,6 +806,8 @@ func TestDecodeConfig(t *testing.T) {
"Notes": "jj",
"Script": "kk",
"HTTP": "ll",
"Header": {"aa":["bb"], "cc":["dd", "ee"]},
"Method": "xx",
"TCP": "mm",
"DockerContainerID": "nn",
"Shell": "oo",
@ -830,6 +838,8 @@ func TestDecodeConfig(t *testing.T) {
Notes: "j",
Script: "k",
HTTP: "l",
Header: map[string][]string{"a": []string{"b"}, "c": []string{"d", "e"}},
Method: "x",
TCP: "m",
DockerContainerID: "n",
Shell: "o",
@ -846,6 +856,8 @@ func TestDecodeConfig(t *testing.T) {
Notes: "jj",
Script: "kk",
HTTP: "ll",
Header: map[string][]string{"aa": []string{"bb"}, "cc": []string{"dd", "ee"}},
Method: "xx",
TCP: "mm",
DockerContainerID: "nn",
Shell: "oo",
@ -879,6 +891,8 @@ func TestDecodeConfig(t *testing.T) {
"Notes": "j",
"Script": "k",
"HTTP": "l",
"Header": {"a":["b"], "c":["d", "e"]},
"Method": "x",
"TCP": "m",
"DockerContainerID": "n",
"Shell": "o",
@ -904,6 +918,8 @@ func TestDecodeConfig(t *testing.T) {
"Notes": "jj",
"Script": "kk",
"HTTP": "ll",
"Header": {"aa":["bb"], "cc":["dd", "ee"]},
"Method": "xx",
"TCP": "mm",
"DockerContainerID": "nn",
"Shell": "oo",
@ -933,6 +949,8 @@ func TestDecodeConfig(t *testing.T) {
Notes: "j",
Script: "k",
HTTP: "l",
Header: map[string][]string{"a": []string{"b"}, "c": []string{"d", "e"}},
Method: "x",
TCP: "m",
DockerContainerID: "n",
Shell: "o",
@ -958,6 +976,8 @@ func TestDecodeConfig(t *testing.T) {
Notes: "jj",
Script: "kk",
HTTP: "ll",
Header: map[string][]string{"aa": []string{"bb"}, "cc": []string{"dd", "ee"}},
Method: "xx",
TCP: "mm",
DockerContainerID: "nn",
Shell: "oo",
@ -985,6 +1005,8 @@ func TestDecodeConfig(t *testing.T) {
"script": "d",
"shell": "e",
"http": "f",
"Header": {"a":["b"], "c":["d", "e"]},
"Method": "x",
"tcp": "g",
"docker_container_id": "h",
"tls_skip_verify": true,
@ -1006,6 +1028,8 @@ func TestDecodeConfig(t *testing.T) {
Script: "d",
Shell: "e",
HTTP: "f",
Header: map[string][]string{"a": []string{"b"}, "c": []string{"d", "e"}},
Method: "x",
TCP: "g",
DockerContainerID: "h",
TLSSkipVerify: true,
@ -1031,6 +1055,8 @@ func TestDecodeConfig(t *testing.T) {
"script": "g",
"shell": "h",
"http": "i",
"Header": {"a":["b"], "c":["d", "e"]},
"Method": "x",
"tcp": "j",
"docker_container_id": "k",
"tls_skip_verify": true,
@ -1049,6 +1075,8 @@ func TestDecodeConfig(t *testing.T) {
"script": "gg",
"shell": "hh",
"http": "ii",
"Header": {"aa":["bb"], "cc":["dd", "ee"]},
"Method": "xx",
"tcp": "jj",
"docker_container_id": "kk",
"tls_skip_verify": false,
@ -1071,6 +1099,8 @@ func TestDecodeConfig(t *testing.T) {
Script: "g",
Shell: "h",
HTTP: "i",
Header: map[string][]string{"a": []string{"b"}, "c": []string{"d", "e"}},
Method: "x",
TCP: "j",
DockerContainerID: "k",
TLSSkipVerify: true,
@ -1089,6 +1119,8 @@ func TestDecodeConfig(t *testing.T) {
Script: "gg",
Shell: "hh",
HTTP: "ii",
Header: map[string][]string{"aa": []string{"bb"}, "cc": []string{"dd", "ee"}},
Method: "xx",
TCP: "jj",
DockerContainerID: "kk",
TLSSkipVerify: false,

View File

@ -62,6 +62,8 @@ type CheckDefinition struct {
//
Script string
HTTP string
Header map[string][]string
Method string
TCP string
Interval time.Duration
DockerContainerID string

View File

@ -27,17 +27,21 @@ There are five different kinds of checks:
`timeout` field in the check definition.
* HTTP + Interval - These checks make an HTTP `GET` request every Interval (e.g.
every 30 seconds) to the specified URL. The status of the service depends on the HTTP response code:
any `2xx` code is considered passing, a `429 Too Many Requests` is a warning, and anything else is a failure.
This type of check should be preferred over a script that uses `curl` or another external process
to check a simple HTTP operation. By default, HTTP checks will be configured
with a request timeout equal to the check interval, with a max of 10 seconds.
It is possible to configure a custom HTTP check timeout value by specifying
the `timeout` field in the check definition. The output of the check is
limited to roughly 4K. Responses larger than this will be truncated. HTTP checks
also support SSL. By default, a valid SSL certificate is expected. Certificate
verification can be turned off by setting the `tls_skip_verify` field to `true`
in the check definition.
every 30 seconds) to the specified URL. The status of the service depends on
the HTTP response code: any `2xx` code is considered passing, a `429 Too Many
Requests` is a warning, and anything else is a failure. This type of check
should be preferred over a script that uses `curl` or another external process
to check a simple HTTP operation. By default, HTTP checks are `GET` requests
unless the `method` field specifies a different method. Additional header
fields can be set through the `header` field which is a map of lists of
strings, e.g. `{"x-foo": ["bar", "baz"]}`. By default, HTTP checks will be
configured with a request timeout equal to the check interval, with a max of
10 seconds. It is possible to configure a custom HTTP check timeout value by
specifying the `timeout` field in the check definition. The output of the
check is limited to roughly 4K. Responses larger than this will be truncated.
HTTP checks also support SSL. By default, a valid SSL certificate is expected.
Certificate verification can be turned off by setting the `tls_skip_verify`
field to `true` in the check definition.
* TCP + Interval - These checks make an TCP connection attempt every Interval
(e.g. every 30 seconds) to the specified IP/hostname and port. If no hostname
@ -104,6 +108,8 @@ A HTTP check:
"id": "api",
"name": "HTTP API on port 5000",
"http": "http://localhost:5000/health",
"method": "POST",
"header": {"x-foo":["bar", "baz"]},
"interval": "10s",
"timeout": "1s"
}