Create HTTP endpoint

This commit is contained in:
freddygv 2020-06-11 23:30:21 -06:00
parent 806b1fb608
commit 1cab73e609
3 changed files with 264 additions and 0 deletions

View File

@ -414,3 +414,43 @@ RETRY_ONCE:
[]metrics.Label{{Name: "node", Value: s.nodeName()}})
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
}

View File

@ -2,6 +2,7 @@ package agent
import (
"fmt"
"github.com/hashicorp/consul/api"
"net/http"
"net/http/httptest"
"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.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)
})
}

View File

@ -68,6 +68,7 @@ func init() {
registerEndpoint("/v1/catalog/service/", []string{"GET"}, (*HTTPServer).CatalogServiceNodes)
registerEndpoint("/v1/catalog/node/", []string{"GET"}, (*HTTPServer).CatalogNodeServices)
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{"PUT"}, (*HTTPServer).ConfigApply)
registerEndpoint("/v1/connect/ca/configuration", []string{"GET", "PUT"}, (*HTTPServer).ConnectCAConfiguration)