1
0
Fork 0

Multi-layer routing

Co-authored-by: Romain <rtribotte@users.noreply.github.com>
This commit is contained in:
Simon Delicata 2025-10-22 11:58:05 +02:00 committed by GitHub
parent 8392503df7
commit d6598f370c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 2834 additions and 37 deletions

View file

@ -0,0 +1,28 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-cross
namespace: ns-a
spec:
entryPoints:
- web
routes:
- match: Host(`cross.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-cross-allowed
namespace: ns-b
spec:
parentRefs:
- name: parent-cross
namespace: ns-a
routes:
- match: Path(`/cross`)
kind: Rule
services:
- name: cross-service
port: 9000

View file

@ -0,0 +1,28 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-cross
namespace: ns-a
spec:
entryPoints:
- web
routes:
- match: Host(`cross.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-cross-denied
namespace: ns-b
spec:
parentRefs:
- name: parent-cross
namespace: ns-a
routes:
- match: Path(`/denied`)
kind: Rule
services:
- name: cross-service
port: 9000

View file

@ -0,0 +1,27 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-default
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`default.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-same
namespace: default
spec:
parentRefs:
- name: parent-default
routes:
- match: Path(`/same`)
kind: Rule
services:
- name: same-service
port: 9000

View file

@ -0,0 +1,16 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-missing-parent
namespace: default
spec:
parentRefs:
- name: non-existent-parent
namespace: default
routes:
- match: Path(`/missing`)
kind: Rule
services:
- name: child-service
port: 9000

View file

@ -0,0 +1,42 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-a
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`a.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-b
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`b.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-multi-parents
namespace: default
spec:
parentRefs:
- name: parent-a
namespace: default
- name: parent-b
namespace: default
routes:
- match: Path(`/shared`)
kind: Rule
services:
- name: shared-service
port: 9000

View file

@ -0,0 +1,139 @@
apiVersion: v1
kind: Service
metadata:
name: child-service
namespace: default
spec:
ports:
- name: web
port: 9000
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: child-service-abc
namespace: default
labels:
kubernetes.io/service-name: child-service
addressType: IPv4
ports:
- name: web
port: 9000
endpoints:
- addresses:
- 10.10.2.1
- 10.10.2.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: users-service
namespace: default
spec:
ports:
- name: web
port: 9000
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: users-service-abc
namespace: default
labels:
kubernetes.io/service-name: users-service
addressType: IPv4
ports:
- name: web
port: 9000
endpoints:
- addresses:
- 10.10.5.1
- 10.10.5.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: shared-service
namespace: default
spec:
ports:
- name: web
port: 9000
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: shared-service-abc
namespace: default
labels:
kubernetes.io/service-name: shared-service
addressType: IPv4
ports:
- name: web
port: 9000
endpoints:
- addresses:
- 10.10.8.1
- 10.10.8.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: cross-service
namespace: ns-b
spec:
ports:
- name: web
port: 9000
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: cross-service-abc
namespace: ns-b
labels:
kubernetes.io/service-name: cross-service
addressType: IPv4
ports:
- name: web
port: 9000
endpoints:
- addresses:
- 10.10.11.1
- 10.10.11.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: same-service
namespace: default
spec:
ports:
- name: web
port: 9000
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: same-service-abc
namespace: default
labels:
kubernetes.io/service-name: same-service
addressType: IPv4
ports:
- name: web
port: 9000
endpoints:
- addresses:
- 10.10.14.1
- 10.10.14.2
conditions:
ready: true

View file

@ -0,0 +1,30 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-multi
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`api.example.com`) && PathPrefix(`/v1`)
kind: Rule
- match: Host(`api.example.com`) && PathPrefix(`/v2`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-multi-routes
namespace: default
spec:
parentRefs:
- name: parent-multi
namespace: default
routes:
- match: Path(`/users`)
kind: Rule
services:
- name: users-service
port: 9000

View file

@ -0,0 +1,28 @@
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: parent-single
namespace: default
spec:
entryPoints:
- web
routes:
- match: Host(`parent.example.com`)
kind: Rule
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: child-single
namespace: default
spec:
parentRefs:
- name: parent-single
namespace: default
routes:
- match: Path(`/api`)
kind: Rule
services:
- name: child-service
port: 9000

View file

@ -1379,15 +1379,18 @@ func buildCertificates(client Client, tlsStore, namespace string, certificates [
return nil
}
func makeServiceKey(rule, ingressName string) (string, error) {
func makeServiceKey(rule, ingressName string) string {
h := sha256.New()
// As explained in https://pkg.go.dev/hash#Hash,
// Write never returns an error.
if _, err := h.Write([]byte(rule)); err != nil {
return "", err
return ""
}
key := fmt.Sprintf("%s-%.10x", ingressName, h.Sum(nil))
return key, nil
return key
}
func makeID(namespace, name string) string {

View file

@ -61,6 +61,12 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
disableClusterScopeResources: p.DisableClusterScopeResources,
}
parentRouterNames, err := resolveParentRouterNames(client, ingressRoute, p.AllowCrossNamespace)
if err != nil {
logger.Error().Err(err).Msg("Error resolving parent routers")
continue
}
for _, route := range ingressRoute.Spec.Routes {
if len(route.Kind) > 0 && route.Kind != "Rule" {
logger.Error().Msgf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
@ -72,11 +78,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
continue
}
serviceKey, err := makeServiceKey(route.Match, ingressName)
if err != nil {
logger.Error().Err(err).Send()
continue
}
serviceKey := makeServiceKey(route.Match, ingressName)
mds, err := p.makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares)
if err != nil {
@ -87,7 +89,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
normalized := provider.Normalize(makeID(ingressRoute.Namespace, serviceKey))
serviceName := normalized
if len(route.Services) > 1 {
switch {
case len(route.Services) > 1:
spec := traefikv1alpha1.TraefikServiceSpec{
Weighted: &traefikv1alpha1.WeightedRoundRobin{
Services: route.Services,
@ -99,7 +102,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
logger.Error().Err(errBuild).Send()
continue
}
} else if len(route.Services) == 1 {
case len(route.Services) == 1:
fullName, serversLB, err := cb.nameAndService(ctx, ingressRoute.Namespace, route.Services[0].LoadBalancerSpec)
if err != nil {
logger.Error().Err(err).Send()
@ -111,6 +114,9 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
} else {
serviceName = fullName
}
default:
// Routes without services leave serviceName empty.
serviceName = ""
}
r := &dynamic.Router{
@ -121,6 +127,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
Rule: route.Match,
Service: serviceName,
Observability: route.Observability,
ParentRefs: parentRouterNames,
}
if ingressRoute.Spec.TLS != nil {
@ -202,6 +209,50 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
return mds, nil
}
// resolveParentRouterNames resolves parent IngressRoute references to router names.
// It returns the list of parent router names and an error if one occurred during processing.
func resolveParentRouterNames(client Client, ingressRoute *traefikv1alpha1.IngressRoute, allowCrossNamespace bool) ([]string, error) {
// If no parent refs, return empty list (not an error).
if len(ingressRoute.Spec.ParentRefs) == 0 {
return nil, nil
}
var parentRouterNames []string
for _, parentRef := range ingressRoute.Spec.ParentRefs {
// Determine parent namespace (default to child namespace if not specified).
parentNamespace := parentRef.Namespace
if parentNamespace == "" {
parentNamespace = ingressRoute.Namespace
}
// Validate cross-namespace access.
if !isNamespaceAllowed(allowCrossNamespace, ingressRoute.Namespace, parentNamespace) {
return nil, fmt.Errorf("cross-namespace reference to parent IngressRoute %s/%s not allowed", parentNamespace, parentRef.Name)
}
var parentIngressRoute *traefikv1alpha1.IngressRoute
for _, ir := range client.GetIngressRoutes() {
if ir.Name == parentRef.Name && ir.Namespace == parentNamespace {
parentIngressRoute = ir
break
}
}
if parentIngressRoute == nil {
return nil, fmt.Errorf("parent IngressRoute %s/%s does not exist", parentNamespace, parentRef.Name)
}
// Compute router names for all routes in parent IngressRoute.
for _, route := range parentIngressRoute.Spec.Routes {
serviceKey := makeServiceKey(route.Match, parentIngressRoute.Name)
routerName := provider.Normalize(makeID(parentIngressRoute.Namespace, serviceKey))
parentRouterNames = append(parentRouterNames, routerName)
}
}
return parentRouterNames, nil
}
type configBuilder struct {
client Client
allowCrossNamespace bool

View file

@ -50,11 +50,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
continue
}
key, err := makeServiceKey(route.Match, ingressName)
if err != nil {
logger.Error().Err(err).Send()
continue
}
key := makeServiceKey(route.Match, ingressName)
mds, err := p.makeMiddlewareTCPKeys(ctx, ingressRouteTCP.Namespace, route.Middlewares)
if err != nil {

View file

@ -5351,6 +5351,322 @@ func TestLoadIngressRoutes(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with single parent (single route)",
paths: []string{"parent_refs_services.yml", "parent_refs_single_parent_single_route.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{
"default-parent-single-3c07cfffe8e5f876a01e": {
EntryPoints: []string{"web"},
Rule: "Host(`parent.example.com`)",
},
"default-child-single-2bba0a3de1b50b70a519": {
Service: "default-child-single-2bba0a3de1b50b70a519",
Rule: "Path(`/api`)",
ParentRefs: []string{"default-parent-single-3c07cfffe8e5f876a01e"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-child-single-2bba0a3de1b50b70a519": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.2.1:9000",
},
{
URL: "http://10.10.2.2:9000",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with single parent (multiple routes) - all parent routers in ParentRefs",
paths: []string{"parent_refs_services.yml", "parent_refs_single_parent_multiple_routes.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{
"default-parent-multi-4aac0d541c2b669a2d5d": {
EntryPoints: []string{"web"},
Rule: "Host(`api.example.com`) && PathPrefix(`/v1`)",
},
"default-parent-multi-0af1ca0a94f5b87a125e": {
EntryPoints: []string{"web"},
Rule: "Host(`api.example.com`) && PathPrefix(`/v2`)",
},
"default-child-multi-routes-b0479051e6a353d66211": {
Service: "default-child-multi-routes-b0479051e6a353d66211",
Rule: "Path(`/users`)",
ParentRefs: []string{"default-parent-multi-4aac0d541c2b669a2d5d", "default-parent-multi-0af1ca0a94f5b87a125e"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-child-multi-routes-b0479051e6a353d66211": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.5.1:9000",
},
{
URL: "http://10.10.5.2:9000",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with multiple parents",
paths: []string{"parent_refs_services.yml", "parent_refs_multiple_parents.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{
"default-parent-a-629990b524bf9a1a8d27": {
EntryPoints: []string{"web"},
Rule: "Host(`a.example.com`)",
},
"default-parent-b-add617f9b95cff009054": {
EntryPoints: []string{"web"},
Rule: "Host(`b.example.com`)",
},
"default-child-multi-parents-8013b5025acddd1761d1": {
Service: "default-child-multi-parents-8013b5025acddd1761d1",
Rule: "Path(`/shared`)",
ParentRefs: []string{"default-parent-a-629990b524bf9a1a8d27", "default-parent-b-add617f9b95cff009054"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-child-multi-parents-8013b5025acddd1761d1": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.8.1:9000",
},
{
URL: "http://10.10.8.2:9000",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with missing parent - routers skipped",
paths: []string{"parent_refs_services.yml", "parent_refs_missing_parent.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{},
},
},
{
desc: "IngressRoute with cross-namespace parent allowed",
allowCrossNamespace: true,
paths: []string{"parent_refs_services.yml", "parent_refs_cross_namespace_allowed.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{
"ns-a-parent-cross-74575ab54671a3ede28c": {
EntryPoints: []string{"web"},
Rule: "Host(`cross.example.com`)",
},
"ns-b-child-cross-allowed-0bad04de665623bf2362": {
Service: "ns-b-child-cross-allowed-0bad04de665623bf2362",
Rule: "Path(`/cross`)",
ParentRefs: []string{"ns-a-parent-cross-74575ab54671a3ede28c"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"ns-b-child-cross-allowed-0bad04de665623bf2362": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.11.1:9000",
},
{
URL: "http://10.10.11.2:9000",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with cross-namespace parent denied",
allowCrossNamespace: false,
paths: []string{"parent_refs_services.yml", "parent_refs_cross_namespace_denied.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{
"ns-a-parent-cross-74575ab54671a3ede28c": {
EntryPoints: []string{"web"},
Rule: "Host(`cross.example.com`)",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute with parent namespace defaulting to child namespace",
paths: []string{"parent_refs_services.yml", "parent_refs_default_namespace.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{
"default-parent-default-9b8ab283eeed3eb66561": {
EntryPoints: []string{"web"},
Rule: "Host(`default.example.com`)",
},
"default-child-same-9234eba1edcfbd8a7723": {
Service: "default-child-same-9234eba1edcfbd8a7723",
Rule: "Path(`/same`)",
ParentRefs: []string{"default-parent-default-9b8ab283eeed3eb66561"},
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-child-same-9234eba1edcfbd8a7723": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Strategy: dynamic.BalancerStrategyWRR,
Servers: []dynamic.Server{
{
URL: "http://10.10.14.1:9000",
},
{
URL: "http://10.10.14.2:9000",
},
},
PassHostHeader: pointer(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
}
for _, test := range testCases {

View file

@ -19,6 +19,10 @@ type IngressRouteSpec struct {
// TLS defines the TLS configuration.
// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls
TLS *TLS `json:"tls,omitempty"`
// ParentRefs defines references to parent IngressRoute resources for multi-layer routing.
// When set, this IngressRoute's routers will be children of the referenced parent IngressRoute's routers.
// More info: https://doc.traefik.io/traefik/v3.5/routing/routers/#parentrefs
ParentRefs []IngressRouteRef `json:"parentRefs,omitempty"`
}
// Route holds the HTTP route configuration.
@ -211,6 +215,14 @@ type MiddlewareRef struct {
Namespace string `json:"namespace,omitempty"`
}
// IngressRouteRef is a reference to an IngressRoute resource.
type IngressRouteRef struct {
// Name defines the name of the referenced IngressRoute resource.
Name string `json:"name"`
// Namespace defines the namespace of the referenced IngressRoute resource.
Namespace string `json:"namespace,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +kubebuilder:storageversion

View file

@ -432,6 +432,22 @@ func (in *IngressRouteList) DeepCopyObject() runtime.Object {
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressRouteRef) DeepCopyInto(out *IngressRouteRef) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteRef.
func (in *IngressRouteRef) DeepCopy() *IngressRouteRef {
if in == nil {
return nil
}
out := new(IngressRouteRef)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IngressRouteSpec) DeepCopyInto(out *IngressRouteSpec) {
*out = *in
@ -452,6 +468,11 @@ func (in *IngressRouteSpec) DeepCopyInto(out *IngressRouteSpec) {
*out = new(TLS)
(*in).DeepCopyInto(*out)
}
if in.ParentRefs != nil {
in, out := &in.ParentRefs, &out.ParentRefs
*out = make([]IngressRouteRef, len(*in))
copy(*out, *in)
}
return
}