[NET-5457] Golden Files for Multiple Virtual Hosts (#19131)

* Add new golden file tests

* Update with latest deterministic code
This commit is contained in:
John Maguire 2023-10-11 14:11:29 -04:00 committed by GitHub
parent ca1a755f0c
commit 7a323c492b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 163 additions and 2 deletions

View File

@ -15,6 +15,7 @@ import (
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
"golang.org/x/exp/maps" "golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
@ -435,9 +436,15 @@ func (s *ResourceGenerator) routesForIngressGateway(cfgSnap *proxycfg.ConfigSnap
func (s *ResourceGenerator) routesForAPIGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { func (s *ResourceGenerator) routesForAPIGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
var result []proto.Message var result []proto.Message
// Build up the routes in a deterministic way
readyListeners := getReadyListeners(cfgSnap) readyListeners := getReadyListeners(cfgSnap)
listenerNames := maps.Keys(readyListeners)
for _, readyListener := range readyListeners { sort.Strings(listenerNames)
for _, listenerName := range listenerNames {
readyListener, ok := readyListeners[listenerName]
if !ok {
continue
}
// Do not create any route configuration for TCP listeners // Do not create any route configuration for TCP listeners
if readyListener.listenerCfg.Protocol != structs.ListenerProtocolHTTP { if readyListener.listenerCfg.Protocol != structs.ListenerProtocolHTTP {
continue continue
@ -466,6 +473,7 @@ func (s *ResourceGenerator) routesForAPIGateway(cfgSnap *proxycfg.ConfigSnapshot
// Gateway + HTTPRoutes, then the virtual host will be "*". // Gateway + HTTPRoutes, then the virtual host will be "*".
for _, consolidatedRoute := range consolidatedRoutes { for _, consolidatedRoute := range consolidatedRoutes {
upstream := buildHTTPRouteUpstream(consolidatedRoute, readyListener.listenerCfg) upstream := buildHTTPRouteUpstream(consolidatedRoute, readyListener.listenerCfg)
// Consolidate all routes for this listener into the minimum possible set based on hostname matching.
uid := proxycfg.NewUpstreamID(&upstream) uid := proxycfg.NewUpstreamID(&upstream)
chain := cfgSnap.APIGateway.DiscoveryChain[uid] chain := cfgSnap.APIGateway.DiscoveryChain[uid]
if chain == nil { if chain == nil {
@ -485,6 +493,13 @@ func (s *ResourceGenerator) routesForAPIGateway(cfgSnap *proxycfg.ConfigSnapshot
} }
if len(listenerRoute.VirtualHosts) > 0 { if len(listenerRoute.VirtualHosts) > 0 {
// Build up the virtual hosts in a deterministic way
slices.SortStableFunc(listenerRoute.VirtualHosts, func(a, b *envoy_route_v3.VirtualHost) int {
if a.Name < b.Name {
return -1
}
return 1
})
result = append(result, listenerRoute) result = append(result, listenerRoute)
} }
} }

View File

@ -262,6 +262,65 @@ func TestRoutesFromSnapshot(t *testing.T) {
// TODO(proxystate): terminating gateway will come at a later time // TODO(proxystate): terminating gateway will come at a later time
alsoRunTestForV2: false, alsoRunTestForV2: false,
}, },
{
name: "api-gateway-with-multiple-hostnames",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) {
entry.Listeners = []structs.APIGatewayListener{
{
Name: "http",
Protocol: structs.ListenerProtocolHTTP,
Port: 8080,
Hostname: "*.example.com",
},
}
bound.Listeners = []structs.BoundAPIGatewayListener{
{
Name: "http",
Routes: []structs.ResourceReference{
{Kind: structs.HTTPRoute, Name: "backend-route"},
{Kind: structs.HTTPRoute, Name: "frontend-route"},
{Kind: structs.HTTPRoute, Name: "generic-route"},
}},
}
},
[]structs.BoundRoute{
&structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "backend-route",
Hostnames: []string{"backend.example.com"},
Parents: []structs.ResourceReference{{Kind: structs.APIGateway, Name: "api-gateway"}},
Rules: []structs.HTTPRouteRule{
{Services: []structs.HTTPService{{Name: "backend"}}},
},
},
&structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "frontend-route",
Hostnames: []string{"frontend.example.com"},
Parents: []structs.ResourceReference{{Kind: structs.APIGateway, Name: "api-gateway"}},
Rules: []structs.HTTPRouteRule{
{Services: []structs.HTTPService{{Name: "frontend"}}},
},
},
&structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "generic-route",
Parents: []structs.ResourceReference{{Kind: structs.APIGateway, Name: "api-gateway"}},
Rules: []structs.HTTPRouteRule{
{
Matches: []structs.HTTPMatch{{Path: structs.HTTPPathMatch{Match: structs.HTTPPathMatchPrefix, Value: "/frontend"}}},
Services: []structs.HTTPService{{Name: "frontend"}},
},
{
Matches: []structs.HTTPMatch{{Path: structs.HTTPPathMatch{Match: structs.HTTPPathMatchPrefix, Value: "/backend"}}},
Services: []structs.HTTPService{{Name: "backend"}},
},
},
},
}, nil, nil)
},
},
} }
tests = append(tests, makeRouteDiscoChainTests(false)...) tests = append(tests, makeRouteDiscoChainTests(false)...)

View File

@ -0,0 +1,87 @@
{
"nonce": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "8080",
"validateClusters": true,
"virtualHosts": [
{
"domains": [
"backend.example.com",
"backend.example.com:8080"
],
"name": "api-gateway-http-5a84e719",
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "backend.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "8080",
"validateClusters": true,
"virtualHosts": [
{
"domains": [
"frontend.example.com",
"frontend.example.com:8080"
],
"name": "api-gateway-http-54620b06",
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "frontend.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "8080",
"validateClusters": true,
"virtualHosts": [
{
"domains": [
"*.example.com",
"*.example.com:8080"
],
"name": "api-gateway-http-aa289ce2",
"routes": [
{
"match": {
"prefix": "/frontend"
},
"route": {
"cluster": "frontend.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
},
{
"match": {
"prefix": "/backend"
},
"route": {
"cluster": "backend.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
]
}
],
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"versionInfo": "00000001"
}