Commit 94751640 authored by Michael Schurter's avatar Michael Schurter
Browse files

Add Header and Method support for HTTP checks

parent 9d4b98f1
Showing with 112 additions and 13 deletions
+112 -13
......@@ -94,6 +94,8 @@ type ServiceCheck struct {
Timeout time.Duration
InitialStatus string `mapstructure:"initial_status"`
TLSSkipVerify bool `mapstructure:"tls_skip_verify"`
Header map[string][]string
Method string
}
// The Service model represents a Consul service definition
......
......@@ -1002,6 +1002,8 @@ func createCheckReg(serviceID, checkID string, check *structs.ServiceCheck, host
}
url := base.ResolveReference(relative)
chkReg.HTTP = url.String()
chkReg.Method = check.Method
chkReg.Header = check.Header
case structs.ServiceCheckTCP:
chkReg.TCP = net.JoinHostPort(host, strconv.Itoa(port))
case structs.ServiceCheckScript:
......
......@@ -699,6 +699,8 @@ func ApiTaskToStructsTask(apiTask *api.Task, structsTask *structs.Task) {
Timeout: check.Timeout,
InitialStatus: check.InitialStatus,
TLSSkipVerify: check.TLSSkipVerify,
Header: check.Header,
Method: check.Method,
}
}
}
......
......@@ -212,6 +212,21 @@ func CopyMapStringFloat64(m map[string]float64) map[string]float64 {
return c
}
// CopyMapStringSliceString copies a map of strings to string slices such as
// http.Header
func CopyMapStringSliceString(m map[string][]string) map[string][]string {
l := len(m)
if l == 0 {
return nil
}
c := make(map[string][]string, l)
for k, v := range m {
c[k] = CopySliceString(v)
}
return c
}
func CopySliceString(s []string) []string {
l := len(s)
if l == 0 {
......
......@@ -36,6 +36,24 @@ func TestMapStringStringSliceValueSet(t *testing.T) {
}
}
func TestCopyMapStringSliceString(t *testing.T) {
m := map[string][]string{
"x": []string{"a", "b", "c"},
"y": []string{"1", "2", "3"},
"z": nil,
}
c := CopyMapStringSliceString(m)
if !reflect.DeepEqual(c, m) {
t.Fatalf("%#v != %#v", m, c)
}
c["x"][1] = "---"
if reflect.DeepEqual(c, m) {
t.Fatalf("Shared slices: %#v == %#v", m["x"], c["x"])
}
}
func TestClearEnvVar(t *testing.T) {
type testCase struct {
input string
......
......@@ -962,6 +962,8 @@ func parseChecks(service *api.Service, checkObjs *ast.ObjectList) error {
"args",
"initial_status",
"tls_skip_verify",
"header",
"method",
}
if err := checkHCLKeys(co.Val, valid); err != nil {
return multierror.Prefix(err, "check ->")
......@@ -972,6 +974,37 @@ func parseChecks(service *api.Service, checkObjs *ast.ObjectList) error {
if err := hcl.DecodeObject(&cm, co.Val); err != nil {
return err
}
// HCL allows repeating stanzas so merge 'header' into a single
// map[string][]string.
if headerI, ok := cm["header"]; ok {
headerRaw, ok := headerI.([]map[string]interface{})
if !ok {
return fmt.Errorf("check -> header -> expected a []map[string]interface{} but found %T", headerI)
}
m := map[string][]string{}
for _, rawm := range headerRaw {
for k, vI := range rawm {
vs, ok := vI.([]interface{})
if !ok {
return fmt.Errorf("check -> header -> %q expected a []string but found %T", k, vI)
}
for _, vI := range vs {
v, ok := vI.(string)
if !ok {
return fmt.Errorf("check -> header -> %q expected a string but found %T", k, vI)
}
m[k] = append(m[k], v)
}
}
}
check.Header = m
// Remove "header" as it has been parsed
delete(cm, "header")
}
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
WeaklyTypedInput: true,
......
......@@ -2669,17 +2669,19 @@ const (
// The ServiceCheck data model represents the consul health check that
// Nomad registers for a Task
type ServiceCheck struct {
Name string // Name of the check, defaults to id
Type string // Type of the check - tcp, http, docker and script
Command string // Command is the command to run for script checks
Args []string // Args is a list of argumes for script checks
Path string // path of the health check url for http type check
Protocol string // Protocol to use if check is http, defaults to http
PortLabel string // The port to use for tcp/http checks
Interval time.Duration // Interval of the check
Timeout time.Duration // Timeout of the response from the check before consul fails the check
InitialStatus string // Initial status of the check
TLSSkipVerify bool // Skip TLS verification when Protocol=https
Name string // Name of the check, defaults to id
Type string // Type of the check - tcp, http, docker and script
Command string // Command is the command to run for script checks
Args []string // Args is a list of argumes for script checks
Path string // path of the health check url for http type check
Protocol string // Protocol to use if check is http, defaults to http
PortLabel string // The port to use for tcp/http checks
Interval time.Duration // Interval of the check
Timeout time.Duration // Timeout of the response from the check before consul fails the check
InitialStatus string // Initial status of the check
TLSSkipVerify bool // Skip TLS verification when Protocol=https
Method string // HTTP Method to use (GET by default)
Header map[string][]string // HTTP Headers for Consul to set when making HTTP checks
}
func (sc *ServiceCheck) Copy() *ServiceCheck {
......@@ -2688,16 +2690,28 @@ func (sc *ServiceCheck) Copy() *ServiceCheck {
}
nsc := new(ServiceCheck)
*nsc = *sc
nsc.Args = helper.CopySliceString(sc.Args)
nsc.Header = helper.CopyMapStringSliceString(sc.Header)
return nsc
}
func (sc *ServiceCheck) Canonicalize(serviceName string) {
// Ensure empty slices are treated as null to avoid scheduling issues when
// using DeepEquals.
// Ensure empty maps/slices are treated as null to avoid scheduling
// issues when using DeepEquals.
if len(sc.Args) == 0 {
sc.Args = nil
}
if len(sc.Header) == 0 {
sc.Header = nil
} else {
for k, v := range sc.Header {
if len(v) == 0 {
sc.Header[k] = nil
}
}
}
if sc.Name == "" {
sc.Name = fmt.Sprintf("service: %q check", serviceName)
}
......@@ -2772,10 +2786,23 @@ func (sc *ServiceCheck) Hash(serviceID string) string {
io.WriteString(h, sc.PortLabel)
io.WriteString(h, sc.Interval.String())
io.WriteString(h, sc.Timeout.String())
io.WriteString(h, sc.Method)
// Only include TLSSkipVerify if set to maintain ID stability with Nomad <0.6
if sc.TLSSkipVerify {
io.WriteString(h, "true")
}
// Since map iteration order isn't stable we need to write k/v pairs to
// a slice and sort it before hashing.
if len(sc.Header) > 0 {
headers := make([]string, 0, len(sc.Header))
for k, v := range sc.Header {
headers = append(headers, k+strings.Join(v, ""))
}
sort.Strings(headers)
io.WriteString(h, strings.Join(headers, ""))
}
return fmt.Sprintf("%x", h.Sum(nil))
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment