mirror of
https://github.com/traefik/traefik.git
synced 2025-10-13 08:31:15 +02:00
479 lines
15 KiB
Go
479 lines
15 KiB
Go
package knative
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/traefik/paerser/types"
|
|
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
|
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
kubefake "k8s.io/client-go/kubernetes/fake"
|
|
kscheme "k8s.io/client-go/kubernetes/scheme"
|
|
"k8s.io/utils/ptr"
|
|
knativenetworkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1"
|
|
knfake "knative.dev/networking/pkg/client/clientset/versioned/fake"
|
|
)
|
|
|
|
func init() {
|
|
// required by k8s.MustParseYaml
|
|
if err := knativenetworkingv1alpha1.AddToScheme(kscheme.Scheme); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func Test_loadConfiguration(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
paths []string
|
|
want *dynamic.Configuration
|
|
wantLen int
|
|
}{
|
|
{
|
|
desc: "Wrong ingress class",
|
|
paths: []string{"wrong_ingress_class.yaml"},
|
|
wantLen: 0,
|
|
want: &dynamic.Configuration{
|
|
HTTP: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{},
|
|
Services: map[string]*dynamic.Service{},
|
|
Middlewares: map[string]*dynamic.Middleware{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "Cluster Local",
|
|
paths: []string{"cluster_local.yaml", "services.yaml"},
|
|
wantLen: 1,
|
|
want: &dynamic.Configuration{
|
|
HTTP: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{
|
|
"default-helloworld-go-rule-0-path-0": {
|
|
EntryPoints: []string{"priv-http", "priv-https"},
|
|
Service: "default-helloworld-go-rule-0-path-0-wrr",
|
|
Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))",
|
|
Middlewares: []string{},
|
|
},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"default-helloworld-go-rule-0-path-0-split-0": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.38.208:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-split-1": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.44.18:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-wrr": {
|
|
Weighted: &dynamic.WeightedRoundRobin{
|
|
Services: []dynamic.WRRService{
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-0",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00001",
|
|
},
|
|
},
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-1",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00002",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "External IP",
|
|
paths: []string{"external_ip.yaml", "services.yaml"},
|
|
wantLen: 1,
|
|
want: &dynamic.Configuration{
|
|
HTTP: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{
|
|
"default-helloworld-go-rule-0-path-0": {
|
|
EntryPoints: []string{"http", "https"},
|
|
Service: "default-helloworld-go-rule-0-path-0-wrr",
|
|
Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))",
|
|
Middlewares: []string{},
|
|
},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"default-helloworld-go-rule-0-path-0-split-0": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.38.208:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-split-1": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.44.18:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-wrr": {
|
|
Weighted: &dynamic.WeightedRoundRobin{
|
|
Services: []dynamic.WRRService{
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-0",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00001",
|
|
},
|
|
},
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-1",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00002",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "TLS",
|
|
paths: []string{"tls.yaml", "services.yaml"},
|
|
wantLen: 1,
|
|
want: &dynamic.Configuration{
|
|
HTTP: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{
|
|
"default-helloworld-go-rule-0-path-0": {
|
|
EntryPoints: []string{"http", "https"},
|
|
Service: "default-helloworld-go-rule-0-path-0-wrr",
|
|
Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))",
|
|
Middlewares: []string{},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-tls": {
|
|
EntryPoints: []string{"http", "https"},
|
|
Service: "default-helloworld-go-rule-0-path-0-wrr",
|
|
Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))",
|
|
Middlewares: []string{},
|
|
TLS: &dynamic.RouterTLSConfig{},
|
|
},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"default-helloworld-go-rule-0-path-0-split-0": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.38.208:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-split-1": {
|
|
LoadBalancer: &dynamic.ServersLoadBalancer{
|
|
Strategy: "wrr",
|
|
PassHostHeader: ptr.To(true),
|
|
ResponseForwarding: &dynamic.ResponseForwarding{
|
|
FlushInterval: types.Duration(100 * time.Millisecond),
|
|
},
|
|
Servers: []dynamic.Server{
|
|
{
|
|
URL: "http://10.43.44.18:80",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"default-helloworld-go-rule-0-path-0-wrr": {
|
|
Weighted: &dynamic.WeightedRoundRobin{
|
|
Services: []dynamic.WRRService{
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-0",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00001",
|
|
},
|
|
},
|
|
{
|
|
Name: "default-helloworld-go-rule-0-path-0-split-1",
|
|
Weight: ptr.To(50),
|
|
Headers: map[string]string{
|
|
"Knative-Serving-Namespace": "default",
|
|
"Knative-Serving-Revision": "helloworld-go-00002",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
k8sObjects, knObjects := readResources(t, testCase.paths)
|
|
|
|
k8sClient := kubefake.NewClientset(k8sObjects...)
|
|
knClient := knfake.NewSimpleClientset(knObjects...)
|
|
|
|
client := newClientImpl(knClient, k8sClient)
|
|
|
|
eventCh, err := client.WatchAll(nil, make(chan struct{}))
|
|
require.NoError(t, err)
|
|
|
|
if len(k8sObjects) > 0 || len(knObjects) > 0 {
|
|
// just wait for the first event
|
|
<-eventCh
|
|
}
|
|
|
|
p := Provider{
|
|
PublicEntrypoints: []string{"http", "https"},
|
|
PrivateEntrypoints: []string{"priv-http", "priv-https"},
|
|
client: client,
|
|
}
|
|
|
|
got, gotIngresses := p.loadConfiguration(t.Context())
|
|
assert.Len(t, gotIngresses, testCase.wantLen)
|
|
assert.Equal(t, testCase.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_buildRule(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
hosts []string
|
|
headers map[string]knativenetworkingv1alpha1.HeaderMatch
|
|
path string
|
|
want string
|
|
}{
|
|
{
|
|
desc: "single host, no headers, no path",
|
|
hosts: []string{"example.com"},
|
|
want: "(Host(`example.com`))",
|
|
},
|
|
{
|
|
desc: "multiple hosts, no headers, no path",
|
|
hosts: []string{"example.com", "foo.com"},
|
|
want: "(Host(`example.com`) || Host(`foo.com`))",
|
|
},
|
|
{
|
|
desc: "single host, single header, no path",
|
|
hosts: []string{"example.com"},
|
|
headers: map[string]knativenetworkingv1alpha1.HeaderMatch{
|
|
"X-Header": {Exact: "value"},
|
|
},
|
|
want: "(Host(`example.com`)) && (Header(`X-Header`,`value`))",
|
|
},
|
|
{
|
|
desc: "single host, multiple headers, no path",
|
|
hosts: []string{"example.com"},
|
|
headers: map[string]knativenetworkingv1alpha1.HeaderMatch{
|
|
"X-Header": {Exact: "value"},
|
|
"X-Header2": {Exact: "value2"},
|
|
},
|
|
want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`))",
|
|
},
|
|
{
|
|
desc: "single host, multiple headers, with path",
|
|
hosts: []string{"example.com"},
|
|
headers: map[string]knativenetworkingv1alpha1.HeaderMatch{
|
|
"X-Header": {Exact: "value"},
|
|
"X-Header2": {Exact: "value2"},
|
|
},
|
|
path: "/foo",
|
|
want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`)) && PathPrefix(`/foo`)",
|
|
},
|
|
{
|
|
desc: "single host, no headers, with path",
|
|
hosts: []string{"example.com"},
|
|
path: "/foo",
|
|
want: "(Host(`example.com`)) && PathPrefix(`/foo`)",
|
|
},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
t.Run(test.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := buildRule(test.hosts, test.headers, test.path)
|
|
assert.Equal(t, test.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_mergeHTTPConfigs(t *testing.T) {
|
|
testCases := []struct {
|
|
desc string
|
|
configs []*dynamic.HTTPConfiguration
|
|
want *dynamic.HTTPConfiguration
|
|
}{
|
|
{
|
|
desc: "one empty configuration",
|
|
configs: []*dynamic.HTTPConfiguration{
|
|
{
|
|
Routers: map[string]*dynamic.Router{
|
|
"router1": {Rule: "Host(`example.com`)"},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{
|
|
"middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
},
|
|
},
|
|
{
|
|
Routers: map[string]*dynamic.Router{},
|
|
Middlewares: map[string]*dynamic.Middleware{},
|
|
Services: map[string]*dynamic.Service{},
|
|
},
|
|
},
|
|
want: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{
|
|
"router1": {Rule: "Host(`example.com`)"},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{
|
|
"middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
desc: "merging two non-empty configurations",
|
|
configs: []*dynamic.HTTPConfiguration{
|
|
{
|
|
Routers: map[string]*dynamic.Router{
|
|
"router1": {Rule: "Host(`example.com`)"},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{
|
|
"middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
},
|
|
},
|
|
{
|
|
Routers: map[string]*dynamic.Router{
|
|
"router2": {Rule: "PathPrefix(`/test`)"},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{
|
|
"middleware2": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"service2": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
},
|
|
},
|
|
},
|
|
want: &dynamic.HTTPConfiguration{
|
|
Routers: map[string]*dynamic.Router{
|
|
"router1": {Rule: "Host(`example.com`)"},
|
|
"router2": {Rule: "PathPrefix(`/test`)"},
|
|
},
|
|
Middlewares: map[string]*dynamic.Middleware{
|
|
"middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
"middleware2": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}},
|
|
},
|
|
Services: map[string]*dynamic.Service{
|
|
"service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
"service2": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range testCases {
|
|
t.Run(test.desc, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := mergeHTTPConfigs(test.configs...)
|
|
assert.Equal(t, test.want, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func readResources(t *testing.T, paths []string) ([]runtime.Object, []runtime.Object) {
|
|
t.Helper()
|
|
|
|
var (
|
|
k8sObjects []runtime.Object
|
|
knObjects []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 "networking.internal.knative.dev":
|
|
knObjects = append(knObjects, obj)
|
|
default:
|
|
k8sObjects = append(k8sObjects, obj)
|
|
}
|
|
}
|
|
}
|
|
|
|
return k8sObjects, knObjects
|
|
}
|