open-consul/agent/consul/gateways/controller_gateways_test.go

4115 lines
112 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package gateways
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
"time"
2023-04-04 16:30:06 +00:00
"github.com/stretchr/testify/require"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/consul/controller"
"github.com/hashicorp/consul/agent/consul/controller/queue"
"github.com/hashicorp/consul/agent/consul/fsm"
"github.com/hashicorp/consul/agent/consul/state"
"github.com/hashicorp/consul/agent/consul/stream"
"github.com/hashicorp/consul/agent/structs"
)
func TestBoundAPIGatewayBindRoute(t *testing.T) {
t.Parallel()
cases := map[string]struct {
gateway gatewayMeta
route structs.BoundRoute
expectedBoundGateway structs.BoundAPIGatewayConfigEntry
expectedDidBind bool
expectedErr error
}{
"Bind TCP Route to Gateway": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.APIGateway,
Name: "Gateway",
SectionName: "Listener",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Kind: structs.TCPRoute,
Name: "Route",
},
},
},
},
},
expectedDidBind: true,
},
"Bind TCP Route with wildcard section name to all listeners on Gateway": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 3",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 3",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.APIGateway,
Name: "Gateway",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Kind: structs.TCPRoute,
Name: "Route",
},
},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Kind: structs.TCPRoute,
Name: "Route",
},
},
},
{
Name: "Listener 3",
Routes: []structs.ResourceReference{
{
Kind: structs.TCPRoute,
Name: "Route",
},
},
},
},
},
expectedDidBind: true,
},
"TCP Route cannot bind to Gateway because the parent reference kind is not APIGateway": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: "Foo",
Name: "Gateway",
SectionName: "Listener",
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.TerminatingGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
expectedDidBind: false,
},
"TCP Route cannot bind to Gateway because the parent reference name does not match": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.APIGateway,
Name: "Other Gateway",
SectionName: "Listener",
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
expectedDidBind: false,
},
"TCP Route cannot bind to Gateway because it lacks listeners": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.APIGateway,
Name: "Gateway",
SectionName: "Listener",
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{},
},
expectedDidBind: false,
expectedErr: fmt.Errorf("route cannot bind because gateway has no listeners"),
},
"TCP Route cannot bind to Gateway because it has an invalid section name": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.APIGateway,
Name: "Gateway",
SectionName: "Other Listener",
},
},
},
expectedBoundGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
expectedDidBind: false,
expectedErr: fmt.Errorf("failed to bind route Route to gateway Gateway with listener 'Other Listener'"),
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
ref := tc.route.GetParents()[0]
actualDidBind, _, actualErrors := (&tc.gateway).initialize().updateRouteBinding(tc.route)
require.Equal(t, tc.expectedDidBind, actualDidBind)
require.Equal(t, tc.expectedErr, actualErrors[ref])
require.Equal(t, tc.expectedBoundGateway.Listeners, tc.gateway.BoundGateway.Listeners)
})
}
}
func TestBoundAPIGatewayUnbindRoute(t *testing.T) {
t.Parallel()
cases := map[string]struct {
gateway gatewayMeta
route structs.BoundRoute
expectedGateway structs.BoundAPIGatewayConfigEntry
expectedDidUnbind bool
}{
"TCP Route unbinds from Gateway": {
gateway: gatewayMeta{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Kind: structs.TCPRoute,
Name: "Route",
},
},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{},
},
route: &structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "Route",
Parents: []structs.ResourceReference{
{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
SectionName: "Listener",
},
},
},
expectedGateway: structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
expectedDidUnbind: true,
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
routeRef := structs.ResourceReference{
Kind: tc.route.GetKind(),
Name: tc.route.GetName(),
EnterpriseMeta: *tc.route.GetEnterpriseMeta(),
}
actualDidUnbind := (&tc.gateway).initialize().unbindRoute(routeRef)
require.Equal(t, tc.expectedDidUnbind, actualDidUnbind)
require.Equal(t, tc.expectedGateway.Listeners, tc.gateway.BoundGateway.Listeners)
})
}
}
func TestBindRoutesToGateways(t *testing.T) {
t.Parallel()
type testCase struct {
gateways []*gatewayMeta
routes []structs.BoundRoute
expectedBoundAPIGateways []*structs.BoundAPIGatewayConfigEntry
expectedReferenceErrors map[structs.ResourceReference]error
}
cases := map[string]testCase{
"TCP Route binds to gateway": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "TCP Route",
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Listener",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route unbinds from gateway": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "TCP Route",
Parents: []structs.ResourceReference{},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route binds to multiple gateways": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "TCP Route",
Parents: []structs.ResourceReference{
{
Name: "Gateway 1",
Kind: structs.APIGateway,
SectionName: "Listener",
},
{
Name: "Gateway 2",
Kind: structs.APIGateway,
SectionName: "Listener",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route binds to a single listener on a gateway with multiple listeners": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolHTTP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Listener 2",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route binds to all listeners on a gateway": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route binds to gateway with multiple listeners, one of which is already bound": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route binds to a listener on multiple gateways": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway 1",
Kind: structs.APIGateway,
SectionName: "Listener 2",
},
{
Name: "Gateway 2",
Kind: structs.APIGateway,
SectionName: "Listener 2",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route swaps from one listener to another on a gateway": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Listener 2",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"Multiple TCP Routes bind to different gateways": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 1",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway 2",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route 1",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway 1",
Kind: structs.APIGateway,
SectionName: "Listener 1",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
&structs.TCPRouteConfigEntry{
Name: "TCP Route 2",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway 2",
Kind: structs.APIGateway,
SectionName: "Listener 2",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway 1",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{
{
Name: "TCP Route 1",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
{
Name: "Gateway 2",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route 2",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route cannot be bound to a listener with an HTTP protocol": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolHTTP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Listener",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{},
expectedReferenceErrors: map[structs.ResourceReference]error{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Listener",
}: fmt.Errorf("failed to bind route TCP Route to gateway Gateway: listener Listener is not a tcp listener"),
},
},
"If a route/listener protocol mismatch occurs with the wildcard, but a bind to another listener was possible, no error is returned": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener 1",
Protocol: structs.ListenerProtocolHTTP,
},
{
Name: "Listener 2",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "",
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{
{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener 1",
Routes: []structs.ResourceReference{},
},
{
Name: "Listener 2",
Routes: []structs.ResourceReference{
{
Name: "TCP Route",
Kind: structs.TCPRoute,
SectionName: "",
},
},
},
},
},
},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
"TCP Route references a listener that does not exist": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Non-existent Listener",
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{},
expectedReferenceErrors: map[structs.ResourceReference]error{
{
Name: "Gateway",
Kind: structs.APIGateway,
SectionName: "Non-existent Listener",
}: fmt.Errorf("failed to bind route TCP Route to gateway Gateway with listener 'Non-existent Listener'"),
},
},
"Already bound TCP Route": {
gateways: []*gatewayMeta{
{
BoundGateway: &structs.BoundAPIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.BoundAPIGatewayListener{
{
Name: "Listener",
Routes: []structs.ResourceReference{{
Kind: structs.TCPRoute,
Name: "TCP Route",
}},
},
},
},
Gateway: &structs.APIGatewayConfigEntry{
Name: "Gateway",
Listeners: []structs.APIGatewayListener{
{
Name: "Listener",
Protocol: structs.ListenerProtocolTCP,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
},
},
routes: []structs.BoundRoute{
&structs.TCPRouteConfigEntry{
Name: "TCP Route",
Kind: structs.TCPRoute,
Parents: []structs.ResourceReference{
{
Name: "Gateway",
Kind: structs.APIGateway,
},
},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
},
expectedBoundAPIGateways: []*structs.BoundAPIGatewayConfigEntry{},
expectedReferenceErrors: map[structs.ResourceReference]error{},
},
}
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
for i := range tc.gateways {
tc.gateways[i].initialize()
}
actualBoundAPIGatewaysMap := make(map[string]*structs.BoundAPIGatewayConfigEntry)
referenceErrors := make(map[structs.ResourceReference]error)
for _, route := range tc.routes {
bound, _, errs := bindRoutesToGateways(route, tc.gateways...)
for ref, err := range errs {
referenceErrors[ref] = err
}
for _, g := range bound {
actualBoundAPIGatewaysMap[g.Name] = g
}
}
actualBoundAPIGateways := []*structs.BoundAPIGatewayConfigEntry{}
for _, g := range actualBoundAPIGatewaysMap {
actualBoundAPIGateways = append(actualBoundAPIGateways, g)
}
require.ElementsMatch(t, tc.expectedBoundAPIGateways, actualBoundAPIGateways)
require.Equal(t, tc.expectedReferenceErrors, referenceErrors)
})
}
}
func TestAPIGatewayController(t *testing.T) {
defaultMeta := acl.DefaultEnterpriseMeta()
for name, tc := range map[string]struct {
requests []controller.Request
initialEntries []structs.ConfigEntry
finalEntries []structs.ConfigEntry
enqueued []controller.Request
}{
"gateway-no-routes": {
requests: []controller.Request{{
Kind: structs.APIGateway,
Name: "gateway",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
},
"tcp-route-no-gateways-no-targets": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeNoUpstreams(),
},
},
},
},
},
"http-route-no-gateways-no-targets": {
requests: []controller.Request{{
Kind: structs.HTTPRoute,
Name: "http-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.HTTPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "http-route",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.HTTPRouteConfigEntry{
Kind: structs.HTTPRoute,
Name: "http-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeNoUpstreams(),
},
},
},
},
},
"tcp-route-not-accepted-bind": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
}},
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.APIGatewayListener{{
Name: "listener",
Port: 80,
}},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.BoundAPIGatewayListener{{
Name: "listener",
}},
},
},
finalEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
}, errors.New("failed to bind route to gateway api-gateway: gateway has not been accepted")),
},
},
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.APIGatewayListener{{
Name: "listener",
Port: 80,
}},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "api-gateway",
SectionName: "listener",
EnterpriseMeta: *defaultMeta,
}),
},
},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "api-gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.BoundAPIGatewayListener{{
Name: "listener",
}},
},
},
},
"tcp-route-no-gateways-invalid-targets-bad-protocol": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
Protocol: "http",
},
},
finalEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeInvalidDiscoveryChain(errInvalidProtocol),
},
},
},
},
},
"tcp-route-no-gateways-invalid-parent": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}),
},
},
},
},
},
"tcp-route-parent-unreconciled": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}),
},
},
},
},
},
"tcp-route-parent-no-listeners": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}, errors.New("route cannot bind because gateway has no listeners")),
},
},
},
},
},
"tcp-route-parent-out-of-date-state": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.APIGatewayListener{{
Name: "listener",
Protocol: structs.ListenerProtocolTCP,
Port: 22,
}},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
gatewayNotFound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}),
},
},
},
},
},
"tcp-route-parent-out-of-date-state-reconcile": {
requests: []controller.Request{{
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}, {
Kind: structs.APIGateway,
Name: "gateway",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.APIGatewayListener{{
Name: "listener",
Protocol: structs.ListenerProtocolTCP,
Port: 22,
}},
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
},
},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.BoundAPIGatewayListener{{
Name: "listener",
Routes: []structs.ResourceReference{{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
}},
}},
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
},
},
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
routeBound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}),
},
},
},
},
},
"tcp-route-parent-protocol-mismatch": {
requests: []controller.Request{{
Kind: structs.APIGateway,
Name: "gateway",
Meta: acl.DefaultEnterpriseMeta(),
}, {
Kind: structs.TCPRoute,
Name: "tcp-route",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",
EnterpriseMeta: *defaultMeta,
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.APIGatewayListener{{
Name: "listener",
Protocol: structs.ListenerProtocolHTTP,
Port: 80,
}},
},
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
},
},
finalEntries: []structs.ConfigEntry{
&structs.BoundAPIGatewayConfigEntry{
Kind: structs.BoundAPIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Listeners: []structs.BoundAPIGatewayListener{{
Name: "listener",
}},
},
&structs.APIGatewayConfigEntry{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
gatewayAccepted(),
gatewayListenerNoConflicts(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
validCertificate(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
SectionName: "listener",
}),
},
},
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route",
EnterpriseMeta: *defaultMeta,
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
routeUnbound(structs.ResourceReference{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}, errors.New("failed to bind route tcp-route to gateway gateway with listener ''")),
},
},
},
},
},
"tcp-conflicted-routes": {
requests: []controller.Request{{
Kind: structs.APIGateway,
Name: "gateway",
Meta: acl.DefaultEnterpriseMeta(),
}, {
Kind: structs.TCPRoute,
Name: "tcp-route-one",
Meta: acl.DefaultEnterpriseMeta(),
}, {
Kind: structs.TCPRoute,
Name: "tcp-route-two",
Meta: acl.DefaultEnterpriseMeta(),
}},
initialEntries: []structs.ConfigEntry{
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route-one",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
&structs.TCPRouteConfigEntry{
Kind: structs.TCPRoute,
Name: "tcp-route-two",
EnterpriseMeta: *defaultMeta,
Services: []structs.TCPService{{
Name: "tcp-upstream",
}},
Parents: []structs.ResourceReference{{
Kind: structs.APIGateway,
Name: "gateway",
EnterpriseMeta: *defaultMeta,
}},
Status: structs.Status{
Conditions: []structs.Condition{
routeAccepted(),
},
},
},
&structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "tcp-upstream",