mirror of https://github.com/status-im/consul.git
Add Connect agent, catalog and health endpoints to api Client
This commit is contained in:
parent
845f7cd8ad
commit
9309422fd9
|
@ -464,7 +464,7 @@ type ServiceKind string
|
|||
|
||||
const (
|
||||
// ServiceKindTypical is a typical, classic Consul service. This is
|
||||
// represented by the absense of a value. This was chosen for ease of
|
||||
// represented by the absence of a value. This was chosen for ease of
|
||||
// backwards compatibility: existing services in the catalog would
|
||||
// default to the typical service.
|
||||
ServiceKindTypical ServiceKind = ""
|
||||
|
|
20
api/agent.go
20
api/agent.go
|
@ -5,6 +5,22 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// ServiceKind is the kind of service being registered.
|
||||
type ServiceKind string
|
||||
|
||||
const (
|
||||
// ServiceKindTypical is a typical, classic Consul service. This is
|
||||
// represented by the absence of a value. This was chosen for ease of
|
||||
// backwards compatibility: existing services in the catalog would
|
||||
// default to the typical service.
|
||||
ServiceKindTypical ServiceKind = ""
|
||||
|
||||
// ServiceKindConnectProxy is a proxy for the Connect feature. This
|
||||
// service proxies another service within Consul and speaks the connect
|
||||
// protocol.
|
||||
ServiceKindConnectProxy ServiceKind = "connect-proxy"
|
||||
)
|
||||
|
||||
// AgentCheck represents a check known to the agent
|
||||
type AgentCheck struct {
|
||||
Node string
|
||||
|
@ -20,6 +36,7 @@ type AgentCheck struct {
|
|||
|
||||
// AgentService represents a service known to the agent
|
||||
type AgentService struct {
|
||||
Kind ServiceKind
|
||||
ID string
|
||||
Service string
|
||||
Tags []string
|
||||
|
@ -29,6 +46,7 @@ type AgentService struct {
|
|||
EnableTagOverride bool
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
ProxyDestination string
|
||||
}
|
||||
|
||||
// AgentMember represents a cluster member known to the agent
|
||||
|
@ -61,6 +79,7 @@ type MembersOpts struct {
|
|||
|
||||
// AgentServiceRegistration is used to register a new service
|
||||
type AgentServiceRegistration struct {
|
||||
Kind ServiceKind `json:",omitempty"`
|
||||
ID string `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Tags []string `json:",omitempty"`
|
||||
|
@ -70,6 +89,7 @@ type AgentServiceRegistration struct {
|
|||
Meta map[string]string `json:",omitempty"`
|
||||
Check *AgentServiceCheck
|
||||
Checks AgentServiceChecks
|
||||
ProxyDestination string `json:",omitempty"`
|
||||
}
|
||||
|
||||
// AgentCheckRegistration is used to register a new check
|
||||
|
|
|
@ -185,6 +185,51 @@ func TestAPI_AgentServices(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAPI_AgentServices_ConnectProxy(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
defer s.Stop()
|
||||
|
||||
agent := c.Agent()
|
||||
|
||||
// Register service
|
||||
reg := &AgentServiceRegistration{
|
||||
Name: "foo",
|
||||
Port: 8000,
|
||||
}
|
||||
if err := agent.ServiceRegister(reg); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
// Register proxy
|
||||
reg = &AgentServiceRegistration{
|
||||
Kind: ServiceKindConnectProxy,
|
||||
Name: "foo-proxy",
|
||||
Port: 8001,
|
||||
ProxyDestination: "foo",
|
||||
}
|
||||
if err := agent.ServiceRegister(reg); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
services, err := agent.Services()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if _, ok := services["foo"]; !ok {
|
||||
t.Fatalf("missing service: %v", services)
|
||||
}
|
||||
if _, ok := services["foo-proxy"]; !ok {
|
||||
t.Fatalf("missing proxy service: %v", services)
|
||||
}
|
||||
|
||||
if err := agent.ServiceDeregister("foo"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if err := agent.ServiceDeregister("foo-proxy"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPI_AgentServices_CheckPassing(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
|
|
|
@ -156,7 +156,20 @@ func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, er
|
|||
|
||||
// Service is used to query catalog entries for a given service
|
||||
func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) {
|
||||
r := c.c.newRequest("GET", "/v1/catalog/service/"+service)
|
||||
return c.service(service, tag, q, false)
|
||||
}
|
||||
|
||||
// Connect is used to query catalog entries for a given Connect-enabled service
|
||||
func (c *Catalog) Connect(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) {
|
||||
return c.service(service, tag, q, true)
|
||||
}
|
||||
|
||||
func (c *Catalog) service(service, tag string, q *QueryOptions, connect bool) ([]*CatalogService, *QueryMeta, error) {
|
||||
path := "/v1/catalog/service/" + service
|
||||
if connect {
|
||||
path = "/v1/catalog/connect/" + service
|
||||
}
|
||||
r := c.c.newRequest("GET", path)
|
||||
r.setQueryOptions(q)
|
||||
if tag != "" {
|
||||
r.params.Set("tag", tag)
|
||||
|
|
|
@ -186,6 +186,7 @@ func TestAPI_CatalogService(t *testing.T) {
|
|||
defer s.Stop()
|
||||
|
||||
catalog := c.Catalog()
|
||||
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
services, meta, err := catalog.Service("consul", "", nil)
|
||||
if err != nil {
|
||||
|
@ -235,6 +236,80 @@ func TestAPI_CatalogService_NodeMetaFilter(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAPI_CatalogConnect(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
defer s.Stop()
|
||||
|
||||
catalog := c.Catalog()
|
||||
|
||||
// Register service and proxy instances to test against.
|
||||
service := &AgentService{
|
||||
ID: "redis1",
|
||||
Service: "redis",
|
||||
Port: 8000,
|
||||
}
|
||||
proxy := &AgentService{
|
||||
Kind: ServiceKindConnectProxy,
|
||||
ProxyDestination: "redis",
|
||||
ID: "redis-proxy1",
|
||||
Service: "redis-proxy",
|
||||
Port: 8001,
|
||||
}
|
||||
check := &AgentCheck{
|
||||
Node: "foobar",
|
||||
CheckID: "service:redis1",
|
||||
Name: "Redis health check",
|
||||
Notes: "Script based health check",
|
||||
Status: HealthPassing,
|
||||
ServiceID: "redis1",
|
||||
}
|
||||
|
||||
reg := &CatalogRegistration{
|
||||
Datacenter: "dc1",
|
||||
Node: "foobar",
|
||||
Address: "192.168.10.10",
|
||||
Service: service,
|
||||
Check: check,
|
||||
}
|
||||
proxyReg := &CatalogRegistration{
|
||||
Datacenter: "dc1",
|
||||
Node: "foobar",
|
||||
Address: "192.168.10.10",
|
||||
Service: proxy,
|
||||
}
|
||||
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if _, err := catalog.Register(reg, nil); err != nil {
|
||||
r.Fatal(err)
|
||||
}
|
||||
if _, err := catalog.Register(proxyReg, nil); err != nil {
|
||||
r.Fatal(err)
|
||||
}
|
||||
|
||||
services, meta, err := catalog.Connect("redis", "", nil)
|
||||
if err != nil {
|
||||
r.Fatal(err)
|
||||
}
|
||||
|
||||
if meta.LastIndex == 0 {
|
||||
r.Fatalf("Bad: %v", meta)
|
||||
}
|
||||
|
||||
if len(services) == 0 {
|
||||
r.Fatalf("Bad: %v", services)
|
||||
}
|
||||
|
||||
if services[0].Datacenter != "dc1" {
|
||||
r.Fatalf("Bad datacenter: %v", services[0])
|
||||
}
|
||||
|
||||
if services[0].ServicePort != proxy.Port {
|
||||
r.Fatalf("Returned port should be for proxy: %v", services[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPI_CatalogNode(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
|
@ -297,10 +372,28 @@ func TestAPI_CatalogRegistration(t *testing.T) {
|
|||
Service: service,
|
||||
Check: check,
|
||||
}
|
||||
// Register a connect proxy for that service too
|
||||
proxy := &AgentService{
|
||||
ID: "redis-proxy1",
|
||||
Service: "redis-proxy",
|
||||
Port: 8001,
|
||||
Kind: ServiceKindConnectProxy,
|
||||
ProxyDestination: service.ID,
|
||||
}
|
||||
proxyReg := &CatalogRegistration{
|
||||
Datacenter: "dc1",
|
||||
Node: "foobar",
|
||||
Address: "192.168.10.10",
|
||||
NodeMeta: map[string]string{"somekey": "somevalue"},
|
||||
Service: proxy,
|
||||
}
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if _, err := catalog.Register(reg, nil); err != nil {
|
||||
r.Fatal(err)
|
||||
}
|
||||
if _, err := catalog.Register(proxyReg, nil); err != nil {
|
||||
r.Fatal(err)
|
||||
}
|
||||
|
||||
node, _, err := catalog.Node("foobar", nil)
|
||||
if err != nil {
|
||||
|
@ -311,6 +404,10 @@ func TestAPI_CatalogRegistration(t *testing.T) {
|
|||
r.Fatal("missing service: redis1")
|
||||
}
|
||||
|
||||
if _, ok := node.Services["redis-proxy1"]; !ok {
|
||||
r.Fatal("missing service: redis-proxy1")
|
||||
}
|
||||
|
||||
health, _, err := c.Health().Node("foobar", nil)
|
||||
if err != nil {
|
||||
r.Fatal(err)
|
||||
|
@ -333,10 +430,22 @@ func TestAPI_CatalogRegistration(t *testing.T) {
|
|||
ServiceID: "redis1",
|
||||
}
|
||||
|
||||
// ... and proxy
|
||||
deregProxy := &CatalogDeregistration{
|
||||
Datacenter: "dc1",
|
||||
Node: "foobar",
|
||||
Address: "192.168.10.10",
|
||||
ServiceID: "redis-proxy1",
|
||||
}
|
||||
|
||||
if _, err := catalog.Deregister(dereg, nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if _, err := catalog.Deregister(deregProxy, nil); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
node, _, err := catalog.Node("foobar", nil)
|
||||
if err != nil {
|
||||
|
@ -346,6 +455,10 @@ func TestAPI_CatalogRegistration(t *testing.T) {
|
|||
if _, ok := node.Services["redis1"]; ok {
|
||||
r.Fatal("ServiceID:redis1 is not deregistered")
|
||||
}
|
||||
|
||||
if _, ok := node.Services["redis-proxy1"]; ok {
|
||||
r.Fatal("ServiceID:redis-proxy1 is not deregistered")
|
||||
}
|
||||
})
|
||||
|
||||
// Test deregistration of the previously registered check
|
||||
|
|
|
@ -159,7 +159,24 @@ func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMe
|
|||
// for a given service. It can optionally do server-side filtering on a tag
|
||||
// or nodes with passing health checks only.
|
||||
func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
|
||||
r := h.c.newRequest("GET", "/v1/health/service/"+service)
|
||||
return h.service(service, tag, passingOnly, q, false)
|
||||
}
|
||||
|
||||
// Connect is equivalent to Service except that it will only return services
|
||||
// which are Connect-enabled and will returns the connection address for Connect
|
||||
// client's to use which may be a proxy in front of the named service. TODO: If
|
||||
// passingOnly is true only instances where both the service and any proxy are
|
||||
// healthy will be returned.
|
||||
func (h *Health) Connect(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) {
|
||||
return h.service(service, tag, passingOnly, q, true)
|
||||
}
|
||||
|
||||
func (h *Health) service(service, tag string, passingOnly bool, q *QueryOptions, connect bool) ([]*ServiceEntry, *QueryMeta, error) {
|
||||
path := "/v1/health/service/" + service
|
||||
if connect {
|
||||
path = "/v1/health/connect/" + service
|
||||
}
|
||||
r := h.c.newRequest("GET", path)
|
||||
r.setQueryOptions(q)
|
||||
if tag != "" {
|
||||
r.params.Set("tag", tag)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/hashicorp/consul/testutil"
|
||||
"github.com/hashicorp/consul/testutil/retry"
|
||||
"github.com/pascaldekloe/goe/verify"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAPI_HealthNode(t *testing.T) {
|
||||
|
@ -282,6 +283,56 @@ func TestAPI_HealthService(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAPI_HealthConnect(t *testing.T) {
|
||||
t.Parallel()
|
||||
c, s := makeClient(t)
|
||||
defer s.Stop()
|
||||
|
||||
agent := c.Agent()
|
||||
health := c.Health()
|
||||
|
||||
// Make a service with a proxy
|
||||
reg := &AgentServiceRegistration{
|
||||
Name: "foo",
|
||||
Port: 8000,
|
||||
}
|
||||
err := agent.ServiceRegister(reg)
|
||||
require.Nil(t, err)
|
||||
defer agent.ServiceDeregister("foo")
|
||||
|
||||
// Register the proxy
|
||||
proxyReg := &AgentServiceRegistration{
|
||||
Name: "foo-proxy",
|
||||
Port: 8001,
|
||||
Kind: ServiceKindConnectProxy,
|
||||
ProxyDestination: "foo",
|
||||
}
|
||||
err = agent.ServiceRegister(proxyReg)
|
||||
require.Nil(t, err)
|
||||
defer agent.ServiceDeregister("foo-proxy")
|
||||
|
||||
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_HealthService_NodeMetaFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
meta := map[string]string{"somekey": "somevalue"}
|
||||
|
|
Loading…
Reference in New Issue