fix: multiple grpc/http2 services for ingress listeners
This commit is contained in:
parent
13e2c81451
commit
65ca7e0bfb
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
fix a bug that caused an error when creating `grpc` or `http2` ingress gateway listeners with multiple services
|
||||||
|
```
|
|
@ -800,6 +800,109 @@ func TestConfigSnapshotIngress_HTTPMultipleServices(t testing.T) *ConfigSnapshot
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConfigSnapshotIngress_GRPCMultipleServices(t testing.T) *ConfigSnapshot {
|
||||||
|
// We do not add baz/qux here so that we test the chain.IsDefault() case
|
||||||
|
entries := []structs.ConfigEntry{
|
||||||
|
&structs.ProxyConfigEntry{
|
||||||
|
Kind: structs.ProxyDefaults,
|
||||||
|
Name: structs.ProxyConfigGlobal,
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"protocol": "http",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "foo",
|
||||||
|
ConnectTimeout: 22 * time.Second,
|
||||||
|
},
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "bar",
|
||||||
|
ConnectTimeout: 22 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
foo = structs.NewServiceName("foo", nil)
|
||||||
|
fooUID = NewUpstreamIDFromServiceName(foo)
|
||||||
|
fooChain = discoverychain.TestCompileConfigEntries(t, "foo", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...)
|
||||||
|
|
||||||
|
bar = structs.NewServiceName("bar", nil)
|
||||||
|
barUID = NewUpstreamIDFromServiceName(bar)
|
||||||
|
barChain = discoverychain.TestCompileConfigEntries(t, "bar", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...)
|
||||||
|
)
|
||||||
|
|
||||||
|
require.False(t, fooChain.Default)
|
||||||
|
require.False(t, barChain.Default)
|
||||||
|
|
||||||
|
return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) {
|
||||||
|
entry.Listeners = []structs.IngressListener{
|
||||||
|
{
|
||||||
|
Port: 8080,
|
||||||
|
Protocol: "grpc",
|
||||||
|
Services: []structs.IngressService{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Hosts: []string{
|
||||||
|
"test1.example.com",
|
||||||
|
"test2.example.com",
|
||||||
|
"test2.example.com:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{Name: "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, []UpdateEvent{
|
||||||
|
{
|
||||||
|
CorrelationID: gatewayServicesWatchID,
|
||||||
|
Result: &structs.IndexedGatewayServices{
|
||||||
|
Services: []*structs.GatewayService{
|
||||||
|
{
|
||||||
|
Service: foo,
|
||||||
|
Port: 8080,
|
||||||
|
Protocol: "grpc",
|
||||||
|
Hosts: []string{
|
||||||
|
"test1.example.com",
|
||||||
|
"test2.example.com",
|
||||||
|
"test2.example.com:8080",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Service: bar,
|
||||||
|
Port: 8080,
|
||||||
|
Protocol: "grpc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CorrelationID: "discovery-chain:" + fooUID.String(),
|
||||||
|
Result: &structs.DiscoveryChainResponse{
|
||||||
|
Chain: fooChain,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CorrelationID: "upstream-target:" + fooChain.ID() + ":" + fooUID.String(),
|
||||||
|
Result: &structs.IndexedCheckServiceNodes{
|
||||||
|
Nodes: TestUpstreamNodes(t, "foo"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CorrelationID: "discovery-chain:" + barUID.String(),
|
||||||
|
Result: &structs.DiscoveryChainResponse{
|
||||||
|
Chain: barChain,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CorrelationID: "upstream-target:" + barChain.ID() + ":" + barUID.String(),
|
||||||
|
Result: &structs.IndexedCheckServiceNodes{
|
||||||
|
Nodes: TestUpstreamNodes(t, "bar"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestConfigSnapshotIngress_MultipleListenersDuplicateService(t testing.T) *ConfigSnapshot {
|
func TestConfigSnapshotIngress_MultipleListenersDuplicateService(t testing.T) *ConfigSnapshot {
|
||||||
var (
|
var (
|
||||||
foo = structs.NewServiceName("foo", nil)
|
foo = structs.NewServiceName("foo", nil)
|
||||||
|
|
|
@ -275,8 +275,8 @@ func (e *IngressGatewayConfigEntry) Validate() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate that http features aren't being used with tcp or another non-supported protocol.
|
// Validate that http features aren't being used with tcp or another non-supported protocol.
|
||||||
if listener.Protocol != "http" && len(listener.Services) > 1 {
|
if !IsProtocolHTTPLike(listener.Protocol) && len(listener.Services) > 1 {
|
||||||
return fmt.Errorf("Multiple services per listener are only supported for protocol = 'http' (listener on port %d)",
|
return fmt.Errorf("Multiple services per listener are only supported for L7 protocols, 'http', 'grpc' and 'http2' (listener on port %d)",
|
||||||
listener.Port)
|
listener.Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,26 @@ func TestIngressGatewayConfigEntry(t *testing.T) {
|
||||||
validateErr: "Wildcard service name is only valid for protocol",
|
validateErr: "Wildcard service name is only valid for protocol",
|
||||||
},
|
},
|
||||||
"http features: multiple services": {
|
"http features: multiple services": {
|
||||||
|
entry: &IngressGatewayConfigEntry{
|
||||||
|
Kind: "ingress-gateway",
|
||||||
|
Name: "ingress-web",
|
||||||
|
Listeners: []IngressListener{
|
||||||
|
{
|
||||||
|
Port: 1111,
|
||||||
|
Protocol: "http",
|
||||||
|
Services: []IngressService{
|
||||||
|
{
|
||||||
|
Name: "db1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "db2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"http features: multiple services on tcp listener": {
|
||||||
entry: &IngressGatewayConfigEntry{
|
entry: &IngressGatewayConfigEntry{
|
||||||
Kind: "ingress-gateway",
|
Kind: "ingress-gateway",
|
||||||
Name: "ingress-web",
|
Name: "ingress-web",
|
||||||
|
@ -153,7 +173,7 @@ func TestIngressGatewayConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
validateErr: "Multiple services per listener are only supported for protocol",
|
validateErr: "Multiple services per listener are only supported for L7",
|
||||||
},
|
},
|
||||||
// ==========================
|
// ==========================
|
||||||
"tcp listener requires a defined service": {
|
"tcp listener requires a defined service": {
|
||||||
|
|
|
@ -680,6 +680,10 @@ func TestListenersFromSnapshot(t *testing.T) {
|
||||||
name: "ingress-http-multiple-services",
|
name: "ingress-http-multiple-services",
|
||||||
create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices,
|
create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ingress-grpc-multiple-services",
|
||||||
|
create: proxycfg.TestConfigSnapshotIngress_GRPCMultipleServices,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "terminating-gateway-no-api-cert",
|
name: "terminating-gateway-no-api-cert",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
|
|
@ -142,6 +142,10 @@ func TestRoutesFromSnapshot(t *testing.T) {
|
||||||
name: "ingress-http-multiple-services",
|
name: "ingress-http-multiple-services",
|
||||||
create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices,
|
create: proxycfg.TestConfigSnapshotIngress_HTTPMultipleServices,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ingress-grpc-multiple-services",
|
||||||
|
create: proxycfg.TestConfigSnapshotIngress_GRPCMultipleServices,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ingress-with-chain-and-router-header-manip",
|
name: "ingress-with-chain-and-router-header-manip",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||||
|
"name": "grpc:1.2.3.4:8080",
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "1.2.3.4",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filterChains": [
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.http_connection_manager",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||||
|
"statPrefix": "ingress_upstream_8080",
|
||||||
|
"rds": {
|
||||||
|
"configSource": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
},
|
||||||
|
"routeConfigName": "8080"
|
||||||
|
},
|
||||||
|
"httpFilters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.grpc_stats",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig",
|
||||||
|
"statsForAllMethods": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.grpc_http1_bridge",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.http.router",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tracing": {
|
||||||
|
"randomSampling": {}
|
||||||
|
},
|
||||||
|
"http2ProtocolOptions": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trafficDirection": "OUTBOUND"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||||
|
"name": "8080",
|
||||||
|
"virtualHosts": [
|
||||||
|
{
|
||||||
|
"name": "foo",
|
||||||
|
"domains": [
|
||||||
|
"test1.example.com",
|
||||||
|
"test2.example.com",
|
||||||
|
"test2.example.com:8080",
|
||||||
|
"test1.example.com:8080"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "foo.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bar",
|
||||||
|
"domains": [
|
||||||
|
"bar.ingress.*",
|
||||||
|
"bar.ingress.*:8080"
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"cluster": "bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validateClusters": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
Loading…
Reference in New Issue