Add http routing support and integration test to ingress gateways

This commit is contained in:
Kyle Havlovitz 2020-04-03 12:41:10 -07:00 committed by Chris Piraino
parent 1194fe441f
commit e7b1ee55de
7 changed files with 167 additions and 10 deletions

View File

@ -23,6 +23,8 @@ func routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, _ string) ([]proto.Mes
switch cfgSnap.Kind { switch cfgSnap.Kind {
case structs.ServiceKindConnectProxy: case structs.ServiceKindConnectProxy:
return routesFromSnapshotConnectProxy(cfgSnap) return routesFromSnapshotConnectProxy(cfgSnap)
case structs.ServiceKindIngressGateway:
return routesFromSnapshotIngressGateway(cfgSnap)
default: default:
return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
} }
@ -35,20 +37,34 @@ func routesFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.M
return nil, errors.New("nil config given") return nil, errors.New("nil config given")
} }
return routesFromUpstreams(cfgSnap.ConnectProxy.ConfigSnapshotUpstreams, cfgSnap.Proxy.Upstreams)
}
// routesFromSnapshotIngressGateway returns the xDS API representation of the
// "routes" in the snapshot.
func routesFromSnapshotIngressGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
if cfgSnap == nil {
return nil, errors.New("nil config given")
}
return routesFromUpstreams(cfgSnap.IngressGateway.ConfigSnapshotUpstreams, cfgSnap.IngressGateway.Upstreams)
}
func routesFromUpstreams(snap proxycfg.ConfigSnapshotUpstreams, upstreams structs.Upstreams) ([]proto.Message, error) {
var resources []proto.Message var resources []proto.Message
for _, u := range cfgSnap.Proxy.Upstreams { for _, u := range upstreams {
upstreamID := u.Identifier() upstreamID := u.Identifier()
var chain *structs.CompiledDiscoveryChain var chain *structs.CompiledDiscoveryChain
if u.DestinationType != structs.UpstreamDestTypePreparedQuery { if u.DestinationType != structs.UpstreamDestTypePreparedQuery {
chain = cfgSnap.ConnectProxy.DiscoveryChain[upstreamID] chain = snap.DiscoveryChain[upstreamID]
} }
if chain == nil || chain.IsDefault() { if chain == nil || chain.IsDefault() {
// TODO(rb): make this do the old school stuff too // TODO(rb): make this do the old school stuff too
} else { } else {
upstreamRoute, err := makeUpstreamRouteForDiscoveryChain(&u, chain, cfgSnap) upstreamRoute, err := makeUpstreamRouteForDiscoveryChain(&u, chain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -66,7 +82,6 @@ func routesFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.M
func makeUpstreamRouteForDiscoveryChain( func makeUpstreamRouteForDiscoveryChain(
u *structs.Upstream, u *structs.Upstream,
chain *structs.CompiledDiscoveryChain, chain *structs.CompiledDiscoveryChain,
cfgSnap *proxycfg.ConfigSnapshot,
) (*envoy.RouteConfiguration, error) { ) (*envoy.RouteConfiguration, error) {
upstreamID := u.Identifier() upstreamID := u.Identifier()
routeName := upstreamID routeName := upstreamID
@ -93,13 +108,13 @@ func makeUpstreamRouteForDiscoveryChain(
nextNode := chain.Nodes[discoveryRoute.NextNode] nextNode := chain.Nodes[discoveryRoute.NextNode]
switch nextNode.Type { switch nextNode.Type {
case structs.DiscoveryGraphNodeTypeSplitter: case structs.DiscoveryGraphNodeTypeSplitter:
routeAction, err = makeRouteActionForSplitter(nextNode.Splits, chain, cfgSnap) routeAction, err = makeRouteActionForSplitter(nextNode.Splits, chain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
case structs.DiscoveryGraphNodeTypeResolver: case structs.DiscoveryGraphNodeTypeResolver:
routeAction = makeRouteActionForSingleCluster(nextNode.Resolver.Target, chain, cfgSnap) routeAction = makeRouteActionForSingleCluster(nextNode.Resolver.Target, chain)
default: default:
return nil, fmt.Errorf("unexpected graph node after route %q", nextNode.Type) return nil, fmt.Errorf("unexpected graph node after route %q", nextNode.Type)
@ -147,7 +162,7 @@ func makeUpstreamRouteForDiscoveryChain(
} }
case structs.DiscoveryGraphNodeTypeSplitter: case structs.DiscoveryGraphNodeTypeSplitter:
routeAction, err := makeRouteActionForSplitter(startNode.Splits, chain, cfgSnap) routeAction, err := makeRouteActionForSplitter(startNode.Splits, chain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -160,7 +175,7 @@ func makeUpstreamRouteForDiscoveryChain(
routes = []envoyroute.Route{defaultRoute} routes = []envoyroute.Route{defaultRoute}
case structs.DiscoveryGraphNodeTypeResolver: case structs.DiscoveryGraphNodeTypeResolver:
routeAction := makeRouteActionForSingleCluster(startNode.Resolver.Target, chain, cfgSnap) routeAction := makeRouteActionForSingleCluster(startNode.Resolver.Target, chain)
defaultRoute := envoyroute.Route{ defaultRoute := envoyroute.Route{
Match: makeDefaultRouteMatch(), Match: makeDefaultRouteMatch(),
@ -307,7 +322,7 @@ func makeDefaultRouteMatch() envoyroute.RouteMatch {
} }
} }
func makeRouteActionForSingleCluster(targetID string, chain *structs.CompiledDiscoveryChain, cfgSnap *proxycfg.ConfigSnapshot) *envoyroute.Route_Route { func makeRouteActionForSingleCluster(targetID string, chain *structs.CompiledDiscoveryChain) *envoyroute.Route_Route {
target := chain.Targets[targetID] target := chain.Targets[targetID]
clusterName := CustomizeClusterName(target.Name, chain) clusterName := CustomizeClusterName(target.Name, chain)
@ -321,7 +336,7 @@ func makeRouteActionForSingleCluster(targetID string, chain *structs.CompiledDis
} }
} }
func makeRouteActionForSplitter(splits []*structs.DiscoverySplit, chain *structs.CompiledDiscoveryChain, cfgSnap *proxycfg.ConfigSnapshot) (*envoyroute.Route_Route, error) { func makeRouteActionForSplitter(splits []*structs.DiscoverySplit, chain *structs.CompiledDiscoveryChain) (*envoyroute.Route_Route, error) {
clusters := make([]*envoyroute.WeightedCluster_ClusterWeight, 0, len(splits)) clusters := make([]*envoyroute.WeightedCluster_ClusterWeight, 0, len(splits))
for _, split := range splits { for _, split := range splits {
nextNode := chain.Nodes[split.NextNode] nextNode := chain.Nodes[split.NextNode]

View File

@ -0,0 +1,3 @@
#!/bin/bash
snapshot_envoy_admin localhost:20000 ingress-gateway primary || true

View File

@ -0,0 +1,62 @@
enable_central_service_config = true
config_entries {
bootstrap = [
{
kind = "ingress-gateway"
name = "ingress-gateway"
listeners = [
{
port = 9999
protocol = "http"
services = [
{
name = "router"
}
]
}
]
},
{
kind = "proxy-defaults"
name = "global"
config {
protocol = "http"
}
},
{
kind = "service-router"
// This is a "virtual" service name and will not have a backing
// service definition. It must match the name defined in the ingress
// configuration.
name = "router"
routes = [
{
match {
http {
path_prefix = "/s1/"
}
}
destination {
service = "s1"
prefix_rewrite = "/"
}
},
{
match {
http {
path_prefix = "/s2/"
}
}
destination {
service = "s2"
prefix_rewrite = "/"
}
}
]
}
]
}

View File

@ -0,0 +1,4 @@
services {
name = "ingress-gateway"
kind = "ingress-gateway"
}

View File

@ -0,0 +1,12 @@
#!/bin/bash
set -euo pipefail
# wait for bootstrap to apply config entries
wait_for_config_entry ingress-gateway ingress-gateway
wait_for_config_entry proxy-defaults global
wait_for_config_entry service-router router
gen_envoy_bootstrap ingress-gateway 20000 primary true
gen_envoy_bootstrap s1 19000
gen_envoy_bootstrap s2 19001

View File

@ -0,0 +1,3 @@
#!/bin/bash
export REQUIRED_SERVICES="$DEFAULT_REQUIRED_SERVICES ingress-gateway-primary"

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bats
load helpers
@test "ingress proxy admin is up on :20000" {
retry_default curl -f -s localhost:20000/stats -o /dev/null
}
@test "s1 proxy admin is up on :19000" {
retry_default curl -f -s localhost:19000/stats -o /dev/null
}
@test "s2 proxy admin is up on :19001" {
retry_default curl -f -s localhost:19001/stats -o /dev/null
}
@test "s1 proxy listener should be up and have right cert" {
assert_proxy_presents_cert_uri localhost:21000 s1
}
@test "s2 proxy listener should be up and have right cert" {
assert_proxy_presents_cert_uri localhost:21001 s2
}
@test "ingress-gateway should have healthy endpoints for s1" {
assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s1 HEALTHY 1
}
@test "ingress-gateway should have healthy endpoints for s2" {
assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s2 HEALTHY 1
}
@test "ingress should be able to connect to s1 via configured path" {
run retry_default curl -s -f localhost:9999/s1/debug?env=dump
[ "$status" -eq 0 ]
GOT=$(echo "$output" | grep -E "^FORTIO_NAME=")
EXPECT_NAME="s1"
if [ "$GOT" != "FORTIO_NAME=${EXPECT_NAME}" ]; then
echo "expected name: $EXPECT_NAME, actual name: $GOT" 1>&2
return 1
fi
}
@test "ingress should be able to connect to s2 via configured path" {
run retry_default curl -s -f localhost:9999/s2/debug?env=dump
[ "$status" -eq 0 ]
GOT=$(echo "$output" | grep -E "^FORTIO_NAME=")
EXPECT_NAME="s2"
if [ "$GOT" != "FORTIO_NAME=${EXPECT_NAME}" ]; then
echo "expected name: $EXPECT_NAME, actual name: $GOT" 1>&2
return 1
fi
}