Add http routing support and integration test to ingress gateways
This commit is contained in:
parent
ef88089924
commit
d433679bd8
|
@ -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]
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
snapshot_envoy_admin localhost:20000 ingress-gateway primary || true
|
|
@ -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 = "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
services {
|
||||||
|
name = "ingress-gateway"
|
||||||
|
kind = "ingress-gateway"
|
||||||
|
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="$DEFAULT_REQUIRED_SERVICES ingress-gateway-primary"
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue