3717 lines
103 KiB
Go
3717 lines
103 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package gateways
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/consul/controller"
|
|
"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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().gatewayAccepted(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
route: &structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "Route",
|
|
Parents: []structs.ResourceReference{
|
|
{
|
|
Kind: structs.APIGateway,
|
|
Name: "Gateway",
|
|
},
|
|
},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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) {
|
|
conditions := newGatewayConditionGenerator()
|
|
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{
|
|
conditions.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{
|
|
conditions.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{
|
|
conditions.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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
conditions.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{
|
|
conditions.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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
newGatewayConditionGenerator().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{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(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{
|
|
conditions.routeAccepted(),
|
|
conditions.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{
|
|
newGatewayConditionGenerator().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{
|
|
newGatewayConditionGenerator().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,
|
|
}},
|
|
},
|
|
&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-one",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}, {
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
SectionName: "listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route-one",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"http-route-multiple-routes": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-one",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-two",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-one",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Rules: []structs.HTTPRouteRule{{
|
|
Services: []structs.HTTPService{{
|
|
Name: "http-upstream",
|
|
}},
|
|
}},
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
newGatewayConditionGenerator().routeAccepted(),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Rules: []structs.HTTPRouteRule{{
|
|
Services: []structs.HTTPService{{
|
|
Name: "http-upstream",
|
|
}},
|
|
}},
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
newGatewayConditionGenerator().routeAccepted(),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "http-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "http",
|
|
},
|
|
&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",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-one",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
SectionName: "listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-one",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"mixed-routes-mismatch": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Rules: []structs.HTTPRouteRule{{
|
|
Services: []structs.HTTPService{{
|
|
Name: "http-upstream",
|
|
}},
|
|
}},
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
},
|
|
&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: "http-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "http",
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&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",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
SectionName: "listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeUnbound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}, errors.New("failed to bind route tcp-route to gateway gateway with listener ''")),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"mixed-routes-multi-listener": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Rules: []structs.HTTPRouteRule{{
|
|
Services: []structs.HTTPService{{
|
|
Name: "http-upstream",
|
|
}},
|
|
}},
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
newGatewayConditionGenerator().routeAccepted(),
|
|
},
|
|
},
|
|
},
|
|
&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{
|
|
newGatewayConditionGenerator().routeAccepted(),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "http-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "http",
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Protocol: structs.ListenerProtocolHTTP,
|
|
Port: 80,
|
|
}, {
|
|
Name: "tcp-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,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "http-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}, {
|
|
Name: "tcp-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{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"basic-route-removal": {
|
|
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",
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Protocol: structs.ListenerProtocolTCP,
|
|
Port: 22,
|
|
}},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"invalidated-route": {
|
|
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,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Protocol: structs.ListenerProtocolTCP,
|
|
Port: 22,
|
|
}},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeNoUpstreams(),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"swap-protocols": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Services: []structs.TCPService{{
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Rules: []structs.HTTPRouteRule{{
|
|
Services: []structs.HTTPService{{
|
|
Name: "http-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeUnbound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}, errors.New("foo")),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "http-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "http",
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Protocol: structs.ListenerProtocolHTTP,
|
|
Port: 80,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "http-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeUnbound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}, errors.New("failed to bind route tcp-route to gateway gateway with listener ''")),
|
|
},
|
|
},
|
|
},
|
|
&structs.HTTPRouteConfigEntry{
|
|
Kind: structs.HTTPRoute,
|
|
Name: "http-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"delete-gateway": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Services: []structs.TCPService{{
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.ServiceConfigEntry{
|
|
Kind: structs.ServiceDefaults,
|
|
Name: "tcp-upstream",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Protocol: "tcp",
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
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{
|
|
conditions.routeAccepted(),
|
|
conditions.gatewayNotFound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"delete-route": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Port: 22,
|
|
Protocol: structs.ListenerProtocolTCP,
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "tcp-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"orphaned-bound-gateway": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}, {
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "tcp-listener",
|
|
Routes: []structs.ResourceReference{{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Parents: []structs.ResourceReference{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.routeBound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.TCPRouteConfigEntry{
|
|
Kind: structs.TCPRoute,
|
|
Name: "tcp-route",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.routeAccepted(),
|
|
conditions.gatewayNotFound(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"invalid-gateway-certificates": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.invalidCertificate(structs.ResourceReference{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}, errors.New("certificate not found")),
|
|
conditions.invalidCertificates(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "http-listener",
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
"valid-gateway-certificates": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
&structs.InlineCertificateConfigEntry{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
EnterpriseMeta: *defaultMeta,
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "http-listener",
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
"updated-gateway-certificates": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: acl.DefaultEnterpriseMeta(),
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.invalidCertificate(structs.ResourceReference{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}, errors.New("certificate not found")),
|
|
conditions.invalidCertificates(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.InlineCertificateConfigEntry{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
EnterpriseMeta: *defaultMeta,
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
Status: structs.Status{
|
|
Conditions: []structs.Condition{
|
|
conditions.gatewayAccepted(),
|
|
conditions.gatewayListenerNoConflicts(structs.ResourceReference{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
SectionName: "http-listener",
|
|
}),
|
|
},
|
|
},
|
|
},
|
|
&structs.BoundAPIGatewayConfigEntry{
|
|
Kind: structs.BoundAPIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.BoundAPIGatewayListener{{
|
|
Name: "http-listener",
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
EnterpriseMeta: *defaultMeta,
|
|
}},
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
"trigger-gateway-certificates": {
|
|
requests: []controller.Request{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
Meta: defaultMeta,
|
|
}},
|
|
initialEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
finalEntries: []structs.ConfigEntry{
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
&structs.APIGatewayConfigEntry{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway-two",
|
|
EnterpriseMeta: *defaultMeta,
|
|
Listeners: []structs.APIGatewayListener{{
|
|
Name: "http-listener",
|
|
Port: 80,
|
|
TLS: structs.APIGatewayTLSConfiguration{
|
|
Certificates: []structs.ResourceReference{{
|
|
Kind: structs.InlineCertificate,
|
|
Name: "certificate",
|
|
}},
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
enqueued: []controller.Request{{
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway",
|
|
Meta: defaultMeta,
|
|
}, {
|
|
Kind: structs.APIGateway,
|
|
Name: "gateway-two",
|
|
Meta: defaultMeta,
|
|
}},
|
|
},
|
|
} {
|
|
tc := tc
|
|
t.Run(name, func(t *testing.T) {
|
|
t.Parallel()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
publisher := stream.NewEventPublisher(1 * time.Millisecond)
|
|
go publisher.Run(ctx)
|
|
|
|
fsm := fsm.NewFromDeps(fsm.Deps{
|
|
Logger: hclog.New(nil),
|
|
NewStateStore: func() *state.Store {
|
|
return state.NewStateStoreWithEventPublisher(nil, publisher)
|
|
},
|
|
Publisher: publisher,
|
|
StorageBackend: fsm.NullStorageBackend,
|
|
})
|
|
|
|
var index uint64
|
|
updater := &Updater{
|
|
UpdateWithStatus: func(entry structs.ControlledConfigEntry) error {
|
|
index++
|
|
store := fsm.State()
|
|
_, err := store.EnsureConfigEntryWithStatusCAS(index, entry.GetRaftIndex().ModifyIndex, entry)
|
|
return err
|
|
},
|
|
Update: func(entry structs.ConfigEntry) error {
|
|
index++
|
|
store := fsm.State()
|
|
_, err := store.EnsureConfigEntryCAS(index, entry.GetRaftIndex().ModifyIndex, entry)
|
|
return err
|
|
},
|
|
Delete: func(entry structs.ConfigEntry) error {
|
|
index++
|
|
store := fsm.State()
|
|
_, err := store.DeleteConfigEntryCAS(index, entry.GetRaftIndex().ModifyIndex, entry)
|
|
return err
|
|
},
|
|
}
|
|
|
|
for _, entry := range tc.initialEntries {
|
|
if controlled, ok := entry.(structs.ControlledConfigEntry); ok {
|
|
require.NoError(t, updater.UpdateWithStatus(controlled))
|
|
continue
|
|
}
|
|
err := updater.Update(entry)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
controller := &noopController{
|
|
triggers: make(map[controller.Request]struct{}),
|
|
}
|
|
reconciler := apiGatewayReconciler{
|
|
fsm: fsm,
|
|
logger: hclog.Default(),
|
|
updater: updater,
|
|
controller: controller,
|
|
}
|
|
|
|
for _, req := range tc.requests {
|
|
require.NoError(t, reconciler.Reconcile(ctx, req))
|
|
}
|
|
|
|
_, entries, err := fsm.State().ConfigEntries(nil, acl.WildcardEnterpriseMeta())
|
|
require.NoError(t, err)
|
|
for _, entry := range entries {
|
|
controlled, ok := entry.(structs.ControlledConfigEntry)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
found := false
|
|
for _, expected := range tc.finalEntries {
|
|
if controlled.GetKind() == expected.GetKind() &&
|
|
controlled.GetName() == expected.GetName() &&
|
|
controlled.GetEnterpriseMeta().IsSame(expected.GetEnterpriseMeta()) {
|
|
expectedStatus := expected.(structs.ControlledConfigEntry).GetStatus()
|
|
acualStatus := controlled.GetStatus()
|
|
statusEqual := acualStatus.SameConditions(expectedStatus)
|
|
ppActual, err := json.MarshalIndent(acualStatus, "", " ")
|
|
require.NoError(t, err)
|
|
ppExpected, err := json.MarshalIndent(expectedStatus, "", " ")
|
|
require.NoError(t, err)
|
|
require.True(t, statusEqual, fmt.Sprintf("statuses are unequal: %+v != %+v", string(ppActual), string(ppExpected)))
|
|
if bound, ok := controlled.(*structs.BoundAPIGatewayConfigEntry); ok {
|
|
ppActual, err := json.MarshalIndent(bound, "", " ")
|
|
require.NoError(t, err)
|
|
ppExpected, err := json.MarshalIndent(expected, "", " ")
|
|
require.NoError(t, err)
|
|
require.True(t, bound.IsSame(expected.(*structs.BoundAPIGatewayConfigEntry)), fmt.Sprintf("api bound states do not match: %+v != %+v", string(ppActual), string(ppExpected)))
|
|
}
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found, fmt.Sprintf("unexpected entry found: %+v", entry))
|
|
}
|
|
for _, expected := range tc.finalEntries {
|
|
found := false
|
|
for _, entry := range entries {
|
|
if entry.GetKind() == expected.GetKind() &&
|
|
entry.GetName() == expected.GetName() &&
|
|
entry.GetEnterpriseMeta().IsSame(expected.GetEnterpriseMeta()) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found, fmt.Sprintf("expected entry not found: %+v", expected))
|
|
}
|
|
for _, queued := range tc.enqueued {
|
|
found := false
|
|
for _, entry := range controller.enqueued {
|
|
if entry.Kind == queued.Kind &&
|
|
entry.Name == queued.Name &&
|
|
entry.Meta.IsSame(queued.Meta) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found, fmt.Sprintf("expected queued entry not found: %+v", queued))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewAPIGatewayController(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
publisher := stream.NewEventPublisher(1 * time.Millisecond)
|
|
fsm := fsm.NewFromDeps(fsm.Deps{
|
|
Logger: hclog.New(nil),
|
|
NewStateStore: func() *state.Store {
|
|
return state.NewStateStoreWithEventPublisher(nil, publisher)
|
|
},
|
|
Publisher: publisher,
|
|
StorageBackend: fsm.NullStorageBackend,
|
|
})
|
|
|
|
updater := &Updater{
|
|
UpdateWithStatus: func(entry structs.ControlledConfigEntry) error { return nil },
|
|
Update: func(entry structs.ConfigEntry) error { return nil },
|
|
Delete: func(entry structs.ConfigEntry) error { return nil },
|
|
}
|
|
|
|
require.NotNil(t, NewAPIGatewayController(fsm, publisher, updater, hclog.Default()))
|
|
}
|
|
|
|
type noopController struct {
|
|
triggers map[controller.Request]struct{}
|
|
enqueued []controller.Request
|
|
}
|
|
|
|
func (n *noopController) Run(ctx context.Context) error { return nil }
|
|
func (n *noopController) Subscribe(request *stream.SubscribeRequest, transformers ...controller.Transformer) controller.Controller {
|
|
return n
|
|
}
|
|
func (n *noopController) WithBackoff(base, max time.Duration) controller.Controller { return n }
|
|
func (n *noopController) WithLogger(logger hclog.Logger) controller.Controller { return n }
|
|
func (n *noopController) WithWorkers(i int) controller.Controller { return n }
|
|
func (n *noopController) WithQueueFactory(fn func(ctx context.Context, baseBackoff time.Duration, maxBackoff time.Duration) controller.WorkQueue) controller.Controller {
|
|
return n
|
|
}
|
|
func (n *noopController) AddTrigger(request controller.Request, trigger func(ctx context.Context) error) {
|
|
n.triggers[request] = struct{}{}
|
|
}
|
|
func (n *noopController) RemoveTrigger(request controller.Request) {
|
|
delete(n.triggers, request)
|
|
}
|
|
func (n *noopController) Enqueue(requests ...controller.Request) {
|
|
n.enqueued = append(n.enqueued, requests...)
|
|
}
|