open-consul/agent/consul/discoverychain/gateway_test.go

546 lines
13 KiB
Go

package discoverychain
import (
"testing"
"github.com/hashicorp/consul/agent/structs"
"github.com/stretchr/testify/require"
)
func TestGatewayChainSynthesizer_AddTCPRoute(t *testing.T) {
t.Parallel()
datacenter := "dc1"
gateway := &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
}
route := structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "route",
}
expected := GatewayChainSynthesizer{
datacenter: datacenter,
gateway: gateway,
matchesByHostname: map[string][]hostnameMatch{},
tcpRoutes: []structs.TCPRouteConfigEntry{
route,
},
}
gatewayChainSynthesizer := NewGatewayChainSynthesizer(datacenter, gateway)
// Add a TCP route
gatewayChainSynthesizer.AddTCPRoute(route)
require.Equal(t, expected, *gatewayChainSynthesizer)
}
func TestGatewayChainSynthesizer_AddHTTPRoute(t *testing.T) {
t.Parallel()
cases := map[string]struct {
route structs.HTTPRouteConfigEntry
expectedMatchesByHostname map[string][]hostnameMatch
}{
"no hostanames": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
},
expectedMatchesByHostname: map[string][]hostnameMatch{},
},
"single hostname with no rules": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {},
},
},
"single hostname with a single rule and no matches": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "/",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
"single hostname with a single rule and a single match": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
"single hostname with a single rule and multiple matches": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
"multiple hostnames with a single rule and a single match": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
"example.net",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
"example.net": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
"multiple hostnames with a single rule and multiple matches": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
"example.net",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
"example.net": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
"multiple hostnames with multiple rules and multiple matches": {
route: structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "route",
Hostnames: []string{
"example.com",
"example.net",
},
Rules: []structs.HTTPRouteRule{
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
},
Services: []structs.HTTPService{},
},
{
Filters: structs.HTTPFilters{},
Matches: []structs.HTTPMatch{
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "baz-",
},
},
{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "qux-",
},
},
},
Services: []structs.HTTPService{},
},
},
},
expectedMatchesByHostname: map[string][]hostnameMatch{
"example.com": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "baz-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "qux-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
"example.net": {
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "foo-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "bar-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "baz-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
{
match: structs.HTTPMatch{
Path: structs.HTTPPathMatch{
Match: "prefix",
Value: "qux-",
},
},
filters: structs.HTTPFilters{},
services: []structs.HTTPService{},
},
},
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
datacenter := "dc1"
gateway := &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
}
gatewayChainSynthesizer := NewGatewayChainSynthesizer(datacenter, gateway)
gatewayChainSynthesizer.AddHTTPRoute(tc.route)
require.Equal(t, tc.expectedMatchesByHostname, gatewayChainSynthesizer.matchesByHostname)
})
}
}
func TestGatewayChainSynthesizer_Synthesize(t *testing.T) {
t.Parallel()
cases := map[string]struct {
synthesizer *GatewayChainSynthesizer
tcpRoutes []*structs.TCPRouteConfigEntry
httpRoutes []*structs.HTTPRouteConfigEntry
chain *structs.CompiledDiscoveryChain
extra []*structs.CompiledDiscoveryChain
expectedIngressServices []structs.IngressService
expectedDiscoveryChain *structs.CompiledDiscoveryChain
}{
// TODO Add tests for other synthesizer types.
"TCPRoute-based listener": {
synthesizer: NewGatewayChainSynthesizer("dc1", &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
}),
tcpRoutes: []*structs.TCPRouteConfigEntry{
{
Kind: structs.TCPRoute,
Name: "tcp-route",
},
},
chain: &structs.CompiledDiscoveryChain{
ServiceName: "foo",
Namespace: "default",
Datacenter: "dc1",
},
extra: []*structs.CompiledDiscoveryChain{},
expectedIngressServices: []structs.IngressService{},
expectedDiscoveryChain: &structs.CompiledDiscoveryChain{
ServiceName: "foo",
Namespace: "default",
Datacenter: "dc1",
},
},
"HTTPRoute-based listener": {
synthesizer: NewGatewayChainSynthesizer("dc1", &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
}),
httpRoutes: []*structs.HTTPRouteConfigEntry{
{
Kind: structs.HTTPRoute,
Name: "http-route",
},
},
chain: &structs.CompiledDiscoveryChain{
ServiceName: "foo",
Namespace: "default",
Datacenter: "dc1",
},
extra: []*structs.CompiledDiscoveryChain{},
expectedIngressServices: []structs.IngressService{},
expectedDiscoveryChain: &structs.CompiledDiscoveryChain{
ServiceName: "foo",
Namespace: "default",
Datacenter: "dc1",
},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
for _, tcpRoute := range tc.tcpRoutes {
tc.synthesizer.AddTCPRoute(*tcpRoute)
}
for _, httpRoute := range tc.httpRoutes {
tc.synthesizer.AddHTTPRoute(*httpRoute)
}
chains := append([]*structs.CompiledDiscoveryChain{tc.chain}, tc.extra...)
ingressServices, discoveryChain, err := tc.synthesizer.Synthesize(chains...)
require.NoError(t, err)
require.Equal(t, tc.expectedIngressServices, ingressServices)
require.Equal(t, tc.expectedDiscoveryChain, discoveryChain)
})
}
}