Enable CLI to register terminating gateways (#7500)
* Enable CLI to register terminating gateways * Centralize gateway proxy configuration
This commit is contained in:
parent
759ea803e5
commit
cb55fa3742
|
@ -210,9 +210,7 @@ func buildAgentService(s *structs.NodeService) api.AgentService {
|
||||||
as.Meta = map[string]string{}
|
as.Meta = map[string]string{}
|
||||||
}
|
}
|
||||||
// Attach Proxy config if exists
|
// Attach Proxy config if exists
|
||||||
if s.Kind == structs.ServiceKindConnectProxy ||
|
if s.Kind == structs.ServiceKindConnectProxy || s.IsGateway() {
|
||||||
s.Kind == structs.ServiceKindMeshGateway {
|
|
||||||
|
|
||||||
as.Proxy = s.Proxy.ToAPI()
|
as.Proxy = s.Proxy.ToAPI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ func TestAgent_Services_Sidecar(t *testing.T) {
|
||||||
assert.NotContains(string(output), "locally_registered_as_sidecar")
|
assert.NotContains(string(output), "locally_registered_as_sidecar")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Thie tests that a mesh gateway service is returned as expected.
|
// This tests that a mesh gateway service is returned as expected.
|
||||||
func TestAgent_Services_MeshGateway(t *testing.T) {
|
func TestAgent_Services_MeshGateway(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -233,6 +233,38 @@ func TestAgent_Services_MeshGateway(t *testing.T) {
|
||||||
require.Equal(t, srv1.Proxy.ToAPI(), actual.Proxy)
|
require.Equal(t, srv1.Proxy.ToAPI(), actual.Proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This tests that a terminating gateway service is returned as expected.
|
||||||
|
func TestAgent_Services_TerminatingGateway(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
a := NewTestAgent(t, t.Name(), "")
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||||
|
srv1 := &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
ID: "tg-dc1-01",
|
||||||
|
Service: "tg-dc1",
|
||||||
|
Port: 8443,
|
||||||
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, a.State.AddService(srv1, ""))
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("GET", "/v1/agent/services", nil)
|
||||||
|
obj, err := a.srv.AgentServices(nil, req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
val := obj.(map[string]*api.AgentService)
|
||||||
|
require.Len(t, val, 1)
|
||||||
|
actual := val["tg-dc1-01"]
|
||||||
|
require.NotNil(t, actual)
|
||||||
|
require.Equal(t, api.ServiceKindTerminatingGateway, actual.Kind)
|
||||||
|
require.Equal(t, srv1.Proxy.ToAPI(), actual.Proxy)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgent_Services_ACLFilter(t *testing.T) {
|
func TestAgent_Services_ACLFilter(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a := NewTestAgent(t, t.Name(), TestACLConfig())
|
a := NewTestAgent(t, t.Name(), TestACLConfig())
|
||||||
|
|
|
@ -1410,6 +1410,8 @@ func (b *Builder) serviceKindVal(v *string) structs.ServiceKind {
|
||||||
return structs.ServiceKindConnectProxy
|
return structs.ServiceKindConnectProxy
|
||||||
case string(structs.ServiceKindMeshGateway):
|
case string(structs.ServiceKindMeshGateway):
|
||||||
return structs.ServiceKindMeshGateway
|
return structs.ServiceKindMeshGateway
|
||||||
|
case string(structs.ServiceKindTerminatingGateway):
|
||||||
|
return structs.ServiceKindTerminatingGateway
|
||||||
default:
|
default:
|
||||||
return structs.ServiceKindTypical
|
return structs.ServiceKindTypical
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,10 +497,23 @@ func registerTestCatalogEntries(t *testing.T, codec rpc.ClientCodec) {
|
||||||
registerTestCatalogEntriesMap(t, codec, registrations)
|
registerTestCatalogEntriesMap(t, codec, registrations)
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerTestCatalogEntriesMeshGateway(t *testing.T, codec rpc.ClientCodec) {
|
func registerTestCatalogProxyEntries(t *testing.T, codec rpc.ClientCodec) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
registrations := map[string]*structs.RegisterRequest{
|
registrations := map[string]*structs.RegisterRequest{
|
||||||
|
"Service tg-gw": &structs.RegisterRequest{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Node: "terminating-gateway",
|
||||||
|
ID: types.NodeID("3a9d7530-20d4-443a-98d3-c10fe78f09f4"),
|
||||||
|
Address: "10.1.2.2",
|
||||||
|
Service: &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
ID: "tg-gw-01",
|
||||||
|
Service: "tg-gw",
|
||||||
|
Port: 8443,
|
||||||
|
Address: "198.18.1.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
"Service mg-gw": &structs.RegisterRequest{
|
"Service mg-gw": &structs.RegisterRequest{
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
Node: "gateway",
|
Node: "gateway",
|
||||||
|
|
|
@ -611,7 +611,7 @@ func TestInternal_ServiceDump_Kind(t *testing.T) {
|
||||||
|
|
||||||
// prep the cluster with some data we can use in our filters
|
// prep the cluster with some data we can use in our filters
|
||||||
registerTestCatalogEntries(t, codec)
|
registerTestCatalogEntries(t, codec)
|
||||||
registerTestCatalogEntriesMeshGateway(t, codec)
|
registerTestCatalogProxyEntries(t, codec)
|
||||||
|
|
||||||
doRequest := func(t *testing.T, kind structs.ServiceKind) structs.CheckServiceNodes {
|
doRequest := func(t *testing.T, kind structs.ServiceKind) structs.CheckServiceNodes {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
@ -633,6 +633,13 @@ func TestInternal_ServiceDump_Kind(t *testing.T) {
|
||||||
require.Len(t, nodes, 9)
|
require.Len(t, nodes, 9)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Terminating Gateway", func(t *testing.T) {
|
||||||
|
nodes := doRequest(t, structs.ServiceKindTerminatingGateway)
|
||||||
|
require.Len(t, nodes, 1)
|
||||||
|
require.Equal(t, "tg-gw", nodes[0].Service.Service)
|
||||||
|
require.Equal(t, "tg-gw-01", nodes[0].Service.ID)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Mesh Gateway", func(t *testing.T) {
|
t.Run("Mesh Gateway", func(t *testing.T) {
|
||||||
nodes := doRequest(t, structs.ServiceKindMeshGateway)
|
nodes := doRequest(t, structs.ServiceKindMeshGateway)
|
||||||
require.Len(t, nodes, 1)
|
require.Len(t, nodes, 1)
|
||||||
|
|
|
@ -120,9 +120,9 @@ func (s *ServiceManager) AddService(req *addServiceRequest) error {
|
||||||
|
|
||||||
req.service.EnterpriseMeta.Normalize()
|
req.service.EnterpriseMeta.Normalize()
|
||||||
|
|
||||||
// For now only sidecar proxies have anything that can be configured
|
// For now only proxies have anything that can be configured
|
||||||
// centrally. So bypass the whole manager for regular services.
|
// centrally. So bypass the whole manager for regular services.
|
||||||
if !req.service.IsSidecarProxy() && !req.service.IsMeshGateway() {
|
if !req.service.IsSidecarProxy() && !req.service.IsGateway() {
|
||||||
// previousDefaults are ignored here because they are only relevant for central config.
|
// previousDefaults are ignored here because they are only relevant for central config.
|
||||||
req.persistService = nil
|
req.persistService = nil
|
||||||
req.persistDefaults = nil
|
req.persistDefaults = nil
|
||||||
|
|
|
@ -205,6 +205,62 @@ func TestServiceManager_RegisterMeshGateway(t *testing.T) {
|
||||||
}, gateway)
|
}, gateway)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServiceManager_RegisterTerminatingGateway(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
a := NewTestAgent(t, t.Name(), "enable_central_service_config = true")
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
testrpc.WaitForLeader(t, a.RPC, "dc1")
|
||||||
|
|
||||||
|
// Register a global proxy and service config
|
||||||
|
testApplyConfigEntries(t, a,
|
||||||
|
&structs.ProxyConfigEntry{
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.ServiceConfigEntry{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: "terminating-gateway",
|
||||||
|
Protocol: "http",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// Now register a terminating-gateway.
|
||||||
|
svc := &structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
ID: "terminating-gateway",
|
||||||
|
Service: "terminating-gateway",
|
||||||
|
Port: 443,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(a.AddService(svc, nil, false, "", ConfigSourceLocal))
|
||||||
|
|
||||||
|
// Verify gateway got global config loaded
|
||||||
|
gateway := a.State.Service(structs.NewServiceID("terminating-gateway", nil))
|
||||||
|
require.NotNil(gateway)
|
||||||
|
require.Equal(&structs.NodeService{
|
||||||
|
Kind: structs.ServiceKindTerminatingGateway,
|
||||||
|
ID: "terminating-gateway",
|
||||||
|
Service: "terminating-gateway",
|
||||||
|
Port: 443,
|
||||||
|
TaggedAddresses: map[string]structs.ServiceAddress{},
|
||||||
|
Proxy: structs.ConnectProxyConfig{
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": int64(1),
|
||||||
|
"protocol": "http",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Weights: &structs.Weights{
|
||||||
|
Passing: 1,
|
||||||
|
Warning: 1,
|
||||||
|
},
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
}, gateway)
|
||||||
|
}
|
||||||
|
|
||||||
func TestServiceManager_PersistService_API(t *testing.T) {
|
func TestServiceManager_PersistService_API(t *testing.T) {
|
||||||
// This is the ServiceManager version of TestAgent_PersistService and
|
// This is the ServiceManager version of TestAgent_PersistService and
|
||||||
// TestAgent_PurgeService.
|
// TestAgent_PurgeService.
|
||||||
|
|
|
@ -890,21 +890,11 @@ const (
|
||||||
// service will proxy connections based off the SNI header set by other
|
// service will proxy connections based off the SNI header set by other
|
||||||
// connect proxies
|
// connect proxies
|
||||||
ServiceKindMeshGateway ServiceKind = "mesh-gateway"
|
ServiceKindMeshGateway ServiceKind = "mesh-gateway"
|
||||||
)
|
|
||||||
|
|
||||||
func ServiceKindFromString(kind string) (ServiceKind, error) {
|
// ServiceKindTerminatingGateway is a Terminating Gateway for the Connect
|
||||||
switch kind {
|
// feature. This service will proxy connections to services outside the mesh.
|
||||||
case string(ServiceKindTypical):
|
ServiceKindTerminatingGateway ServiceKind = "terminating-gateway"
|
||||||
return ServiceKindTypical, nil
|
)
|
||||||
case string(ServiceKindConnectProxy):
|
|
||||||
return ServiceKindConnectProxy, nil
|
|
||||||
case string(ServiceKindMeshGateway):
|
|
||||||
return ServiceKindMeshGateway, nil
|
|
||||||
default:
|
|
||||||
// have to return something and it may as well be typical
|
|
||||||
return ServiceKindTypical, fmt.Errorf("Invalid service kind: %s", kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type to hold a address and port of a service
|
// Type to hold a address and port of a service
|
||||||
type ServiceAddress struct {
|
type ServiceAddress struct {
|
||||||
|
@ -1048,9 +1038,8 @@ func (s *NodeService) IsSidecarProxy() bool {
|
||||||
return s.Kind == ServiceKindConnectProxy && s.Proxy.DestinationServiceID != ""
|
return s.Kind == ServiceKindConnectProxy && s.Proxy.DestinationServiceID != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *NodeService) IsMeshGateway() bool {
|
func (s *NodeService) IsGateway() bool {
|
||||||
// TODO (mesh-gateway) any other things to check?
|
return s.Kind == ServiceKindMeshGateway || s.Kind == ServiceKindTerminatingGateway
|
||||||
return s.Kind == ServiceKindMeshGateway
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the node service configuration.
|
// Validate validates the node service configuration.
|
||||||
|
@ -1147,36 +1136,36 @@ func (s *NodeService) Validate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeshGateway validation
|
// Gateway validation
|
||||||
if s.Kind == ServiceKindMeshGateway {
|
if s.IsGateway() {
|
||||||
// Gateways must have a port
|
// Gateways must have a port
|
||||||
if s.Port == 0 {
|
if s.Port == 0 {
|
||||||
result = multierror.Append(result, fmt.Errorf("Port must be non-zero for a Mesh Gateway"))
|
result = multierror.Append(result, fmt.Errorf("Port must be non-zero for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gateways cannot have sidecars
|
// Gateways cannot have sidecars
|
||||||
if s.Connect.SidecarService != nil {
|
if s.Connect.SidecarService != nil {
|
||||||
result = multierror.Append(result, fmt.Errorf("Mesh Gateways cannot have a sidecar service defined"))
|
result = multierror.Append(result, fmt.Errorf("A %s cannot have a sidecar service defined", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Proxy.DestinationServiceName != "" {
|
if s.Proxy.DestinationServiceName != "" {
|
||||||
result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceName configuration is invalid for Mesh Gateways"))
|
result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceName configuration is invalid for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Proxy.DestinationServiceID != "" {
|
if s.Proxy.DestinationServiceID != "" {
|
||||||
result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceID configuration is invalid for Mesh Gateways"))
|
result = multierror.Append(result, fmt.Errorf("The Proxy.DestinationServiceID configuration is invalid for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Proxy.LocalServiceAddress != "" {
|
if s.Proxy.LocalServiceAddress != "" {
|
||||||
result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServiceAddress configuration is invalid for Mesh Gateways"))
|
result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServiceAddress configuration is invalid for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Proxy.LocalServicePort != 0 {
|
if s.Proxy.LocalServicePort != 0 {
|
||||||
result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServicePort configuration is invalid for Mesh Gateways"))
|
result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServicePort configuration is invalid for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.Proxy.Upstreams) != 0 {
|
if len(s.Proxy.Upstreams) != 0 {
|
||||||
result = multierror.Append(result, fmt.Errorf("The Proxy.Upstreams configuration is invalid for Mesh Gateways"))
|
result = multierror.Append(result, fmt.Errorf("The Proxy.Upstreams configuration is invalid for a %s", s.Kind))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -415,6 +415,58 @@ func TestStructs_NodeService_ValidateMeshGateway(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStructs_NodeService_ValidateTerminatingGateway(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
Modify func(*NodeService)
|
||||||
|
Err string
|
||||||
|
}
|
||||||
|
cases := map[string]testCase{
|
||||||
|
"valid": testCase{
|
||||||
|
func(x *NodeService) {},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
"sidecar-service": testCase{
|
||||||
|
func(x *NodeService) { x.Connect.SidecarService = &ServiceDefinition{} },
|
||||||
|
"cannot have a sidecar service",
|
||||||
|
},
|
||||||
|
"proxy-destination-name": testCase{
|
||||||
|
func(x *NodeService) { x.Proxy.DestinationServiceName = "foo" },
|
||||||
|
"Proxy.DestinationServiceName configuration is invalid",
|
||||||
|
},
|
||||||
|
"proxy-destination-id": testCase{
|
||||||
|
func(x *NodeService) { x.Proxy.DestinationServiceID = "foo" },
|
||||||
|
"Proxy.DestinationServiceID configuration is invalid",
|
||||||
|
},
|
||||||
|
"proxy-local-address": testCase{
|
||||||
|
func(x *NodeService) { x.Proxy.LocalServiceAddress = "127.0.0.1" },
|
||||||
|
"Proxy.LocalServiceAddress configuration is invalid",
|
||||||
|
},
|
||||||
|
"proxy-local-port": testCase{
|
||||||
|
func(x *NodeService) { x.Proxy.LocalServicePort = 36 },
|
||||||
|
"Proxy.LocalServicePort configuration is invalid",
|
||||||
|
},
|
||||||
|
"proxy-upstreams": testCase{
|
||||||
|
func(x *NodeService) { x.Proxy.Upstreams = []Upstream{Upstream{}} },
|
||||||
|
"Proxy.Upstreams configuration is invalid",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, tc := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
ns := TestNodeServiceTerminatingGateway(t, "10.0.0.5")
|
||||||
|
tc.Modify(ns)
|
||||||
|
|
||||||
|
err := ns.Validate()
|
||||||
|
if tc.Err == "" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
} else {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, strings.ToLower(err.Error()), strings.ToLower(tc.Err))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStructs_NodeService_ValidateExposeConfig(t *testing.T) {
|
func TestStructs_NodeService_ValidateExposeConfig(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
Modify func(*NodeService)
|
Modify func(*NodeService)
|
||||||
|
|
|
@ -85,6 +85,15 @@ func TestNodeServiceMeshGateway(t testing.T) *NodeService {
|
||||||
ServiceAddress{Address: "198.18.4.5", Port: 443})
|
ServiceAddress{Address: "198.18.4.5", Port: 443})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNodeServiceTerminatingGateway(t testing.T, address string) *NodeService {
|
||||||
|
return &NodeService{
|
||||||
|
Kind: ServiceKindTerminatingGateway,
|
||||||
|
Port: 8443,
|
||||||
|
Service: "terminating-gateway",
|
||||||
|
Address: address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNodeServiceMeshGatewayWithAddrs(t testing.T, address string, port int, lanAddr, wanAddr ServiceAddress) *NodeService {
|
func TestNodeServiceMeshGatewayWithAddrs(t testing.T, address string, port int, lanAddr, wanAddr ServiceAddress) *NodeService {
|
||||||
return &NodeService{
|
return &NodeService{
|
||||||
Kind: ServiceKindMeshGateway,
|
Kind: ServiceKindMeshGateway,
|
||||||
|
|
|
@ -480,7 +480,7 @@ func (s *Server) makeMeshGatewayCluster(clusterName string, cfgSnap *proxycfg.Co
|
||||||
// defaults to use the mesh gateway timeout.
|
// defaults to use the mesh gateway timeout.
|
||||||
func (s *Server) makeMeshGatewayClusterWithConnectTimeout(clusterName string, cfgSnap *proxycfg.ConfigSnapshot,
|
func (s *Server) makeMeshGatewayClusterWithConnectTimeout(clusterName string, cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
connectTimeout time.Duration) (*envoy.Cluster, error) {
|
connectTimeout time.Duration) (*envoy.Cluster, error) {
|
||||||
cfg, err := ParseMeshGatewayConfig(cfgSnap.Proxy.Config)
|
cfg, err := ParseGatewayConfig(cfgSnap.Proxy.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't hard fail on a config typo, just warn. The parse func returns
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
// default config if there is an error so it's safe to continue.
|
// default config if there is an error so it's safe to continue.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package xds
|
package xds
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/consul/lib"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
@ -67,7 +68,7 @@ func ParseProxyConfig(m map[string]interface{}) (ProxyConfig, error) {
|
||||||
return cfg, err
|
return cfg, err
|
||||||
}
|
}
|
||||||
|
|
||||||
type MeshGatewayConfig struct {
|
type GatewayConfig struct {
|
||||||
// BindTaggedAddresses when set will cause all of the services tagged
|
// BindTaggedAddresses when set will cause all of the services tagged
|
||||||
// addresses to have listeners bound to them in addition to the main service
|
// addresses to have listeners bound to them in addition to the main service
|
||||||
// address listener. This is only suitable when the tagged addresses are IP
|
// address listener. This is only suitable when the tagged addresses are IP
|
||||||
|
@ -75,27 +76,32 @@ type MeshGatewayConfig struct {
|
||||||
// for those addresses or where an external entity maps that IP to the Envoy
|
// for those addresses or where an external entity maps that IP to the Envoy
|
||||||
// (like AWS EC2 mapping a public IP to the private interface) then this
|
// (like AWS EC2 mapping a public IP to the private interface) then this
|
||||||
// cannot be used. See the BindAddresses config instead
|
// cannot be used. See the BindAddresses config instead
|
||||||
//
|
BindTaggedAddresses bool `mapstructure:"envoy_gateway_bind_tagged_addresses"`
|
||||||
// TODO - wow this is a verbose setting name. Maybe shorten this
|
|
||||||
BindTaggedAddresses bool `mapstructure:"envoy_mesh_gateway_bind_tagged_addresses"`
|
|
||||||
|
|
||||||
// BindAddresses additional bind addresses to configure listeners for
|
// BindAddresses additional bind addresses to configure listeners for
|
||||||
BindAddresses map[string]structs.ServiceAddress `mapstructure:"envoy_mesh_gateway_bind_addresses"`
|
BindAddresses map[string]structs.ServiceAddress `mapstructure:"envoy_gateway_bind_addresses"`
|
||||||
|
|
||||||
// NoDefaultBind indicates that we should not bind to the default address of the
|
// NoDefaultBind indicates that we should not bind to the default address of the
|
||||||
// gateway service
|
// gateway service
|
||||||
NoDefaultBind bool `mapstructure:"envoy_mesh_gateway_no_default_bind"`
|
NoDefaultBind bool `mapstructure:"envoy_gateway_no_default_bind"`
|
||||||
|
|
||||||
// ConnectTimeoutMs is the number of milliseconds to timeout making a new
|
// ConnectTimeoutMs is the number of milliseconds to timeout making a new
|
||||||
// connection to this upstream. Defaults to 5000 (5 seconds) if not set.
|
// connection to this upstream. Defaults to 5000 (5 seconds) if not set.
|
||||||
ConnectTimeoutMs int `mapstructure:"connect_timeout_ms"`
|
ConnectTimeoutMs int `mapstructure:"connect_timeout_ms"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseMeshGatewayConfig returns the MeshGatewayConfig parsed from an opaque map. If an
|
// ParseGatewayConfig returns the GatewayConfig parsed from an opaque map. If an
|
||||||
// error occurs during parsing, it is returned along with the default config. This
|
// error occurs during parsing, it is returned along with the default config. This
|
||||||
// allows the caller to choose whether and how to report the error
|
// allows the caller to choose whether and how to report the error
|
||||||
func ParseMeshGatewayConfig(m map[string]interface{}) (MeshGatewayConfig, error) {
|
func ParseGatewayConfig(m map[string]interface{}) (GatewayConfig, error) {
|
||||||
var cfg MeshGatewayConfig
|
// Fixup for deprecated mesh gateway names
|
||||||
|
lib.TranslateKeys(m, map[string]string{
|
||||||
|
"envoy_mesh_gateway_bind_tagged_addresses": "envoy_gateway_bind_tagged_addresses",
|
||||||
|
"envoy_mesh_gateway_bind_addresses": "envoy_gateway_bind_addresses",
|
||||||
|
"envoy_mesh_gateway_no_default_bind": "envoy_gateway_no_default_bind",
|
||||||
|
})
|
||||||
|
|
||||||
|
var cfg GatewayConfig
|
||||||
err := mapstructure.WeakDecode(m, &cfg)
|
err := mapstructure.WeakDecode(m, &cfg)
|
||||||
|
|
||||||
if cfg.ConnectTimeoutMs < 1 {
|
if cfg.ConnectTimeoutMs < 1 {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package xds
|
package xds
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -253,6 +254,96 @@ func TestParseUpstreamConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseGatewayConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input map[string]interface{}
|
||||||
|
want GatewayConfig
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults - nil",
|
||||||
|
input: nil,
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - empty",
|
||||||
|
input: map[string]interface{}{},
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "defaults - other stuff",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"envoy_foo": "envoy_bar",
|
||||||
|
},
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "kitchen sink",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"envoy_gateway_bind_tagged_addresses": true,
|
||||||
|
"envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{"foo": {Address: "127.0.0.1", Port: 80}},
|
||||||
|
"envoy_gateway_no_default_bind": true,
|
||||||
|
"connect_timeout_ms": 10,
|
||||||
|
},
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 10,
|
||||||
|
BindTaggedAddresses: true,
|
||||||
|
NoDefaultBind: true,
|
||||||
|
BindAddresses: map[string]structs.ServiceAddress{"foo": {Address: "127.0.0.1", Port: 80}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deprecated kitchen sink",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
"envoy_mesh_gateway_bind_tagged_addresses": true,
|
||||||
|
"envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{"foo": {Address: "127.0.0.1", Port: 80}},
|
||||||
|
"envoy_mesh_gateway_no_default_bind": true,
|
||||||
|
"connect_timeout_ms": 10,
|
||||||
|
},
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 10,
|
||||||
|
BindTaggedAddresses: true,
|
||||||
|
NoDefaultBind: true,
|
||||||
|
BindAddresses: map[string]structs.ServiceAddress{"foo": {Address: "127.0.0.1", Port: 80}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "new fields override deprecated ones",
|
||||||
|
input: map[string]interface{}{
|
||||||
|
// Deprecated
|
||||||
|
"envoy_mesh_gateway_bind_tagged_addresses": true,
|
||||||
|
"envoy_mesh_gateway_bind_addresses": map[string]structs.ServiceAddress{"foo": {Address: "127.0.0.1", Port: 80}},
|
||||||
|
"envoy_mesh_gateway_no_default_bind": true,
|
||||||
|
|
||||||
|
// New
|
||||||
|
"envoy_gateway_bind_tagged_addresses": false,
|
||||||
|
"envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{"bar": {Address: "127.0.0.1", Port: 8080}},
|
||||||
|
"envoy_gateway_no_default_bind": false,
|
||||||
|
},
|
||||||
|
want: GatewayConfig{
|
||||||
|
ConnectTimeoutMs: 5000,
|
||||||
|
BindTaggedAddresses: false,
|
||||||
|
NoDefaultBind: false,
|
||||||
|
BindAddresses: map[string]structs.ServiceAddress{"bar": {Address: "127.0.0.1", Port: 8080}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, err := ParseGatewayConfig(tt.input)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func intPointer(i int) *int {
|
func intPointer(i int) *int {
|
||||||
return &i
|
return &i
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ func parseCheckPath(check structs.CheckType) (structs.ExposePath, error) {
|
||||||
|
|
||||||
// listenersFromSnapshotMeshGateway returns the "listener" for a mesh-gateway service
|
// listenersFromSnapshotMeshGateway returns the "listener" for a mesh-gateway service
|
||||||
func (s *Server) listenersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
func (s *Server) listenersFromSnapshotMeshGateway(cfgSnap *proxycfg.ConfigSnapshot, token string) ([]proto.Message, error) {
|
||||||
cfg, err := ParseMeshGatewayConfig(cfgSnap.Proxy.Config)
|
cfg, err := ParseGatewayConfig(cfgSnap.Proxy.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't hard fail on a config typo, just warn. The parse func returns
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
// default config if there is an error so it's safe to continue.
|
// default config if there is an error so it's safe to continue.
|
||||||
|
|
|
@ -28,6 +28,10 @@ const (
|
||||||
// service will proxy connections based off the SNI header set by other
|
// service will proxy connections based off the SNI header set by other
|
||||||
// connect proxies
|
// connect proxies
|
||||||
ServiceKindMeshGateway ServiceKind = "mesh-gateway"
|
ServiceKindMeshGateway ServiceKind = "mesh-gateway"
|
||||||
|
|
||||||
|
// ServiceKindTerminatingGateway is a Terminating Gateway for the Connect
|
||||||
|
// feature. This service will proxy connections to services outside the mesh.
|
||||||
|
ServiceKindTerminatingGateway ServiceKind = "terminating-gateway"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpstreamDestType is the type of upstream discovery mechanism.
|
// UpstreamDestType is the type of upstream discovery mechanism.
|
||||||
|
|
|
@ -1705,6 +1705,37 @@ func TestAgentService_Register_MeshGateway(t *testing.T) {
|
||||||
require.Equal(t, "bar", svc.Proxy.Config["foo"])
|
require.Equal(t, "bar", svc.Proxy.Config["foo"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAgentService_Register_TerminatingGateway(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
c, s := makeClient(t)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
agent := c.Agent()
|
||||||
|
|
||||||
|
reg := AgentServiceRegistration{
|
||||||
|
Kind: ServiceKindTerminatingGateway,
|
||||||
|
Name: "terminating-gateway",
|
||||||
|
Address: "10.1.2.3",
|
||||||
|
Port: 8443,
|
||||||
|
Proxy: &AgentServiceConnectProxyConfig{
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := agent.ServiceRegister(®)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
svc, _, err := agent.Service("terminating-gateway", nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, svc)
|
||||||
|
require.Equal(t, ServiceKindTerminatingGateway, svc.Kind)
|
||||||
|
require.NotNil(t, svc.Proxy)
|
||||||
|
require.Contains(t, svc.Proxy.Config, "foo")
|
||||||
|
require.Equal(t, "bar", svc.Proxy.Config["foo"])
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgentService_ExposeChecks(t *testing.T) {
|
func TestAgentService_ExposeChecks(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
c, s := makeClient(t)
|
c, s := makeClient(t)
|
||||||
|
|
|
@ -46,6 +46,7 @@ type cmd struct {
|
||||||
|
|
||||||
// flags
|
// flags
|
||||||
meshGateway bool
|
meshGateway bool
|
||||||
|
gateway string
|
||||||
proxyID string
|
proxyID string
|
||||||
sidecarFor string
|
sidecarFor string
|
||||||
adminAccessLogPath string
|
adminAccessLogPath string
|
||||||
|
@ -64,10 +65,19 @@ type cmd struct {
|
||||||
bindAddresses ServiceAddressMapValue
|
bindAddresses ServiceAddressMapValue
|
||||||
exposeServers bool
|
exposeServers bool
|
||||||
|
|
||||||
meshGatewaySvcName string
|
gatewaySvcName string
|
||||||
|
gatewayKind api.ServiceKind
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultEnvoyVersion = "1.13.1"
|
const (
|
||||||
|
defaultEnvoyVersion = "1.13.1"
|
||||||
|
meshGatewayVal = "mesh"
|
||||||
|
)
|
||||||
|
|
||||||
|
var supportedGateways = map[string]api.ServiceKind{
|
||||||
|
"mesh": api.ServiceKindMeshGateway,
|
||||||
|
"terminating": api.ServiceKindTerminatingGateway,
|
||||||
|
}
|
||||||
|
|
||||||
func (c *cmd) init() {
|
func (c *cmd) init() {
|
||||||
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
c.flags = flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
@ -75,9 +85,13 @@ func (c *cmd) init() {
|
||||||
c.flags.StringVar(&c.proxyID, "proxy-id", os.Getenv("CONNECT_PROXY_ID"),
|
c.flags.StringVar(&c.proxyID, "proxy-id", os.Getenv("CONNECT_PROXY_ID"),
|
||||||
"The proxy's ID on the local agent.")
|
"The proxy's ID on the local agent.")
|
||||||
|
|
||||||
|
// Deprecated in favor of `gateway`
|
||||||
c.flags.BoolVar(&c.meshGateway, "mesh-gateway", false,
|
c.flags.BoolVar(&c.meshGateway, "mesh-gateway", false,
|
||||||
"Configure Envoy as a Mesh Gateway.")
|
"Configure Envoy as a Mesh Gateway.")
|
||||||
|
|
||||||
|
c.flags.StringVar(&c.gateway, "gateway", "",
|
||||||
|
"The type of gateway to register. One of: terminating or mesh")
|
||||||
|
|
||||||
c.flags.StringVar(&c.sidecarFor, "sidecar-for", os.Getenv("CONNECT_SIDECAR_FOR"),
|
c.flags.StringVar(&c.sidecarFor, "sidecar-for", os.Getenv("CONNECT_SIDECAR_FOR"),
|
||||||
"The ID of a service instance on the local agent that this proxy should "+
|
"The ID of a service instance on the local agent that this proxy should "+
|
||||||
"become a sidecar for. It requires that the proxy service is registered "+
|
"become a sidecar for. It requires that the proxy service is registered "+
|
||||||
|
@ -128,7 +142,7 @@ func (c *cmd) init() {
|
||||||
"address to use instead of the default binding rules given as `<name>=<ip>:<port>` "+
|
"address to use instead of the default binding rules given as `<name>=<ip>:<port>` "+
|
||||||
"pairs. This flag may be specified multiple times to add multiple bind addresses.")
|
"pairs. This flag may be specified multiple times to add multiple bind addresses.")
|
||||||
|
|
||||||
c.flags.StringVar(&c.meshGatewaySvcName, "service", "mesh-gateway",
|
c.flags.StringVar(&c.gatewaySvcName, "service", "",
|
||||||
"Service name to use for the registration")
|
"Service name to use for the registration")
|
||||||
|
|
||||||
c.flags.BoolVar(&c.exposeServers, "expose-servers", false,
|
c.flags.BoolVar(&c.exposeServers, "expose-servers", false,
|
||||||
|
@ -195,8 +209,17 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
c.client = client
|
c.client = client
|
||||||
|
|
||||||
|
// Fixup for deprecated mesh-gateway flag
|
||||||
|
if c.meshGateway && c.gateway != "" {
|
||||||
|
c.UI.Error("The mesh-gateway flag is deprecated and cannot be used alongside the gateway flag")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if c.meshGateway {
|
||||||
|
c.gateway = meshGatewayVal
|
||||||
|
}
|
||||||
|
|
||||||
if c.exposeServers {
|
if c.exposeServers {
|
||||||
if !c.meshGateway {
|
if c.gateway != meshGatewayVal {
|
||||||
c.UI.Error("'-expose-servers' can only be used for mesh gateways")
|
c.UI.Error("'-expose-servers' can only be used for mesh gateways")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
@ -207,11 +230,22 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.register {
|
if c.register {
|
||||||
if !c.meshGateway {
|
if c.gateway == "" {
|
||||||
c.UI.Error("Auto-Registration can only be used for mesh gateways")
|
c.UI.Error("Auto-Registration can only be used for gateways")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
kind, ok := supportedGateways[c.gateway]
|
||||||
|
if !ok {
|
||||||
|
c.UI.Error("Gateway must be one of: terminating or mesh")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
c.gatewayKind = kind
|
||||||
|
|
||||||
|
if c.gatewaySvcName == "" {
|
||||||
|
c.gatewaySvcName = string(c.gatewayKind)
|
||||||
|
}
|
||||||
|
|
||||||
taggedAddrs := make(map[string]api.ServiceAddress)
|
taggedAddrs := make(map[string]api.ServiceAddress)
|
||||||
lanAddr := c.lanAddress.Value()
|
lanAddr := c.lanAddress.Value()
|
||||||
if lanAddr.Address != "" {
|
if lanAddr.Address != "" {
|
||||||
|
@ -231,13 +265,12 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
var proxyConf *api.AgentServiceConnectProxyConfig
|
var proxyConf *api.AgentServiceConnectProxyConfig
|
||||||
|
|
||||||
if len(c.bindAddresses.value) > 0 {
|
if len(c.bindAddresses.value) > 0 {
|
||||||
// override all default binding rules and just bind to the user-supplied addresses
|
// override all default binding rules and just bind to the user-supplied addresses
|
||||||
proxyConf = &api.AgentServiceConnectProxyConfig{
|
proxyConf = &api.AgentServiceConnectProxyConfig{
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"envoy_mesh_gateway_no_default_bind": true,
|
"envoy_gateway_no_default_bind": true,
|
||||||
"envoy_mesh_gateway_bind_addresses": c.bindAddresses.value,
|
"envoy_gateway_bind_addresses": c.bindAddresses.value,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if canBind(lanAddr) && canBind(wanAddr) {
|
} else if canBind(lanAddr) && canBind(wanAddr) {
|
||||||
|
@ -245,8 +278,8 @@ func (c *cmd) Run(args []string) int {
|
||||||
// for creating the envoy listeners
|
// for creating the envoy listeners
|
||||||
proxyConf = &api.AgentServiceConnectProxyConfig{
|
proxyConf = &api.AgentServiceConnectProxyConfig{
|
||||||
Config: map[string]interface{}{
|
Config: map[string]interface{}{
|
||||||
"envoy_mesh_gateway_no_default_bind": true,
|
"envoy_gateway_no_default_bind": true,
|
||||||
"envoy_mesh_gateway_bind_tagged_addresses": true,
|
"envoy_gateway_bind_tagged_addresses": true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if !canBind(lanAddr) && lanAddr.Address != "" {
|
} else if !canBind(lanAddr) && lanAddr.Address != "" {
|
||||||
|
@ -260,15 +293,15 @@ func (c *cmd) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
svc := api.AgentServiceRegistration{
|
svc := api.AgentServiceRegistration{
|
||||||
Kind: api.ServiceKindMeshGateway,
|
Kind: c.gatewayKind,
|
||||||
Name: c.meshGatewaySvcName,
|
Name: c.gatewaySvcName,
|
||||||
Address: lanAddr.Address,
|
Address: lanAddr.Address,
|
||||||
Port: lanAddr.Port,
|
Port: lanAddr.Port,
|
||||||
Meta: meta,
|
Meta: meta,
|
||||||
TaggedAddresses: taggedAddrs,
|
TaggedAddresses: taggedAddrs,
|
||||||
Proxy: proxyConf,
|
Proxy: proxyConf,
|
||||||
Check: &api.AgentServiceCheck{
|
Check: &api.AgentServiceCheck{
|
||||||
Name: "Mesh Gateway Listening",
|
Name: fmt.Sprintf("%s listening", c.gatewayKind),
|
||||||
TCP: ipaddr.FormatAddressPort(tcpCheckAddr, lanAddr.Port),
|
TCP: ipaddr.FormatAddressPort(tcpCheckAddr, lanAddr.Port),
|
||||||
Interval: "10s",
|
Interval: "10s",
|
||||||
DeregisterCriticalServiceAfter: c.deregAfterCritical,
|
DeregisterCriticalServiceAfter: c.deregAfterCritical,
|
||||||
|
@ -291,18 +324,19 @@ func (c *cmd) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
c.proxyID = proxyID
|
c.proxyID = proxyID
|
||||||
} else if c.proxyID == "" && c.meshGateway {
|
} else if c.proxyID == "" && c.gateway != "" {
|
||||||
gatewaySvc, err := proxyCmd.LookupGatewayProxy(c.client)
|
gatewaySvc, err := proxyCmd.LookupGatewayProxy(c.client, c.gatewayKind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(err.Error())
|
c.UI.Error(err.Error())
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
c.proxyID = gatewaySvc.ID
|
c.proxyID = gatewaySvc.ID
|
||||||
c.meshGatewaySvcName = gatewaySvc.Service
|
c.gatewaySvcName = gatewaySvc.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.proxyID == "" {
|
if c.proxyID == "" {
|
||||||
c.UI.Error("No proxy ID specified. One of -proxy-id or -sidecar-for/-mesh-gateway is required")
|
c.UI.Error("No proxy ID specified. One of -proxy-id or -sidecar-for/-gateway is " +
|
||||||
|
"required")
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,8 +477,8 @@ func (c *cmd) templateArgs() (*BootstrapTplArgs, error) {
|
||||||
cluster := c.proxyID
|
cluster := c.proxyID
|
||||||
if c.sidecarFor != "" {
|
if c.sidecarFor != "" {
|
||||||
cluster = c.sidecarFor
|
cluster = c.sidecarFor
|
||||||
} else if c.meshGateway && c.meshGatewaySvcName != "" {
|
} else if c.gateway != "" && c.gatewaySvcName != "" {
|
||||||
cluster = c.meshGatewaySvcName
|
cluster = c.gatewaySvcName
|
||||||
}
|
}
|
||||||
|
|
||||||
adminAccessLogPath := c.adminAccessLogPath
|
adminAccessLogPath := c.adminAccessLogPath
|
||||||
|
|
|
@ -3,6 +3,12 @@ package envoy
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
|
"github.com/hashicorp/consul/agent"
|
||||||
|
"github.com/hashicorp/consul/agent/xds"
|
||||||
|
"github.com/hashicorp/consul/api"
|
||||||
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -11,13 +17,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent"
|
|
||||||
"github.com/hashicorp/consul/agent/xds"
|
|
||||||
"github.com/hashicorp/consul/api"
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
|
||||||
"github.com/mitchellh/cli"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var update = flag.Bool("update", false, "update golden files")
|
var update = flag.Bool("update", false, "update golden files")
|
||||||
|
@ -29,6 +28,50 @@ func TestEnvoyCommand_noTabs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnvoyGateway_Validation(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
output string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"-register for non-gateway",
|
||||||
|
[]string{"-register"},
|
||||||
|
"Auto-Registration can only be used for gateways",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"-mesh-gateway and -gateway cannot be combined",
|
||||||
|
[]string{"-register", "-mesh-gateway", "-gateway", "mesh"},
|
||||||
|
"The mesh-gateway flag is deprecated and cannot be used alongside the gateway flag",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no proxy registration specified nor discovered",
|
||||||
|
[]string{""},
|
||||||
|
"No proxy ID specified",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ui := cli.NewMockUi()
|
||||||
|
c := New(ui)
|
||||||
|
c.init()
|
||||||
|
|
||||||
|
code := c.Run(tc.args)
|
||||||
|
if code == 0 {
|
||||||
|
t.Errorf("%s: expected non-zero exit", tc.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := ui.ErrorWriter.String()
|
||||||
|
if !strings.Contains(output, tc.output) {
|
||||||
|
t.Errorf("expected %q to contain %q", output, tc.output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// testSetAndResetEnv sets the env vars passed as KEY=value strings in the
|
// testSetAndResetEnv sets the env vars passed as KEY=value strings in the
|
||||||
// current ENV and returns a func() that will undo it's work at the end of the
|
// current ENV and returns a func() that will undo it's work at the end of the
|
||||||
// test for use with defer.
|
// test for use with defer.
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"github.com/hashicorp/go-sockaddr/template"
|
"github.com/hashicorp/go-sockaddr/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultMeshGatewayPort int = 443
|
const defaultGatewayPort int = 443
|
||||||
|
|
||||||
// ServiceAddressValue implements a flag.Value that may be used to parse an
|
// ServiceAddressValue implements a flag.Value that may be used to parse an
|
||||||
// addr:port string into an api.ServiceAddress.
|
// addr:port string into an api.ServiceAddress.
|
||||||
|
@ -21,14 +21,14 @@ type ServiceAddressValue struct {
|
||||||
|
|
||||||
func (s *ServiceAddressValue) String() string {
|
func (s *ServiceAddressValue) String() string {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return fmt.Sprintf(":%d", defaultMeshGatewayPort)
|
return fmt.Sprintf(":%d", defaultGatewayPort)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v:%d", s.value.Address, s.value.Port)
|
return fmt.Sprintf("%v:%d", s.value.Address, s.value.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceAddressValue) Value() api.ServiceAddress {
|
func (s *ServiceAddressValue) Value() api.ServiceAddress {
|
||||||
if s == nil || s.value.Port == 0 && s.value.Address == "" {
|
if s == nil || s.value.Port == 0 && s.value.Address == "" {
|
||||||
return api.ServiceAddress{Port: defaultMeshGatewayPort}
|
return api.ServiceAddress{Port: defaultGatewayPort}
|
||||||
}
|
}
|
||||||
return s.value
|
return s.value
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ func parseAddress(raw string) (api.ServiceAddress, error) {
|
||||||
return result, fmt.Errorf("Error parsing address %q: %v", x, err)
|
return result, fmt.Errorf("Error parsing address %q: %v", x, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
port := defaultMeshGatewayPort
|
port := defaultGatewayPort
|
||||||
if portStr != "" {
|
if portStr != "" {
|
||||||
port, err = strconv.Atoi(portStr)
|
port, err = strconv.Atoi(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -10,12 +10,12 @@ import (
|
||||||
func TestServiceAddressValue_Value(t *testing.T) {
|
func TestServiceAddressValue_Value(t *testing.T) {
|
||||||
t.Run("nil receiver", func(t *testing.T) {
|
t.Run("nil receiver", func(t *testing.T) {
|
||||||
var addr *ServiceAddressValue
|
var addr *ServiceAddressValue
|
||||||
require.Equal(t, addr.Value(), api.ServiceAddress{Port: defaultMeshGatewayPort})
|
require.Equal(t, addr.Value(), api.ServiceAddress{Port: defaultGatewayPort})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("default value", func(t *testing.T) {
|
t.Run("default value", func(t *testing.T) {
|
||||||
addr := &ServiceAddressValue{}
|
addr := &ServiceAddressValue{}
|
||||||
require.Equal(t, addr.Value(), api.ServiceAddress{Port: defaultMeshGatewayPort})
|
require.Equal(t, addr.Value(), api.ServiceAddress{Port: defaultGatewayPort})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("set value", func(t *testing.T) {
|
t.Run("set value", func(t *testing.T) {
|
||||||
|
@ -40,7 +40,7 @@ func TestServiceAddressValue_Set(t *testing.T) {
|
||||||
input: "8.8.8.8:",
|
input: "8.8.8.8:",
|
||||||
expectedValue: api.ServiceAddress{
|
expectedValue: api.ServiceAddress{
|
||||||
Address: "8.8.8.8",
|
Address: "8.8.8.8",
|
||||||
Port: defaultMeshGatewayPort,
|
Port: defaultGatewayPort,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -248,13 +248,13 @@ func LookupProxyIDForSidecar(client *api.Client, sidecarFor string) (string, err
|
||||||
return proxyIDs[0], nil
|
return proxyIDs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupGatewayProxyID finds the mesh-gateway service registered with the local
|
// LookupGatewayProxyID finds the gateway service registered with the local
|
||||||
// agent if any and returns its service ID. It will return an ID if and only if
|
// agent if any and returns its service ID. It will return an ID if and only if
|
||||||
// there is exactly one registered mesh-gateway registered to the agent.
|
// there is exactly one gateway of this kind registered to the agent.
|
||||||
func LookupGatewayProxy(client *api.Client) (*api.AgentService, error) {
|
func LookupGatewayProxy(client *api.Client, kind api.ServiceKind) (*api.AgentService, error) {
|
||||||
svcs, err := client.Agent().ServicesWithFilter("Kind == `mesh-gateway`")
|
svcs, err := client.Agent().ServicesWithFilter(fmt.Sprintf("Kind == `%s`", kind))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed looking up mesh-gateway instances: %v", err)
|
return nil, fmt.Errorf("Failed looking up %s instances: %v", kind, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var proxyIDs []string
|
var proxyIDs []string
|
||||||
|
@ -264,14 +264,14 @@ func LookupGatewayProxy(client *api.Client) (*api.AgentService, error) {
|
||||||
|
|
||||||
switch len(svcs) {
|
switch len(svcs) {
|
||||||
case 0:
|
case 0:
|
||||||
return nil, fmt.Errorf("No mesh-gateway services registered with this agent")
|
return nil, fmt.Errorf("No %s services registered with this agent", kind)
|
||||||
case 1:
|
case 1:
|
||||||
for _, svc := range svcs {
|
for _, svc := range svcs {
|
||||||
return svc, nil
|
return svc, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("This should be unreachable")
|
return nil, fmt.Errorf("This should be unreachable")
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Cannot lookup the mesh-gateway's proxy ID because multiple are registered with the agent")
|
return nil, fmt.Errorf("Cannot lookup the %s's proxy ID because multiple are registered with the agent", kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -279,7 +279,7 @@ definition](/docs/connect/registration/service-registration.html) or
|
||||||
since HTTP/2 has many requests per connection. For this configuration to be
|
since HTTP/2 has many requests per connection. For this configuration to be
|
||||||
respected, a L7 protocol must be defined in the `protocol` field.
|
respected, a L7 protocol must be defined in the `protocol` field.
|
||||||
|
|
||||||
### Mesh Gateway Options
|
### Gateway Options
|
||||||
|
|
||||||
These fields may also be overridden explicitly in the [proxy service
|
These fields may also be overridden explicitly in the [proxy service
|
||||||
definition](/docs/connect/registration/service-registration.html), or defined in
|
definition](/docs/connect/registration/service-registration.html), or defined in
|
||||||
|
@ -287,25 +287,29 @@ the [global `proxy-defaults` configuration
|
||||||
entry](/docs/agent/config_entries.html#proxy-defaults-proxy-defaults) to act as
|
entry](/docs/agent/config_entries.html#proxy-defaults-proxy-defaults) to act as
|
||||||
defaults that are inherited by all services.
|
defaults that are inherited by all services.
|
||||||
|
|
||||||
|
Prior to 1.8.0 these settings were specific to Mesh Gateways. The deprecated
|
||||||
|
names such as `envoy_mesh_gateway_bind_addresses` and `envoy_mesh_gateway_no_default_bind`
|
||||||
|
will continue to be supported.
|
||||||
|
|
||||||
- `connect_timeout_ms` - The number of milliseconds to allow when making upstream
|
- `connect_timeout_ms` - The number of milliseconds to allow when making upstream
|
||||||
connections before timing out. Defaults to 5000 (5 seconds). If the upstream
|
connections before timing out. Defaults to 5000 (5 seconds). If the upstream
|
||||||
service has the configuration option
|
service has the configuration option
|
||||||
[`connect_timeout_ms`](/docs/agent/config-entries/service-resolver.html#connecttimeout)
|
[`connect_timeout_ms`](/docs/agent/config-entries/service-resolver.html#connecttimeout)
|
||||||
set for the `service-resolver`, that timeout value will take precedence over
|
set for the `service-resolver`, that timeout value will take precedence over
|
||||||
this mesh gateway option.
|
this gateway option.
|
||||||
|
|
||||||
- `envoy_mesh_gateway_bind_tagged_addresses` - Indicates that the mesh gateway
|
- `envoy_gateway_bind_tagged_addresses` - Indicates that the gateway
|
||||||
services tagged addresses should be bound to listeners in addition to the
|
services tagged addresses should be bound to listeners in addition to the
|
||||||
default listener address.
|
default listener address.
|
||||||
|
|
||||||
- `envoy_mesh_gateway_bind_addresses` - A map of additional addresses to be bound.
|
- `envoy_gateway_bind_addresses` - A map of additional addresses to be bound.
|
||||||
This map's keys are the name of the listeners to be created and the values are
|
This map's keys are the name of the listeners to be created and the values are
|
||||||
a map with two keys, address and port, that combined make the address to bind the
|
a map with two keys, address and port, that combined make the address to bind the
|
||||||
listener to. These are bound in addition to the default address.
|
listener to. These are bound in addition to the default address.
|
||||||
|
|
||||||
- `envoy_mesh_gateway_no_default_bind` - Prevents binding to the default address
|
- `envoy_gateway_no_default_bind` - Prevents binding to the default address
|
||||||
of the mesh gateway service. This should be used with one of the other options
|
of the gateway service. This should be used with one of the other options
|
||||||
to configure the gateways bind addresses.
|
to configure the gateway's bind addresses.
|
||||||
|
|
||||||
## Advanced Configuration
|
## Advanced Configuration
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue