mirror of https://github.com/hashicorp/consul
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
671 lines
15 KiB
671 lines
15 KiB
package api |
|
|
|
import ( |
|
"fmt" |
|
"testing" |
|
|
|
"github.com/hashicorp/consul/sdk/testutil" |
|
"github.com/hashicorp/consul/sdk/testutil/retry" |
|
"github.com/stretchr/testify/require" |
|
) |
|
|
|
func TestAPI_HealthNode(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
info, err := agent.Self() |
|
if err != nil { |
|
t.Fatalf("err: %v", err) |
|
} |
|
name := info["Config"]["NodeName"].(string) |
|
retry.Run(t, func(r *retry.R) { |
|
checks, meta, err := health.Node(name, nil) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
if len(checks) == 0 { |
|
r.Fatalf("bad: %v", checks) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthNode_Filter(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
// this sets up the catalog entries with things we can filter on |
|
testNodeServiceCheckRegistrations(t, c, "dc1") |
|
|
|
health := c.Health() |
|
|
|
// filter for just the redis service checks |
|
checks, _, err := health.Node("foo", &QueryOptions{Filter: "ServiceName == redis"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 2) |
|
|
|
// filter out service checks |
|
checks, _, err = health.Node("foo", &QueryOptions{Filter: "ServiceID == ``"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 2) |
|
} |
|
|
|
func TestAPI_HealthChecks_AggregatedStatus(t *testing.T) { |
|
t.Parallel() |
|
|
|
cases := []struct { |
|
name string |
|
checks HealthChecks |
|
exp string |
|
}{ |
|
{ |
|
"empty", |
|
nil, |
|
HealthPassing, |
|
}, |
|
{ |
|
"passing", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthPassing, |
|
}, |
|
}, |
|
HealthPassing, |
|
}, |
|
{ |
|
"warning", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthWarning, |
|
}, |
|
}, |
|
HealthWarning, |
|
}, |
|
{ |
|
"critical", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthCritical, |
|
}, |
|
}, |
|
HealthCritical, |
|
}, |
|
{ |
|
"node_maintenance", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
CheckID: NodeMaint, |
|
}, |
|
}, |
|
HealthMaint, |
|
}, |
|
{ |
|
"service_maintenance", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
CheckID: ServiceMaintPrefix + "service", |
|
}, |
|
}, |
|
HealthMaint, |
|
}, |
|
{ |
|
"unknown", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: "nope-nope-noper", |
|
}, |
|
}, |
|
"", |
|
}, |
|
{ |
|
"maintenance_over_critical", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
CheckID: NodeMaint, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthCritical, |
|
}, |
|
}, |
|
HealthMaint, |
|
}, |
|
{ |
|
"critical_over_warning", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthCritical, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthWarning, |
|
}, |
|
}, |
|
HealthCritical, |
|
}, |
|
{ |
|
"warning_over_passing", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthWarning, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthPassing, |
|
}, |
|
}, |
|
HealthWarning, |
|
}, |
|
{ |
|
"lots", |
|
HealthChecks{ |
|
&HealthCheck{ |
|
Status: HealthPassing, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthPassing, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthPassing, |
|
}, |
|
&HealthCheck{ |
|
Status: HealthWarning, |
|
}, |
|
}, |
|
HealthWarning, |
|
}, |
|
} |
|
|
|
for i, tc := range cases { |
|
t.Run(fmt.Sprintf("%d_%s", i, tc.name), func(t *testing.T) { |
|
act := tc.checks.AggregatedStatus() |
|
if tc.exp != act { |
|
t.Errorf("\nexp: %#v\nact: %#v", tc.exp, act) |
|
} |
|
}) |
|
} |
|
} |
|
|
|
func TestAPI_HealthChecks(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeName = "node123" |
|
}) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
// Make a service with a check |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
Tags: []string{"bar"}, |
|
Check: &AgentServiceCheck{ |
|
TTL: "15s", |
|
}, |
|
} |
|
if err := agent.ServiceRegister(reg); err != nil { |
|
t.Fatalf("err: %v", err) |
|
} |
|
|
|
retry.Run(t, func(r *retry.R) { |
|
checks := HealthChecks{ |
|
&HealthCheck{ |
|
Node: "node123", |
|
CheckID: "service:foo", |
|
Name: "Service 'foo' check", |
|
Status: "critical", |
|
ServiceID: "foo", |
|
ServiceName: "foo", |
|
ServiceTags: []string{"bar"}, |
|
Type: "ttl", |
|
Namespace: defaultNamespace, |
|
}, |
|
} |
|
|
|
out, meta, err := health.Checks("foo", nil) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
checks[0].CreateIndex = out[0].CreateIndex |
|
checks[0].ModifyIndex = out[0].ModifyIndex |
|
require.Equal(r, checks, out) |
|
}) |
|
} |
|
|
|
func TestAPI_HealthChecks_NodeMetaFilter(t *testing.T) { |
|
t.Parallel() |
|
meta := map[string]string{"somekey": "somevalue"} |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeMeta = meta |
|
}) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
s.WaitForSerfCheck(t) |
|
|
|
// Make a service with a check |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
Check: &AgentServiceCheck{ |
|
TTL: "15s", |
|
}, |
|
} |
|
if err := agent.ServiceRegister(reg); err != nil { |
|
t.Fatalf("err: %v", err) |
|
} |
|
|
|
retry.Run(t, func(r *retry.R) { |
|
checks, meta, err := health.Checks("foo", &QueryOptions{NodeMeta: meta}) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
if len(checks) != 1 { |
|
r.Fatalf("expected 1 check, got %d", len(checks)) |
|
} |
|
if checks[0].Type != "ttl" { |
|
r.Fatalf("expected type ttl, got %s", checks[0].Type) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthChecks_Filter(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
// this sets up the catalog entries with things we can filter on |
|
testNodeServiceCheckRegistrations(t, c, "dc1") |
|
|
|
health := c.Health() |
|
|
|
checks, _, err := health.Checks("redis", &QueryOptions{Filter: "Node == foo"}) |
|
require.NoError(t, err) |
|
// 1 service check for each instance |
|
require.Len(t, checks, 2) |
|
|
|
checks, _, err = health.Checks("redis", &QueryOptions{Filter: "Node == bar"}) |
|
require.NoError(t, err) |
|
// 1 service check for each instance |
|
require.Len(t, checks, 1) |
|
|
|
checks, _, err = health.Checks("redis", &QueryOptions{Filter: "Node == foo and v1 in ServiceTags"}) |
|
require.NoError(t, err) |
|
// 1 service check for the matching instance |
|
require.Len(t, checks, 1) |
|
} |
|
|
|
func TestAPI_HealthService(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
health := c.Health() |
|
retry.Run(t, func(r *retry.R) { |
|
// consul service should always exist... |
|
checks, meta, err := health.Service("consul", "", true, nil) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
if len(checks) == 0 { |
|
r.Fatalf("Bad: %v", checks) |
|
} |
|
if _, ok := checks[0].Node.TaggedAddresses["wan"]; !ok { |
|
r.Fatalf("Bad: %v", checks[0].Node) |
|
} |
|
if checks[0].Node.Datacenter != "dc1" { |
|
r.Fatalf("Bad datacenter: %v", checks[0].Node) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthService_SingleTag(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeName = "node123" |
|
}) |
|
defer s.Stop() |
|
agent := c.Agent() |
|
health := c.Health() |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
ID: "foo1", |
|
Tags: []string{"bar"}, |
|
Check: &AgentServiceCheck{ |
|
Status: HealthPassing, |
|
TTL: "15s", |
|
}, |
|
} |
|
require.NoError(t, agent.ServiceRegister(reg)) |
|
retry.Run(t, func(r *retry.R) { |
|
services, meta, err := health.Service("foo", "bar", true, nil) |
|
require.NoError(r, err) |
|
require.NotEqual(r, meta.LastIndex, 0) |
|
require.Len(r, services, 1) |
|
require.Equal(r, services[0].Service.ID, "foo1") |
|
|
|
for _, check := range services[0].Checks { |
|
if check.CheckID == "service:foo1" && check.Type != "ttl" { |
|
r.Fatalf("expected type ttl, got %s", check.Type) |
|
} |
|
} |
|
}) |
|
} |
|
func TestAPI_HealthService_MultipleTags(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeName = "node123" |
|
}) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
// Make two services with a check |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
ID: "foo1", |
|
Tags: []string{"bar"}, |
|
Check: &AgentServiceCheck{ |
|
Status: HealthPassing, |
|
TTL: "15s", |
|
}, |
|
} |
|
require.NoError(t, agent.ServiceRegister(reg)) |
|
|
|
reg2 := &AgentServiceRegistration{ |
|
Name: "foo", |
|
ID: "foo2", |
|
Tags: []string{"bar", "v2"}, |
|
Check: &AgentServiceCheck{ |
|
Status: HealthPassing, |
|
TTL: "15s", |
|
}, |
|
} |
|
require.NoError(t, agent.ServiceRegister(reg2)) |
|
|
|
// Test searching with one tag (two results) |
|
retry.Run(t, func(r *retry.R) { |
|
services, meta, err := health.ServiceMultipleTags("foo", []string{"bar"}, true, nil) |
|
|
|
require.NoError(r, err) |
|
require.NotEqual(r, meta.LastIndex, 0) |
|
require.Len(r, services, 2) |
|
}) |
|
|
|
// Test searching with two tags (one result) |
|
retry.Run(t, func(r *retry.R) { |
|
services, meta, err := health.ServiceMultipleTags("foo", []string{"bar", "v2"}, true, nil) |
|
|
|
require.NoError(r, err) |
|
require.NotEqual(r, meta.LastIndex, 0) |
|
require.Len(r, services, 1) |
|
require.Equal(r, services[0].Service.ID, "foo2") |
|
}) |
|
} |
|
|
|
func TestAPI_HealthService_NodeMetaFilter(t *testing.T) { |
|
t.Parallel() |
|
meta := map[string]string{"somekey": "somevalue"} |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeMeta = meta |
|
}) |
|
defer s.Stop() |
|
|
|
s.WaitForSerfCheck(t) |
|
|
|
health := c.Health() |
|
retry.Run(t, func(r *retry.R) { |
|
// consul service should always exist... |
|
checks, meta, err := health.Service("consul", "", true, &QueryOptions{NodeMeta: meta}) |
|
require.NoError(r, err) |
|
require.NotEqual(r, meta.LastIndex, 0) |
|
require.NotEqual(r, len(checks), 0) |
|
require.Equal(r, checks[0].Node.Datacenter, "dc1") |
|
require.Contains(r, checks[0].Node.TaggedAddresses, "wan") |
|
}) |
|
} |
|
|
|
func TestAPI_HealthService_Filter(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
// this sets up the catalog entries with things we can filter on |
|
testNodeServiceCheckRegistrations(t, c, "dc1") |
|
|
|
health := c.Health() |
|
|
|
services, _, err := health.Service("redis", "", false, &QueryOptions{Filter: "Service.Meta.version == 2"}) |
|
require.NoError(t, err) |
|
require.Len(t, services, 1) |
|
|
|
services, _, err = health.Service("web", "", false, &QueryOptions{Filter: "Node.Meta.os == linux"}) |
|
require.NoError(t, err) |
|
require.Len(t, services, 2) |
|
require.Equal(t, "baz", services[0].Node.Node) |
|
require.Equal(t, "baz", services[1].Node.Node) |
|
|
|
services, _, err = health.Service("web", "", false, &QueryOptions{Filter: "Node.Meta.os == linux and Service.Meta.version == 1"}) |
|
require.NoError(t, err) |
|
require.Len(t, services, 1) |
|
} |
|
|
|
func TestAPI_HealthConnect(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
s.WaitForSerfCheck(t) |
|
|
|
// Make a service with a proxy |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
Port: 8000, |
|
} |
|
err := agent.ServiceRegister(reg) |
|
require.NoError(t, err) |
|
|
|
// Register the proxy |
|
proxyReg := &AgentServiceRegistration{ |
|
Name: "foo-proxy", |
|
Port: 8001, |
|
Kind: ServiceKindConnectProxy, |
|
Proxy: &AgentServiceConnectProxyConfig{ |
|
DestinationServiceName: "foo", |
|
}, |
|
} |
|
err = agent.ServiceRegister(proxyReg) |
|
require.NoError(t, err) |
|
|
|
retry.Run(t, func(r *retry.R) { |
|
services, meta, err := health.Connect("foo", "", true, nil) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
// Should be exactly 1 service - the original shouldn't show up as a connect |
|
// endpoint, only it's proxy. |
|
if len(services) != 1 { |
|
r.Fatalf("Bad: %v", services) |
|
} |
|
if services[0].Node.Datacenter != "dc1" { |
|
r.Fatalf("Bad datacenter: %v", services[0].Node) |
|
} |
|
if services[0].Service.Port != proxyReg.Port { |
|
r.Fatalf("Bad port: %v", services[0]) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthConnect_Filter(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
// this sets up the catalog entries with things we can filter on |
|
testNodeServiceCheckRegistrations(t, c, "dc1") |
|
|
|
health := c.Health() |
|
|
|
services, _, err := health.Connect("web", "", false, &QueryOptions{Filter: "Node.Meta.os == linux"}) |
|
require.NoError(t, err) |
|
require.Len(t, services, 2) |
|
require.Equal(t, "baz", services[0].Node.Node) |
|
require.Equal(t, "baz", services[1].Node.Node) |
|
|
|
services, _, err = health.Service("web", "", false, &QueryOptions{Filter: "Node.Meta.os == linux and Service.Meta.version == 1"}) |
|
require.NoError(t, err) |
|
require.Len(t, services, 1) |
|
} |
|
|
|
func TestAPI_HealthIngress(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
agent := c.Agent() |
|
health := c.Health() |
|
|
|
s.WaitForSerfCheck(t) |
|
|
|
// Make a service with a proxy |
|
reg := &AgentServiceRegistration{ |
|
Name: "foo", |
|
Port: 8000, |
|
} |
|
err := agent.ServiceRegister(reg) |
|
require.NoError(t, err) |
|
|
|
// Register the gateway |
|
gatewayReg := &AgentServiceRegistration{ |
|
Name: "foo-gateway", |
|
Port: 8001, |
|
Kind: ServiceKindIngressGateway, |
|
} |
|
err = agent.ServiceRegister(gatewayReg) |
|
require.NoError(t, err) |
|
|
|
// Associate service and gateway |
|
gatewayConfig := &IngressGatewayConfigEntry{ |
|
Kind: IngressGateway, |
|
Name: "foo-gateway", |
|
Listeners: []IngressListener{ |
|
{ |
|
Port: 2222, |
|
Protocol: "tcp", |
|
Services: []IngressService{ |
|
{ |
|
Name: "foo", |
|
}, |
|
}, |
|
}, |
|
}, |
|
} |
|
_, wm, err := c.ConfigEntries().Set(gatewayConfig, nil) |
|
require.NoError(t, err) |
|
require.NotNil(t, wm) |
|
|
|
retry.Run(t, func(r *retry.R) { |
|
services, meta, err := health.Ingress("foo", true, nil) |
|
require.NoError(r, err) |
|
|
|
require.NotZero(r, meta.LastIndex) |
|
|
|
// Should be exactly 1 service - the original shouldn't show up as a connect |
|
// endpoint, only it's proxy. |
|
require.Len(r, services, 1) |
|
require.Equal(r, services[0].Node.Datacenter, "dc1") |
|
require.Equal(r, services[0].Service.Service, gatewayReg.Name) |
|
}) |
|
} |
|
|
|
func TestAPI_HealthState(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
health := c.Health() |
|
retry.Run(t, func(r *retry.R) { |
|
checks, meta, err := health.State("any", nil) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
if len(checks) == 0 { |
|
r.Fatalf("Bad: %v", checks) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthState_NodeMetaFilter(t *testing.T) { |
|
t.Parallel() |
|
meta := map[string]string{"somekey": "somevalue"} |
|
c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { |
|
conf.NodeMeta = meta |
|
}) |
|
defer s.Stop() |
|
|
|
health := c.Health() |
|
retry.Run(t, func(r *retry.R) { |
|
checks, meta, err := health.State("any", &QueryOptions{NodeMeta: meta}) |
|
if err != nil { |
|
r.Fatal(err) |
|
} |
|
if meta.LastIndex == 0 { |
|
r.Fatalf("bad: %v", meta) |
|
} |
|
if len(checks) == 0 { |
|
r.Fatalf("Bad: %v", checks) |
|
} |
|
}) |
|
} |
|
|
|
func TestAPI_HealthState_Filter(t *testing.T) { |
|
t.Parallel() |
|
c, s := makeClient(t) |
|
defer s.Stop() |
|
|
|
// this sets up the catalog entries with things we can filter on |
|
testNodeServiceCheckRegistrations(t, c, "dc1") |
|
|
|
health := c.Health() |
|
|
|
checks, _, err := health.State(HealthAny, &QueryOptions{Filter: "Node == baz"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 6) |
|
|
|
checks, _, err = health.State(HealthAny, &QueryOptions{Filter: "Status == warning or Status == critical"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 2) |
|
|
|
checks, _, err = health.State(HealthCritical, &QueryOptions{Filter: "Node == baz"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 1) |
|
|
|
checks, _, err = health.State(HealthWarning, &QueryOptions{Filter: "Node == baz"}) |
|
require.NoError(t, err) |
|
require.Len(t, checks, 1) |
|
}
|
|
|