mirror of https://github.com/hashicorp/consul
agent: support custom check id and name
This patch adds support for a custom check id and name when registering a service. This is achieved by adding a CheckID and a Name field to the CheckType structure which is used to register checks with a service and when returning health check definitions. CheckDefinition is a superset of CheckType which duplicates some of the fields of CheckType. This patch decouples these two structures by removing the embedding of CheckType in CheckDefinition. Fixes #3047pull/3051/merge
parent
ad40a855bd
commit
8ad66f4bea
|
@ -1158,14 +1158,21 @@ func (a *Agent) AddService(service *structs.NodeService, chkTypes CheckTypes, pe
|
||||||
|
|
||||||
// Create an associated health check
|
// Create an associated health check
|
||||||
for i, chkType := range chkTypes {
|
for i, chkType := range chkTypes {
|
||||||
checkID := fmt.Sprintf("service:%s", service.ID)
|
checkID := string(chkType.CheckID)
|
||||||
|
if checkID == "" {
|
||||||
|
checkID = fmt.Sprintf("service:%s", service.ID)
|
||||||
if len(chkTypes) > 1 {
|
if len(chkTypes) > 1 {
|
||||||
checkID += fmt.Sprintf(":%d", i+1)
|
checkID += fmt.Sprintf(":%d", i+1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
name := chkType.Name
|
||||||
|
if name == "" {
|
||||||
|
name = fmt.Sprintf("Service '%s' check", service.Service)
|
||||||
|
}
|
||||||
check := &structs.HealthCheck{
|
check := &structs.HealthCheck{
|
||||||
Node: a.config.NodeName,
|
Node: a.config.NodeName,
|
||||||
CheckID: types.CheckID(checkID),
|
CheckID: types.CheckID(checkID),
|
||||||
Name: fmt.Sprintf("Service '%s' check", service.Service),
|
Name: name,
|
||||||
Status: api.HealthCritical,
|
Status: api.HealthCritical,
|
||||||
Notes: chkType.Notes,
|
Notes: chkType.Notes,
|
||||||
ServiceID: service.ID,
|
ServiceID: service.ID,
|
||||||
|
@ -1703,7 +1710,7 @@ func (a *Agent) loadChecks(conf *Config) error {
|
||||||
// Register the checks from config
|
// Register the checks from config
|
||||||
for _, check := range conf.Checks {
|
for _, check := range conf.Checks {
|
||||||
health := check.HealthCheck(conf.NodeName)
|
health := check.HealthCheck(conf.NodeName)
|
||||||
chkType := &check.CheckType
|
chkType := check.CheckType()
|
||||||
if err := a.AddCheck(health, chkType, false, check.Token); err != nil {
|
if err := a.AddCheck(health, chkType, false, check.Token); err != nil {
|
||||||
return fmt.Errorf("Failed to register check '%s': %v %v", check.Name, err, check)
|
return fmt.Errorf("Failed to register check '%s': %v %v", check.Name, err, check)
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
|
||||||
health := args.HealthCheck(s.agent.config.NodeName)
|
health := args.HealthCheck(s.agent.config.NodeName)
|
||||||
|
|
||||||
// Verify the check type.
|
// Verify the check type.
|
||||||
chkType := &args.CheckType
|
chkType := args.CheckType()
|
||||||
if !chkType.Valid() {
|
if !chkType.Valid() {
|
||||||
resp.WriteHeader(400)
|
resp.WriteHeader(400)
|
||||||
fmt.Fprint(resp, invalidCheckMessage)
|
fmt.Fprint(resp, invalidCheckMessage)
|
||||||
|
|
|
@ -650,9 +650,7 @@ func TestAgent_RegisterCheck(t *testing.T) {
|
||||||
// Register node
|
// Register node
|
||||||
args := &CheckDefinition{
|
args := &CheckDefinition{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 15 * time.Second,
|
TTL: 15 * time.Second,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
req, _ := http.NewRequest("GET", "/v1/agent/check/register?token=abc123", jsonReader(args))
|
req, _ := http.NewRequest("GET", "/v1/agent/check/register?token=abc123", jsonReader(args))
|
||||||
obj, err := srv.AgentRegisterCheck(nil, req)
|
obj, err := srv.AgentRegisterCheck(nil, req)
|
||||||
|
@ -694,9 +692,7 @@ func TestAgent_RegisterCheck_Passing(t *testing.T) {
|
||||||
// Register node
|
// Register node
|
||||||
args := &CheckDefinition{
|
args := &CheckDefinition{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 15 * time.Second,
|
TTL: 15 * time.Second,
|
||||||
},
|
|
||||||
Status: api.HealthPassing,
|
Status: api.HealthPassing,
|
||||||
}
|
}
|
||||||
req, _ := http.NewRequest("GET", "/v1/agent/check/register", jsonReader(args))
|
req, _ := http.NewRequest("GET", "/v1/agent/check/register", jsonReader(args))
|
||||||
|
@ -733,9 +729,7 @@ func TestAgent_RegisterCheck_BadStatus(t *testing.T) {
|
||||||
// Register node
|
// Register node
|
||||||
args := &CheckDefinition{
|
args := &CheckDefinition{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 15 * time.Second,
|
TTL: 15 * time.Second,
|
||||||
},
|
|
||||||
Status: "fluffy",
|
Status: "fluffy",
|
||||||
}
|
}
|
||||||
req, _ := http.NewRequest("GET", "/v1/agent/check/register", jsonReader(args))
|
req, _ := http.NewRequest("GET", "/v1/agent/check/register", jsonReader(args))
|
||||||
|
@ -756,9 +750,7 @@ func TestAgent_RegisterCheck_ACLDeny(t *testing.T) {
|
||||||
|
|
||||||
args := &CheckDefinition{
|
args := &CheckDefinition{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 15 * time.Second,
|
TTL: 15 * time.Second,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("no token", func(t *testing.T) {
|
t.Run("no token", func(t *testing.T) {
|
||||||
|
@ -1596,9 +1588,7 @@ func TestAgent_RegisterCheck_Service(t *testing.T) {
|
||||||
checkArgs := &CheckDefinition{
|
checkArgs := &CheckDefinition{
|
||||||
Name: "memcache_check2",
|
Name: "memcache_check2",
|
||||||
ServiceID: "memcache",
|
ServiceID: "memcache",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 15 * time.Second,
|
TTL: 15 * time.Second,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
req, _ = http.NewRequest("GET", "/v1/agent/check/register", jsonReader(checkArgs))
|
req, _ = http.NewRequest("GET", "/v1/agent/check/register", jsonReader(checkArgs))
|
||||||
if _, err := srv.AgentRegisterCheck(nil, req); err != nil {
|
if _, err := srv.AgentRegisterCheck(nil, req); err != nil {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/hashicorp/consul/version"
|
"github.com/hashicorp/consul/version"
|
||||||
"github.com/hashicorp/go-uuid"
|
"github.com/hashicorp/go-uuid"
|
||||||
"github.com/hashicorp/raft"
|
"github.com/hashicorp/raft"
|
||||||
|
"github.com/pascaldekloe/goe/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -423,100 +424,148 @@ func TestAgent_makeNodeID(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAgent_AddService(t *testing.T) {
|
func TestAgent_AddService(t *testing.T) {
|
||||||
dir, agent := makeAgent(t, nextConfig())
|
cfg := nextConfig()
|
||||||
|
cfg.NodeName = "node1"
|
||||||
|
dir, agent := makeAgent(t, cfg)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
defer agent.Shutdown()
|
defer agent.Shutdown()
|
||||||
|
|
||||||
// Service registration with a single check
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
srv *structs.NodeService
|
||||||
|
chkTypes CheckTypes
|
||||||
|
healthChks map[string]*structs.HealthCheck
|
||||||
|
}{
|
||||||
{
|
{
|
||||||
srv := &structs.NodeService{
|
"one check",
|
||||||
ID: "redis",
|
&structs.NodeService{
|
||||||
Service: "redis",
|
ID: "svcid1",
|
||||||
Tags: []string{"foo"},
|
Service: "svcname1",
|
||||||
Port: 8000,
|
Tags: []string{"tag1"},
|
||||||
}
|
Port: 8100,
|
||||||
chkTypes := CheckTypes{
|
},
|
||||||
|
CheckTypes{
|
||||||
|
&CheckType{
|
||||||
|
CheckID: "check1",
|
||||||
|
Name: "name1",
|
||||||
|
TTL: time.Minute,
|
||||||
|
Notes: "note1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]*structs.HealthCheck{
|
||||||
|
"check1": &structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "check1",
|
||||||
|
Name: "name1",
|
||||||
|
Status: "critical",
|
||||||
|
Notes: "note1",
|
||||||
|
ServiceID: "svcid1",
|
||||||
|
ServiceName: "svcname1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"multiple checks",
|
||||||
|
&structs.NodeService{
|
||||||
|
ID: "svcid2",
|
||||||
|
Service: "svcname2",
|
||||||
|
Tags: []string{"tag2"},
|
||||||
|
Port: 8200,
|
||||||
|
},
|
||||||
|
CheckTypes{
|
||||||
|
&CheckType{
|
||||||
|
CheckID: "check1",
|
||||||
|
Name: "name1",
|
||||||
|
TTL: time.Minute,
|
||||||
|
Notes: "note1",
|
||||||
|
},
|
||||||
|
&CheckType{
|
||||||
|
CheckID: "check-noname",
|
||||||
|
TTL: time.Minute,
|
||||||
|
},
|
||||||
|
&CheckType{
|
||||||
|
Name: "check-noid",
|
||||||
|
TTL: time.Minute,
|
||||||
|
},
|
||||||
&CheckType{
|
&CheckType{
|
||||||
TTL: time.Minute,
|
TTL: time.Minute,
|
||||||
Notes: "redis heath check 2",
|
},
|
||||||
|
},
|
||||||
|
map[string]*structs.HealthCheck{
|
||||||
|
"check1": &structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "check1",
|
||||||
|
Name: "name1",
|
||||||
|
Status: "critical",
|
||||||
|
Notes: "note1",
|
||||||
|
ServiceID: "svcid2",
|
||||||
|
ServiceName: "svcname2",
|
||||||
|
},
|
||||||
|
"check-noname": &structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "check-noname",
|
||||||
|
Name: "Service 'svcname2' check",
|
||||||
|
Status: "critical",
|
||||||
|
ServiceID: "svcid2",
|
||||||
|
ServiceName: "svcname2",
|
||||||
|
},
|
||||||
|
"service:svcid2:3": &structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "service:svcid2:3",
|
||||||
|
Name: "check-noid",
|
||||||
|
Status: "critical",
|
||||||
|
ServiceID: "svcid2",
|
||||||
|
ServiceName: "svcname2",
|
||||||
|
},
|
||||||
|
"service:svcid2:4": &structs.HealthCheck{
|
||||||
|
Node: "node1",
|
||||||
|
CheckID: "service:svcid2:4",
|
||||||
|
Name: "Service 'svcname2' check",
|
||||||
|
Status: "critical",
|
||||||
|
ServiceID: "svcid2",
|
||||||
|
ServiceName: "svcname2",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := agent.AddService(srv, chkTypes, false, "")
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.desc, func(t *testing.T) {
|
||||||
|
// check the service registration
|
||||||
|
t.Run(tt.srv.ID, func(t *testing.T) {
|
||||||
|
err := agent.AddService(tt.srv, tt.chkTypes, false, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %v", err)
|
t.Fatalf("err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have a state mapping
|
got, want := agent.state.Services()[tt.srv.ID], tt.srv
|
||||||
if _, ok := agent.state.Services()["redis"]; !ok {
|
verify.Values(t, "", got, want)
|
||||||
t.Fatalf("missing redis service")
|
})
|
||||||
|
|
||||||
|
// check the health checks
|
||||||
|
for k, v := range tt.healthChks {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
got, want := agent.state.Checks()[types.CheckID(k)], v
|
||||||
|
verify.Values(t, k, got, want)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the check is registered
|
// check the ttl checks
|
||||||
if _, ok := agent.state.Checks()["service:redis"]; !ok {
|
for k := range tt.healthChks {
|
||||||
t.Fatalf("missing redis check")
|
t.Run(k+" ttl", func(t *testing.T) {
|
||||||
|
chk := agent.checkTTLs[types.CheckID(k)]
|
||||||
|
if chk == nil {
|
||||||
|
t.Fatal("got nil want TTL check")
|
||||||
}
|
}
|
||||||
|
if got, want := string(chk.CheckID), k; got != want {
|
||||||
// Ensure a TTL is setup
|
t.Fatalf("got CheckID %v want %v", got, want)
|
||||||
if _, ok := agent.checkTTLs["service:redis"]; !ok {
|
|
||||||
t.Fatalf("missing redis check ttl")
|
|
||||||
}
|
}
|
||||||
|
if got, want := chk.TTL, time.Minute; got != want {
|
||||||
// Ensure the notes are passed through
|
t.Fatalf("got TTL %v want %v", got, want)
|
||||||
if agent.state.Checks()["service:redis"].Notes == "" {
|
|
||||||
t.Fatalf("missing redis check notes")
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
})
|
||||||
// Service registration with multiple checks
|
|
||||||
{
|
|
||||||
srv := &structs.NodeService{
|
|
||||||
ID: "memcache",
|
|
||||||
Service: "memcache",
|
|
||||||
Tags: []string{"bar"},
|
|
||||||
Port: 8000,
|
|
||||||
}
|
|
||||||
chkTypes := CheckTypes{
|
|
||||||
&CheckType{
|
|
||||||
TTL: time.Minute,
|
|
||||||
Notes: "memcache health check 1",
|
|
||||||
},
|
|
||||||
&CheckType{
|
|
||||||
TTL: time.Second,
|
|
||||||
Notes: "memcache heath check 2",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := agent.AddService(srv, chkTypes, false, ""); err != nil {
|
|
||||||
t.Fatalf("err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure we have a state mapping
|
|
||||||
if _, ok := agent.state.Services()["memcache"]; !ok {
|
|
||||||
t.Fatalf("missing memcache service")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure both checks were added
|
|
||||||
if _, ok := agent.state.Checks()["service:memcache:1"]; !ok {
|
|
||||||
t.Fatalf("missing memcache:1 check")
|
|
||||||
}
|
|
||||||
if _, ok := agent.state.Checks()["service:memcache:2"]; !ok {
|
|
||||||
t.Fatalf("missing memcache:2 check")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a TTL is setup
|
|
||||||
if _, ok := agent.checkTTLs["service:memcache:1"]; !ok {
|
|
||||||
t.Fatalf("missing memcache:1 check ttl")
|
|
||||||
}
|
|
||||||
if _, ok := agent.checkTTLs["service:memcache:2"]; !ok {
|
|
||||||
t.Fatalf("missing memcache:2 check ttl")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the notes are passed through
|
|
||||||
if agent.state.Checks()["service:memcache:1"].Notes == "" {
|
|
||||||
t.Fatalf("missing redis check notes")
|
|
||||||
}
|
|
||||||
if agent.state.Checks()["service:memcache:2"].Notes == "" {
|
|
||||||
t.Fatalf("missing redis check notes")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,10 +607,10 @@ func TestAgent_RemoveService(t *testing.T) {
|
||||||
ID: "check2",
|
ID: "check2",
|
||||||
Name: "check2",
|
Name: "check2",
|
||||||
ServiceID: "memcache",
|
ServiceID: "memcache",
|
||||||
CheckType: CheckType{TTL: time.Minute},
|
TTL: time.Minute,
|
||||||
}
|
}
|
||||||
hc := check.HealthCheck("node1")
|
hc := check.HealthCheck("node1")
|
||||||
if err := agent.AddCheck(hc, &check.CheckType, false, ""); err != nil {
|
if err := agent.AddCheck(hc, check.CheckType(), false, ""); err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,6 +668,56 @@ func TestAgent_RemoveService(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAgent_RemoveServiceRemovesAllChecks(t *testing.T) {
|
||||||
|
cfg := nextConfig()
|
||||||
|
cfg.NodeName = "node1"
|
||||||
|
dir, agent := makeAgent(t, cfg)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
defer agent.Shutdown()
|
||||||
|
|
||||||
|
svc := &structs.NodeService{ID: "redis", Service: "redis", Port: 8000}
|
||||||
|
chk1 := &CheckType{CheckID: "chk1", Name: "chk1", TTL: time.Minute}
|
||||||
|
chk2 := &CheckType{CheckID: "chk2", Name: "chk2", TTL: 2 * time.Minute}
|
||||||
|
hchk1 := &structs.HealthCheck{Node: "node1", CheckID: "chk1", Name: "chk1", Status: "critical", ServiceID: "redis", ServiceName: "redis"}
|
||||||
|
hchk2 := &structs.HealthCheck{Node: "node1", CheckID: "chk2", Name: "chk2", Status: "critical", ServiceID: "redis", ServiceName: "redis"}
|
||||||
|
|
||||||
|
// register service with chk1
|
||||||
|
if err := agent.AddService(svc, CheckTypes{chk1}, false, ""); err != nil {
|
||||||
|
t.Fatal("Failed to register service", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify chk1 exists
|
||||||
|
if agent.state.Checks()["chk1"] == nil {
|
||||||
|
t.Fatal("Could not find health check chk1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the service with chk2
|
||||||
|
if err := agent.AddService(svc, CheckTypes{chk2}, false, ""); err != nil {
|
||||||
|
t.Fatal("Failed to update service", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that both checks are there
|
||||||
|
if got, want := agent.state.Checks()["chk1"], hchk1; !verify.Values(t, "", got, want) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
if got, want := agent.state.Checks()["chk2"], hchk2; !verify.Values(t, "", got, want) {
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove service
|
||||||
|
if err := agent.RemoveService("redis", false); err != nil {
|
||||||
|
t.Fatal("Failed to remove service", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that both checks are gone
|
||||||
|
if agent.state.Checks()["chk1"] != nil {
|
||||||
|
t.Fatal("Found health check chk1 want nil")
|
||||||
|
}
|
||||||
|
if agent.state.Checks()["chk2"] != nil {
|
||||||
|
t.Fatal("Found health check chk2 want nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgent_AddCheck(t *testing.T) {
|
func TestAgent_AddCheck(t *testing.T) {
|
||||||
dir, agent := makeAgent(t, nextConfig())
|
dir, agent := makeAgent(t, nextConfig())
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
@ -1275,10 +1374,8 @@ func TestAgent_PurgeCheckOnDuplicate(t *testing.T) {
|
||||||
ID: "mem",
|
ID: "mem",
|
||||||
Name: "memory check",
|
Name: "memory check",
|
||||||
Notes: "my cool notes",
|
Notes: "my cool notes",
|
||||||
CheckType: CheckType{
|
|
||||||
Script: "/bin/check-redis.py",
|
Script: "/bin/check-redis.py",
|
||||||
Interval: 30 * time.Second,
|
Interval: 30 * time.Second,
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Checks = []*CheckDefinition{check2}
|
config.Checks = []*CheckDefinition{check2}
|
||||||
|
@ -1308,9 +1405,7 @@ func TestAgent_loadChecks_token(t *testing.T) {
|
||||||
ID: "rabbitmq",
|
ID: "rabbitmq",
|
||||||
Name: "rabbitmq",
|
Name: "rabbitmq",
|
||||||
Token: "abc123",
|
Token: "abc123",
|
||||||
CheckType: CheckType{
|
|
||||||
TTL: 10 * time.Second,
|
TTL: 10 * time.Second,
|
||||||
},
|
|
||||||
})
|
})
|
||||||
dir, agent := makeAgent(t, config)
|
dir, agent := makeAgent(t, config)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
|
@ -44,6 +44,17 @@ const (
|
||||||
// provided: TTL or Script/Interval or HTTP/Interval or TCP/Interval or
|
// provided: TTL or Script/Interval or HTTP/Interval or TCP/Interval or
|
||||||
// Docker/Interval.
|
// Docker/Interval.
|
||||||
type CheckType struct {
|
type CheckType struct {
|
||||||
|
// fields already embedded in CheckDefinition
|
||||||
|
// Note: CheckType.CheckID == CheckDefinition.ID
|
||||||
|
|
||||||
|
CheckID types.CheckID
|
||||||
|
Name string
|
||||||
|
Status string
|
||||||
|
Notes string
|
||||||
|
|
||||||
|
// fields copied to CheckDefinition
|
||||||
|
// Update CheckDefinition when adding fields here
|
||||||
|
|
||||||
Script string
|
Script string
|
||||||
HTTP string
|
HTTP string
|
||||||
TCP string
|
TCP string
|
||||||
|
@ -51,7 +62,6 @@ type CheckType struct {
|
||||||
DockerContainerID string
|
DockerContainerID string
|
||||||
Shell string
|
Shell string
|
||||||
TLSSkipVerify bool
|
TLSSkipVerify bool
|
||||||
|
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
|
|
||||||
|
@ -59,10 +69,6 @@ type CheckType struct {
|
||||||
// service, if any, to be deregistered if this check is critical for
|
// service, if any, to be deregistered if this check is critical for
|
||||||
// longer than this duration.
|
// longer than this duration.
|
||||||
DeregisterCriticalServiceAfter time.Duration
|
DeregisterCriticalServiceAfter time.Duration
|
||||||
|
|
||||||
Status string
|
|
||||||
|
|
||||||
Notes string
|
|
||||||
}
|
}
|
||||||
type CheckTypes []*CheckType
|
type CheckTypes []*CheckType
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/consul/testutil"
|
"github.com/hashicorp/consul/testutil"
|
||||||
|
"github.com/pascaldekloe/goe/verify"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigEncryptBytes(t *testing.T) {
|
func TestConfigEncryptBytes(t *testing.T) {
|
||||||
|
@ -1324,13 +1325,13 @@ func TestDecodeConfig_Checks(t *testing.T) {
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "chk1",
|
"id": "chk1",
|
||||||
"name": "mem",
|
"name": "name1",
|
||||||
"script": "/bin/check_mem",
|
"script": "/bin/check_mem",
|
||||||
"interval": "5s"
|
"interval": "5s"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "chk2",
|
"id": "chk2",
|
||||||
"name": "cpu",
|
"name": "name2",
|
||||||
"script": "/bin/check_cpu",
|
"script": "/bin/check_cpu",
|
||||||
"interval": "10s"
|
"interval": "10s"
|
||||||
},
|
},
|
||||||
|
@ -1369,76 +1370,61 @@ func TestDecodeConfig_Checks(t *testing.T) {
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
got, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := &Config{
|
want := &Config{
|
||||||
Checks: []*CheckDefinition{
|
Checks: []*CheckDefinition{
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk1",
|
ID: "chk1",
|
||||||
Name: "mem",
|
Name: "name1",
|
||||||
CheckType: CheckType{
|
|
||||||
Script: "/bin/check_mem",
|
Script: "/bin/check_mem",
|
||||||
Interval: 5 * time.Second,
|
Interval: 5 * time.Second,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk2",
|
ID: "chk2",
|
||||||
Name: "cpu",
|
Name: "name2",
|
||||||
CheckType: CheckType{
|
|
||||||
Script: "/bin/check_cpu",
|
Script: "/bin/check_cpu",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk3",
|
ID: "chk3",
|
||||||
Name: "service:redis:tx",
|
Name: "service:redis:tx",
|
||||||
ServiceID: "redis",
|
ServiceID: "redis",
|
||||||
CheckType: CheckType{
|
|
||||||
Script: "/bin/check_redis_tx",
|
Script: "/bin/check_redis_tx",
|
||||||
Interval: time.Minute,
|
Interval: time.Minute,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk4",
|
ID: "chk4",
|
||||||
Name: "service:elasticsearch:health",
|
Name: "service:elasticsearch:health",
|
||||||
ServiceID: "elasticsearch",
|
ServiceID: "elasticsearch",
|
||||||
CheckType: CheckType{
|
|
||||||
HTTP: "http://localhost:9200/_cluster_health",
|
HTTP: "http://localhost:9200/_cluster_health",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
Timeout: 100 * time.Millisecond,
|
Timeout: 100 * time.Millisecond,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk5",
|
ID: "chk5",
|
||||||
Name: "service:sslservice",
|
Name: "service:sslservice",
|
||||||
ServiceID: "sslservice",
|
ServiceID: "sslservice",
|
||||||
CheckType: CheckType{
|
|
||||||
HTTP: "https://sslservice/status",
|
HTTP: "https://sslservice/status",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
Timeout: 100 * time.Millisecond,
|
Timeout: 100 * time.Millisecond,
|
||||||
TLSSkipVerify: false,
|
TLSSkipVerify: false,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk6",
|
ID: "chk6",
|
||||||
Name: "service:insecure-sslservice",
|
Name: "service:insecure-sslservice",
|
||||||
ServiceID: "insecure-sslservice",
|
ServiceID: "insecure-sslservice",
|
||||||
CheckType: CheckType{
|
|
||||||
HTTP: "https://insecure-sslservice/status",
|
HTTP: "https://insecure-sslservice/status",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
Timeout: 100 * time.Millisecond,
|
Timeout: 100 * time.Millisecond,
|
||||||
TLSSkipVerify: true,
|
TLSSkipVerify: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(config, expected) {
|
|
||||||
t.Fatalf("bad: %#v", config)
|
|
||||||
}
|
}
|
||||||
|
verify.Values(t, "", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeConfig_Multiples(t *testing.T) {
|
func TestDecodeConfig_Multiples(t *testing.T) {
|
||||||
|
@ -1452,6 +1438,8 @@ func TestDecodeConfig_Multiples(t *testing.T) {
|
||||||
],
|
],
|
||||||
"port": 6000,
|
"port": 6000,
|
||||||
"check": {
|
"check": {
|
||||||
|
"checkID": "chk1",
|
||||||
|
"name": "name1",
|
||||||
"script": "/bin/check_redis -p 6000",
|
"script": "/bin/check_redis -p 6000",
|
||||||
"interval": "5s",
|
"interval": "5s",
|
||||||
"ttl": "20s"
|
"ttl": "20s"
|
||||||
|
@ -1460,23 +1448,25 @@ func TestDecodeConfig_Multiples(t *testing.T) {
|
||||||
],
|
],
|
||||||
"checks": [
|
"checks": [
|
||||||
{
|
{
|
||||||
"id": "chk1",
|
"id": "chk2",
|
||||||
"name": "mem",
|
"name": "name2",
|
||||||
"script": "/bin/check_mem",
|
"script": "/bin/check_mem",
|
||||||
"interval": "10s"
|
"interval": "10s"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
config, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
got, err := DecodeConfig(bytes.NewReader([]byte(input)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := &Config{
|
want := &Config{
|
||||||
Services: []*ServiceDefinition{
|
Services: []*ServiceDefinition{
|
||||||
&ServiceDefinition{
|
&ServiceDefinition{
|
||||||
Check: CheckType{
|
Check: CheckType{
|
||||||
|
CheckID: "chk1",
|
||||||
|
Name: "name1",
|
||||||
Interval: 5 * time.Second,
|
Interval: 5 * time.Second,
|
||||||
Script: "/bin/check_redis -p 6000",
|
Script: "/bin/check_redis -p 6000",
|
||||||
TTL: 20 * time.Second,
|
TTL: 20 * time.Second,
|
||||||
|
@ -1491,19 +1481,15 @@ func TestDecodeConfig_Multiples(t *testing.T) {
|
||||||
},
|
},
|
||||||
Checks: []*CheckDefinition{
|
Checks: []*CheckDefinition{
|
||||||
&CheckDefinition{
|
&CheckDefinition{
|
||||||
ID: "chk1",
|
ID: "chk2",
|
||||||
Name: "mem",
|
Name: "name2",
|
||||||
CheckType: CheckType{
|
|
||||||
Script: "/bin/check_mem",
|
Script: "/bin/check_mem",
|
||||||
Interval: 10 * time.Second,
|
Interval: 10 * time.Second,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(config, expected) {
|
verify.Values(t, "", got, want)
|
||||||
t.Fatalf("bad: %#v", config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeConfig_Service(t *testing.T) {
|
func TestDecodeConfig_Service(t *testing.T) {
|
||||||
|
@ -1864,3 +1850,43 @@ func TestUnixSockets(t *testing.T) {
|
||||||
t.Fatalf("bad: %v %v", ok, path2)
|
t.Fatalf("bad: %v %v", ok, path2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCheckDefinitionToCheckType(t *testing.T) {
|
||||||
|
got := &CheckDefinition{
|
||||||
|
ID: "id",
|
||||||
|
Name: "name",
|
||||||
|
Status: "green",
|
||||||
|
Notes: "notes",
|
||||||
|
|
||||||
|
ServiceID: "svcid",
|
||||||
|
Token: "tok",
|
||||||
|
Script: "/bin/foo",
|
||||||
|
HTTP: "someurl",
|
||||||
|
TCP: "host:port",
|
||||||
|
Interval: 1 * time.Second,
|
||||||
|
DockerContainerID: "abc123",
|
||||||
|
Shell: "/bin/ksh",
|
||||||
|
TLSSkipVerify: true,
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
TTL: 3 * time.Second,
|
||||||
|
DeregisterCriticalServiceAfter: 4 * time.Second,
|
||||||
|
}
|
||||||
|
want := &CheckType{
|
||||||
|
CheckID: "id",
|
||||||
|
Name: "name",
|
||||||
|
Status: "green",
|
||||||
|
Notes: "notes",
|
||||||
|
|
||||||
|
Script: "/bin/foo",
|
||||||
|
HTTP: "someurl",
|
||||||
|
TCP: "host:port",
|
||||||
|
Interval: 1 * time.Second,
|
||||||
|
DockerContainerID: "abc123",
|
||||||
|
Shell: "/bin/ksh",
|
||||||
|
TLSSkipVerify: true,
|
||||||
|
Timeout: 2 * time.Second,
|
||||||
|
TTL: 3 * time.Second,
|
||||||
|
DeregisterCriticalServiceAfter: 4 * time.Second,
|
||||||
|
}
|
||||||
|
verify.Values(t, "", got.CheckType(), want)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
"github.com/hashicorp/consul/consul/structs"
|
"github.com/hashicorp/consul/consul/structs"
|
||||||
"github.com/hashicorp/consul/types"
|
"github.com/hashicorp/consul/types"
|
||||||
|
@ -52,7 +54,22 @@ type CheckDefinition struct {
|
||||||
ServiceID string
|
ServiceID string
|
||||||
Token string
|
Token string
|
||||||
Status string
|
Status string
|
||||||
CheckType `mapstructure:",squash"`
|
|
||||||
|
// Copied fields from CheckType without the fields
|
||||||
|
// already present in CheckDefinition:
|
||||||
|
//
|
||||||
|
// ID (CheckID), Name, Status, Notes
|
||||||
|
//
|
||||||
|
Script string
|
||||||
|
HTTP string
|
||||||
|
TCP string
|
||||||
|
Interval time.Duration
|
||||||
|
DockerContainerID string
|
||||||
|
Shell string
|
||||||
|
TLSSkipVerify bool
|
||||||
|
Timeout time.Duration
|
||||||
|
TTL time.Duration
|
||||||
|
DeregisterCriticalServiceAfter time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CheckDefinition) HealthCheck(node string) *structs.HealthCheck {
|
func (c *CheckDefinition) HealthCheck(node string) *structs.HealthCheck {
|
||||||
|
@ -73,6 +90,25 @@ func (c *CheckDefinition) HealthCheck(node string) *structs.HealthCheck {
|
||||||
return health
|
return health
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CheckDefinition) CheckType() *CheckType {
|
||||||
|
return &CheckType{
|
||||||
|
CheckID: c.ID,
|
||||||
|
Name: c.Name,
|
||||||
|
Script: c.Script,
|
||||||
|
HTTP: c.HTTP,
|
||||||
|
TCP: c.TCP,
|
||||||
|
Interval: c.Interval,
|
||||||
|
DockerContainerID: c.DockerContainerID,
|
||||||
|
Shell: c.Shell,
|
||||||
|
TLSSkipVerify: c.TLSSkipVerify,
|
||||||
|
Timeout: c.Timeout,
|
||||||
|
TTL: c.TTL,
|
||||||
|
DeregisterCriticalServiceAfter: c.DeregisterCriticalServiceAfter,
|
||||||
|
Status: c.Status,
|
||||||
|
Notes: c.Notes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// persistedService is used to wrap a service definition and bundle it
|
// persistedService is used to wrap a service definition and bundle it
|
||||||
// with an ACL token so we can restore both at a later agent start.
|
// with an ACL token so we can restore both at a later agent start.
|
||||||
type persistedService struct {
|
type persistedService struct {
|
||||||
|
|
|
@ -98,7 +98,18 @@ The table below shows this endpoint's support for
|
||||||
|
|
||||||
- `Check` `(Check: nil)` - Specifies a check. Please see the
|
- `Check` `(Check: nil)` - Specifies a check. Please see the
|
||||||
[check documentation](/api/agent/check.html) for more information about the
|
[check documentation](/api/agent/check.html) for more information about the
|
||||||
accepted fields.
|
accepted fields. If you don't provide a name or id for the check then they
|
||||||
|
will be generated. To provide a custom id and/or name set the `CheckID`
|
||||||
|
and/or `Name` field.
|
||||||
|
|
||||||
|
- `Checks` `(array<Check>: nil`) - Specifies a list of checks. Please see the
|
||||||
|
[check documentation](/api/agent/check.html) for more information about the
|
||||||
|
accepted fields. If you don't provide a name or id for the check then they
|
||||||
|
will be generated. To provide a custom id and/or name set the `CheckID`
|
||||||
|
and/or `Name` field. The automatically generated `Name` and `CheckID` depend
|
||||||
|
on the position of the check within the array, so even though the behavior is
|
||||||
|
determinsitic, it is recommended for all checks to either let consul set the
|
||||||
|
`CheckID` by leaving the field empty/omitting it or to provide a unique value.
|
||||||
|
|
||||||
- `EnableTagOverride` `(bool: false)` - Specifies to disable the anti-entropy
|
- `EnableTagOverride` `(bool: false)` - Specifies to disable the anti-entropy
|
||||||
feature for this service's tags. If `EnableTagOverride` is set to `true` then
|
feature for this service's tags. If `EnableTagOverride` is set to `true` then
|
||||||
|
|
Loading…
Reference in New Issue