Skip to content

Commit

Permalink
Store check type in catalog (#6561)
Browse files Browse the repository at this point in the history
  • Loading branch information
freddygv committed Oct 17, 2019
1 parent dc6df0b commit 95518c9
Show file tree
Hide file tree
Showing 15 changed files with 239 additions and 4 deletions.
3 changes: 3 additions & 0 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,7 @@ func (a *Agent) addServiceInternal(req *addServiceRequest) error {
ServiceID: service.ID,
ServiceName: service.Service,
ServiceTags: service.Tags,
Type: chkType.Type(),
}
if chkType.Status != "" {
check.Status = chkType.Status
Expand Down Expand Up @@ -3614,6 +3615,7 @@ func (a *Agent) EnableServiceMaintenance(serviceID, reason, token string) error
ServiceID: service.ID,
ServiceName: service.Service,
Status: api.HealthCritical,
Type: "maintenance",
}
a.AddCheck(check, nil, true, token, ConfigSourceLocal)
a.logger.Printf("[INFO] agent: Service %q entered maintenance mode", serviceID)
Expand Down Expand Up @@ -3660,6 +3662,7 @@ func (a *Agent) EnableNodeMaintenance(reason, token string) {
Name: "Node Maintenance Mode",
Notes: reason,
Status: api.HealthCritical,
Type: "maintenance",
}
a.AddCheck(check, nil, true, token, ConfigSourceLocal)
a.logger.Printf("[INFO] agent: Node entered maintenance mode")
Expand Down
3 changes: 3 additions & 0 deletions agent/agent_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,9 @@ func (s *HTTPServer) AgentRegisterCheck(resp http.ResponseWriter, req *http.Requ
return nil, nil
}

// Store the type of check based on the definition
health.Type = chkType.Type()

if health.ServiceID != "" {
// fixup the service name so that vetCheckRegister requires the right ACLs
service := s.agent.State.Service(health.ServiceID)
Expand Down
10 changes: 10 additions & 0 deletions agent/agent_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2478,6 +2478,11 @@ func testAgent_RegisterService(t *testing.T, extraHCL string) {
if len(checks) != 3 {
t.Fatalf("bad: %v", checks)
}
for _, c := range checks {
if c.Type != "ttl" {
t.Fatalf("expected ttl check type, got %s", c.Type)
}
}

if len(a.checkTTLs) != 3 {
t.Fatalf("missing test check ttls: %v", a.checkTTLs)
Expand Down Expand Up @@ -4115,6 +4120,11 @@ func TestAgent_RegisterCheck_Service(t *testing.T) {
if result["memcache_check2"].ServiceID != "memcache" {
t.Fatalf("bad: %#v", result["memcached_check2"])
}

// Make sure the new check has the right type
if result["memcache_check2"].Type != "ttl" {
t.Fatalf("expected TTL type, got %s", result["memcache_check2"].Type)
}
}

func TestAgent_Monitor(t *testing.T) {
Expand Down
24 changes: 22 additions & 2 deletions agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ func testAgent_AddService(t *testing.T, extraHCL string) {
ServiceID: "svcid1",
ServiceName: "svcname1",
ServiceTags: []string{"tag1"},
Type: "ttl",
},
},
},
Expand Down Expand Up @@ -438,6 +439,7 @@ func testAgent_AddService(t *testing.T, extraHCL string) {
ServiceID: "svcid2",
ServiceName: "svcname2",
ServiceTags: []string{"tag2"},
Type: "ttl",
},
"check-noname": &structs.HealthCheck{
Node: "node1",
Expand All @@ -447,6 +449,7 @@ func testAgent_AddService(t *testing.T, extraHCL string) {
ServiceID: "svcid2",
ServiceName: "svcname2",
ServiceTags: []string{"tag2"},
Type: "ttl",
},
"service:svcid2:3": &structs.HealthCheck{
Node: "node1",
Expand All @@ -456,6 +459,7 @@ func testAgent_AddService(t *testing.T, extraHCL string) {
ServiceID: "svcid2",
ServiceName: "svcname2",
ServiceTags: []string{"tag2"},
Type: "ttl",
},
"service:svcid2:4": &structs.HealthCheck{
Node: "node1",
Expand All @@ -465,6 +469,7 @@ func testAgent_AddService(t *testing.T, extraHCL string) {
ServiceID: "svcid2",
ServiceName: "svcname2",
ServiceTags: []string{"tag2"},
Type: "ttl",
},
},
},
Expand Down Expand Up @@ -828,8 +833,23 @@ func testAgent_RemoveServiceRemovesAllChecks(t *testing.T, extraHCL string) {
svc := &structs.NodeService{ID: "redis", Service: "redis", Port: 8000}
chk1 := &structs.CheckType{CheckID: "chk1", Name: "chk1", TTL: time.Minute}
chk2 := &structs.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"}
hchk1 := &structs.HealthCheck{
Node: "node1",
CheckID: "chk1",
Name: "chk1",
Status: "critical",
ServiceID: "redis",
ServiceName: "redis",
Type: "ttl",
}
hchk2 := &structs.HealthCheck{Node: "node1",
CheckID: "chk2",
Name: "chk2",
Status: "critical",
ServiceID: "redis",
ServiceName: "redis",
Type: "ttl",
}

// register service with chk1
if err := a.AddService(svc, []*structs.CheckType{chk1}, false, "", ConfigSourceLocal); err != nil {
Expand Down
53 changes: 53 additions & 0 deletions agent/catalog_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,59 @@ func TestCatalogServices_NodeMetaFilter(t *testing.T) {
}
}

func TestCatalogRegister_checkRegistration(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()

// Register node with a service and check
check := structs.HealthCheck{
Node: "foo",
CheckID: "foo-check",
Name: "foo check",
ServiceID: "api",
Definition: structs.HealthCheckDefinition{
TCP: "localhost:8888",
Interval: 5 * time.Second,
},
}

args := &structs.RegisterRequest{
Datacenter: "dc1",
Node: "foo",
Address: "127.0.0.1",
Service: &structs.NodeService{
Service: "api",
},
Check: &check,
}

var out struct{}
if err := a.RPC("Catalog.Register", args, &out); err != nil {
t.Fatalf("err: %v", err)
}

retry.Run(t, func(r *retry.R) {
req, _ := http.NewRequest("GET", "/v1/health/checks/api", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceChecks(resp, req)
if err != nil {
r.Fatalf("err: %v", err)
}

checks := obj.(structs.HealthChecks)
if len(checks) != 1 {
r.Fatalf("expected 1 check, got: %d", len(checks))
}
if checks[0].CheckID != check.CheckID {
r.Fatalf("expected check id %s, got %s", check.Type, checks[0].Type)
}
if checks[0].Type != "tcp" {
r.Fatalf("expected check type tcp, got %s", checks[0].Type)
}
})
}

func TestCatalogServiceNodes(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
Expand Down
7 changes: 7 additions & 0 deletions agent/consul/catalog_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error
check.Node = args.Node
}
checkPreApply(check)

// Populate check type for cases when a check is registered in the catalog directly
// and not via anti-entropy
if check.Type == "" {
chkType := check.CheckType()
check.Type = chkType.Type()
}
}

// Check the complete register request against the given ACL policy.
Expand Down
57 changes: 57 additions & 0 deletions agent/health_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@ func TestHealthServiceChecks(t *testing.T) {
Node: a.Config.NodeName,
Name: "consul check",
ServiceID: "consul",
Type: "grpc",
},
}

Expand All @@ -357,6 +358,9 @@ func TestHealthServiceChecks(t *testing.T) {
if len(nodes) != 1 {
t.Fatalf("bad: %v", obj)
}
if nodes[0].Type != "grpc" {
t.Fatalf("expected grpc check type, got %s", nodes[0].Type)
}
}

func TestHealthServiceChecks_NodeMetaFilter(t *testing.T) {
Expand Down Expand Up @@ -970,6 +974,59 @@ func TestHealthServiceNodes_PassingFilter(t *testing.T) {
})
}

func TestHealthServiceNodes_CheckType(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")

req, _ := http.NewRequest("GET", "/v1/health/service/consul?dc=dc1", nil)
resp := httptest.NewRecorder()
obj, err := a.srv.HealthServiceNodes(resp, req)
require.NoError(t, err)
assertIndex(t, resp)

// Should be 1 health check for consul
nodes := obj.(structs.CheckServiceNodes)
if len(nodes) != 1 {
t.Fatalf("expected 1 node, got %d", len(nodes))
}

args := &structs.RegisterRequest{
Datacenter: "dc1",
Node: a.Config.NodeName,
Address: "127.0.0.1",
NodeMeta: map[string]string{"somekey": "somevalue"},
Check: &structs.HealthCheck{
Node: a.Config.NodeName,
Name: "consul check",
ServiceID: "consul",
Type: "grpc",
},
}

var out struct{}
require.NoError(t, a.RPC("Catalog.Register", args, &out))

req, _ = http.NewRequest("GET", "/v1/health/service/consul?dc=dc1", nil)
resp = httptest.NewRecorder()
obj, err = a.srv.HealthServiceNodes(resp, req)
require.NoError(t, err)

assertIndex(t, resp)

// Should be a non-nil empty list for checks
nodes = obj.(structs.CheckServiceNodes)
require.Len(t, nodes, 1)
require.Len(t, nodes[0].Checks, 2)

for _, check := range nodes[0].Checks {
if check.Name == "consul check" && check.Type != "grpc" {
t.Fatalf("exptected grpc check type, got %s", check.Type)
}
}
}

func TestHealthServiceNodes_WanTranslation(t *testing.T) {
t.Parallel()
a1 := NewTestAgent(t, t.Name(), `
Expand Down
21 changes: 21 additions & 0 deletions agent/structs/check_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,24 @@ func (c *CheckType) IsDocker() bool {
func (c *CheckType) IsGRPC() bool {
return c.GRPC != "" && c.Interval > 0
}

func (c *CheckType) Type() string {
switch {
case c.IsGRPC():
return "grpc"
case c.IsHTTP():
return "http"
case c.IsTTL():
return "ttl"
case c.IsTCP():
return "tcp"
case c.IsAlias():
return "alias"
case c.IsDocker():
return "docker"
case c.IsScript():
return "script"
default:
return ""
}
}
27 changes: 27 additions & 0 deletions agent/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,7 @@ type HealthCheck struct {
ServiceID string // optional associated service
ServiceName string // optional service name
ServiceTags []string // optional service tags
Type string // Check type: http/ttl/tcp/etc

Definition HealthCheckDefinition `bexpr:"-"`

Expand Down Expand Up @@ -1254,6 +1255,32 @@ func (c *HealthCheck) Clone() *HealthCheck {
return clone
}

func (c *HealthCheck) CheckType() *CheckType {
return &CheckType{
CheckID: c.CheckID,
Name: c.Name,
Status: c.Status,
Notes: c.Notes,

ScriptArgs: c.Definition.ScriptArgs,
AliasNode: c.Definition.AliasNode,
AliasService: c.Definition.AliasService,
HTTP: c.Definition.HTTP,
GRPC: c.Definition.GRPC,
GRPCUseTLS: c.Definition.GRPCUseTLS,
Header: c.Definition.Header,
Method: c.Definition.Method,
TCP: c.Definition.TCP,
Interval: c.Definition.Interval,
DockerContainerID: c.Definition.DockerContainerID,
Shell: c.Definition.Shell,
TLSSkipVerify: c.Definition.TLSSkipVerify,
Timeout: c.Definition.Timeout,
TTL: c.Definition.TTL,
DeregisterCriticalServiceAfter: c.Definition.DeregisterCriticalServiceAfter,
}
}

// HealthChecks is a collection of HealthCheck structs.
type HealthChecks []*HealthCheck

Expand Down
5 changes: 5 additions & 0 deletions agent/structs/structs_filtering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,11 @@ var expectedFieldConfigHealthCheck bexpr.FieldConfigurations = bexpr.FieldConfig
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchIsEmpty, bexpr.MatchIsNotEmpty, bexpr.MatchIn, bexpr.MatchNotIn},
StructFieldName: "ServiceTags",
},
"Type": &bexpr.FieldConfiguration{
CoerceFn: bexpr.CoerceString,
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches},
StructFieldName: "Type",
},
}

var expectedFieldConfigCheckServiceNode bexpr.FieldConfigurations = bexpr.FieldConfigurations{
Expand Down
1 change: 1 addition & 0 deletions api/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type AgentCheck struct {
Output string
ServiceID string
ServiceName string
Type string
Definition HealthCheckDefinition
}

Expand Down
Loading

0 comments on commit 95518c9

Please sign in to comment.