Files
traefik/pkg/provider/kubernetes/gateway/kubernetes_test.go
T

14833 lines
518 KiB
Go

package gateway
import (
"crypto/x509"
"encoding/pem"
"errors"
"net/http"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/provider"
traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
"github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types"
"google.golang.org/grpc/codes"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ktypes "k8s.io/apimachinery/pkg/types"
kubefake "k8s.io/client-go/kubernetes/fake"
kscheme "k8s.io/client-go/kubernetes/scheme"
ktesting "k8s.io/client-go/testing"
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
gatefake "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake"
)
var _ provider.Provider = (*Provider)(nil)
func defaultListenerTLSOptions() tls.Options {
var opts tls.Options
opts.SetDefaults()
return opts
}
func init() {
// required by k8s.MustParseYaml
if err := gatev1.Install(kscheme.Scheme); err != nil {
panic(err)
}
}
const (
listenerCert string = `-----BEGIN CERTIFICATE-----
MIIBqDCCAU6gAwIBAgIUYOsr0QgHOBq4kYRCL5+TDdVt6bQwCgYIKoZIzj0EAwIw
FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUxMDEwMDcxNzMwWhcNMzUxMDA4
MDcxNzMwWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABDOriw3ZQ7wIhWrbPS6JFQT3bToNCF00vSV5fab6TbXyL8tlsGre
TRIF2EwgsteMOkxGKKSlDvuaDwq8p/qV+0ujejB4MB0GA1UdDgQWBBRMEkuexXQh
UtDgRg1KBv72CDq+EzAfBgNVHSMEGDAWgBRMEkuexXQhUtDgRg1KBv72CDq+EzAP
BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByCC2V4YW1wbGUuY29tgg0qLmV4YW1w
bGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIQDs87Vk0swA6HgOJjROye1mxD83qcGy
peFgoxV93Dy+cwIgV0MMEJJbVsTyZK3EQ++Xc5rEL78nrJ+YIEV+rCUWj5U=
-----END CERTIFICATE-----`
listenerKey string = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgnwgL5DY4UB14sM6f
DikQdtqh2QW1ArfF4fc1UFzifdGhRANCAAQzq4sN2UO8CIVq2z0uiRUE9206DQhd
NL0leX2m+k218i/LZbBq3k0SBdhMILLXjDpMRiikpQ77mg8KvKf6lftL
-----END PRIVATE KEY-----`
)
func TestGatewayClassLabelSelector(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"gatewayclass_labelselector.yaml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
// This is initialized by the Provider init method but this cannot be called in a unit test.
client.labelSelector = "name=traefik-internal"
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: map[string]Entrypoint{"http": {Address: ":9080"}},
StatusAddress: &StatusAddress{IP: "1.2.3.4"},
client: client,
}
_, statusReport, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
statusReport.Flush(t.Context(), p.client)
gw, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-external", metav1.GetOptions{})
require.NoError(t, err)
assert.Empty(t, gw.Status.Addresses)
gw, err = gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-internal", metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, gw.Status.Addresses, 1)
require.NotNil(t, gw.Status.Addresses[0].Type)
assert.Equal(t, gatev1.IPAddressType, *gw.Status.Addresses[0].Type)
assert.Equal(t, "1.2.3.4", gw.Status.Addresses[0].Value)
}
func TestGatewayClassFieldSelector(t *testing.T) {
// The resources are only there to make the informer emit a first event to wait for.
_, gwObjects := readResources(t, []string{"gatewayclass_labelselector.yaml"})
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubefake.NewClientset(), gwClient)
client.gatewayClass = "traefik-internal"
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
<-eventCh
// The fake clientset ignores field selectors, so the lister cannot show the filtering.
// The API server does the filtering. This test only checks the selector is sent to it.
var (
listed bool
watched bool
)
for _, action := range gwClient.Actions() {
if action.GetResource().Resource != "gatewayclasses" {
continue
}
switch a := action.(type) {
case ktesting.ListAction:
listed = true
assert.Equal(t, "metadata.name=traefik-internal", a.GetListRestrictions().Fields.String())
case ktesting.WatchAction:
watched = true
assert.Equal(t, "metadata.name=traefik-internal", a.GetWatchRestrictions().Fields.String())
}
}
assert.True(t, listed)
assert.True(t, watched)
}
func TestGatewayClassAndLabelSelectorExclusive(t *testing.T) {
p := Provider{
GatewayClass: "traefik-internal",
LabelSelector: "name=traefik-internal",
}
_, err := p.newK8sClient(t.Context())
require.EqualError(t, err, "gatewayClass and labelSelector options are mutually exclusive")
}
func TestGatewayScoping(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"gateway_scoping.yaml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
<-eventCh
}
p := Provider{
EntryPoints: map[string]Entrypoint{"http": {Address: ":9080"}},
StatusAddress: &StatusAddress{IP: "1.2.3.4"},
// Scope the provider to a single Gateway.
Gateways: []NamespacedName{{Namespace: "default", Name: "traefik-internal"}},
client: client,
}
_, statusReport, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
statusReport.Flush(t.Context(), p.client)
// The scoped Gateway is managed and gets a status address.
gw, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-internal", metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, gw.Status.Addresses, 1)
assert.Equal(t, "1.2.3.4", gw.Status.Addresses[0].Value)
// The other Gateway is ignored.
gw, err = gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-external", metav1.GetOptions{})
require.NoError(t, err)
assert.Empty(t, gw.Status.Addresses)
}
func TestLoadHTTPRoutes(t *testing.T) {
testCases := []struct {
desc string
ingressClass string
paths []string
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
nativeLB bool
}{
{
desc: "Empty",
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because missing entry point",
paths: []string{"services.yml", "httproute/simple.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":443",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because no http route defined",
paths: []string{"services.yml", "httproute/without_httproute.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by missing GatewayClass",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
paths: []string{"services.yml", "httproute/without_gatewayclass.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by unknown GatewayClass controller desc",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
paths: []string{"services.yml", "httproute/gatewayclass_with_unknown_controller.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Router with service in error caused by wrong TargetPort",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
paths: []string{"services.yml", "httproute/with_wrong_service_port.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by HTTPS without TLS",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
paths: []string{"services.yml", "httproute/with_protocol_https_without_tls.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by HTTPS with TLS passthrough",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
paths: []string{"services.yml", "httproute/with_protocol_https_with_tls_passthrough.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by HTTPRoute with protocol TLS",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
paths: []string{"services.yml", "httproute/with_protocol_tls.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by HTTPRoute with protocol TCP",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
paths: []string{"services.yml", "httproute/with_protocol_tcp.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TCPRoute with protocol HTTP",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":9000",
}},
paths: []string{"services.yml", "tcproute/with_protocol_http.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TCPRoute with protocol HTTPS",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":9000",
}},
paths: []string{"services.yml", "tcproute/with_protocol_https.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TLSRoute with protocol TCP",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":9001",
}},
paths: []string{"services.yml", "tlsroute/with_protocol_tcp.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TLSRoute with protocol HTTP",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":9001",
}},
paths: []string{"services.yml", "tlsroute/with_protocol_http.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TLSRoute with protocol HTTPS",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":9001",
}},
paths: []string{"services.yml", "tlsroute/with_protocol_https.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused use http entrypoint with tls activated with HTTPRoute",
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
HasHTTPTLSConf: true,
}},
paths: []string{"services.yml", "httproute/simple_with_tls_entrypoint.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because no tcp route defined tls protocol",
paths: []string{"services.yml", "tcproute/without_tcproute_tls_protocol.yml"},
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Empty caused by HTTPRoute with TLS configuration",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
paths: []string{"services.yml", "httproute/with_tls_configuration.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute",
paths: []string{"services.yml", "httproute/simple.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Listeners isolation with a specificity chain spread over three Gateways",
paths: []string{"services.yml", "httproute/listener_isolation_three_gateways.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `(Host("*")) && !(Host("**.example.com") || Host("abc.example.com"))`,
},
"listener-web-http-example-com-e7b9da953ca48d642d6b": {
EntryPoints: []string{"web"},
Rule: `(Host("**.example.com")) && !(Host("abc.example.com"))`,
},
"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca": {
EntryPoints: []string{"web"},
Rule: `Host("abc.example.com")`,
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb": {
ParentRefs: []string{
"listener-web-http-5b3226d4ebd42b7200f1",
"listener-web-http-example-com-e7b9da953ca48d642d6b",
},
Service: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-wrr",
Rule: `Path("/catchall")`,
Priority: 100001,
RuleSyntax: "default",
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b": {
ParentRefs: []string{"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca"},
Service: "httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-wrr",
Rule: `Host("abc.example.com") && Path("/precise")`,
Priority: 100016,
RuleSyntax: "default",
},
"httproute-default-http-app-wildcard-gw-default-my-wildcard-gateway-ep-web-0-63284dea2e7614ab00a2": {
ParentRefs: []string{
"listener-web-http-example-com-e7b9da953ca48d642d6b",
"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca",
},
Service: "httproute-default-http-app-wildcard-gw-default-my-wildcard-gateway-ep-web-0-63284dea2e7614ab00a2-wrr",
Rule: `Host("**.example.com") && Path("/wildcard")`,
Priority: 100014,
RuleSyntax: "default",
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce": {
ParentRefs: []string{
"listener-web-http-5b3226d4ebd42b7200f1",
"listener-web-http-example-com-e7b9da953ca48d642d6b",
"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca",
},
Service: "httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-wrr",
Rule: `Path("/other")`,
Priority: 100001,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-wildcard-gw-default-my-wildcard-gateway-ep-web-0-63284dea2e7614ab00a2-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-wildcard-gw-default-my-wildcard-gateway-ep-web-0-63284dea2e7614ab00a2-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-wildcard-gw-default-my-wildcard-gateway-ep-web-0-63284dea2e7614ab00a2-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Listeners isolation with a hostname listener and a catch-all listener on two Gateways",
paths: []string{"services.yml", "httproute/listener_isolation.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `(Host("*")) && !(Host("abc.example.com"))`,
},
"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca": {
EntryPoints: []string{"web"},
Rule: `Host("abc.example.com")`,
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-wrr",
Rule: `Path("/catchall")`,
Priority: 100001,
RuleSyntax: "default",
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b": {
ParentRefs: []string{"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca"},
Service: "httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-wrr",
Rule: `Host("abc.example.com") && Path("/precise")`,
Priority: 100016,
RuleSyntax: "default",
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce": {
ParentRefs: []string{
"listener-web-http-5b3226d4ebd42b7200f1",
"listener-web-http-abc-example-com-05dd4f497bcb27f2cdca",
},
Service: "httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-wrr",
Rule: `Path("/other")`,
Priority: 100001,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-web-0-6bf410c47a90e258b1eb-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-precise-gw-default-my-gateway-ep-web-0-5b6dacf4c7da9367767b-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-other-gw-default-my-other-gateway-ep-web-0-d7590587db124f0d35ce-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with a wildcard listener hostname",
paths: []string{"services.yml", "httproute/simple_with_wildcard_listener_hostname.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-example-com-e7b9da953ca48d642d6b": {
EntryPoints: []string{"web"},
Rule: `Host("**.example.com")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-2944dbefe4493e703281": {
ParentRefs: []string{"listener-web-http-example-com-e7b9da953ca48d642d6b"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-2944dbefe4493e703281-wrr",
Rule: `Host("**.example.com") && Path("/bar")`,
Priority: 100014,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-2944dbefe4493e703281-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-2944dbefe4493e703281-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-2944dbefe4493e703281-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, without backendRefs",
paths: []string{"services.yml", "httproute/without_backendrefs.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-b0d3512b71b7695aa511": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-b0d3512b71b7695aa511-wrr",
Rule: `Host("foo.com") && Path("/omitted")`,
Priority: 100009,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-28c8ec797c4b0f521cf7": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-28c8ec797c4b0f521cf7-wrr",
Rule: `Host("foo.com") && Path("/empty")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-b0d3512b71b7695aa511-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "no-backend-refs",
Status: new(500),
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-28c8ec797c4b0f521cf7-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "no-backend-refs",
Status: new(500),
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with timeouts",
paths: []string{"services.yml", "httproute/with_request_timeout.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100010,
RuleSyntax: "default",
RespondingTimeouts: &dynamic.RouterRespondingTimeouts{ReadTimeout: new(ptypes.Duration(10 * time.Second)), WriteTimeout: new(ptypes.Duration(10 * time.Second))},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-6bebfd4e0e692b59df74": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-6bebfd4e0e692b59df74-wrr",
Rule: `Host("foo.com") && Path("/baz")`,
Priority: 100009,
RuleSyntax: "default",
RespondingTimeouts: &dynamic.RouterRespondingTimeouts{ReadTimeout: new(ptypes.Duration(0)), WriteTimeout: new(ptypes.Duration(0))},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-wrr",
Rule: `Host("foo.com") && Path("/buz")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-6bebfd4e0e692b59df74-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-6bebfd4e0e692b59df74-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-6bebfd4e0e692b59df74-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
DialTimeout: ptypes.Duration(30 * time.Second),
ResponseHeaderTimeout: ptypes.Duration(5 * time.Second),
IdleConnTimeout: ptypes.Duration(90 * time.Second),
PingTimeout: ptypes.Duration(15 * time.Second),
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-c465583028340322911a-svc-default-whoami-0": {
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
DialTimeout: ptypes.Duration(30 * time.Second),
ResponseHeaderTimeout: ptypes.Duration(5 * time.Second),
IdleConnTimeout: ptypes.Duration(90 * time.Second),
PingTimeout: ptypes.Duration(15 * time.Second),
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with api@internal service",
paths: []string{"services.yml", "httproute/simple_to_api_internal.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "api@internal",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with protocol HTTPS",
paths: []string{"services.yml", "httproute/with_protocol_https.yml"},
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "HTTPRoute with multiple TLS certificateRefs",
paths: []string{"services.yml", "httproute/with_multiple_certificaterefs.yml"},
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "HTTPRoute with multiple certificateRefs, one missing",
paths: []string{"services.yml", "httproute/with_multiple_certificaterefs_one_missing.yml"},
entryPoints: map[string]Entrypoint{"websecure": {
Address: ":443",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with multiple hosts",
paths: []string{"services.yml", "httproute/with_multiple_host.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-fe758dd8380cc8acabbc": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-fe758dd8380cc8acabbc-wrr",
Rule: `(Host("foo.com") || Host("bar.com")) && PathPrefix("/")`,
Priority: 9,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-fe758dd8380cc8acabbc-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-fe758dd8380cc8acabbc-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-fe758dd8380cc8acabbc-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with two hosts one wildcard",
paths: []string{"services.yml", "with_two_hosts_one_wildcard.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-5af0a8dc466bcb103301": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-5af0a8dc466bcb103301-wrr",
Rule: `(Host("foo.com") || Host("**.bar.com")) && PathPrefix("/")`,
Priority: 11,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-5af0a8dc466bcb103301-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-5af0a8dc466bcb103301-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-5af0a8dc466bcb103301-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with one host and a wildcard",
paths: []string{"services.yml", "with_two_hosts_wildcard.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-764e519b9557509caa7d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-764e519b9557509caa7d-wrr",
Rule: `(Host("foo.com") || Host("**.foo.com")) && PathPrefix("/")`,
Priority: 11,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-764e519b9557509caa7d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-764e519b9557509caa7d-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-764e519b9557509caa7d-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "One HTTPRoute with two different rules",
paths: []string{"services.yml", "httproute/two_rules.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100009,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-71ae3ac5f5f8583c5cfa": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && Path("/bir")`,
Priority: 100008,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-71ae3ac5f5f8583c5cfa-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-71ae3ac5f5f8583c5cfa-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-71ae3ac5f5f8583c5cfa-svc-default-whoami2-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-71ae3ac5f5f8583c5cfa-svc-default-whoami2-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "One HTTPRoute with one rule two targets",
paths: []string{"services.yml", "httproute/one_rule_two_targets.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami2-1",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami2-1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Two Gateways and one HTTPRoute",
paths: []string{"services.yml", "httproute/with_two_gateways_one_httproute.yml"},
entryPoints: map[string]Entrypoint{
"web": {
Address: ":80",
},
"websecure": {
Address: ":443",
},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-http-ep-web-0-779a0bc558422ee1b154": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-http-ep-web-0-779a0bc558422ee1b154-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-https-ep-websecure-0-036eecf3c21d00eac117": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-https-ep-websecure-0-036eecf3c21d00eac117-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-http-ep-web-0-779a0bc558422ee1b154-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-http-ep-web-0-779a0bc558422ee1b154-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-https-ep-websecure-0-036eecf3c21d00eac117-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-https-ep-websecure-0-036eecf3c21d00eac117-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-http-ep-web-0-779a0bc558422ee1b154-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-https-ep-websecure-0-036eecf3c21d00eac117-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Gateway with two listeners and one HTTPRoute",
paths: []string{"services.yml", "httproute/with_two_listeners_one_httproute.yml"},
entryPoints: map[string]Entrypoint{
"web": {
Address: ":80",
},
"websecure": {
Address: ":443",
},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Simple HTTPRoute, with several rules",
paths: []string{"services.yml", "httproute/with_several_rules.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-cbebdedef34a6261eced": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && (Path("/bar") || PathPrefix("/bar/")) && Header("my-header","foo") && Header("my-header2","bar")`,
Priority: 10610,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-cbebdedef34a6261eced-wrr",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-74d0c698c3be747a738f": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && PathRegexp("^/buzz/[0-9]+$")`,
Priority: 11408,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-74d0c698c3be747a738f-wrr",
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-d4501fe0597184abd40e": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && Path("/bar") && Header("my-header","bar")`,
Priority: 100109,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-d4501fe0597184abd40e-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-cbebdedef34a6261eced-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-cbebdedef34a6261eced-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-74d0c698c3be747a738f-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-74d0c698c3be747a738f-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-d4501fe0597184abd40e-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-d4501fe0597184abd40e-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-cbebdedef34a6261eced-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-2-74d0c698c3be747a738f-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-1-d4501fe0597184abd40e-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with method matching",
paths: []string{"services.yml", "httproute/with_method_matching.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-eaf4f488cc5127097136": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && (Path("/foo") || PathPrefix("/foo/")) && Method("GET")`,
Priority: 11408,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-eaf4f488cc5127097136-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-eaf4f488cc5127097136-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-eaf4f488cc5127097136-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-eaf4f488cc5127097136-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with query param matching",
paths: []string{"services.yml", "httproute/with_query_param_matching.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-8dc41dbb469b11d7ce9c": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Rule: `Host("foo.com") && (Path("/foo") || PathPrefix("/foo/")) && Query("foo","bar") && QueryRegexp("baz","buz")`,
Priority: 10428,
RuleSyntax: "default",
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-8dc41dbb469b11d7ce9c-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-8dc41dbb469b11d7ce9c-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-8dc41dbb469b11d7ce9c-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-8dc41dbb469b11d7ce9c-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with Same namespace selector",
paths: []string{"services.yml", "httproute/with_namespace_same.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with All namespace selector",
paths: []string{"services.yml", "httproute/with_namespace_all.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-wrr",
Rule: `Host("foo.com") && Path("/foo")`,
Priority: 100008,
RuleSyntax: "default",
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-wrr",
Rule: `Host("bar.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-71c892303a6d70cb4efe-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with namespace selector",
paths: []string{"services.yml", "httproute/with_namespace_selector.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-wrr",
Rule: `Host("bar.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-eb47eeb38d713d8e351d-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, request header modifier",
paths: []string{"services.yml", "httproute/filter_request_header_modifier.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
Rule: `Host("example.org") && PathPrefix("/")`,
Priority: 13,
RuleSyntax: "default",
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestheadermodifier-0": {
RequestHeaderModifier: &dynamic.HeaderModifier{
Set: map[string]string{"X-Foo": "Bar"},
Add: map[string]string{"X-Bar": "Foo"},
Remove: []string{"X-Baz"},
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, response header modifier",
paths: []string{"services.yml", "httproute/filter_response_header_modifier.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
Rule: `Host("example.org") && PathPrefix("/")`,
Priority: 13,
RuleSyntax: "default",
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-responseheadermodifier-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-responseheadermodifier-0": {
ResponseHeaderModifier: &dynamic.HeaderModifier{
Set: map[string]string{"X-Foo": "Bar"},
Add: map[string]string{"X-Bar": "Foo"},
Remove: []string{"X-Baz"},
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, backend filter request header modifier",
paths: []string{"services.yml", "httproute/backend_filter_request_header_modifier.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-wrr",
Rule: `Host("foo.com") && (Path("/bar") || PathPrefix("/bar/"))`,
Priority: 10408,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-svc-default-whoami-0-requestheadermodifier-0": {
RequestHeaderModifier: &dynamic.HeaderModifier{
Set: map[string]string{"X-Foo": "Bar"},
Add: map[string]string{"X-Bar": "Foo"},
Remove: []string{"X-Baz"},
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-svc-default-whoami-0": {
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-78ef4770613eae69b5da-svc-default-whoami-0-requestheadermodifier-0"},
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, redirect HTTP to HTTPS",
paths: []string{"services.yml", "httproute/filter_http_to_https.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
Rule: `Host("example.org") && PathPrefix("/")`,
Priority: 13,
RuleSyntax: "default",
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestredirect-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestredirect-0": {
RequestRedirect: &dynamic.RequestRedirect{
Scheme: new("https"),
Port: new(""),
StatusCode: http.StatusMovedPermanently,
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
Weighted: &dynamic.WeightedRoundRobin{},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, redirect HTTP to HTTPS with hostname",
paths: []string{"services.yml", "httproute/filter_http_to_https_with_hostname_and_port.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr",
Rule: `Host("example.org") && PathPrefix("/")`,
Priority: 13,
RuleSyntax: "default",
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestredirect-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-requestredirect-0": {
RequestRedirect: &dynamic.RequestRedirect{
Hostname: new("example.com"),
Port: new("443"),
StatusCode: http.StatusFound,
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-c683caccb22c43e5e664-wrr": {
Weighted: &dynamic.WeightedRoundRobin{},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute URL rewrite FullPath",
paths: []string{"services.yml", "httproute/filter_url_rewrite_fullpath.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr",
Rule: `Host("example.com") && (Path("/foo") || PathPrefix("/foo/"))`,
RuleSyntax: "default",
Priority: 10412,
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0": {
URLRewrite: &dynamic.URLRewrite{
Path: new("/bar"),
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute URL rewrite Hostname",
paths: []string{"services.yml", "httproute/filter_url_rewrite_hostname.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr",
Rule: `Host("example.com") && (Path("/foo") || PathPrefix("/foo/"))`,
RuleSyntax: "default",
Priority: 10412,
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0": {
URLRewrite: &dynamic.URLRewrite{
Hostname: new("www.foo.bar"),
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute URL rewrite Combined",
paths: []string{"services.yml", "httproute/filter_url_rewrite_combined.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr",
Rule: `Host("example.com") && (Path("/foo") || PathPrefix("/foo/"))`,
RuleSyntax: "default",
Priority: 10412,
Middlewares: []string{"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0"},
},
},
Middlewares: map[string]*dynamic.Middleware{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-urlrewrite-0": {
URLRewrite: &dynamic.URLRewrite{
Hostname: new("www.foo.bar"),
Path: new("/xyz"),
PathPrefix: new("/foo"),
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0",
Weight: func(i int) *int { return &i }(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-d1e350a646631250e99d-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute and BackendTLSPolicy with CA certificate",
paths: []string{"services.yml", "httproute/with_backend_tls_policy.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
"CA2",
"CA1-secret",
"CA2-secret",
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "httproute/with_backend_tls_policy_san.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ServerName: "whoami",
InsecureSkipVerify: true,
PeerCertSANs: []tls.SAN{
{Type: tls.SANDNSNameType, Value: "whoami.default.svc.cluster.local"},
{Type: tls.SANURIType, Value: "spiffe://cluster.local/ns/default/sa/whoami"},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute and BackendTLSPolicy with System CA",
paths: []string{"services.yml", "httproute/with_backend_tls_policy_system.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
ServerName: "whoami",
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with NativeLBByDefault enabled",
paths: []string{"services.yml", "httproute/simple.yml"},
nativeLB: true,
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.10.1:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with NativeLB annotation",
paths: []string{"services.yml", "httproute/simple_nativelb.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-native-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-native-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.10.1:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with NativeLBByDefault enabled but service has disabled nativelb",
paths: []string{"services.yml", "httproute/simple_nativelb_disabled.yml"},
nativeLB: true,
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-native-disabled-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-native-disabled-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.20:80",
},
{
URL: "http://10.10.0.21:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with nil endpointslice port name",
paths: []string{"services.yml", "httproute/with_nil_endpointslice_port_name.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-nil-port-name-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-nil-port-name-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute with nil endpointslice port value",
paths: []string{"services.yml", "httproute/with_nil_endpointslice_port_value.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-nil-port-value-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-nil-port-value-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoutes with identical rules",
paths: []string{"services.yml", "httproute/with_identical_rules.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-foo-com-5f20d578e2e3eccac466": {
EntryPoints: []string{"web"},
Rule: `Host("foo.com")`,
},
"httproute-default-http-app-catch-all-gw-default-my-gateway-ep-web-0-402d8b0319543c8e637b": {
Service: "httproute-default-http-app-catch-all-gw-default-my-gateway-ep-web-0-402d8b0319543c8e637b-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 9,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-foo-com-5f20d578e2e3eccac466"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-catch-all-gw-default-my-gateway-ep-web-0-402d8b0319543c8e637b-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-catch-all-gw-default-my-gateway-ep-web-0-402d8b0319543c8e637b-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-catch-all-gw-default-my-gateway-ep-web-0-402d8b0319543c8e637b-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute attached to a ListenerSet HTTPS listener",
paths: []string{"services.yml", "httproute/with_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr",
Rule: `Host("foo.example.com") && Path("/bar")`,
ParentRefs: []string{
"listener-websecure-https-439feda8e3f1dbdca529",
},
RuleSyntax: "default",
Priority: 100016,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{
"websecure",
},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: dynamic.BalancerStrategy("wrr"),
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100000000),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": {
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
ALPNProtocols: []string{
"h2",
"http/1.1",
"acme-tls/1",
},
},
},
},
},
},
{
desc: "Gateway tls.frontend Default and PerPort validation register TLS options",
paths: []string{"services.yml", "httproute/with_frontend_validation.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
"websecure2": {Address: ":8443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"listener-websecure2-https-dd662c6921e9998d0cca": {
EntryPoints: []string{"websecure2"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure2-https-dd662c6921e9998d0cca",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure2-0-e22d24f42cd0b2e1fc86": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure2-0-e22d24f42cd0b2e1fc86-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure2-https-dd662c6921e9998d0cca"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-398c6f76284d21e6e3b3-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure2-0-e22d24f42cd0b2e1fc86-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure2-0-e22d24f42cd0b2e1fc86-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure2-0-e22d24f42cd0b2e1fc86-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
"listener-websecure2-https-dd662c6921e9998d0cca": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequestClientCert,
}
return opts
}(),
},
},
},
},
{
desc: "Gateways sharing an entry point with the same frontend validation keep their listeners isolated",
paths: []string{"services.yml", "httproute/with_frontend_validation_listener_isolation.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `(Host("*")) && !(Host("foo.com"))`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"listener-websecure-https-foo-com-c4461032f627a1b9033b": {
EntryPoints: []string{"websecure"},
Rule: `Host("foo.com")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-foo-com-c4461032f627a1b9033b",
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269": {
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529", "listener-websecure-https-foo-com-c4461032f627a1b9033b"},
Service: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-wrr",
Rule: `Path("/catchall")`,
Priority: 100001,
RuleSyntax: "default",
},
"httproute-default-http-app-secure-gw-default-my-secure-gateway-ep-websecure-0-b7eaf4c1f751a09c9658": {
ParentRefs: []string{"listener-websecure-https-foo-com-c4461032f627a1b9033b"},
Service: "httproute-default-http-app-secure-gw-default-my-secure-gateway-ep-websecure-0-b7eaf4c1f751a09c9658-wrr",
Rule: `Host("foo.com") && Path("/secure")`,
Priority: 100008,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-secure-gw-default-my-secure-gateway-ep-websecure-0-b7eaf4c1f751a09c9658-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-secure-gw-default-my-secure-gateway-ep-websecure-0-b7eaf4c1f751a09c9658-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-secure-gw-default-my-secure-gateway-ep-websecure-0-b7eaf4c1f751a09c9658-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
"listener-websecure-https-foo-com-c4461032f627a1b9033b": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
},
},
},
},
{
desc: "Catch-all listeners sharing an entry point with different frontend validations are conflicted, the oldest Gateway wins",
paths: []string{"services.yml", "httproute/with_frontend_validation_shared_entrypoint.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269": {
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
Service: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-wrr",
Rule: `Path("/catchall")`,
Priority: 100001,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-catchall-gw-default-my-gateway-ep-websecure-0-d0a1acd3dc8dc5905269-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
NativeLBByDefault: test.nativeLB,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) {
testCases := []struct {
desc string
paths []string
groupKindBackendFuncs map[string]map[string]BuildBackendFunc
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "Simple HTTPRoute with TraefikService",
paths: []string{"services.yml", "httproute/simple_with_TraefikService.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
return name, nil, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "whoami",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with TraefikService with service configuration",
paths: []string{"services.yml", "httproute/simple_with_TraefikService.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
return name, &dynamic.Service{LoadBalancer: &dynamic.ServersLoadBalancer{Strategy: dynamic.BalancerStrategyWRR, Servers: []dynamic.Server{{URL: "foobar"}}}}, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "whoami",
Weight: new(1),
},
},
},
},
"whoami": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "foobar"},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with invalid TraefikService kind",
paths: []string{"services.yml", "httproute/simple_with_TraefikService.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute with backendFunc error",
paths: []string{"services.yml", "httproute/simple_with_TraefikService.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with myservice@file service",
paths: []string{"services.yml", "httproute/simple_cross_provider.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
// func should never be executed in case of cross-provider reference.
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "service@file",
Weight: new(1),
},
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-1",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple HTTPRoute, with appProtocol service",
paths: []string{"services.yml", "httproute/with_app_protocol_service.yml"},
groupKindBackendFuncs: map[string]map[string]BuildBackendFunc{
traefikv1alpha1.GroupName: {"TraefikService": func(name, namespace string) (string, *dynamic.Service, error) {
// func should never be executed in case of cross-provider reference.
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd": {
Service: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-h2c-0",
Weight: new(1),
},
{
Name: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-ws-1",
Weight: new(1),
},
{
Name: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-wss-2",
Weight: new(1),
},
{
Name: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-HTTP-3",
Weight: new(1),
},
{
Name: "httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-HTTPS-4",
Weight: new(1),
},
},
},
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-h2c-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.13:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-ws-1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.14:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-wss-2": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.15:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-HTTPS-4": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.16:8443",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-multi-protocols-gw-default-my-gateway-ep-web-0-bbdaa7d1e726d90930fd-svc-default-whoami-HTTP-3": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.17:8080",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
for group, kindFuncs := range test.groupKindBackendFuncs {
for kind, backendFunc := range kindFuncs {
p.RegisterBackendFuncs(group, kind, backendFunc)
}
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) {
testCases := []struct {
desc string
groupKindFilterFuncs map[string]map[string]BuildFilterFunc
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "ExtensionRef filter",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return namespace + "-" + name, nil, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Middlewares: []string{
"default-my-first-middleware",
"default-my-second-middleware",
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-bar-whoami-bar-1",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
// Cross-namespace backendRef: its ExtensionRef filter must resolve to the route's own namespace,
// not the backendRef's namespace.
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-bar-whoami-bar-1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
Middlewares: []string{"default-my-backend-middleware"},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "ExtensionRef filter with middleware creation",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return namespace + "-" + name, &dynamic.Middleware{Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Middlewares: []string{
"default-my-first-middleware",
"default-my-second-middleware",
},
},
},
Middlewares: map[string]*dynamic.Middleware{
"default-my-first-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
"default-my-second-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
"default-my-backend-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0",
Weight: new(1),
},
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-bar-whoami-bar-1",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
// Cross-namespace backendRef: its ExtensionRef filter must resolve to the route's own namespace,
// not the backendRef's namespace.
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-svc-bar-whoami-bar-1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
Middlewares: []string{"default-my-backend-middleware"},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Unknown ExtensionRef filter",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-err-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-err-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "invalid-httproute-filter",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "ExtensionRef filter with filterFunc error",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-err-wrr",
Rule: `Host("foo.com") && Path("/bar")`,
Priority: 100008,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93-err-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "invalid-httproute-filter",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "httproute/filter_extension_ref.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
for group, kindFuncs := range test.groupKindFilterFuncs {
for kind, filterFunc := range kindFuncs {
p.RegisterFilterFuncs(group, kind, filterFunc)
}
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
assert.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadGRPCRoutes(t *testing.T) {
testCases := []struct {
desc string
paths []string
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "Simple GRPCRoute and BackendTLSPolicy with CA certificate",
paths: []string{"services.yml", "grpcroute/with_backend_tls_policy.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
"CA2",
"CA1-secret",
"CA2-secret",
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple GRPCRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "grpcroute/with_backend_tls_policy_san.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
ServerName: "whoami",
InsecureSkipVerify: true,
PeerCertSANs: []tls.SAN{
{Type: tls.SANDNSNameType, Value: "whoami.default.svc.cluster.local"},
{Type: tls.SANURIType, Value: "spiffe://cluster.local/ns/default/sa/whoami"},
},
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple GRPCRoute and BackendTLSPolicy with System CA",
paths: []string{"services.yml", "grpcroute/with_backend_tls_policy_system.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "https://10.10.0.1:80",
},
{
URL: "https://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
ServersTransport: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
ServerName: "whoami",
},
},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "GRPCRoute Gateway tls.frontend Default and PerPort validation register TLS options",
paths: []string{"services.yml", "grpcroute/with_frontend_validation.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
"websecure2": {Address: ":8443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"listener-websecure2-https-dd662c6921e9998d0cca": {
EntryPoints: []string{"websecure2"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure2-https-dd662c6921e9998d0cca",
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure-0-5015e40a36a5159a5ca2": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure-0-5015e40a36a5159a5ca2-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure2-0-c1153de85fa10a42dc33": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure2-0-c1153de85fa10a42dc33-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure2-https-dd662c6921e9998d0cca"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure-0-5015e40a36a5159a5ca2-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure-0-5015e40a36a5159a5ca2-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure-0-5015e40a36a5159a5ca2-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.1:80",
},
{
URL: "h2c://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure2-0-c1153de85fa10a42dc33-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure2-0-c1153de85fa10a42dc33-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-websecure2-0-c1153de85fa10a42dc33-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.1:80",
},
{
URL: "h2c://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
"listener-websecure2-https-dd662c6921e9998d0cca": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequestClientCert,
}
return opts
}(),
},
},
},
},
{
desc: "GRPCRoute Gateways sharing an entry point with the same frontend validation keep their listeners isolated",
paths: []string{"services.yml", "grpcroute/with_frontend_validation_listener_isolation.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `(Host("*")) && !(Host("foo.com"))`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"listener-websecure-https-foo-com-c4461032f627a1b9033b": {
EntryPoints: []string{"websecure"},
Rule: `Host("foo.com")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-foo-com-c4461032f627a1b9033b",
},
},
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57": {
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529", "listener-websecure-https-foo-com-c4461032f627a1b9033b"},
Service: "grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-wrr",
Rule: `PathPrefix("/")`,
Priority: 15,
RuleSyntax: "default",
},
"grpcroute-default-grpc-app-secure-gw-default-my-secure-gateway-ep-websecure-0-3aae484bce849b96c90c": {
ParentRefs: []string{"listener-websecure-https-foo-com-c4461032f627a1b9033b"},
Service: "grpcroute-default-grpc-app-secure-gw-default-my-secure-gateway-ep-websecure-0-3aae484bce849b96c90c-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "h2c://10.10.0.1:80"},
{URL: "h2c://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"grpcroute-default-grpc-app-secure-gw-default-my-secure-gateway-ep-websecure-0-3aae484bce849b96c90c-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-secure-gw-default-my-secure-gateway-ep-websecure-0-3aae484bce849b96c90c-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-secure-gw-default-my-secure-gateway-ep-websecure-0-3aae484bce849b96c90c-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "h2c://10.10.0.1:80"},
{URL: "h2c://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
"listener-websecure-https-foo-com-c4461032f627a1b9033b": func() tls.Options {
opts := defaultListenerTLSOptions()
opts.ClientAuth = tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
}
return opts
}(),
},
},
},
},
{
desc: "GRPCRoute catch-all listeners sharing an entry point with different frontend validations are conflicted, the oldest Gateway wins",
paths: []string{"services.yml", "grpcroute/with_frontend_validation_shared_entrypoint.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57": {
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
Service: "grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-wrr",
Rule: `PathPrefix("/")`,
Priority: 15,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-catchall-gw-default-my-gateway-ep-websecure-0-4b61347f65fc7f1a8a57-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "h2c://10.10.0.1:80"},
{URL: "h2c://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
},
},
},
{
desc: "GRPCRoute with nil endpointslice port name",
paths: []string{"services.yml", "grpcroute/with_nil_endpointslice_port_name.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-nil-port-name-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-nil-port-name-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "GRPCRoute with nil endpointslice port value",
paths: []string{"services.yml", "grpcroute/with_nil_endpointslice_port_value.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-nil-port-value-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-nil-port-value-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "GRPCRoutes with identical rules",
paths: []string{"services.yml", "grpcroute/with_identical_rules.yml"},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-foo-com-5f20d578e2e3eccac466": {
EntryPoints: []string{"web"},
Rule: `Host("foo.com")`,
},
"grpcroute-default-grpc-app-catch-all-gw-default-my-gateway-ep-web-0-24ad420c9f1560b62d23": {
Service: "grpcroute-default-grpc-app-catch-all-gw-default-my-gateway-ep-web-0-24ad420c9f1560b62d23-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-foo-com-5f20d578e2e3eccac466"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-catch-all-gw-default-my-gateway-ep-web-0-24ad420c9f1560b62d23-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-catch-all-gw-default-my-gateway-ep-web-0-24ad420c9f1560b62d23-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-catch-all-gw-default-my-gateway-ep-web-0-24ad420c9f1560b62d23-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.1:80",
},
{
URL: "h2c://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) {
testCases := []struct {
desc string
groupKindFilterFuncs map[string]map[string]BuildFilterFunc
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "ExtensionRef filter",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return namespace + "-" + name, nil, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Middlewares: []string{
"default-my-first-middleware",
"default-my-second-middleware",
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.1:80",
},
{
URL: "h2c://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "ExtensionRef filter with middleware creation",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return namespace + "-" + name, &dynamic.Middleware{Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}}, nil
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Middlewares: []string{
"default-my-first-middleware",
"default-my-second-middleware",
},
},
},
Middlewares: map[string]*dynamic.Middleware{
"default-my-first-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
"default-my-second-middleware": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"Test-Header": "Test"}}},
},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "h2c://10.10.0.1:80",
},
{
URL: "h2c://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Unknown ExtensionRef filter",
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-err-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-err-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "invalid-grpcroute-filter",
Weight: new(1),
GRPCStatus: &dynamic.GRPCStatus{
Code: codes.Unavailable,
Msg: "Service Unavailable",
},
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "ExtensionRef filter with filterFunc error",
groupKindFilterFuncs: map[string]map[string]BuildFilterFunc{
traefikv1alpha1.GroupName: {"Middleware": func(name, namespace string) (string, *dynamic.Middleware, error) {
return "", nil, errors.New("BOOM")
}},
},
entryPoints: map[string]Entrypoint{"web": {
Address: ":80",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296": {
Service: "grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-err-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-1-gw-default-my-gateway-ep-web-0-0abda411573f19f36296-err-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "invalid-grpcroute-filter",
Weight: new(1),
GRPCStatus: &dynamic.GRPCStatus{
Code: codes.Unavailable,
Msg: "Service Unavailable",
},
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, []string{"services.yml", "grpcroute/filter_extension_ref.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
for group, kindFuncs := range test.groupKindFilterFuncs {
for kind, filterFunc := range kindFuncs {
p.RegisterFilterFuncs(group, kind, filterFunc)
}
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
assert.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadTCPRoutes(t *testing.T) {
testCases := []struct {
desc string
ingressClass string
paths []string
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
nativeLB bool
}{
{
desc: "Empty",
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because missing entry point",
paths: []string{"services.yml", "tcproute/simple.yml"},
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8000",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because no tcp route defined",
paths: []string{"services.yml", "tcproute/without_tcproute.yml"},
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by missing GatewayClass",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tcproute/without_gatewayclass.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by unknown GatewayClass controller desc",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tcproute/gatewayclass_with_unknown_controller.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by HTTPRoute with TLS configuration",
entryPoints: map[string]Entrypoint{"web": {
Address: ":8080",
}},
paths: []string{"services.yml", "tcproute/with_tls_configuration.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Router with service in error caused by wrong TargetPort",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tcproute/with_wrong_service_port.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-TCP-app-1-gw-default-my-gateway-ep-TCP-0-b06faef5537f5b1e4c70": {
EntryPoints: []string{"TCP"},
Rule: `HostSNI("*")`,
RuleSyntax: "default",
Service: "tcproute-default-TCP-app-1-gw-default-my-gateway-ep-TCP-0-b06faef5537f5b1e4c70-wrr",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-TCP-app-1-gw-default-my-gateway-ep-TCP-0-b06faef5537f5b1e4c70-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{{
Name: "tcproute-default-TCP-app-1-gw-default-my-gateway-ep-TCP-0-b06faef5537f5b1e4c70-err-lb",
Weight: new(1),
}},
},
},
"tcproute-default-TCP-app-1-gw-default-my-gateway-ep-TCP-0-b06faef5537f5b1e4c70-err-lb": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TCPRoute",
paths: []string{"services.yml", "tcproute/simple.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Multiple TCPRoute",
paths: []string{"services.yml", "tcproute/with_multiple_routes.yml"},
entryPoints: map[string]Entrypoint{
"tcp-1": {Address: ":9000"},
"tcp-2": {Address: ":10000"},
"not-tcp": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-1-0-538fb5c01603fd936dbf": {
EntryPoints: []string{"tcp-1"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-1-0-538fb5c01603fd936dbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
"tcproute-default-tcp-app-2-gw-default-my-tcp-gateway-ep-tcp-2-0-8eec98026c1dabcb62e0": {
EntryPoints: []string{"tcp-2"},
Service: "tcproute-default-tcp-app-2-gw-default-my-tcp-gateway-ep-tcp-2-0-8eec98026c1dabcb62e0-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-1-0-538fb5c01603fd936dbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-1-0-538fb5c01603fd936dbf-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-2-gw-default-my-tcp-gateway-ep-tcp-2-0-8eec98026c1dabcb62e0-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-2-gw-default-my-tcp-gateway-ep-tcp-2-0-8eec98026c1dabcb62e0-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-1-0-538fb5c01603fd936dbf-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tcproute-default-tcp-app-2-gw-default-my-tcp-gateway-ep-tcp-2-0-8eec98026c1dabcb62e0-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:10000",
},
{
Address: "10.10.0.10:10000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with multiple rules",
paths: []string{"services.yml", "tcproute/with_multiple_rules.yml"},
entryPoints: map[string]Entrypoint{
"tcp-1": {Address: ":9000"},
"tcp-2": {Address: ":10000"},
"not-tcp": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-1-0-1767e543195002a45b0b": {
EntryPoints: []string{"tcp-1"},
Service: "tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-1-0-1767e543195002a45b0b-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-1-0-1767e543195002a45b0b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-1-0-1767e543195002a45b0b-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-1-0-1767e543195002a45b0b-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TCPRoute, with backendRef",
paths: []string{"services.yml", "tcproute/simple_cross_provider.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "service@file",
Weight: new(1),
},
{
Name: "tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-svc-default-whoamitcp-1",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-svc-default-whoamitcp-1": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TCPRoute attached to a TLS listener",
paths: []string{"services.yml", "tcproute/with_protocol_tls.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Conflicting TCPRoutes on the same listener, only the oldest is bound",
paths: []string{"services.yml", "tcproute/with_conflicting_routes.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-older-gw-default-my-tcp-gateway-ep-tcp-0-6aa3057a8ee9e5d61398": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-older-gw-default-my-tcp-gateway-ep-tcp-0-6aa3057a8ee9e5d61398-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-older-gw-default-my-tcp-gateway-ep-tcp-0-6aa3057a8ee9e5d61398-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-older-gw-default-my-tcp-gateway-ep-tcp-0-6aa3057a8ee9e5d61398-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-older-gw-default-my-tcp-gateway-ep-tcp-0-6aa3057a8ee9e5d61398-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with Same namespace selector",
paths: []string{"services.yml", "tcproute/with_namespace_same.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-default-gw-default-my-tcp-gateway-ep-tcp-0-9c02509721599badb6c4": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-default-gw-default-my-tcp-gateway-ep-tcp-0-9c02509721599badb6c4-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-default-gw-default-my-tcp-gateway-ep-tcp-0-9c02509721599badb6c4-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-default-gw-default-my-tcp-gateway-ep-tcp-0-9c02509721599badb6c4-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-default-gw-default-my-tcp-gateway-ep-tcp-0-9c02509721599badb6c4-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with All namespace selector",
paths: []string{"services.yml", "tcproute/with_namespace_all.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171": {
EntryPoints: []string{"tcp"},
Service: "tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with namespace selector",
paths: []string{"services.yml", "tcproute/with_namespace_selector.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171": {
EntryPoints: []string{"tcp"},
Service: "tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-tcp-gateway-ep-tcp-0-18313c5ba079a1d9e171-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TCPRoute with NativeLBByDefault",
paths: []string{"services.yml", "tcproute/simple.yml"},
nativeLB: true,
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.10.1:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TCPRoute with NativeLB annotation",
paths: []string{"services.yml", "tcproute/simple_nativelb.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-native-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-native-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.10.1:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TCPRoute with NativeLBByDefault enabled but service has disabled nativelb",
paths: []string{"services.yml", "tcproute/simple_nativelb_disabled.yml"},
nativeLB: true,
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-native-disabled-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-native-disabled-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.30:9000",
},
{
Address: "10.10.0.31:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with nil endpointslice port name",
paths: []string{"services.yml", "tcproute/with_nil_endpointslice_port_name.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-nil-port-name-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-nil-port-name-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute with nil endpointslice port value",
paths: []string{"services.yml", "tcproute/with_nil_endpointslice_port_value.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-nil-port-value-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-nil-port-value-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoutes with identical rules",
paths: []string{"services.yml", "tcproute/with_identical_rules.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-first-gw-default-my-tcp-gateway-ep-tcp-0-79a3b41b9e16bdaa2841": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-first-gw-default-my-tcp-gateway-ep-tcp-0-79a3b41b9e16bdaa2841-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-first-gw-default-my-tcp-gateway-ep-tcp-0-79a3b41b9e16bdaa2841-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-first-gw-default-my-tcp-gateway-ep-tcp-0-79a3b41b9e16bdaa2841-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-first-gw-default-my-tcp-gateway-ep-tcp-0-79a3b41b9e16bdaa2841-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TCPRoute attached to a ListenerSet listener",
paths: []string{"services.yml", "tcproute/with_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf": {
EntryPoints: []string{
"tcp",
},
Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-b0953a7f472d14e31cbf-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
NativeLBByDefault: test.nativeLB,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadTLSRoutes(t *testing.T) {
testCases := []struct {
desc string
ingressClass string
paths []string
entryPoints map[string]Entrypoint
nativeLB bool
expected *dynamic.Configuration
}{
{
desc: "Empty",
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because no matching entry point",
paths: []string{"services.yml", "tlsroute/simple_TLS_to_TCPRoute.yml"},
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8000",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because no tls route defined",
paths: []string{"services.yml", "tlsroute/without_tlsroute.yml"},
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by missing GatewayClass",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tlsroute/without_gatewayclass.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by unknown GatewayClass controller desc",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tlsroute/gatewayclass_with_unknown_controller.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Router with service in error caused by wrong TargetPort",
entryPoints: map[string]Entrypoint{"TCP": {
Address: ":8080",
}},
paths: []string{"services.yml", "tlsroute/with_wrong_service_port.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-TCP-0-45c28fdcc89d443a020b": {
EntryPoints: []string{"TCP"},
Priority: 0,
Rule: `HostSNI("*")`,
RuleSyntax: "default",
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-TCP-0-45c28fdcc89d443a020b-wrr",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-TCP-0-45c28fdcc89d443a020b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-TCP-0-45c28fdcc89d443a020b-err-lb",
Weight: new(1),
}},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-TCP-0-45c28fdcc89d443a020b-err-lb": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Empty caused by mixed routes with wrong parent ref",
entryPoints: map[string]Entrypoint{
"tcp": {
Address: ":9000",
},
"tcp-tls": {
Address: ":9443",
},
"http": {
Address: ":80",
},
},
paths: []string{"services.yml", "mixed/with_wrong_routes_selector.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TCPRoute attached to a TLS listener in Terminate mode",
paths: []string{"services.yml", "tlsroute/simple_TLS_to_TCPRoute.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by TCPRoute attached to a TLS listener in Passthrough mode",
paths: []string{"services.yml", "tlsroute/simple_TLS_to_TCPRoute_passthrough.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLS listener to TLSRoute",
paths: []string{"services.yml", "tlsroute/simple_TLS_to_TLSRoute.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Multiple TLSRoute",
paths: []string{"services.yml", "tlsroute/with_multiple_routes_kind.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
"tcp": {Address: ":10000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-24d35d31a7578c5062a7": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-24d35d31a7578c5062a7-wrr",
Priority: 0,
Rule: `HostSNI("*")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-24d35d31a7578c5062a7-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-24d35d31a7578c5062a7-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-24d35d31a7578c5062a7-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:10000",
},
{
Address: "10.10.0.10:10000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute, with backendRef",
paths: []string{"services.yml", "tlsroute/simple_cross_provider.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "service@file",
Weight: new(1),
},
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592-svc-default-whoamitcp-1",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592-svc-default-whoamitcp-1": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Simple TLSRoute, with Passthrough and TLS configuration should raise a warn",
paths: []string{"services.yml", "tlsroute/with_passthrough_tls.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute, with Passthrough",
paths: []string{"services.yml", "tlsroute/with_passthrough.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute, with single SNI matching",
paths: []string{"services.yml", "tlsroute/with_SNI_matching.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute attached to TLS listeners with different hostnames on the same entry point",
paths: []string{"services.yml", "tlsroute/with_multiple_listeners_same_port.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-276f6c243a7761966c5b": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-276f6c243a7761966c5b-wrr",
Priority: 15,
Rule: `HostSNI("bar.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-276f6c243a7761966c5b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-276f6c243a7761966c5b-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-276f6c243a7761966c5b-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute, with multiple SNI matching",
paths: []string{"services.yml", "tlsroute/with_multiple_SNI_matching.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-035a9ab93ab8d0d12874": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-035a9ab93ab8d0d12874-wrr",
Priority: 15,
Rule: `HostSNI("bar.example.com") || HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-035a9ab93ab8d0d12874-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-035a9ab93ab8d0d12874-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-035a9ab93ab8d0d12874-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute with Same namespace selector",
paths: []string{"services.yml", "tlsroute/with_namespace_same.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-wrr",
Priority: 11,
Rule: `HostSNI("foo.default")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute with All namespace selector",
paths: []string{"services.yml", "tlsroute/with_namespace_all.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-wrr",
Priority: 11,
Rule: `HostSNI("foo.default")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b": {
EntryPoints: []string{"tls"},
Service: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-wrr",
Priority: 7,
Rule: `HostSNI("foo.bar")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-0-76eff62990bc46a9697d-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute with namespace selector",
paths: []string{"services.yml", "tlsroute/with_namespace_selector.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b": {
EntryPoints: []string{"tls"},
Service: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-wrr",
Priority: 7,
Rule: `HostSNI("foo.bar")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-0-58a6fd0c8bc014d99d8b-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute with multiple rules",
paths: []string{"services.yml", "tlsroute/with_multiple_rules.yml"},
entryPoints: map[string]Entrypoint{
"tcp-1": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-gw-default-my-gateway-ep-tcp-1-0-2e99b4a7e81cc622b335": {
EntryPoints: []string{"tcp-1"},
Service: "tlsroute-default-tls-app-gw-default-my-gateway-ep-tcp-1-0-2e99b4a7e81cc622b335-wrr",
Priority: 0,
Rule: `HostSNI("*")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-gw-default-my-gateway-ep-tcp-1-0-2e99b4a7e81cc622b335-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-gw-default-my-gateway-ep-tcp-1-0-2e99b4a7e81cc622b335-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-gw-default-my-gateway-ep-tcp-1-0-2e99b4a7e81cc622b335-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute with NativeLBByDefault",
paths: []string{"services.yml", "tlsroute/simple_TLS_to_TLSRoute.yml"},
nativeLB: true,
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.10.1:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute with NativeLB annotation",
paths: []string{"services.yml", "tlsroute/simple_nativelb.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-native-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-native-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.10.1:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Simple TLSRoute and BackendTLSPolicy with CA certificate",
paths: []string{"services.yml", "tlsroute/with_backend_tls_policy.yml"},
entryPoints: map[string]Entrypoint{"tls": {
Address: ":9001",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr",
Rule: `HostSNI("foo.com")`,
Priority: 7,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:80",
TLS: true,
},
{
Address: "10.10.0.2:80",
TLS: true,
},
},
ServersTransport: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
TLS: &dynamic.TLSClientConfig{
ServerName: "whoami",
RootCAs: []types.FileOrContent{
"CA1",
"CA2",
"CA1-secret",
"CA2-secret",
},
},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Simple TLSRoute and BackendTLSPolicy with subjectAltNames",
paths: []string{"services.yml", "tlsroute/with_backend_tls_policy_san.yml"},
entryPoints: map[string]Entrypoint{"tls": {
Address: ":9001",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr",
Rule: `HostSNI("foo.com")`,
Priority: 7,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:80",
TLS: true,
},
{
Address: "10.10.0.2:80",
TLS: true,
},
},
ServersTransport: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
TLS: &dynamic.TLSClientConfig{
ServerName: "whoami",
InsecureSkipVerify: true,
PeerCertSANs: []tls.SAN{
{Type: tls.SANDNSNameType, Value: "whoami.default.svc.cluster.local"},
{Type: tls.SANURIType, Value: "spiffe://cluster.local/ns/default/sa/whoami"},
},
},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Simple TLSRoute and BackendTLSPolicy with System CA",
paths: []string{"services.yml", "tlsroute/with_backend_tls_policy_system.yml"},
entryPoints: map[string]Entrypoint{"tls": {
Address: ":9001",
}},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr",
Rule: `HostSNI("foo.com")`,
Priority: 7,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.1:80",
TLS: true,
},
{
Address: "10.10.0.2:80",
TLS: true,
},
},
ServersTransport: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0",
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-5c999a999d12ac0531c5-svc-default-whoami-0": {
TLS: &dynamic.TLSClientConfig{
ServerName: "whoami",
},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "TLSRoute with nil endpointslice port name",
paths: []string{"services.yml", "tlsroute/with_nil_endpointslice_port_name.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-nil-port-name-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-nil-port-name-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute with nil endpointslice port value",
paths: []string{"services.yml", "tlsroute/with_nil_endpointslice_port_value.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr",
Priority: 15,
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-nil-port-value-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-tls-gateway-ep-tcp-0-d01849ae1c96d9ffe0ef-svc-default-whoamitcp-nil-port-value-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoutes with identical rules",
paths: []string{"services.yml", "tlsroute/with_identical_rules.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tlsroute-default-tls-app-catch-all-gw-default-my-tls-gateway-ep-tcp-0-60f8cf9276253172d000": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-catch-all-gw-default-my-tls-gateway-ep-tcp-0-60f8cf9276253172d000-wrr",
Rule: `HostSNI("foo.com")`,
Priority: 7,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tlsroute-default-tls-app-catch-all-gw-default-my-tls-gateway-ep-tcp-0-60f8cf9276253172d000-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-catch-all-gw-default-my-tls-gateway-ep-tcp-0-60f8cf9276253172d000-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-catch-all-gw-default-my-tls-gateway-ep-tcp-0-60f8cf9276253172d000-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute attached to a ListenerSet Passthrough listener",
paths: []string{"services.yml", "tlsroute/with_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Service: "deny-unknown-host",
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{
"tls",
},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
Priority: 15,
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
NativeLBByDefault: test.nativeLB,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadMixedRoutes(t *testing.T) {
testCases := []struct {
desc string
ingressClass string
paths []string
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "Empty",
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by unsupported listener.Protocol",
paths: []string{"services.yml", "mixed/with_bad_listener_protocol.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by unsupported listener.Route.Kind",
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
},
paths: []string{"services.yml", "mixed/with_bad_listener_route_kind.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty caused by listener.Protocol does not support listener.Route.Kind",
paths: []string{"services.yml", "mixed/with_incompatible_protocol_and_route_kind.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Mixed routes",
paths: []string{"services.yml", "mixed/simple.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"websecure": {Address: ":9443"},
"tcp": {Address: ":9000"},
"tls-1": {Address: ":10000"},
"tls-2": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-2-0-34e311881843bc2963c9": {
EntryPoints: []string{"tls-2"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-2-0-34e311881843bc2963c9-wrr",
Priority: 24,
Rule: `HostSNI("pass.tls.foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-2-0-34e311881843bc2963c9-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-2-0-34e311881843bc2963c9-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-2-0-34e311881843bc2963c9-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-f20abd7604e9c4caa5f3": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-f20abd7604e9c4caa5f3-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-c319354d6e97e7ccf839": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-c319354d6e97e7ccf839-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-f20abd7604e9c4caa5f3-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-f20abd7604e9c4caa5f3-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-c319354d6e97e7ccf839-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-c319354d6e97e7ccf839-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-f20abd7604e9c4caa5f3-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-c319354d6e97e7ccf839-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Empty caused by mixed routes with multiple listeners using same hostname, port and protocol",
paths: []string{"services.yml", "mixed/with_multiple_listeners_using_same_hostname_port_protocol.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"tcp": {Address: ":9000"},
"tls": {Address: ":9001"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Mixed routes with Same namespace selector",
paths: []string{"services.yml", "mixed/with_namespace_same.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"websecure": {Address: ":9443"},
"tcp": {Address: ":9000"},
"tls-1": {Address: ":10000"},
"tls-2": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8": {
EntryPoints: []string{"tls-2"},
Service: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-wrr",
Priority: 24,
Rule: `HostSNI("pass.tls.foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432": {
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9": {
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Mixed routes with All namespace selector",
paths: []string{"services.yml", "mixed/with_namespace_all.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"websecure": {Address: ":9443"},
"tcp": {Address: ":9000"},
"tls-1": {Address: ":10000"},
"tls-2": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8": {
EntryPoints: []string{"tls-2"},
Service: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-wrr",
Priority: 24,
Rule: `HostSNI("pass.tls.foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7": {
EntryPoints: []string{"tcp"},
Service: "tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-default-gw-default-my-gateway-ep-tls-2-0-30cc8c9fb5be933316f8-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755": {
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13": {
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Mixed routes with Selector Route Binding",
paths: []string{"services.yml", "mixed/with_namespace_selector.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"websecure": {Address: ":9443"},
"tcp": {Address: ":9000"},
"tls-1": {Address: ":10000"},
"tls-2": {Address: ":11000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7": {
EntryPoints: []string{"tcp"},
Service: "tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-2-0-2a95ae92bba77504addc": {
EntryPoints: []string{"tls-2"},
Service: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-2-0-2a95ae92bba77504addc-wrr",
Priority: 24,
Rule: `HostSNI("pass.tls.foo.example.com")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-2-0-2a95ae92bba77504addc-svc-bar-whoamitcp-bar-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.13:9000",
},
{
Address: "10.10.0.14:9000",
},
},
},
},
"tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-bar-tcp-app-bar-gw-default-my-gateway-ep-tcp-0-c5d9e24bf010118167e7-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
"tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-2-0-2a95ae92bba77504addc-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-bar-tls-app-bar-gw-default-my-gateway-ep-tls-2-0-2a95ae92bba77504addc-svc-bar-whoamitcp-bar-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755": {
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13": {
Service: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-svc-bar-whoami-bar-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.11:80",
},
{
URL: "http://10.10.0.12:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-web-0-cf99cd1374e6056a6755-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
"httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-bar-http-app-bar-gw-default-my-gateway-ep-websecure-0-135c6826c1baedf74c13-svc-bar-whoami-bar-0",
Weight: new(1),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "Mixed routes with core group",
paths: []string{"services.yml", "mixed/with_core_group.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
"websecure": {Address: ":9443"},
"tcp": {Address: ":9000"},
"tls": {Address: ":10000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-default-gw-default-my-gateway-ep-tcp-0-df24b7f0f1d9b443e55b-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{"websecure"},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432": {
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9": {
Service: "httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-wrr",
Rule: `PathPrefix("/")`,
Priority: 2,
RuleSyntax: "default",
ParentRefs: []string{"listener-websecure-https-439feda8e3f1dbdca529"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-web-0-3b55179f1d48675f1432-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
"httproute-default-http-app-default-gw-default-my-gateway-ep-websecure-0-4a26538fbaaf60dc52c9-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": defaultListenerTLSOptions(),
},
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
},
},
},
{
desc: "GRPCRoute created first wins the conflicting hostname over the HTTPRoute",
paths: []string{"services.yml", "mixed/with_grpcroute_priority_over_httproute.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"grpcroute-default-grpc-app-priority-gw-default-my-gateway-ep-web-0-46e5641ba0ccfa2eff8a": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "grpcroute-default-grpc-app-priority-gw-default-my-gateway-ep-web-0-46e5641ba0ccfa2eff8a-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 22,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"grpcroute-default-grpc-app-priority-gw-default-my-gateway-ep-web-0-46e5641ba0ccfa2eff8a-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "grpcroute-default-grpc-app-priority-gw-default-my-gateway-ep-web-0-46e5641ba0ccfa2eff8a-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"grpcroute-default-grpc-app-priority-gw-default-my-gateway-ep-web-0-46e5641ba0ccfa2eff8a-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "h2c://10.10.0.1:80"},
{URL: "h2c://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "HTTPRoute created first wins the conflicting hostname over the GRPCRoute",
paths: []string{"services.yml", "mixed/with_httproute_priority_over_grpcroute.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":9080"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-web-http-5b3226d4ebd42b7200f1": {
EntryPoints: []string{"web"},
Rule: `Host("*")`,
},
"httproute-default-http-app-conflict-gw-default-my-gateway-ep-web-0-3fcf93756cef1b546fd2": {
ParentRefs: []string{"listener-web-http-5b3226d4ebd42b7200f1"},
Service: "httproute-default-http-app-conflict-gw-default-my-gateway-ep-web-0-3fcf93756cef1b546fd2-wrr",
Rule: `Host("foo.com") && PathPrefix("/")`,
Priority: 9,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-conflict-gw-default-my-gateway-ep-web-0-3fcf93756cef1b546fd2-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-conflict-gw-default-my-gateway-ep-web-0-3fcf93756cef1b546fd2-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
"httproute-default-http-app-conflict-gw-default-my-gateway-ep-web-0-3fcf93756cef1b546fd2-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{URL: "http://10.10.0.1:80"},
{URL: "http://10.10.0.2:80"},
},
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "TLSRoute and TCPRoute with the same rule on the same entry point",
paths: []string{"services.yml", "mixed/with_tlsroute_and_tcproute_on_same_entrypoint.yml"},
entryPoints: map[string]Entrypoint{
"tcp": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-gw-default-my-tls-gateway-ep-tcp-0-06fcfb4c19c97feaa646": {
EntryPoints: []string{"tcp"},
Service: "tlsroute-default-tls-app-gw-default-my-tls-gateway-ep-tcp-0-06fcfb4c19c97feaa646-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
},
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-0-ada211f39fad8759e161": {
EntryPoints: []string{"tcp"},
Service: "tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-0-ada211f39fad8759e161-wrr",
Rule: `HostSNI("*")`,
RuleSyntax: "default",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-gw-default-my-tls-gateway-ep-tcp-0-06fcfb4c19c97feaa646-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tlsroute-default-tls-app-gw-default-my-tls-gateway-ep-tcp-0-06fcfb4c19c97feaa646-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tlsroute-default-tls-app-gw-default-my-tls-gateway-ep-tcp-0-06fcfb4c19c97feaa646-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{Address: "10.10.0.9:9000"},
{Address: "10.10.0.10:9000"},
},
},
},
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-0-ada211f39fad8759e161-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{
{
Name: "tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-0-ada211f39fad8759e161-svc-default-whoamitcp-0",
Weight: new(1),
},
},
},
},
"tcproute-default-tcp-app-gw-default-my-tcp-gateway-ep-tcp-0-ada211f39fad8759e161-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{Address: "10.10.0.9:9000"},
{Address: "10.10.0.10:9000"},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "ListenerSet listener taking the hostname of a Gateway listener is not loaded",
paths: []string{"services.yml", "mixed/with_listenerset_conflicts.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-ba3a418469df6d6c8b5a": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-ba3a418469df6d6c8b5a-wrr",
Rule: `Host("baz.example.com") && Path("/bar")`,
ParentRefs: []string{
"listener-websecure-https-baz-example-com-dd68422a9ac9bfc6c47e",
},
RuleSyntax: "default",
Priority: 100016,
},
"listener-websecure-https-baz-example-com-dd68422a9ac9bfc6c47e": {
EntryPoints: []string{
"websecure",
},
Rule: `Host("baz.example.com")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-baz-example-com-dd68422a9ac9bfc6c47e",
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-ba3a418469df6d6c8b5a-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: dynamic.BalancerStrategy("wrr"),
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100000000),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-ba3a418469df6d6c8b5a-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-ba3a418469df6d6c8b5a-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-baz-example-com-dd68422a9ac9bfc6c47e": {
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
ALPNProtocols: []string{
"h2",
"http/1.1",
"acme-tls/1",
},
},
},
},
},
},
{
desc: "Older ListenerSet wins the listener conflict with its sibling",
paths: []string{"services.yml", "mixed/with_listenerset_precedence.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"web2": {Address: ":8080"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-older-gw-default-my-gateway-ep-web2-0-4541b5743d681903cbd4": {
Service: "httproute-default-http-app-older-gw-default-my-gateway-ep-web2-0-4541b5743d681903cbd4-wrr",
Rule: `Host("older.example.com") && Path("/bar")`,
ParentRefs: []string{
"listener-web2-http-4b299c63a1fd0bffdf3d",
},
RuleSyntax: "default",
Priority: 100018,
},
"listener-web2-http-4b299c63a1fd0bffdf3d": {
EntryPoints: []string{
"web2",
},
Rule: `Host("*")`,
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-older-gw-default-my-gateway-ep-web2-0-4541b5743d681903cbd4-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: dynamic.BalancerStrategy("wrr"),
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100000000),
},
},
},
"httproute-default-http-app-older-gw-default-my-gateway-ep-web2-0-4541b5743d681903cbd4-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-older-gw-default-my-gateway-ep-web2-0-4541b5743d681903cbd4-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because the ListenerSet is not allowed by the Gateway",
paths: []string{"services.yml", "mixed/with_listenerset_not_allowed.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"web2": {Address: ":8080"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
// The Gateway declares no valid listener of its own,
// so it is accepted through the listener its ListenerSet delegates, which serves the route.
desc: "ListenerSet serving a Gateway without a valid listener of its own",
paths: []string{"services.yml", "mixed/with_listenerset_delegation.yml"},
entryPoints: map[string]Entrypoint{
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr",
Rule: `Host("foo.example.com") && Path("/bar")`,
ParentRefs: []string{
"listener-websecure-https-439feda8e3f1dbdca529",
},
RuleSyntax: "default",
Priority: 100016,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{
"websecure",
},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: dynamic.BalancerStrategy("wrr"),
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100000000),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": {
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
ALPNProtocols: []string{
"h2",
"http/1.1",
"acme-tls/1",
},
},
},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func TestLoadRoutesWithReferenceGrants(t *testing.T) {
testCases := []struct {
desc string
ingressClass string
paths []string
expected *dynamic.Configuration
entryPoints map[string]Entrypoint
}{
{
desc: "Empty",
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant for Secret is missing",
paths: []string{"services.yml", "referencegrant/for_secret_missing.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant spec.from does not match secret",
paths: []string{"services.yml", "referencegrant/for_secret_not_matching_from.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant spec.to does not match secret",
paths: []string{"services.yml", "referencegrant/for_secret_not_matching_to.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "For Secret",
paths: []string{"services.yml", "referencegrant/for_secret.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"deny-unknown-host": {
Rule: "HostSNI(`*`) && !ALPN(`h2`) && !ALPN(`http/1.1`)",
Priority: 1,
Service: "deny-unknown-host",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174": {
EntryPoints: []string{"tls"},
Service: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr",
Rule: `HostSNI("foo.example.com")`,
RuleSyntax: "default",
Priority: 15,
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"deny-unknown-host": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-wrr": {
Weighted: &dynamic.TCPWeightedRoundRobin{
Services: []dynamic.TCPWRRService{{
Name: "tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0",
Weight: new(1),
}},
},
},
"tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-85354ef9915a52028174-svc-default-whoamitcp-0": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{
Servers: []dynamic.TCPServer{
{
Address: "10.10.0.9:9000",
},
{
Address: "10.10.0.10:9000",
},
},
},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant for Service is missing",
paths: []string{"services.yml", "referencegrant/for_secret_missing.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant spec.from does not match service",
paths: []string{"services.yml", "referencegrant/for_service_not_matching_from.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "Empty because ReferenceGrant spec.to does not match service",
paths: []string{"services.yml", "referencegrant/for_service_not_matching_to.yml"},
entryPoints: map[string]Entrypoint{
"tls": {Address: ":9000"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "For Service",
paths: []string{"services.yml", "referencegrant/for_service.yml"},
entryPoints: map[string]Entrypoint{
"http": {Address: ":80"},
},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"listener-http-http-foo-example-com-34297ca2d1326bfcdaad": {
EntryPoints: []string{"http"},
Rule: `Host("foo.example.com")`,
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-http-0-f6b84730f282bab89081": {
Rule: `Host("foo.example.com") && PathPrefix("/")`,
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-http-0-f6b84730f282bab89081-wrr",
RuleSyntax: "default",
Priority: 17,
ParentRefs: []string{"listener-http-http-foo-example-com-34297ca2d1326bfcdaad"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-http-0-f6b84730f282bab89081-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-http-0-f6b84730f282bab89081-svc-default-whoami-bar-0",
Weight: new(1),
Status: new(500),
},
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
{
desc: "For Secret from ListenerSet",
paths: []string{"services.yml", "referencegrant/for_secret_from_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282": {
Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr",
Rule: `Host("foo.example.com") && Path("/bar")`,
ParentRefs: []string{
"listener-websecure-https-439feda8e3f1dbdca529",
},
RuleSyntax: "default",
Priority: 100016,
},
"listener-websecure-https-439feda8e3f1dbdca529": {
EntryPoints: []string{
"websecure",
},
Rule: `Host("*")`,
TLS: &dynamic.RouterTLSConfig{
Options: "listener-websecure-https-439feda8e3f1dbdca529",
},
},
},
Services: map[string]*dynamic.Service{
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
Strategy: dynamic.BalancerStrategy("wrr"),
PassHostHeader: new(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100000000),
},
},
},
"httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "httproute-default-http-app-1-gw-default-my-gateway-ep-websecure-0-3c8b64d308e92f9c9282-svc-default-whoami-0",
Weight: new(1),
},
},
},
},
},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Certificates: []*tls.CertAndStores{
{
CertFile: types.FileOrContent(listenerCert),
KeyFile: types.FileOrContent(listenerKey),
},
},
Options: map[string]tls.Options{
"listener-websecure-https-439feda8e3f1dbdca529": {
CipherSuites: []string{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
},
ALPNProtocols: []string{
"h2",
"http/1.1",
"acme-tls/1",
},
},
},
},
},
},
{
desc: "Empty because ReferenceGrant spec.from does not match ListenerSet",
paths: []string{"services.yml", "referencegrant/for_secret_not_matching_from_listenerset.yml"},
entryPoints: map[string]Entrypoint{
"web": {Address: ":80"},
"websecure": {Address: ":443"},
},
expected: &dynamic.Configuration{
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
Middlewares: map[string]*dynamic.Middleware{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
if test.expected == nil {
return
}
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
assert.Equal(t, test.expected, conf)
})
}
}
func Test_matchingGatewayListener(t *testing.T) {
testCases := []struct {
desc string
gateways []gatewayWithListeners
parentRefs []gatev1.ParentReference
routeNamespace string
want []gatewayListenersForParentRef
}{
{
desc: "Unsupported group",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "foo"}},
}},
parentRefs: []gatev1.ParentReference{{
Group: new(gatev1.Group("foo")),
}},
},
{
desc: "Unsupported kind",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "foo"}},
}},
parentRefs: []gatev1.ParentReference{{
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("foo")),
}},
},
{
desc: "Namespace does not match the listener",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "foo"}},
}},
parentRefs: []gatev1.ParentReference{{
Namespace: new(gatev1.Namespace("foo")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
},
{
desc: "Route namespace defaulting does not match the listener",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "foo"}},
}},
routeNamespace: "foo",
parentRefs: []gatev1.ParentReference{{
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
},
{
desc: "Name does not match the listener",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{}},
}},
parentRefs: []gatev1.ParentReference{{
Namespace: new(gatev1.Namespace("default")),
Name: "foo",
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
},
{
desc: "Match",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{}},
}},
parentRefs: []gatev1.ParentReference{{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
GatewayName: "gateway",
GatewayNamespace: "default",
Listeners: []gatewayListener{{}},
}},
},
{
desc: "Match with route namespace defaulting",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{}},
}},
routeNamespace: "default",
parentRefs: []gatev1.ParentReference{{
Name: "gateway",
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway",
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
GatewayName: "gateway",
GatewayNamespace: "default",
Listeners: []gatewayListener{{}},
}},
},
{
// A Route may reference Gateways owned by other controllers; those
// parentRefs must not be associated to (and thus get status written by)
// this controller.
desc: "Only parentRefs targeting our Gateways are returned",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{}},
}},
parentRefs: []gatev1.ParentReference{
{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
{
Name: "other-gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
GatewayName: "gateway",
GatewayNamespace: "default",
Listeners: []gatewayListener{{}},
}},
},
{
desc: "ParentRef is associated to all the listeners of its Gateway",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{
{SectionName: "web"},
{SectionName: "websecure"},
},
}},
parentRefs: []gatev1.ParentReference{{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
GatewayName: "gateway",
GatewayNamespace: "default",
Listeners: []gatewayListener{
{SectionName: "web"},
{SectionName: "websecure"},
},
}},
},
{
// A parentRef must only be associated to the listeners of the Gateway it
// targets, even when several of our Gateways are known.
desc: "ParentRef is only associated to the referenced Gateway",
gateways: []gatewayWithListeners{
{
gateway: &gatev1.Gateway{Name: "gateway-a", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "web"}},
},
{
gateway: &gatev1.Gateway{Name: "gateway-b", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "web"}},
},
},
parentRefs: []gatev1.ParentReference{{
Name: "gateway-a",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
}},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway-a",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
},
GatewayName: "gateway-a",
GatewayNamespace: "default",
Listeners: []gatewayListener{{SectionName: "web"}},
}},
},
{
// A parentRef with a non-matching SectionName or Port still targets the
// Gateway: all its listeners are returned so the route loader can report
// ResolvedRefs even though the route attaches to none of them.
desc: "ParentRef with a non-matching port still returns the Gateway listeners",
gateways: []gatewayWithListeners{{
gateway: &gatev1.Gateway{Name: "gateway", Namespace: "default"},
listeners: []gatewayListener{{SectionName: "web", Port: 80}},
}},
parentRefs: []gatev1.ParentReference{{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
Port: new(gatev1.PortNumber(8080)),
}},
want: []gatewayListenersForParentRef{{
ParentRef: gatev1.ParentReference{
Name: "gateway",
Namespace: new(gatev1.Namespace("default")),
Group: new(gatev1.Group(gatev1.GroupName)),
Kind: new(gatev1.Kind("Gateway")),
Port: new(gatev1.PortNumber(8080)),
},
GatewayName: "gateway",
GatewayNamespace: "default",
Listeners: []gatewayListener{{SectionName: "web", Port: 80}},
}},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
matches := matchingGatewayListenersForParentRef(test.gateways, test.routeNamespace, test.parentRefs)
assert.Equal(t, test.want, matches)
})
}
}
// Test_loadRoutes_multipleGatewaysParentRefs ensures that, when a Route
// references several Gateways (including one owned by another controller), Traefik
// only manages the parents it is responsible for: it writes a single RouteParentStatus
// for its own parentRef, writes nothing for a Route that targets only a foreign
// Gateway, and counts each attached Route only once in the listener AttachedRoutes.
func Test_loadRoutes_multipleGatewaysParentRefs(t *testing.T) {
testCases := []struct {
desc string
path string
entryPoints map[string]Entrypoint
gatewayName string
route1, route2 string
getParents func(t *testing.T, gwClient *gatefake.Clientset, name string) []gatev1.RouteParentStatus
}{
{
desc: "HTTPRoute",
path: "httproute/with_multiple_gateways_parent_refs.yml",
entryPoints: map[string]Entrypoint{"web": {Address: ":80"}},
gatewayName: "my-gateway",
route1: "http-app-1",
route2: "http-app-2",
getParents: func(t *testing.T, gwClient *gatefake.Clientset, name string) []gatev1.RouteParentStatus {
t.Helper()
route, err := gwClient.GatewayV1().HTTPRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
},
},
{
desc: "GRPCRoute",
path: "grpcroute/with_multiple_gateways_parent_refs.yml",
entryPoints: map[string]Entrypoint{"web": {Address: ":80"}},
gatewayName: "my-gateway",
route1: "grpc-app-1",
route2: "grpc-app-2",
getParents: func(t *testing.T, gwClient *gatefake.Clientset, name string) []gatev1.RouteParentStatus {
t.Helper()
route, err := gwClient.GatewayV1().GRPCRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
},
},
{
desc: "TCPRoute",
path: "tcproute/with_multiple_gateways_parent_refs.yml",
entryPoints: map[string]Entrypoint{"tcp": {Address: ":9000"}},
gatewayName: "my-tcp-gateway",
route1: "tcp-app-1",
route2: "tcp-app-2",
getParents: func(t *testing.T, gwClient *gatefake.Clientset, name string) []gatev1.RouteParentStatus {
t.Helper()
route, err := gwClient.GatewayV1().TCPRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
},
},
{
desc: "TLSRoute",
path: "tlsroute/with_multiple_gateways_parent_refs.yml",
entryPoints: map[string]Entrypoint{"tls": {Address: ":9000"}},
gatewayName: "my-tls-gateway",
route1: "tls-app-1",
route2: "tls-app-2",
getParents: func(t *testing.T, gwClient *gatefake.Clientset, name string) []gatev1.RouteParentStatus {
t.Helper()
route, err := gwClient.GatewayV1().TLSRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.path})
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubefake.NewClientset(k8sObjects...), gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
<-eventCh
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
_, statusReport, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
statusReport.Flush(t.Context(), client)
// route1 references our Gateway and a foreign one: only our parentRef must
// get a status entry, written under our controllerName and accepted.
parents1 := test.getParents(t, gwClient, test.route1)
require.Len(t, parents1, 1)
assert.Equal(t, gatev1.GatewayController(controllerName), parents1[0].ControllerName)
assert.Equal(t, gatev1.ObjectName(test.gatewayName), parents1[0].ParentRef.Name)
acceptedCondition := findCondition(parents1[0].Conditions, gatev1.RouteConditionAccepted)
require.NotNil(t, acceptedCondition)
assert.Equal(t, metav1.ConditionTrue, acceptedCondition.Status)
assert.Equal(t, string(gatev1.RouteReasonAccepted), acceptedCondition.Reason)
// route2 references only the foreign Gateway: Traefik must not write any status.
assert.Empty(t, test.getParents(t, gwClient, test.route2))
// The listener must count route1 once, ignoring the foreign parentRef and
// the foreign-only Route.
gateway, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), test.gatewayName, metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, gateway.Status.Listeners, 1)
assert.Equal(t, int32(1), gateway.Status.Listeners[0].AttachedRoutes)
})
}
}
func Test_loadHTTPAndGRPCRoutes_hostnameConflict(t *testing.T) {
testCases := []struct {
desc string
path string
acceptedRoute string
acceptedIsHTTP bool
conflictedRoute string
}{
{
desc: "HTTPRoute created first",
path: "mixed/with_httproute_priority_over_grpcroute.yml",
acceptedRoute: "http-app-conflict",
acceptedIsHTTP: true,
conflictedRoute: "grpc-app-conflict",
},
{
desc: "GRPCRoute created first",
path: "mixed/with_grpcroute_priority_over_httproute.yml",
acceptedRoute: "grpc-app-priority",
conflictedRoute: "http-app-priority",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.path})
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubefake.NewClientset(k8sObjects...), gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
<-eventCh
p := Provider{
EntryPoints: map[string]Entrypoint{"web": {Address: ":9080"}},
client: client,
}
_, statusReport, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
statusReport.Flush(t.Context(), client)
getParents := func(name string, isHTTP bool) []gatev1.RouteParentStatus {
t.Helper()
if isHTTP {
route, err := gwClient.GatewayV1().HTTPRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
}
route, err := gwClient.GatewayV1().GRPCRoutes("default").Get(t.Context(), name, metav1.GetOptions{})
require.NoError(t, err)
return route.Status.Parents
}
acceptedParents := getParents(test.acceptedRoute, test.acceptedIsHTTP)
require.Len(t, acceptedParents, 1)
acceptedCondition := findCondition(acceptedParents[0].Conditions, gatev1.RouteConditionAccepted)
require.NotNil(t, acceptedCondition)
assert.Equal(t, metav1.ConditionTrue, acceptedCondition.Status)
assert.Equal(t, string(gatev1.RouteReasonAccepted), acceptedCondition.Reason)
conflictedParents := getParents(test.conflictedRoute, !test.acceptedIsHTTP)
require.Len(t, conflictedParents, 1)
conflictedCondition := findCondition(conflictedParents[0].Conditions, gatev1.RouteConditionAccepted)
require.NotNil(t, conflictedCondition)
assert.Equal(t, metav1.ConditionFalse, conflictedCondition.Status)
assert.Equal(t, string(routeReasonHostnameConflict), conflictedCondition.Reason)
gateway, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), "my-gateway", metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, gateway.Status.Listeners, 1)
assert.Equal(t, int32(1), gateway.Status.Listeners[0].AttachedRoutes)
})
}
}
func Test_attachedRoutes_conflicts(t *testing.T) {
testCases := []struct {
desc string
attachedKind string
attachedHostnames []gatev1.Hostname
attachedListener listenerRef
kind string
hostnames []gatev1.Hostname
expected bool
}{
{
desc: "same kind",
attachedKind: kindHTTPRoute,
attachedHostnames: []gatev1.Hostname{"foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindHTTPRoute,
hostnames: []gatev1.Hostname{"foo.com"},
},
{
desc: "another listener",
attachedKind: kindHTTPRoute,
attachedHostnames: []gatev1.Hostname{"foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "websecure",
},
kind: kindGRPCRoute,
hostnames: []gatev1.Hostname{"foo.com"},
},
{
desc: "disjoint hostnames",
attachedKind: kindHTTPRoute,
attachedHostnames: []gatev1.Hostname{"foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindGRPCRoute,
hostnames: []gatev1.Hostname{"bar.com"},
},
{
desc: "same hostname",
attachedKind: kindHTTPRoute,
attachedHostnames: []gatev1.Hostname{"foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindGRPCRoute,
hostnames: []gatev1.Hostname{"bar.com", "foo.com"},
expected: true,
},
{
desc: "attached wildcard hostname",
attachedKind: kindGRPCRoute,
attachedHostnames: []gatev1.Hostname{"*.foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindHTTPRoute,
hostnames: []gatev1.Hostname{"bar.foo.com"},
expected: true,
},
{
desc: "wildcard hostname",
attachedKind: kindGRPCRoute,
attachedHostnames: []gatev1.Hostname{"bar.foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindHTTPRoute,
hostnames: []gatev1.Hostname{"*.foo.com"},
expected: true,
},
{
desc: "attached without hostname",
attachedKind: kindHTTPRoute,
attachedHostnames: nil,
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindGRPCRoute,
hostnames: []gatev1.Hostname{"foo.com"},
expected: true,
},
{
desc: "without hostname",
attachedKind: kindHTTPRoute,
attachedHostnames: []gatev1.Hostname{"foo.com"},
attachedListener: listenerRef{
GatewayNamespace: "default",
GatewayName: "my-gateway",
SectionName: "web",
},
kind: kindGRPCRoute,
hostnames: nil,
expected: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
attached := make(attachedRoutes)
attached.Record(test.attachedListener, test.attachedKind, test.attachedHostnames)
listener := listenerRef{GatewayNamespace: "default", GatewayName: "my-gateway", SectionName: "web"}
assert.Equal(t, test.expected, attached.Conflicts(listener, test.kind, test.hostnames))
})
}
}
// Test_mergeRouteParentStatuses ensures the merge keeps the parentStatuses Traefik
// does not own: those written by another controller, and those written by another
// Traefik instance (our controllerName but a parentRef not targeting a managed
// Gateway), while replacing our stale statuses with the freshly computed ones.
func Test_mergeRouteParentStatuses(t *testing.T) {
gateways := []gatewayWithListeners{{gateway: &gatev1.Gateway{Name: "my-gateway", Namespace: "default"}}}
otherController := gatev1.RouteParentStatus{
ControllerName: "example.com/other-controller",
ParentRef: gatev1.ParentReference{Namespace: new(gatev1.Namespace("default")), Name: "other-gateway"},
}
otherTraefik := gatev1.RouteParentStatus{
ControllerName: controllerName,
ParentRef: gatev1.ParentReference{Namespace: new(gatev1.Namespace("default")), Name: "other-traefik-gateway"},
}
staleManaged := gatev1.RouteParentStatus{
ControllerName: controllerName,
ParentRef: gatev1.ParentReference{Namespace: new(gatev1.Namespace("default")), Name: "my-gateway"},
}
// A parentRef without an explicit namespace defaults to the Route namespace,
// and must still be recognized as targeting a managed Gateway.
staleManagedDefaulted := gatev1.RouteParentStatus{
ControllerName: controllerName,
ParentRef: gatev1.ParentReference{Name: "my-gateway"},
}
desired := gatev1.RouteParentStatus{
ControllerName: controllerName,
ParentRef: gatev1.ParentReference{Namespace: new(gatev1.Namespace("default")), Name: "my-gateway"},
Conditions: []metav1.Condition{{
Type: string(gatev1.RouteConditionAccepted),
Status: metav1.ConditionTrue,
Reason: string(gatev1.RouteReasonAccepted),
}},
}
testCases := []struct {
desc string
current []gatev1.RouteParentStatus
want []gatev1.RouteParentStatus
}{
{
desc: "no current status",
want: []gatev1.RouteParentStatus{desired},
},
{
desc: "keeps status owned by another controller",
current: []gatev1.RouteParentStatus{otherController},
want: []gatev1.RouteParentStatus{desired, otherController},
},
{
desc: "keeps our status targeting a Gateway managed by another Traefik instance",
current: []gatev1.RouteParentStatus{otherTraefik},
want: []gatev1.RouteParentStatus{desired, otherTraefik},
},
{
desc: "drops our stale status targeting a managed Gateway",
current: []gatev1.RouteParentStatus{staleManaged},
want: []gatev1.RouteParentStatus{desired},
},
{
desc: "drops our stale status targeting a managed Gateway via namespace defaulting",
current: []gatev1.RouteParentStatus{staleManagedDefaulted},
want: []gatev1.RouteParentStatus{desired},
},
{
desc: "mixed: keeps unmanaged, drops managed",
current: []gatev1.RouteParentStatus{otherController, otherTraefik, staleManaged},
want: []gatev1.RouteParentStatus{desired, otherController, otherTraefik},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got := mergeRouteParentStatuses("default", test.current, []gatev1.RouteParentStatus{desired}, gateways)
assert.Equal(t, test.want, got)
})
}
}
func Test_managesGateway(t *testing.T) {
testCases := []struct {
desc string
gateways []NamespacedName
gw *gatev1.Gateway
want bool
}{
{
desc: "no scoping manages everything",
gw: &gatev1.Gateway{
Namespace: "default",
Name: "foo",
},
want: true,
},
{
desc: "matching ref",
gateways: []NamespacedName{{Namespace: "default", Name: "foo"}},
gw: &gatev1.Gateway{
Namespace: "default",
Name: "foo",
},
want: true,
},
{
desc: "different name",
gateways: []NamespacedName{{Namespace: "default", Name: "foo"}},
gw: &gatev1.Gateway{
Namespace: "default",
Name: "bar",
},
want: false,
},
{
desc: "different namespace",
gateways: []NamespacedName{{Namespace: "default", Name: "foo"}},
gw: &gatev1.Gateway{
Namespace: "other",
Name: "foo",
},
want: false,
},
{
desc: "list matches first",
gateways: []NamespacedName{
{Namespace: "default", Name: "foo"},
{Namespace: "other", Name: "bar"},
},
gw: &gatev1.Gateway{
Namespace: "default",
Name: "foo",
},
want: true,
},
{
desc: "list matches none",
gateways: []NamespacedName{
{Namespace: "default", Name: "foo"},
{Namespace: "other", Name: "bar"},
},
gw: &gatev1.Gateway{
Namespace: "default",
Name: "baz",
},
want: false,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
p := Provider{Gateways: test.gateways}
assert.Equal(t, test.want, p.managesGateway(test.gw))
})
}
}
func Test_matchListener(t *testing.T) {
testCases := []struct {
desc string
gwListener gatewayListener
parentRef gatev1.ParentReference
wantMatch bool
}{
{
desc: "Section do not match",
gwListener: gatewayListener{
SectionName: "foo",
Port: gatev1.PortNumber(80),
},
parentRef: gatev1.ParentReference{
SectionName: new(gatev1.SectionName("bar")),
Port: new(gatev1.PortNumber(80)),
},
},
{
desc: "Section matches",
gwListener: gatewayListener{
SectionName: "foo",
Port: gatev1.PortNumber(80),
},
parentRef: gatev1.ParentReference{
SectionName: new(gatev1.SectionName("foo")),
Port: new(gatev1.PortNumber(80)),
},
wantMatch: true,
},
{
desc: "Port do not match",
gwListener: gatewayListener{
SectionName: "foo",
Port: gatev1.PortNumber(90),
},
parentRef: gatev1.ParentReference{
SectionName: new(gatev1.SectionName("foo")),
Port: new(gatev1.PortNumber(80)),
},
},
{
desc: "Port matches",
gwListener: gatewayListener{
SectionName: "foo",
Port: gatev1.PortNumber(80),
},
parentRef: gatev1.ParentReference{
SectionName: new(gatev1.SectionName("foo")),
Port: new(gatev1.PortNumber(80)),
},
wantMatch: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gotMatch := matchListener(test.gwListener, test.parentRef)
assert.Equal(t, test.wantMatch, gotMatch)
})
}
}
func Test_allowRoute(t *testing.T) {
testCases := []struct {
desc string
gwListener gatewayListener
routeNamespace string
routeKind string
wantAllow bool
}{
{
desc: "Not allowed Kind",
gwListener: gatewayListener{
SectionName: "foo",
AllowedRouteKinds: []string{
"foo",
"bar",
},
},
routeKind: "baz",
wantAllow: false,
},
{
desc: "Allowed Kind",
gwListener: gatewayListener{
SectionName: "foo",
AllowedRouteKinds: []string{
"foo",
"bar",
},
AllowedNamespaces: []string{
corev1.NamespaceAll,
},
},
routeKind: "bar",
wantAllow: true,
},
{
desc: "Not allowed namespace",
gwListener: gatewayListener{
SectionName: "foo",
AllowedRouteKinds: []string{
"foo",
},
AllowedNamespaces: []string{
"foo",
"bar",
},
},
routeKind: "foo",
routeNamespace: "baz",
wantAllow: false,
},
{
desc: "Allowed namespace",
gwListener: gatewayListener{
SectionName: "foo",
AllowedRouteKinds: []string{
"foo",
},
AllowedNamespaces: []string{
"foo",
"bar",
},
},
routeKind: "foo",
routeNamespace: "foo",
wantAllow: true,
},
{
desc: "Allowed namespace",
gwListener: gatewayListener{
SectionName: "foo",
AllowedRouteKinds: []string{
"foo",
},
AllowedNamespaces: []string{
corev1.NamespaceAll,
"bar",
},
},
routeKind: "foo",
routeNamespace: "foo",
wantAllow: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gotAllow := allowRoute(test.gwListener, test.routeNamespace, test.routeKind)
assert.Equal(t, test.wantAllow, gotAllow)
})
}
}
func Test_findMatchingHostnames(t *testing.T) {
testCases := []struct {
desc string
listenerHostname *gatev1.Hostname
routeHostnames []gatev1.Hostname
want []gatev1.Hostname
wantOk bool
}{
{
desc: "Empty",
wantOk: true,
},
{
desc: "Only listener hostname",
listenerHostname: new(gatev1.Hostname("foo.com")),
want: []gatev1.Hostname{"foo.com"},
wantOk: true,
},
{
desc: "Only Route hostname",
routeHostnames: []gatev1.Hostname{"foo.com"},
want: []gatev1.Hostname{"foo.com"},
wantOk: true,
},
{
desc: "Matching hostname",
listenerHostname: new(gatev1.Hostname("foo.com")),
routeHostnames: []gatev1.Hostname{"foo.com"},
want: []gatev1.Hostname{"foo.com"},
wantOk: true,
},
{
desc: "Matching hostname with wildcard",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"*.foo.com"},
want: []gatev1.Hostname{"*.foo.com"},
wantOk: true,
},
{
desc: "Matching subdomain with listener wildcard",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"bar.foo.com"},
want: []gatev1.Hostname{"bar.foo.com"},
wantOk: true,
},
{
desc: "Matching subdomain wildcard with listener wildcard",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"*.bar.foo.com"},
want: []gatev1.Hostname{"*.bar.foo.com"},
wantOk: true,
},
{
desc: "Matching subsubdomain with listener wildcard",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"baz.bar.foo.com"},
want: []gatev1.Hostname{"baz.bar.foo.com"},
wantOk: true,
},
{
desc: "Matching subdomain with route hostname wildcard",
listenerHostname: new(gatev1.Hostname("bar.foo.com")),
routeHostnames: []gatev1.Hostname{"*.foo.com"},
want: []gatev1.Hostname{"bar.foo.com"},
wantOk: true,
},
{
desc: "Matching subsubdomain with route hostname wildcard",
listenerHostname: new(gatev1.Hostname("baz.bar.foo.com")),
routeHostnames: []gatev1.Hostname{"*.foo.com"},
want: []gatev1.Hostname{"baz.bar.foo.com"},
wantOk: true,
},
{
desc: "Non matching root domain with listener wildcard",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"foo.com"},
},
{
desc: "Non matching root domain with route hostname wildcard",
listenerHostname: new(gatev1.Hostname("foo.com")),
routeHostnames: []gatev1.Hostname{"*.foo.com"},
},
{
desc: "Multiple route hostnames with one matching route hostname",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"bar.com", "test.foo.com", "test.buz.com"},
want: []gatev1.Hostname{"test.foo.com"},
wantOk: true,
},
{
desc: "Multiple route hostnames with non matching route hostname",
listenerHostname: new(gatev1.Hostname("*.fuz.com")),
routeHostnames: []gatev1.Hostname{"bar.com", "test.foo.com", "test.buz.com"},
},
{
desc: "Multiple route hostnames with multiple matching route hostnames",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"toto.foo.com", "test.foo.com", "test.buz.com"},
want: []gatev1.Hostname{"test.foo.com", "toto.foo.com"},
wantOk: true,
},
{
desc: "Matching wildcard subsubdomain with listener wildcard subdomain",
listenerHostname: new(gatev1.Hostname("*.foo.com")),
routeHostnames: []gatev1.Hostname{"*.bar.foo.com"},
want: []gatev1.Hostname{"*.bar.foo.com"},
wantOk: true,
},
{
desc: "Duplicate intersections are returned once",
listenerHostname: new(gatev1.Hostname("bar.foo.com")),
routeHostnames: []gatev1.Hostname{"*.foo.com", "bar.foo.com"},
want: []gatev1.Hostname{"bar.foo.com"},
wantOk: true,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got, ok := findMatchingHostnames(test.listenerHostname, test.routeHostnames)
assert.Equal(t, test.wantOk, ok)
assert.Equal(t, test.want, got)
})
}
}
func Test_allowedRouteKinds(t *testing.T) {
testCases := []struct {
desc string
listener gatev1.Listener
supportedRouteKinds []gatev1.RouteGroupKind
wantKinds []gatev1.RouteGroupKind
wantErr bool
}{
{
desc: "Empty",
},
{
desc: "Empty AllowedRoutes",
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
},
{
desc: "AllowedRoutes with unsupported Group",
listener: gatev1.Listener{
AllowedRoutes: &gatev1.AllowedRoutes{
Kinds: []gatev1.RouteGroupKind{{
Kind: kindTLSRoute, Group: new(gatev1.Group("foo")),
}},
},
},
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantErr: true,
},
{
desc: "AllowedRoutes with nil Group",
listener: gatev1.Listener{
AllowedRoutes: &gatev1.AllowedRoutes{
Kinds: []gatev1.RouteGroupKind{{
Kind: kindTLSRoute, Group: nil,
}},
},
},
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantErr: true,
},
{
desc: "AllowedRoutes with unsupported Kind",
listener: gatev1.Listener{
AllowedRoutes: &gatev1.AllowedRoutes{
Kinds: []gatev1.RouteGroupKind{{
Kind: "foo", Group: new(gatev1.Group(gatev1.GroupName)),
}},
},
},
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantErr: true,
},
{
desc: "Supported AllowedRoutes",
listener: gatev1.Listener{
AllowedRoutes: &gatev1.AllowedRoutes{
Kinds: []gatev1.RouteGroupKind{{
Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName)),
}},
},
},
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
},
{
desc: "Supported AllowedRoutes with duplicates",
listener: gatev1.Listener{
AllowedRoutes: &gatev1.AllowedRoutes{
Kinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
{Kind: kindTCPRoute, Group: new(gatev1.Group(gatev1.GroupName))},
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
{Kind: kindTCPRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
},
},
supportedRouteKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
{Kind: kindTCPRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
wantKinds: []gatev1.RouteGroupKind{
{Kind: kindTLSRoute, Group: new(gatev1.Group(gatev1.GroupName))},
{Kind: kindTCPRoute, Group: new(gatev1.Group(gatev1.GroupName))},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got, conditions := allowedRouteKinds(0, test.listener, test.supportedRouteKinds)
if test.wantErr {
require.NotEmpty(t, conditions, "no conditions")
return
}
require.Empty(t, conditions)
assert.Equal(t, test.wantKinds, got)
})
}
}
func Test_makeListenerKey(t *testing.T) {
testCases := []struct {
desc string
listener gatev1.Listener
expectedKey string
}{
{
desc: "empty",
expectedKey: "||0",
},
{
desc: "listener with port, protocol and hostname",
listener: gatev1.Listener{
Port: 443,
Protocol: gatev1.HTTPSProtocolType,
Hostname: new(gatev1.Hostname("www.example.com")),
},
expectedKey: "HTTPS|www.example.com|443",
},
{
desc: "listener with port, protocol and nil hostname",
listener: gatev1.Listener{
Port: 443,
Protocol: gatev1.HTTPSProtocolType,
},
expectedKey: "HTTPS||443",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.expectedKey, makeListenerKey(test.listener))
})
}
}
func Test_referenceGrantMatchesFrom(t *testing.T) {
testCases := []struct {
desc string
referenceGrant gatev1.ReferenceGrant
group string
kind string
namespace string
expectedResult bool
}{
{
desc: "matches",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
From: []gatev1.ReferenceGrantFrom{
{
Group: "correct-group",
Kind: "correct-kind",
Namespace: "correct-namespace",
},
},
},
},
group: "correct-group",
kind: "correct-kind",
namespace: "correct-namespace",
expectedResult: true,
},
{
desc: "empty group matches core",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
From: []gatev1.ReferenceGrantFrom{
{
Group: "",
Kind: "correct-kind",
Namespace: "correct-namespace",
},
},
},
},
group: "core",
kind: "correct-kind",
namespace: "correct-namespace",
expectedResult: true,
},
{
desc: "wrong group",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
From: []gatev1.ReferenceGrantFrom{
{
Group: "wrong-group",
Kind: "correct-kind",
Namespace: "correct-namespace",
},
},
},
},
group: "correct-group",
kind: "correct-kind",
namespace: "correct-namespace",
expectedResult: false,
},
{
desc: "wrong kind",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
From: []gatev1.ReferenceGrantFrom{
{
Group: "correct-group",
Kind: "wrong-kind",
Namespace: "correct-namespace",
},
},
},
},
group: "correct-group",
kind: "correct-kind",
namespace: "correct-namespace",
expectedResult: false,
},
{
desc: "wrong namespace",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
From: []gatev1.ReferenceGrantFrom{
{
Group: "correct-group",
Kind: "correct-kind",
Namespace: "wrong-namespace",
},
},
},
},
group: "correct-group",
kind: "correct-kind",
namespace: "correct-namespace",
expectedResult: false,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.expectedResult, referenceGrantMatchesFrom(&test.referenceGrant, test.group, test.kind, test.namespace))
})
}
}
func Test_referenceGrantMatchesTo(t *testing.T) {
testCases := []struct {
desc string
referenceGrant gatev1.ReferenceGrant
group string
kind string
name string
expectedResult bool
}{
{
desc: "matches",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "correct-group",
Kind: "correct-kind",
Name: new(gatev1.ObjectName("correct-name")),
},
},
},
},
group: "correct-group",
kind: "correct-kind",
name: "correct-name",
expectedResult: true,
},
{
desc: "matches without name",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "correct-group",
Kind: "correct-kind",
Name: nil,
},
},
},
},
group: "correct-group",
kind: "correct-kind",
name: "some-name",
expectedResult: true,
},
{
desc: "empty group matches core",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "",
Kind: "correct-kind",
Name: new(gatev1.ObjectName("correct-name")),
},
},
},
},
group: "core",
kind: "correct-kind",
name: "correct-name",
expectedResult: true,
},
{
desc: "wrong group",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "wrong-group",
Kind: "correct-kind",
Name: new(gatev1.ObjectName("correct-name")),
},
},
},
},
group: "correct-group",
kind: "correct-kind",
name: "correct-namespace",
expectedResult: false,
},
{
desc: "wrong kind",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "correct-group",
Kind: "wrong-kind",
Name: new(gatev1.ObjectName("correct-name")),
},
},
},
},
group: "correct-group",
kind: "correct-kind",
name: "correct-name",
expectedResult: false,
},
{
desc: "wrong name",
referenceGrant: gatev1.ReferenceGrant{
Spec: gatev1.ReferenceGrantSpec{
To: []gatev1.ReferenceGrantTo{
{
Group: "correct-group",
Kind: "correct-kind",
Name: new(gatev1.ObjectName("wrong-name")),
},
},
},
},
group: "correct-group",
kind: "correct-kind",
name: "correct-name",
expectedResult: false,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.expectedResult, referenceGrantMatchesTo(&test.referenceGrant, test.group, test.kind, test.name))
})
}
}
func Test_gatewayAddresses(t *testing.T) {
testCases := []struct {
desc string
statusAddress *StatusAddress
paths []string
wantErr require.ErrorAssertionFunc
want []gatev1.GatewayStatusAddress
}{
{
desc: "nothing",
wantErr: require.NoError,
},
{
desc: "empty configuration",
statusAddress: &StatusAddress{},
wantErr: require.Error,
},
{
desc: "IP address",
statusAddress: &StatusAddress{
IP: "1.2.3.4",
},
wantErr: require.NoError,
want: []gatev1.GatewayStatusAddress{
{
Type: new(gatev1.IPAddressType),
Value: "1.2.3.4",
},
},
},
{
desc: "hostname address",
statusAddress: &StatusAddress{
Hostname: "foo.bar",
},
wantErr: require.NoError,
want: []gatev1.GatewayStatusAddress{
{
Type: new(gatev1.HostnameAddressType),
Value: "foo.bar",
},
},
},
{
desc: "service",
statusAddress: &StatusAddress{
Service: ServiceRef{
Name: "status-address",
Namespace: "default",
},
},
paths: []string{"services.yml"},
wantErr: require.NoError,
want: []gatev1.GatewayStatusAddress{
{
Type: new(gatev1.HostnameAddressType),
Value: "foo.bar",
},
{
Type: new(gatev1.IPAddressType),
Value: "1.2.3.4",
},
},
},
{
desc: "missing service",
statusAddress: &StatusAddress{
Service: ServiceRef{
Name: "status-address2",
Namespace: "default",
},
},
wantErr: require.Error,
},
{
desc: "service without load-balancer status",
statusAddress: &StatusAddress{
Service: ServiceRef{
Name: "whoamitcp-bar",
Namespace: "bar",
},
},
paths: []string{"services.yml"},
wantErr: require.NoError,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
k8sObjects, gwObjects := readResources(t, test.paths)
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
StatusAddress: test.statusAddress,
client: client,
}
got, err := p.gatewayAddresses()
test.wantErr(t, err)
assert.Equal(t, test.want, got)
})
}
}
func Test_makeGatewayStatus(t *testing.T) {
acceptedListener := func() gatewayListener {
return gatewayListener{Status: &gatev1.ListenerStatus{Name: "valid"}}
}
invalidListener := func() gatewayListener {
return gatewayListener{Status: &gatev1.ListenerStatus{
Name: "invalid",
Conditions: []metav1.Condition{{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(gatev1.ListenerReasonUnsupportedProtocol),
}},
}}
}
testCases := []struct {
desc string
infrastructure *gatev1.GatewayInfrastructure
listeners []gatewayListener
wantStatus metav1.ConditionStatus
wantReason gatev1.GatewayConditionReason
wantProgrammed metav1.ConditionStatus
}{
{
desc: "all listeners valid",
listeners: []gatewayListener{acceptedListener()},
wantStatus: metav1.ConditionTrue,
wantReason: gatev1.GatewayReasonAccepted,
wantProgrammed: metav1.ConditionTrue,
},
{
desc: "one listener valid among invalid ones",
listeners: []gatewayListener{acceptedListener(), invalidListener()},
wantStatus: metav1.ConditionTrue,
wantReason: gatev1.GatewayReasonListenersNotValid,
wantProgrammed: metav1.ConditionTrue,
},
{
desc: "no listener valid",
listeners: []gatewayListener{invalidListener()},
wantStatus: metav1.ConditionFalse,
wantReason: gatev1.GatewayReasonListenersNotValid,
wantProgrammed: metav1.ConditionFalse,
},
{
desc: "infrastructure parametersRef",
listeners: []gatewayListener{acceptedListener()},
infrastructure: &gatev1.GatewayInfrastructure{
ParametersRef: &gatev1.LocalParametersReference{
Group: "invalid.io",
Kind: "InvalidParameters",
Name: "invalid",
},
},
wantStatus: metav1.ConditionFalse,
wantReason: gatev1.GatewayReasonInvalidParameters,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := Provider{}
gateway := &gatev1.Gateway{Spec: gatev1.GatewaySpec{Infrastructure: test.infrastructure}}
status, _ := p.makeGatewayStatus(gateway, test.listeners, nil, slices.ContainsFunc(test.listeners, func(listener gatewayListener) bool {
return len(listener.Status.Conditions) == 0
}))
condition := meta.FindStatusCondition(status.Conditions, string(gatev1.GatewayConditionAccepted))
require.NotNil(t, condition)
assert.Equal(t, test.wantStatus, condition.Status)
assert.Equal(t, string(test.wantReason), condition.Reason)
assert.Len(t, status.Listeners, len(test.listeners))
programmed := meta.FindStatusCondition(status.Conditions, string(gatev1.GatewayConditionProgrammed))
if test.wantProgrammed == "" {
assert.Nil(t, programmed)
return
}
require.NotNil(t, programmed)
assert.Equal(t, test.wantProgrammed, programmed.Status)
})
}
}
func Test_resolveFrontendValidation(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"frontend_validation_ca_refs.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{client: client}
gateway := &gatev1.Gateway{
Name: "my-gateway",
Namespace: "default",
Generation: 1,
}
testCases := []struct {
desc string
validation *gatev1.FrontendTLSValidation
expected frontendValidation
}{
{
desc: "nil validation returns an empty result",
validation: nil,
expected: frontendValidation{},
},
{
desc: "a valid ConfigMap ref is resolved",
validation: &gatev1.FrontendTLSValidation{
Mode: gatev1.AllowValidOnly,
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap"},
},
},
expected: frontendValidation{
clientAuth: &tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequireAndVerifyClientCert,
},
},
},
{
desc: "AllowInsecureFallback mode maps to RequestClientCert",
validation: &gatev1.FrontendTLSValidation{
Mode: gatev1.AllowInsecureFallback,
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap"},
},
},
expected: frontendValidation{
clientAuth: &tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA1"},
ClientAuthType: tls.RequestClientCert,
},
},
},
{
desc: "unsupported kind is rejected and no valid CA certificate remains",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "Service", Name: "whoami"},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateKind),
Message: "Unsupported CACertificateRef group/kind: /Service",
},
},
},
{
desc: "unsupported group is rejected",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Group: "example.com", Kind: "ConfigMap", Name: "ca-configmap"},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateKind),
Message: "Unsupported CACertificateRef group/kind: example.com/ConfigMap",
},
},
},
{
desc: "cross-namespace ref without a ReferenceGrant is rejected",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap-not-granted", Namespace: new(gatev1.Namespace("other-ns"))},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonRefNotPermitted),
Message: "Cannot reference CACertificateRef: /ConfigMap: missing ReferenceGrant",
},
},
},
{
desc: "cross-namespace ref with a matching ReferenceGrant is resolved",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap-granted", Namespace: new(gatev1.Namespace("other-ns"))},
},
},
expected: frontendValidation{
clientAuth: &tls.ClientAuth{
CAFiles: []types.FileOrContent{"CA4"},
ClientAuthType: tls.RequireAndVerifyClientCert,
},
},
},
{
desc: "Secret ref is rejected as an unsupported kind",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "Secret", Name: "ca-secret"},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateKind),
Message: "Unsupported CACertificateRef group/kind: /Secret",
},
},
},
{
desc: "unresolvable ConfigMap is rejected",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "does-not-exist"},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateRef),
Message: `Cannot resolve configmap: default/does-not-exist: configmap "does-not-exist" not found`,
},
},
},
{
desc: "ConfigMap missing the ca.crt key is rejected",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap-empty"},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateRef),
Message: "Cannot find ca.crt: ConfigMap default/ca-configmap-empty",
},
},
},
{
desc: "multiple refs are rejected",
validation: &gatev1.FrontendTLSValidation{
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap"},
{Kind: "ConfigMap", Name: "ca-configmap-granted", Namespace: new(gatev1.Namespace("other-ns"))},
},
},
expected: frontendValidation{
errCondition: &metav1.Condition{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateRef),
Message: "Unsupported number of CACertificateRefs: 2, only one reference is supported",
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
got := p.resolveFrontendValidation(gateway, test.validation)
// LastTransitionTime is set with metav1.Now() and is not relevant to this test.
if got.errCondition != nil {
got.errCondition.LastTransitionTime = metav1.Time{}
}
assert.Equal(t, test.expected, got)
})
}
}
func Test_loadGatewayListeners(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"frontend_validation_ca_refs.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
<-eventCh
p := Provider{
EntryPoints: map[string]Entrypoint{"websecure": {Address: ":443"}},
client: client,
}
newGateway := func(caCertificateRefs []gatev1.ObjectReference) *gatev1.Gateway {
return &gatev1.Gateway{
Name: "my-gateway",
Namespace: "default",
Generation: 1,
Spec: gatev1.GatewaySpec{
TLS: &gatev1.GatewayTLSConfig{
Frontend: &gatev1.FrontendTLSConfig{
Default: gatev1.TLSConfig{
Validation: &gatev1.FrontendTLSValidation{
Mode: gatev1.AllowValidOnly,
CACertificateRefs: caCertificateRefs,
},
},
},
},
Listeners: []gatev1.Listener{
{
Name: "https",
Protocol: gatev1.HTTPSProtocolType,
Port: 443,
TLS: &gatev1.ListenerTLSConfig{
CertificateRefs: []gatev1.SecretObjectReference{
{Kind: new(gatev1.Kind("Secret")), Group: new(gatev1.Group("")), Name: "tls-secret"},
},
},
AllowedRoutes: &gatev1.AllowedRoutes{
Namespaces: &gatev1.RouteNamespaces{From: new(gatev1.NamespacesFromSame)},
},
},
},
},
}
}
testCases := []struct {
desc string
gateway *gatev1.Gateway
expectedAttached bool
expectedConditions []metav1.Condition
}{
{
desc: "fully resolved CACertificateRefs attaches the Listener with no error condition",
gateway: newGateway([]gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap"},
}),
expectedAttached: true,
expectedConditions: []metav1.Condition{},
},
{
desc: "multiple CACertificateRefs are rejected and the Listener is not attached",
gateway: newGateway([]gatev1.ObjectReference{
{Kind: "ConfigMap", Name: "ca-configmap"},
{Kind: "ConfigMap", Name: "ca-configmap-empty"},
}),
expectedAttached: false,
expectedConditions: []metav1.Condition{
{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateRef),
Message: "Unsupported number of CACertificateRefs: 2, only one reference is supported",
},
{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonNoValidCACertificate),
Message: "No valid CA certificate found in CACertificateRefs",
},
{
Type: string(gatev1.ListenerConditionProgrammed),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalid),
Message: "Invalid CA certificate configuration",
},
},
},
{
desc: "no valid CACertificateRefs reports NoValidCACertificate and the Listener is not attached",
gateway: newGateway([]gatev1.ObjectReference{
{Kind: "Service", Name: "whoami"},
}),
expectedAttached: false,
expectedConditions: []metav1.Condition{
{
Type: string(gatev1.ListenerConditionResolvedRefs),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalidCACertificateKind),
Message: "Unsupported CACertificateRef group/kind: /Service",
},
{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonNoValidCACertificate),
Message: "No valid CA certificate found in CACertificateRefs",
},
{
Type: string(gatev1.ListenerConditionProgrammed),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(gatev1.ListenerReasonInvalid),
Message: "Invalid CA certificate configuration",
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
conf := &dynamic.Configuration{TLS: &dynamic.TLSConfiguration{}}
listeners := p.loadGatewayListeners(t.Context(), test.gateway, nil, newListenerClaims(map[gatev1.PortNumber]*tls.ClientAuth{}), conf)
require.Len(t, listeners, 1)
assert.Equal(t, test.expectedAttached, listeners[0].Attached)
// LastTransitionTime is set with metav1.Now() and is not relevant to this test.
for i := range listeners[0].Status.Conditions {
listeners[0].Status.Conditions[i].LastTransitionTime = metav1.Time{}
}
assert.Equal(t, test.expectedConditions, listeners[0].Status.Conditions)
})
}
}
func Test_loadGatewayListeners_frontendValidationConflict(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"frontend_validation_ca_refs.yml"})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
<-eventCh
p := Provider{
EntryPoints: map[string]Entrypoint{"websecure": {Address: ":443"}},
client: client,
}
newValidation := func(caConfigMap string, mode gatev1.FrontendValidationModeType) *gatev1.FrontendTLSValidation {
return &gatev1.FrontendTLSValidation{
Mode: mode,
CACertificateRefs: []gatev1.ObjectReference{
{Kind: "ConfigMap", Name: gatev1.ObjectName(caConfigMap)},
},
}
}
newGateway := func(name string, hostname *gatev1.Hostname, validation *gatev1.FrontendTLSValidation) *gatev1.Gateway {
return &gatev1.Gateway{
Name: name,
Namespace: "default",
Generation: 1,
Spec: gatev1.GatewaySpec{
TLS: &gatev1.GatewayTLSConfig{
Frontend: &gatev1.FrontendTLSConfig{
Default: gatev1.TLSConfig{
Validation: validation,
},
},
},
Listeners: []gatev1.Listener{
{
Name: "https",
Protocol: gatev1.HTTPSProtocolType,
Port: 443,
Hostname: hostname,
TLS: &gatev1.ListenerTLSConfig{
CertificateRefs: []gatev1.SecretObjectReference{
{Kind: new(gatev1.Kind("Secret")), Group: new(gatev1.Group("")), Name: "tls-secret"},
},
},
AllowedRoutes: &gatev1.AllowedRoutes{
Namespaces: &gatev1.RouteNamespaces{From: new(gatev1.NamespacesFromSame)},
},
},
},
},
}
}
conflictedConditions := []metav1.Condition{
{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(listenerReasonFrontendValidationConflict),
Message: "An older Gateway already applies a different frontend validation on this port",
},
{
Type: string(gatev1.ListenerConditionProgrammed),
Status: metav1.ConditionFalse,
ObservedGeneration: 1,
Reason: string(listenerReasonFrontendValidationConflict),
Message: "An older Gateway already applies a different frontend validation on this port",
},
{
Type: string(gatev1.ListenerConditionConflicted),
Status: metav1.ConditionTrue,
ObservedGeneration: 1,
Reason: string(listenerReasonFrontendValidationConflict),
Message: "An older Gateway already applies a different frontend validation on this port",
},
}
testCases := []struct {
desc string
hostname *gatev1.Hostname
validation *gatev1.FrontendTLSValidation
expectedAttached bool
expectedConditions []metav1.Condition
}{
{
desc: "the same validation is shared",
validation: newValidation("ca-configmap", gatev1.AllowValidOnly),
expectedAttached: true,
expectedConditions: []metav1.Condition{},
},
{
desc: "the same validation is shared on another hostname",
hostname: new(gatev1.Hostname("foo.com")),
validation: newValidation("ca-configmap", gatev1.AllowValidOnly),
expectedAttached: true,
expectedConditions: []metav1.Condition{},
},
{
desc: "a different CA is conflicted",
validation: newValidation("ca-configmap-2", gatev1.AllowValidOnly),
expectedConditions: conflictedConditions,
},
{
desc: "a different CA on another hostname is conflicted",
hostname: new(gatev1.Hostname("foo.com")),
validation: newValidation("ca-configmap-2", gatev1.AllowValidOnly),
expectedConditions: conflictedConditions,
},
{
desc: "a different mode is conflicted",
validation: newValidation("ca-configmap", gatev1.AllowInsecureFallback),
expectedConditions: conflictedConditions,
},
{
desc: "no validation is conflicted",
validation: nil,
expectedConditions: conflictedConditions,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
frontendValidations := map[gatev1.PortNumber]*tls.ClientAuth{}
conf := &dynamic.Configuration{TLS: &dynamic.TLSConfiguration{}}
gateway := newGateway("my-gateway", nil, newValidation("ca-configmap", gatev1.AllowValidOnly))
listeners := p.loadGatewayListeners(t.Context(), gateway, nil, newListenerClaims(frontendValidations), conf)
require.Len(t, listeners, 1)
require.True(t, listeners[0].Attached)
gateway = newGateway("my-other-gateway", test.hostname, test.validation)
listeners = p.loadGatewayListeners(t.Context(), gateway, nil, newListenerClaims(frontendValidations), conf)
require.Len(t, listeners, 1)
assert.Equal(t, test.expectedAttached, listeners[0].Attached)
// LastTransitionTime is set with metav1.Now() and is not relevant to this test.
for i := range listeners[0].Status.Conditions {
listeners[0].Status.Conditions[i].LastTransitionTime = metav1.Time{}
}
assert.Equal(t, test.expectedConditions, listeners[0].Status.Conditions)
// The certificate of a conflicted listener does not reach the TLS store.
expectedCertificates := 1
if test.expectedAttached {
expectedCertificates = 2
}
assert.Len(t, conf.TLS.Certificates, expectedCertificates)
})
}
}
func Test_isCrossProviderNamespaceAllowed(t *testing.T) {
testCases := []struct {
desc string
allowList []string
namespace string
want bool
}{
{desc: "nil allowList allows any namespace", allowList: nil, namespace: "ns-a", want: true},
{desc: "empty allowList denies every namespace", allowList: []string{}, namespace: "ns-a", want: false},
{desc: "namespace in allowList is accepted", allowList: []string{"ns-a"}, namespace: "ns-a", want: true},
{desc: "namespace not in allowList is rejected", allowList: []string{"ns-b"}, namespace: "ns-a", want: false},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got := isCrossProviderNamespaceAllowed(test.allowList, test.namespace)
assert.Equal(t, test.want, got)
})
}
}
// Test_makeListenerRouterName checks that hostnames normalizing to the same label
// do not collide, which would collapse the SNI scope they are meant to isolate.
func Test_makeListenerRouterName(t *testing.T) {
uniq := uniqListener{epName: "web", protocol: gatev1.HTTPProtocolType}
first := makeListenerRouterName(uniq, "*.example.com")
second := makeListenerRouterName(uniq, "example.com")
assert.NotEqual(t, first, second)
}
// TestCrossProviderNamespaces_HTTPRoute verifies that the
// CrossProviderNamespaces option gates `@otherProvider` TraefikService
// backendRefs declared on a Gateway HTTPRoute. The check is anchored on the
// HTTPRoute's namespace; when the route is rejected, the whole router is
// dropped from the dynamic configuration.
func TestCrossProviderNamespaces_HTTPRoute(t *testing.T) {
testCases := []struct {
desc string
fixture string
crossProviderNamespaces []string
wantError bool
}{
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "httproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "httproute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: map[string]Entrypoint{"web": {Address: ":80"}},
CrossProviderNamespaces: test.crossProviderNamespaces,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
router, ok := conf.HTTP.Routers["httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-799bcbf0c2317c5d1c93"]
require.True(t, ok)
service, ok := conf.HTTP.Services[router.Service]
require.True(t, ok)
require.NotNil(t, service.Weighted)
require.Len(t, service.Weighted.Services, 2)
var hasError bool
for _, wrrService := range service.Weighted.Services {
// Whenever a service fails to be loaded, a placeholder service is added to the WRR to server a 500 status code.
if wrrService.Status != nil && *wrrService.Status == http.StatusInternalServerError {
hasError = true
break
}
}
assert.Equal(t, test.wantError, hasError)
})
}
}
// TestCrossProviderNamespaces_TCPRoute verifies that the option also gates
// cross-provider TraefikService backendRefs declared on a Gateway TCPRoute.
func TestCrossProviderNamespaces_TCPRoute(t *testing.T) {
testCases := []struct {
desc string
fixture string
crossProviderNamespaces []string
wantError bool
}{
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "tcproute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "tcproute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: map[string]Entrypoint{"tcp": {Address: ":9000"}},
CrossProviderNamespaces: test.crossProviderNamespaces,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
router, ok := conf.TCP.Routers["tcproute-default-tcp-app-1-gw-default-my-gateway-ep-tcp-0-5840f3c3c21dcf0402da"]
require.True(t, ok)
service, ok := conf.TCP.Services[router.Service]
require.True(t, ok)
require.NotNil(t, service.Weighted)
require.Len(t, service.Weighted.Services, 2)
var hasError bool
for _, wrrService := range service.Weighted.Services {
if strings.Contains(wrrService.Name, "@") {
continue
}
lbService, ok := conf.TCP.Services[wrrService.Name]
require.True(t, ok)
require.NotNil(t, lbService)
require.NotNil(t, lbService.LoadBalancer)
if len(lbService.LoadBalancer.Servers) == 0 {
hasError = true
}
}
assert.Equal(t, test.wantError, hasError)
})
}
}
// TestCrossProviderNamespaces_TLSRoute verifies that the option also gates
// cross-provider TraefikService backendRefs declared on a Gateway TLSRoute.
func TestCrossProviderNamespaces_TLSRoute(t *testing.T) {
testCases := []struct {
desc string
fixture string
crossProviderNamespaces []string
wantError bool
}{
{desc: "nil: cross-provider TraefikService backendRefs accepted (backward compatible)", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: nil, wantError: false},
{desc: "empty list: cross-provider TraefikService backendRefs are rejected, route dropped", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{}, wantError: true},
{desc: "namespace allowed: cross-provider TraefikService backendRefs accepted", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{"default"}, wantError: false},
{desc: "namespace not allowed: cross-provider TraefikService backendRefs rejected, route dropped", fixture: "tlsroute/simple_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
{desc: "namespace provided with cross-provider backendRef, route dropped", fixture: "tlsroute/invalid_cross_provider.yml", crossProviderNamespaces: []string{"other"}, wantError: true},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.fixture})
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 || len(gwObjects) > 0 {
// just wait for the first event
<-eventCh
}
p := Provider{
EntryPoints: map[string]Entrypoint{"tls": {Address: ":9000"}},
CrossProviderNamespaces: test.crossProviderNamespaces,
client: client,
}
conf, _, err := p.loadConfigurationFromGateways(t.Context())
assert.NoError(t, err)
router, ok := conf.TCP.Routers["tlsroute-default-tls-app-1-gw-default-my-gateway-ep-tls-0-efc0f8d903baa9fb6592"]
require.True(t, ok)
service, ok := conf.TCP.Services[router.Service]
require.True(t, ok)
require.NotNil(t, service.Weighted)
require.Len(t, service.Weighted.Services, 2)
var hasError bool
for _, wrrService := range service.Weighted.Services {
if strings.Contains(wrrService.Name, "@") {
continue
}
lbService, ok := conf.TCP.Services[wrrService.Name]
require.True(t, ok)
require.NotNil(t, lbService)
require.NotNil(t, lbService.LoadBalancer)
if len(lbService.LoadBalancer.Servers) == 0 {
hasError = true
}
}
assert.Equal(t, test.wantError, hasError)
})
}
}
func findCondition(conditions []metav1.Condition, conditionType gatev1.RouteConditionType) *metav1.Condition {
for i := range conditions {
if conditions[i].Type == string(conditionType) {
return &conditions[i]
}
}
return nil
}
// We cannot use the gateway-api fake.NewClientset due to Gateway being pluralized as "gatewaies" instead of "gateways".
func newGatewaySimpleClientSet(t *testing.T, objects ...runtime.Object) *gatefake.Clientset {
t.Helper()
client := gatefake.NewSimpleClientset(objects...)
for _, object := range objects {
gateway, ok := object.(*gatev1.Gateway)
if !ok {
continue
}
_, err := client.GatewayV1().Gateways(gateway.Namespace).Create(t.Context(), gateway, metav1.CreateOptions{})
require.NoError(t, err)
}
return client
}
func Test_loadConfigurationFromGateways_TLSCertificatesOrder(t *testing.T) {
k8sObjects, gwObjects := readResources(t, []string{"multiple_gateways_tls_certificates.yml"})
client := newClientImpl(kubefake.NewClientset(k8sObjects...), newGatewaySimpleClientSet(t, gwObjects...))
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
// just wait for the first event
<-eventCh
p := Provider{
EntryPoints: map[string]Entrypoint{"websecure": {Address: ":443"}},
client: client,
}
// Gateways are listed from a map, so build the configuration several times to
// make sure the certificates do not follow the iteration order.
conf, _, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
var commonNames []string
for _, cert := range conf.TLS.Certificates {
commonNames = append(commonNames, certificateCommonName(t, cert.Certificate.CertFile))
}
assert.Equal(t, []string{"bar.example.com", "foo.example.com", "default-b.example.com", "default-a.example.com"}, commonNames)
}
func Test_loadConfigurationFromGateways_BackendTLSPolicyAncestorNamespace(t *testing.T) {
testCases := []struct {
desc string
path string
entryPoints map[string]Entrypoint
sectionName gatev1.SectionName
}{
{
desc: "HTTPRoute",
path: "httproute/with_backend_tls_policy_cross_namespace.yml",
entryPoints: map[string]Entrypoint{"web": {Address: ":80"}},
sectionName: "http",
},
{
desc: "GRPCRoute",
path: "grpcroute/with_backend_tls_policy_cross_namespace.yml",
entryPoints: map[string]Entrypoint{"web": {Address: ":80"}},
sectionName: "web",
},
{
desc: "TLSRoute",
path: "tlsroute/with_backend_tls_policy_cross_namespace.yml",
entryPoints: map[string]Entrypoint{"tls": {Address: ":9001"}},
sectionName: "tls",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
k8sObjects, gwObjects := readResources(t, []string{"services.yml", test.path})
gwClient := newGatewaySimpleClientSet(t, gwObjects...)
client := newClientImpl(kubefake.NewClientset(k8sObjects...), gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
// just wait for the first event
<-eventCh
p := Provider{
EntryPoints: test.entryPoints,
client: client,
}
_, statusReport, err := p.loadConfigurationFromGateways(t.Context())
require.NoError(t, err)
statusReport.Flush(t.Context(), client)
policy, err := gwClient.GatewayV1().BackendTLSPolicies("bar").Get(t.Context(), "policy-1", metav1.GetOptions{})
require.NoError(t, err)
require.Len(t, policy.Status.Ancestors, 1)
assert.Equal(t, gatev1.ParentReference{
Group: new(gatev1.Group(groupGateway)),
Kind: new(gatev1.Kind(kindGateway)),
Namespace: new(gatev1.Namespace("default")),
Name: "my-gateway",
SectionName: new(test.sectionName),
}, policy.Status.Ancestors[0].AncestorRef)
})
}
}
func certificateCommonName(t *testing.T, certFile types.FileOrContent) string {
t.Helper()
block, _ := pem.Decode([]byte(certFile))
require.NotNil(t, block)
cert, err := x509.ParseCertificate(block.Bytes)
require.NoError(t, err)
return cert.Subject.CommonName
}
func readResources(t *testing.T, paths []string) ([]runtime.Object, []runtime.Object) {
t.Helper()
var k8sObjects []runtime.Object
var gwObjects []runtime.Object
for _, path := range paths {
yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path))
if err != nil {
panic(err)
}
objects := k8s.MustParseYaml(yamlContent)
for _, obj := range objects {
switch obj.GetObjectKind().GroupVersionKind().Group {
case "gateway.networking.k8s.io":
gwObjects = append(gwObjects, obj)
default:
k8sObjects = append(k8sObjects, obj)
}
}
}
return k8sObjects, gwObjects
}
func Test_listenerSetRefsGateway(t *testing.T) {
gw := &gatev1.Gateway{
Name: "my-gw",
Namespace: "default",
}
testCases := []struct {
desc string
ls *gatev1.ListenerSet
expected bool
}{
{
desc: "Matching reference",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "default",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Name: "my-gw",
},
},
},
expected: true,
},
{
desc: "Wrong name",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "default",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Name: "other-gw",
},
},
},
expected: false,
},
{
desc: "Wrong namespace",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "default",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Name: "my-gw",
Namespace: new(gatev1.Namespace("other")),
},
},
},
expected: false,
},
{
desc: "Cross-namespace matching",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "other",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Name: "my-gw",
Namespace: new(gatev1.Namespace("default")),
},
},
},
expected: true,
},
{
desc: "Wrong group",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "default",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Group: new(gatev1.Group("wrong.group")),
Name: "my-gw",
},
},
},
expected: false,
},
{
desc: "Wrong kind",
ls: &gatev1.ListenerSet{
Name: "my-ls",
Namespace: "default",
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{
Kind: new(gatev1.Kind("NotAGateway")),
Name: "my-gw",
},
},
},
expected: false,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
assert.Equal(t, test.expected, listenerSetRefsGateway(test.ls, gw))
})
}
}
func Test_isListenerSetAllowed(t *testing.T) {
testCases := []struct {
desc string
gw *gatev1.Gateway
ls *gatev1.ListenerSet
namespaces []*corev1.Namespace
expected bool
}{
{
desc: "Nil AllowedListeners",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "default",
},
expected: false,
},
{
desc: "Nil From",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "default",
},
expected: false,
},
{
desc: "FromNone",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromNone),
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "default",
},
expected: false,
},
{
desc: "FromSame - same namespace",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromSame),
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "default",
},
expected: true,
},
{
desc: "FromSame - different namespace",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromSame),
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "other",
},
expected: false,
},
{
desc: "FromAll",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromAll),
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls", Namespace: "any-namespace",
},
expected: true,
},
{
desc: "FromSelector - matching namespace labels",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromSelector),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"env": "prod"},
},
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls",
Namespace: "default",
},
namespaces: []*corev1.Namespace{
{
Name: "default",
Labels: map[string]string{"env": "prod"},
},
},
expected: true,
},
{
desc: "FromSelector - non-matching namespace labels",
gw: &gatev1.Gateway{
Name: "gw", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{
From: new(gatev1.NamespacesFromSelector),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{"env": "prod"},
},
},
},
},
},
ls: &gatev1.ListenerSet{
Name: "ls",
Namespace: "default",
},
namespaces: []*corev1.Namespace{
{
Name: "default",
Labels: map[string]string{"env": "staging"},
},
},
expected: false,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
var k8sObjects []runtime.Object
for _, ns := range test.namespaces {
k8sObjects = append(k8sObjects, ns)
}
kubeClient := kubefake.NewClientset(k8sObjects...)
gwClient := newGatewaySimpleClientSet(t)
client := newClientImpl(kubeClient, gwClient)
eventCh, err := client.WatchAll(nil, make(chan struct{}))
require.NoError(t, err)
if len(k8sObjects) > 0 {
<-eventCh
}
p := Provider{client: client}
assert.Equal(t, test.expected, p.isListenerSetAllowed(t.Context(), test.gw, test.ls))
})
}
}
func Test_makeListenerSetStatus(t *testing.T) {
testCases := []struct {
desc string
listeners []gatewayListener
parentAccepted bool
wantAccepted bool
wantAcceptedStatus metav1.ConditionStatus
wantAcceptedReason string
wantProgrammedStatus metav1.ConditionStatus
wantProgrammedReason string
wantListenerEntryCount int
// wantEntryName, when set, asserts the first listener entry's name and its entry-level Programmed condition,
// so the per-listener status is verified and not only the entry count.
wantEntryName gatev1.SectionName
wantEntryProgrammedStatus metav1.ConditionStatus
wantEntryProgrammedReason string
}{
{
desc: "All listeners valid, Gateway accepted",
listeners: []gatewayListener{
{
SectionName: "http",
ListenerSet: &gatev1.ListenerSet{Namespace: "default", Name: "my-ls"},
Status: &gatev1.ListenerStatus{
Name: "http",
SupportedKinds: []gatev1.RouteGroupKind{{Kind: "HTTPRoute", Group: new(gatev1.Group(gatev1.GroupName))}},
Conditions: []metav1.Condition{}, // No errors.
},
},
},
parentAccepted: true,
wantAccepted: true,
wantAcceptedStatus: metav1.ConditionTrue,
wantAcceptedReason: string(gatev1.ListenerSetReasonAccepted),
wantProgrammedStatus: metav1.ConditionTrue,
wantProgrammedReason: string(gatev1.ListenerSetReasonProgrammed),
wantListenerEntryCount: 1,
wantEntryName: "http",
wantEntryProgrammedStatus: metav1.ConditionTrue,
wantEntryProgrammedReason: string(gatev1.ListenerEntryReasonProgrammed),
},
{
// A parent Gateway is not accepted only when no listener serving it is valid, the ListenerSet ones included.
desc: "Gateway not accepted",
listeners: []gatewayListener{
{
SectionName: "https",
ListenerSet: &gatev1.ListenerSet{Namespace: "default", Name: "my-ls"},
Status: &gatev1.ListenerStatus{
Name: "https",
Conditions: []metav1.Condition{
{Type: string(gatev1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatev1.ListenerReasonInvalidCertificateRef)},
{Type: string(gatev1.ListenerConditionProgrammed), Status: metav1.ConditionFalse, Reason: string(gatev1.ListenerReasonInvalid)},
},
},
},
},
parentAccepted: false,
wantAcceptedStatus: metav1.ConditionFalse,
wantAcceptedReason: string(gatev1.ListenerSetReasonParentNotAccepted),
wantProgrammedStatus: metav1.ConditionFalse,
wantProgrammedReason: "ParentNotProgrammed",
wantListenerEntryCount: 1,
wantEntryName: "https",
wantEntryProgrammedStatus: metav1.ConditionFalse,
wantEntryProgrammedReason: string(gatev1.ListenerEntryReasonInvalid),
},
{
desc: "No valid listener",
listeners: []gatewayListener{
{
SectionName: "http",
ListenerSet: &gatev1.ListenerSet{Namespace: "default", Name: "my-ls"},
Status: &gatev1.ListenerStatus{
Name: "http",
Conditions: []metav1.Condition{
{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(gatev1.ListenerReasonPortUnavailable),
},
},
},
},
},
parentAccepted: true,
wantAcceptedStatus: metav1.ConditionFalse,
wantAcceptedReason: string(gatev1.ListenerSetReasonListenersNotValid),
wantProgrammedStatus: metav1.ConditionFalse,
wantProgrammedReason: string(gatev1.ListenerSetReasonListenersNotValid),
wantListenerEntryCount: 1,
},
{
desc: "At least one valid listener",
listeners: []gatewayListener{
{
SectionName: "http",
ListenerSet: &gatev1.ListenerSet{Namespace: "default", Name: "my-ls"},
Status: &gatev1.ListenerStatus{
Name: "http",
Conditions: []metav1.Condition{},
},
},
{
SectionName: "invalid",
ListenerSet: &gatev1.ListenerSet{Namespace: "default", Name: "my-ls"},
Status: &gatev1.ListenerStatus{
Name: "invalid",
Conditions: []metav1.Condition{
{
Type: string(gatev1.ListenerConditionAccepted),
Status: metav1.ConditionFalse,
Reason: string(gatev1.ListenerReasonPortUnavailable),
},
},
},
},
},
parentAccepted: true,
wantAccepted: true,
wantAcceptedStatus: metav1.ConditionTrue,
wantAcceptedReason: string(gatev1.ListenerSetReasonListenersNotValid),
wantProgrammedStatus: metav1.ConditionTrue,
wantProgrammedReason: string(gatev1.ListenerSetReasonProgrammed),
wantListenerEntryCount: 2,
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
status, accepted := makeListenerSetStatus(0, test.listeners, test.parentAccepted)
assert.Equal(t, test.wantAccepted, accepted)
assert.Len(t, status.Listeners, test.wantListenerEntryCount)
var acceptedCond, programmedCond *metav1.Condition
for _, c := range status.Conditions {
switch c.Type {
case string(gatev1.ListenerSetConditionAccepted):
acceptedCond = &c
case string(gatev1.ListenerSetConditionProgrammed):
programmedCond = &c
}
}
require.NotNil(t, acceptedCond)
assert.Equal(t, test.wantAcceptedStatus, acceptedCond.Status)
assert.Equal(t, test.wantAcceptedReason, acceptedCond.Reason)
require.NotNil(t, programmedCond)
assert.Equal(t, test.wantProgrammedStatus, programmedCond.Status)
assert.Equal(t, test.wantProgrammedReason, programmedCond.Reason)
if test.wantEntryName != "" {
require.NotEmpty(t, status.Listeners)
entry := status.Listeners[0]
assert.Equal(t, test.wantEntryName, entry.Name)
var entryProgrammed *metav1.Condition
for _, c := range entry.Conditions {
if c.Type == string(gatev1.ListenerEntryConditionProgrammed) {
entryProgrammed = &c
}
}
require.NotNil(t, entryProgrammed)
assert.Equal(t, test.wantEntryProgrammedStatus, entryProgrammed.Status)
assert.Equal(t, test.wantEntryProgrammedReason, entryProgrammed.Reason)
}
})
}
}
// Test_loadListenerSetListeners covers the listener-level outcomes of merging the ListenerSet listeners into the Gateway ones:
// the conflict conditions, the precedence of a ListenerSet over its later siblings,
// and the status of the ListenerSets the Gateway does not allow.
func Test_loadListenerSetListeners(t *testing.T) {
gateway := &gatev1.Gateway{
Name: "my-gateway", Namespace: "default",
Spec: gatev1.GatewaySpec{
AllowedListeners: &gatev1.AllowedListeners{
Namespaces: &gatev1.ListenerNamespaces{From: new(gatev1.NamespacesFromAll)},
},
},
}
listenerSet := func(name string, created time.Time, listeners ...gatev1.ListenerEntry) *gatev1.ListenerSet {
return &gatev1.ListenerSet{
Name: name,
Namespace: "default",
CreationTimestamp: metav1.NewTime(created),
Spec: gatev1.ListenerSetSpec{
ParentRef: gatev1.ParentGatewayReference{Name: "my-gateway"},
Listeners: listeners,
},
}
}
type wantListener struct {
name string
attached bool
reason string
}
testCases := []struct {
desc string
allowedListeners *gatev1.AllowedListeners
gatewayListener *gatev1.Listener
listenerSets []*gatev1.ListenerSet
wantListeners []wantListener
wantNotAllowed []string
}{
{
desc: "ListenerSet not allowed by the Gateway",
allowedListeners: &gatev1.AllowedListeners{Namespaces: &gatev1.ListenerNamespaces{From: new(gatev1.NamespacesFromNone)}},
listenerSets: []*gatev1.ListenerSet{
listenerSet("my-listenerset", time.Time{}, gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80}),
},
wantNotAllowed: []string{"my-listenerset"},
},
{
desc: "Hostname conflict with a Gateway listener",
gatewayListener: &gatev1.Listener{Name: "web", Protocol: gatev1.HTTPProtocolType, Port: 80},
listenerSets: []*gatev1.ListenerSet{
listenerSet("my-listenerset", time.Time{}, gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80}),
},
wantListeners: []wantListener{{name: "http", reason: string(gatev1.ListenerReasonHostnameConflict)}},
},
{
desc: "Protocol conflict with a Gateway listener",
gatewayListener: &gatev1.Listener{Name: "web", Protocol: gatev1.HTTPProtocolType, Port: 80},
listenerSets: []*gatev1.ListenerSet{
listenerSet("my-listenerset", time.Time{},
gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80, Hostname: new(gatev1.Hostname("foo.example.com"))},
gatev1.ListenerEntry{Name: "tcp", Protocol: gatev1.TCPProtocolType, Port: 80},
),
},
wantListeners: []wantListener{
{name: "http", attached: true},
{name: "tcp", reason: string(gatev1.ListenerReasonProtocolConflict)},
},
},
{
desc: "First ListenerSet wins the protocol conflict with a later sibling",
listenerSets: []*gatev1.ListenerSet{
listenerSet("ls-first", time.Time{}, gatev1.ListenerEntry{Name: "tcp", Protocol: gatev1.TCPProtocolType, Port: 80}),
listenerSet("ls-second", time.Time{}, gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80}),
},
wantListeners: []wantListener{
{name: "tcp", attached: true},
{name: "http", reason: string(gatev1.ListenerReasonProtocolConflict)},
},
},
{
desc: "First ListenerSet wins the conflict with a later sibling",
listenerSets: []*gatev1.ListenerSet{
listenerSet("ls-first", time.Time{}, gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80}),
listenerSet("ls-second", time.Time{}, gatev1.ListenerEntry{Name: "http", Protocol: gatev1.HTTPProtocolType, Port: 80}),
},
wantListeners: []wantListener{
{name: "http", attached: true},
{name: "http", reason: string(gatev1.ListenerReasonHostnameConflict)},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
p := Provider{EntryPoints: map[string]Entrypoint{"web": {Address: ":80"}}}
gateway := gateway.DeepCopy()
if test.allowedListeners != nil {
gateway.Spec.AllowedListeners = test.allowedListeners
}
claims := newListenerClaims(map[gatev1.PortNumber]*tls.ClientAuth{})
if test.gatewayListener != nil {
gatewayWithListener := gateway.DeepCopy()
gatewayWithListener.Spec.Listeners = []gatev1.Listener{*test.gatewayListener}
p.loadGatewayListeners(t.Context(), gatewayWithListener, nil, claims, &dynamic.Configuration{TLS: &dynamic.TLSConfiguration{}})
}
statusReport := newStatusReport()
listeners := p.loadListenerSetListeners(t.Context(), gateway, test.listenerSets, claims, &dynamic.Configuration{TLS: &dynamic.TLSConfiguration{}}, statusReport)
require.Len(t, listeners, len(test.wantListeners))
require.Len(t, statusReport.listenerSets, len(test.wantNotAllowed))
for _, name := range test.wantNotAllowed {
status, ok := statusReport.listenerSets[ktypes.NamespacedName{Namespace: "default", Name: name}]
require.True(t, ok)
require.Len(t, status.Conditions, 2)
for _, condition := range status.Conditions {
assert.Equal(t, string(gatev1.ListenerSetReasonNotAllowed), condition.Reason)
}
}
for i, want := range test.wantListeners {
assert.Equal(t, want.name, listeners[i].SectionName)
assert.Equal(t, want.attached, listeners[i].Attached)
if want.reason == "" {
assert.Empty(t, listeners[i].Status.Conditions)
continue
}
require.NotEmpty(t, listeners[i].Status.Conditions)
for _, condition := range listeners[i].Status.Conditions {
assert.Equal(t, want.reason, condition.Reason)
}
}
})
}
}