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:
parent
ccfa5dbbac
commit
e206ab5cb6
|
@ -74,6 +74,8 @@ type AgentServiceCheck struct {
|
|||
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"`
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 = 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 {
|
||||
|
|
|
@ -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,48 +180,81 @@ func TestCheckTTL(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func mockHTTPServer(responseCode int) *httptest.Server {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
func TestCheckHTTP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
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},
|
||||
|
||||
// warning
|
||||
{code: 429, status: api.HealthWarning},
|
||||
|
||||
// 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},
|
||||
|
||||
// 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},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
desc := tt.desc
|
||||
if desc == "" {
|
||||
desc = fmt.Sprintf("code %d -> status %s", tt.code, tt.status)
|
||||
}
|
||||
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(responseCode)
|
||||
w.WriteHeader(tt.code)
|
||||
w.Write(body)
|
||||
return
|
||||
})
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
return httptest.NewTLSServer(mux)
|
||||
}
|
||||
|
||||
func expectHTTPStatus(t *testing.T, url string, status string) {
|
||||
notif := mock.NewNotify()
|
||||
check := &CheckHTTP{
|
||||
Notify: notif,
|
||||
CheckID: types.CheckID("foo"),
|
||||
HTTP: url,
|
||||
HTTP: server.URL,
|
||||
Method: tt.method,
|
||||
Header: tt.header,
|
||||
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()
|
||||
|
||||
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"), status; 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
|
||||
|
@ -227,77 +262,16 @@ func expectHTTPStatus(t *testing.T, url string, status string) {
|
|||
r.Fatalf("output too long: %d (%d-byte limit)", n, CheckBufSize)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCheckHTTPCritical(t *testing.T) {
|
||||
t.Parallel()
|
||||
// var server *httptest.Server
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -62,6 +62,8 @@ type CheckDefinition struct {
|
|||
//
|
||||
Script string
|
||||
HTTP string
|
||||
Header map[string][]string
|
||||
Method string
|
||||
TCP string
|
||||
Interval time.Duration
|
||||
DockerContainerID string
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue