mirror of https://github.com/hashicorp/consul
Create HTTP endpoint
parent
15c74d6943
commit
e7b52d35d4
|
@ -414,3 +414,43 @@ RETRY_ONCE:
|
||||||
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
||||||
return &out.NodeServices, nil
|
return &out.NodeServices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *HTTPServer) CatalogGatewayServices(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||||
|
metrics.IncrCounterWithLabels([]string{"client", "api", "catalog_gateway_services"}, 1,
|
||||||
|
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
||||||
|
|
||||||
|
var args structs.ServiceSpecificRequest
|
||||||
|
|
||||||
|
if err := s.parseEntMetaNoWildcard(req, &args.EnterpriseMeta); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if done := s.parse(resp, req, &args.Datacenter, &args.QueryOptions); done {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull out the gateway's service name
|
||||||
|
args.ServiceName = strings.TrimPrefix(req.URL.Path, "/v1/catalog/gateway-services/")
|
||||||
|
if args.ServiceName == "" {
|
||||||
|
resp.WriteHeader(http.StatusBadRequest)
|
||||||
|
fmt.Fprint(resp, "Missing gateway name")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the RPC request
|
||||||
|
var out structs.IndexedGatewayServices
|
||||||
|
defer setMeta(resp, &out.QueryMeta)
|
||||||
|
RETRY_ONCE:
|
||||||
|
if err := s.agent.RPC("Catalog.GatewayServices", &args, &out); err != nil {
|
||||||
|
metrics.IncrCounterWithLabels([]string{"client", "rpc", "error", "catalog_gateway_services"}, 1,
|
||||||
|
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if args.QueryOptions.AllowStale && args.MaxStaleDuration > 0 && args.MaxStaleDuration < out.LastContact {
|
||||||
|
args.AllowStale = false
|
||||||
|
args.MaxStaleDuration = 0
|
||||||
|
goto RETRY_ONCE
|
||||||
|
}
|
||||||
|
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
|
||||||
|
|
||||||
|
return out.Services, nil
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -1320,3 +1321,225 @@ func TestCatalogNodeServices_WanTranslation(t *testing.T) {
|
||||||
require.Equal(t, ns2.Address, "127.0.0.1")
|
require.Equal(t, ns2.Address, "127.0.0.1")
|
||||||
require.Equal(t, ns2.Port, 8080)
|
require.Equal(t, ns2.Port, 8080)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCatalog_GatewayServices_Terminating(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
a := NewTestAgent(t, "")
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
|
||||||
|
|
||||||
|
// Register a terminating gateway
|
||||||
|
args := &structs.RegisterRequest{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Node: "foo",
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Service: &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
Service: "terminating",
|
||||||
|
Port: 443,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var out struct{}
|
||||||
|
assert.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
// Register two services the gateway will route to
|
||||||
|
args = structs.TestRegisterRequest(t)
|
||||||
|
args.Service.Service = "redis"
|
||||||
|
args.Check = &structs.HealthCheck{
|
||||||
|
Name: "redis",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
ServiceID: args.Service.Service,
|
||||||
|
}
|
||||||
|
assert.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
args = structs.TestRegisterRequest(t)
|
||||||
|
args.Service.Service = "api"
|
||||||
|
args.Check = &structs.HealthCheck{
|
||||||
|
Name: "api",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
ServiceID: args.Service.Service,
|
||||||
|
}
|
||||||
|
assert.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
// Associate the gateway and api/redis services
|
||||||
|
entryArgs := &structs.ConfigEntryRequest{
|
||||||
|
Op: structs.ConfigEntryUpsert,
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Entry: &structs.TerminatingGatewayConfigEntry{
|
||||||
|
Kind: "terminating-gateway",
|
||||||
|
Name: "terminating",
|
||||||
|
Services: []structs.LinkedService{
|
||||||
|
{
|
||||||
|
Name: "api",
|
||||||
|
CAFile: "api/ca.crt",
|
||||||
|
CertFile: "api/client.crt",
|
||||||
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "*",
|
||||||
|
CAFile: "ca.crt",
|
||||||
|
CertFile: "client.crt",
|
||||||
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var entryResp bool
|
||||||
|
assert.NoError(t, a.RPC("ConfigEntry.Apply", &entryArgs, &entryResp))
|
||||||
|
|
||||||
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/catalog/gateway-services/terminating", nil)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
obj, err := a.srv.CatalogGatewayServices(resp, req)
|
||||||
|
assert.NoError(r, err)
|
||||||
|
|
||||||
|
header := resp.Header().Get("X-Consul-Index")
|
||||||
|
if header == "" || header == "0" {
|
||||||
|
r.Fatalf("Bad: %v", header)
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayServices := obj.(structs.GatewayServices)
|
||||||
|
|
||||||
|
expect := structs.GatewayServices{
|
||||||
|
{
|
||||||
|
Service: structs.NewServiceID("api", nil),
|
||||||
|
Gateway: structs.NewServiceID("terminating", nil),
|
||||||
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
||||||
|
CAFile: "api/ca.crt",
|
||||||
|
CertFile: "api/client.crt",
|
||||||
|
KeyFile: "api/client.key",
|
||||||
|
SNI: "my-domain",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Service: structs.NewServiceID("redis", nil),
|
||||||
|
Gateway: structs.NewServiceID("terminating", nil),
|
||||||
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
||||||
|
CAFile: "ca.crt",
|
||||||
|
CertFile: "client.crt",
|
||||||
|
KeyFile: "client.key",
|
||||||
|
SNI: "my-alt-domain",
|
||||||
|
FromWildcard: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore raft index for equality
|
||||||
|
for _, s := range gatewayServices {
|
||||||
|
s.RaftIndex = structs.RaftIndex{}
|
||||||
|
}
|
||||||
|
assert.Equal(r, expect, gatewayServices)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCatalog_GatewayServices_Ingress(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
a := NewTestAgent(t, "")
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
|
||||||
|
|
||||||
|
// Register an ingress gateway
|
||||||
|
args := &structs.RegisterRequest{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Node: "foo",
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Service: &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
Service: "ingress",
|
||||||
|
Port: 444,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var out struct{}
|
||||||
|
require.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
// Register two services the gateway will route to
|
||||||
|
args = structs.TestRegisterRequest(t)
|
||||||
|
args.Service.Service = "redis"
|
||||||
|
args.Check = &structs.HealthCheck{
|
||||||
|
Name: "redis",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
ServiceID: args.Service.Service,
|
||||||
|
}
|
||||||
|
require.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
args = structs.TestRegisterRequest(t)
|
||||||
|
args.Service.Service = "api"
|
||||||
|
args.Check = &structs.HealthCheck{
|
||||||
|
Name: "api",
|
||||||
|
Status: api.HealthPassing,
|
||||||
|
ServiceID: args.Service.Service,
|
||||||
|
}
|
||||||
|
require.NoError(t, a.RPC("Catalog.Register", &args, &out))
|
||||||
|
|
||||||
|
// Associate the gateway and db service
|
||||||
|
entryArgs := &structs.ConfigEntryRequest{
|
||||||
|
Op: structs.ConfigEntryUpsert,
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Entry: &structs.IngressGatewayConfigEntry{
|
||||||
|
Kind: "ingress-gateway",
|
||||||
|
Name: "ingress",
|
||||||
|
Listeners: []structs.IngressListener{
|
||||||
|
{
|
||||||
|
Port: 8888,
|
||||||
|
Services: []structs.IngressService{
|
||||||
|
{
|
||||||
|
Name: "api",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Port: 9999,
|
||||||
|
Services: []structs.IngressService{
|
||||||
|
{
|
||||||
|
Name: "redis",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var entryResp bool
|
||||||
|
require.NoError(t, a.RPC("ConfigEntry.Apply", &entryArgs, &entryResp))
|
||||||
|
|
||||||
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/catalog/gateway-services/ingress", nil)
|
||||||
|
resp := httptest.NewRecorder()
|
||||||
|
obj, err := a.srv.CatalogGatewayServices(resp, req)
|
||||||
|
require.NoError(r, err)
|
||||||
|
|
||||||
|
header := resp.Header().Get("X-Consul-Index")
|
||||||
|
if header == "" || header == "0" {
|
||||||
|
r.Fatalf("Bad: %v", header)
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayServices := obj.(structs.GatewayServices)
|
||||||
|
|
||||||
|
expect := structs.GatewayServices{
|
||||||
|
{
|
||||||
|
Service: structs.NewServiceID("api", nil),
|
||||||
|
Gateway: structs.NewServiceID("ingress", nil),
|
||||||
|
GatewayKind: structs.ServiceKindIngressGateway,
|
||||||
|
Protocol: "tcp",
|
||||||
|
Port: 8888,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Service: structs.NewServiceID("redis", nil),
|
||||||
|
Gateway: structs.NewServiceID("ingress", nil),
|
||||||
|
GatewayKind: structs.ServiceKindIngressGateway,
|
||||||
|
Protocol: "tcp",
|
||||||
|
Port: 9999,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore raft index for equality
|
||||||
|
for _, s := range gatewayServices {
|
||||||
|
s.RaftIndex = structs.RaftIndex{}
|
||||||
|
}
|
||||||
|
require.Equal(r, expect, gatewayServices)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ func init() {
|
||||||
registerEndpoint("/v1/catalog/service/", []string{"GET"}, (*HTTPServer).CatalogServiceNodes)
|
registerEndpoint("/v1/catalog/service/", []string{"GET"}, (*HTTPServer).CatalogServiceNodes)
|
||||||
registerEndpoint("/v1/catalog/node/", []string{"GET"}, (*HTTPServer).CatalogNodeServices)
|
registerEndpoint("/v1/catalog/node/", []string{"GET"}, (*HTTPServer).CatalogNodeServices)
|
||||||
registerEndpoint("/v1/catalog/node-services/", []string{"GET"}, (*HTTPServer).CatalogNodeServiceList)
|
registerEndpoint("/v1/catalog/node-services/", []string{"GET"}, (*HTTPServer).CatalogNodeServiceList)
|
||||||
|
registerEndpoint("/v1/catalog/gateway-services/", []string{"GET"}, (*HTTPServer).CatalogGatewayServices)
|
||||||
registerEndpoint("/v1/config/", []string{"GET", "DELETE"}, (*HTTPServer).Config)
|
registerEndpoint("/v1/config/", []string{"GET", "DELETE"}, (*HTTPServer).Config)
|
||||||
registerEndpoint("/v1/config", []string{"PUT"}, (*HTTPServer).ConfigApply)
|
registerEndpoint("/v1/config", []string{"PUT"}, (*HTTPServer).ConfigApply)
|
||||||
registerEndpoint("/v1/connect/ca/configuration", []string{"GET", "PUT"}, (*HTTPServer).ConnectCAConfiguration)
|
registerEndpoint("/v1/connect/ca/configuration", []string{"GET", "PUT"}, (*HTTPServer).ConnectCAConfiguration)
|
||||||
|
|
Loading…
Reference in New Issue