1
0
Fork 0

Migrate to EndpointSlices API

This commit is contained in:
Jesper Noordsij 2024-06-21 14:56:03 +02:00 committed by GitHub
parent 61defcdd66
commit a8a92eb2a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
88 changed files with 2177 additions and 1555 deletions

View file

@ -17,9 +17,11 @@ import (
"github.com/traefik/traefik/v3/pkg/types"
"github.com/traefik/traefik/v3/pkg/version"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
kerror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
kinformers "k8s.io/client-go/informers"
kclientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@ -46,7 +48,7 @@ type Client interface {
GetTLSStores() []*traefikv1alpha1.TLSStore
GetService(namespace, name string) (*corev1.Service, bool, error)
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error)
GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
GetNodes() ([]*corev1.Node, bool, error)
}
@ -219,7 +221,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
if err != nil {
return nil, err
}
_, err = factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
_, err = factoryKube.Discovery().V1().EndpointSlices().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
@ -444,15 +446,20 @@ func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, boo
return service, exist, err
}
// GetEndpoints returns the named endpoints from the given namespace.
func (c *clientWrapper) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
// GetEndpointSlicesForService returns the EndpointSlices for the given service name in the given namespace.
func (c *clientWrapper) GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) {
if !c.isWatchedNamespace(namespace) {
return nil, false, fmt.Errorf("failed to get endpoints %s/%s: namespace is not within watched namespaces", namespace, name)
return nil, fmt.Errorf("failed to get endpointslices for service %s/%s: namespace is not within watched namespaces", namespace, serviceName)
}
endpoint, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Endpoints().Lister().Endpoints(namespace).Get(name)
exist, err := translateNotFoundError(err)
return endpoint, exist, err
serviceLabelRequirement, err := labels.NewRequirement(discoveryv1.LabelServiceName, selection.Equals, []string{serviceName})
if err != nil {
return nil, fmt.Errorf("failed to create service label selector requirement: %w", err)
}
serviceSelector := labels.NewSelector()
serviceSelector = serviceSelector.Add(*serviceLabelRequirement)
return c.factoriesKube[c.lookupNamespace(namespace)].Discovery().V1().EndpointSlices().Lister().EndpointSlices(namespace).List(serviceSelector)
}
// GetSecret returns the named secret from the given namespace.

View file

@ -13,19 +13,24 @@ spec:
task: whoami
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami
name: whoami-abc
namespace: default
labels:
kubernetes.io/service-name: whoami
subsets:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 80
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -43,19 +48,24 @@ spec:
task: whoami2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami2
name: whoami2-abc
namespace: default
labels:
kubernetes.io/service-name: whoami2
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1
@ -74,19 +84,24 @@ spec:
task: whoami2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitls
name: whoamitls-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitls
subsets:
addressType: IPv4
ports:
- name: websecure
port: 8443
endpoints:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
- name: websecure
port: 8443
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
apiVersion: v1
@ -99,25 +114,29 @@ spec:
ports:
- name: websecure2
port: 8443
scheme: https
selector:
app: traefiklabs
task: whoami3
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami3
name: whoami3-abc
namespace: default
labels:
kubernetes.io/service-name: whoami3
subsets:
addressType: IPv4
ports:
- name: websecure2
port: 8443
endpoints:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
- name: websecure2
port: 8443
- 10.10.0.7
- 10.10.0.8
conditions:
ready: true
---
apiVersion: v1
@ -135,18 +154,23 @@ spec:
task: whoami-ipv6
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-ipv6
name: whoami-ipv6-abc
namespace: default
labels:
kubernetes.io/service-name: whoami-ipv6
subsets:
addressType: IPv6
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
- name: web
port: 8080
- "2001:db8:85a3:8d3:1319:8a2e:370:7348"
conditions:
ready: true
---
apiVersion: v1
@ -216,25 +240,30 @@ spec:
task: whoami
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc
name: whoami-svc-abc
namespace: cross-ns
labels:
kubernetes.io/service-name: whoami-svc
subsets:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 80
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: whoami-without-endpoints-subsets
name: whoami-without-endpointslice-endpoints
namespace: default
spec:
@ -247,11 +276,16 @@ spec:
task: whoami
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-without-endpoints-subsets
name: whoami-without-endpointslice-endpoints-abc
namespace: default
labels:
kubernetes.io/service-name: whoami-without-endpointslice-endpoints
addressType: IPv4
endpoints: []
---
apiVersion: v1

View file

@ -13,19 +13,24 @@ spec:
task: whoamitcp
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp
name: whoamitcp-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitcp
subsets:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: myapp
port: 8000
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -43,19 +48,24 @@ spec:
task: whoamitcp2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp2
name: whoamitcp2-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitcp2
subsets:
addressType: IPv4
ports:
- name: myapp2
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: myapp2
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1
@ -73,19 +83,24 @@ spec:
task: whoamitcptls2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcptls
name: whoamitcptls-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitcptls
subsets:
addressType: IPv4
ports:
- name: websecure
port: 443
endpoints:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
- name: websecure
port: 443
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
apiVersion: v1
@ -103,34 +118,44 @@ spec:
task: whoamitcp3
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp3
name: whoamitcp3-abc
namespace: ns3
labels:
kubernetes.io/service-name: whoamitcp3
subsets:
addressType: IPv4
ports:
- name: myapp3
port: 8083
endpoints:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
- name: myapp3
port: 8083
- 10.10.0.7
- 10.10.0.8
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp3
name: whoamitcp3-abc
namespace: ns4
labels:
kubernetes.io/service-name: whoamitcp3
subsets:
addressType: IPv4
ports:
- name: myapp4
port: 8084
endpoints:
- addresses:
- ip: 10.10.0.9
- ip: 10.10.0.10
ports:
- name: myapp4
port: 8084
- 10.10.0.9
- 10.10.0.10
conditions:
ready: true
---
apiVersion: v1
@ -148,19 +173,24 @@ spec:
task: whoamitcp-ipv6
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp-ipv6
name: whoamitcp-ipv6-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitcp-ipv6
subsets:
addressType: IPv6
ports:
- name: myapp-ipv6
port: 8080
endpoints:
- addresses:
- ip: "fd00:10:244:0:1::3"
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
- name: myapp-ipv6
port: 8080
- "fd00:10:244:0:1::3"
- "2001:db8:85a3:8d3:1319:8a2e:370:7348"
conditions:
ready: true
---
apiVersion: v1
@ -212,25 +242,30 @@ spec:
task: whoamitcp
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp-cross-ns
name: whoamitcp-cross-ns-abc
namespace: cross-ns
labels:
kubernetes.io/service-name: whoamitcp-cross-ns
subsets:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: myapp
port: 8000
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: whoamitcp-without-endpoints-subsets
name: whoamitcp-without-endpointslice-endpoints
namespace: default
spec:
@ -243,11 +278,16 @@ spec:
task: whoamitcp
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp-without-endpoints-subsets
name: whoamitcp-without-endpointslice-endpoints-abc
namespace: default
labels:
kubernetes.io/service-name: whoamitcp-without-endpointslice-endpoints
addressType: IPv4
endpoints: []
---
apiVersion: v1

View file

@ -11,5 +11,5 @@ spec:
routes:
- match: HostSNI(`foo.com`)
services:
- name: whoamitcp-without-endpoints-subsets
- name: whoamitcp-without-endpointslice-endpoints
port: 8000

View file

@ -13,19 +13,24 @@ spec:
task: whoamiudp
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp
name: whoamiudp-abc
namespace: default
labels:
kubernetes.io/service-name: whoamiudp
subsets:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: myapp
port: 8000
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -43,19 +48,24 @@ spec:
task: whoamiudp2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp2
name: whoamiudp2-abc
namespace: default
labels:
kubernetes.io/service-name: whoamiudp2
subsets:
addressType: IPv4
ports:
- name: myapp2
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: myapp2
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1
@ -73,34 +83,44 @@ spec:
task: whoamiudp3
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp3
name: whoamiudp3-abc
namespace: ns3
labels:
kubernetes.io/service-name: whoamiudp3
subsets:
addressType: IPv4
ports:
- name: myapp3
port: 8083
endpoints:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
- name: myapp3
port: 8083
- 10.10.0.7
- 10.10.0.8
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp3
name: whoamiudp3-abc
namespace: ns4
labels:
kubernetes.io/service-name: whoamiudp3
subsets:
addressType: IPv4
ports:
- name: myapp4
port: 8084
endpoints:
- addresses:
- ip: 10.10.0.9
- ip: 10.10.0.10
ports:
- name: myapp4
port: 8084
- 10.10.0.9
- 10.10.0.10
conditions:
ready: true
---
apiVersion: v1
@ -118,18 +138,23 @@ spec:
task: whoamiudp-ipv6
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp-ipv6
name: whoamiudp-ipv6-abc
namespace: default
labels:
kubernetes.io/service-name: whoamiudp-ipv6
subsets:
addressType: IPv6
ports:
- name: myapp-ipv6
port: 8080
endpoints:
- addresses:
- ip: "fd00:10:244:0:1::3"
ports:
- name: myapp-ipv6
port: 8080
- "fd00:10:244:0:1::3"
conditions:
ready: true
---
apiVersion: v1
@ -171,25 +196,30 @@ spec:
port: 80
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp-cross-ns
name: whoamiudp-cross-ns-abc
namespace: cross-ns
labels:
kubernetes.io/service-name: whoamiudp-cross-ns
subsets:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: myapp
port: 8000
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
kind: Service
metadata:
name: whoamiudp-without-endpoints-subsets
name: whoamiudp-without-endpointslice-endpoints
namespace: default
spec:
@ -202,11 +232,16 @@ spec:
task: whoamiudp
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamiudp-without-endpoints-subsets
name: whoamiudp-without-endpointslice-endpoints-abc
namespace: default
labels:
kubernetes.io/service-name: whoamiudp-without-endpointslice-endpoints
addressType: IPv4
endpoints: []
---
apiVersion: v1

View file

@ -10,5 +10,5 @@ spec:
routes:
- services:
- name: whoamiudp-without-endpoints-subsets
- name: whoamiudp-without-endpointslice-endpoints
port: 8000

View file

@ -0,0 +1,74 @@
apiVersion: v1
kind: Service
metadata:
name: whoami-svc-duplicated-endpointaddresses
namespace: default
spec:
ports:
- name: web
port: 8080
selector:
app: traefiklabs
task: whoami
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-duplicated-endpointaddresses-abc
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-duplicated-endpointaddresses
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-duplicated-endpointaddresses-def
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-duplicated-endpointaddresses
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule
priority: 12
services:
- name: whoami-svc-duplicated-endpointaddresses
port: 8080

View file

@ -13,5 +13,5 @@ spec:
kind: Rule
priority: 12
services:
- name: whoami-without-endpoints-subsets
- name: whoami-without-endpointslice-endpoints
port: 80

View file

@ -30,7 +30,7 @@ metadata:
spec:
weighted:
services:
- name: whoami-without-endpoints-subsets
- name: whoami-without-endpointslice-endpoints
weight: 1
port: 80
@ -43,10 +43,10 @@ metadata:
spec:
mirroring:
name: whoami-without-endpoints-subsets
name: whoami-without-endpointslice-endpoints
port: 80
mirrors:
- name: whoami-without-endpoints-subsets
- name: whoami-without-endpointslice-endpoints
port: 80
- name: test-weighted
kind: TraefikService
@ -61,5 +61,5 @@ metadata:
spec:
errors:
service:
name: whoami-without-endpoints-subsets
name: whoami-without-endpointslice-endpoints
port: 80

View file

@ -1,17 +1,22 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami4
name: whoami4-abc
namespace: default
labels:
kubernetes.io/service-name: whoami4
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 8080
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -28,20 +33,25 @@ spec:
app: traefiklabs
task: whoami4
------
kind: Endpoints
apiVersion: v1
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1

View file

@ -1,17 +1,22 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami4
name: whoami4-abc
namespace: default
labels:
kubernetes.io/service-name: whoami4
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 8080
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -28,20 +33,25 @@ spec:
app: traefiklabs
task: whoami4
------
kind: Endpoints
apiVersion: v1
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1

View file

@ -0,0 +1,63 @@
apiVersion: v1
kind: Service
metadata:
name: whoami-svc-multiple-endpointaddresses
namespace: default
spec:
ports:
- name: web
port: 80
selector:
app: traefiklabs
task: whoami
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-multiple-endpointaddresses-abc
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-multiple-endpointaddresses
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
- addresses:
- 10.10.0.3
- 10.10.0.4
conditions:
ready: false
serving: true
- addresses:
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule
priority: 12
services:
- name: whoami-svc-multiple-endpointaddresses
port: 80

View file

@ -0,0 +1,94 @@
apiVersion: v1
kind: Service
metadata:
name: whoami-svc-multiple-endpointslices
namespace: default
spec:
ports:
- name: web
port: 80
- name: web2
port: 8080
selector:
app: traefiklabs
task: whoami
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-multiple-endpointslices-abc
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-multiple-endpointslices
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-multiple-endpointslices-def
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-multiple-endpointslices
addressType: IPv4
ports:
- name: web2
port: 8080
endpoints:
- addresses:
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-svc-multiple-endpointslices-ghi
namespace: default
labels:
kubernetes.io/service-name: whoami-svc-multiple-endpointslices
addressType: IPv4
ports:
- name: web2
port: 8080
endpoints:
- addresses:
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule
priority: 12
services:
- name: whoami-svc-multiple-endpointslices
port: 8080

View file

@ -1,54 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: whoami-svc-multiple-subsets
namespace: default
spec:
ports:
- name: web
port: 80
- name: web2
port: 8080
selector:
app: traefiklabs
task: whoami
---
kind: Endpoints
apiVersion: v1
metadata:
name: whoami-svc-multiple-subsets
namespace: default
subsets:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 80
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web2
port: 8080
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
entryPoints:
- foo
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule
priority: 12
services:
- name: whoami-svc-multiple-subsets
port: 8080

View file

@ -1,47 +1,62 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami6
name: whoami6-abc
namespace: baz
labels:
kubernetes.io/service-name: whoami6
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
- name: web
port: 8080
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: foo
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami4
name: whoami4-abc
namespace: foo
labels:
kubernetes.io/service-name: whoami4
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 8080
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1

View file

@ -1,17 +1,22 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1

View file

@ -1,62 +1,82 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami4
name: whoami4-abc
namespace: default
labels:
kubernetes.io/service-name: whoami4
subsets:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 80
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami6
name: whoami6-abc
namespace: default
labels:
kubernetes.io/service-name: whoami6
subsets:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
- name: web
port: 80
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami7
name: whoami7-abc
namespace: default
labels:
kubernetes.io/service-name: whoami7
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
- name: web
port: 8080
- 10.10.0.7
- 10.10.0.8
conditions:
ready: true
---
apiVersion: v1

View file

@ -1,17 +1,22 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: traefik.io/v1alpha1

View file

@ -1,32 +1,42 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami4
name: whoami4-abc
namespace: default
labels:
kubernetes.io/service-name: whoami4
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
- name: web
port: 8080
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1

View file

@ -1,17 +1,22 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami5
name: whoami5-abc
namespace: default
labels:
kubernetes.io/service-name: whoami5
subsets:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
- name: web
port: 8080
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1

View file

@ -409,9 +409,8 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
return nil, err
}
var servers []dynamic.Server
if service.Spec.Type != corev1.ServiceTypeExternalName && svc.HealthCheck != nil {
return nil, fmt.Errorf("HealthCheck allowed only for ExternalName services: %s/%s", namespace, sanitizedName)
return nil, fmt.Errorf("healthCheck allowed only for ExternalName services: %s/%s", namespace, sanitizedName)
}
if service.Spec.Type == corev1.ServiceTypeExternalName {
@ -426,9 +425,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
hostPort := net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(svcPort.Port)))
return append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s", protocol, hostPort),
}), nil
return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, hostPort)}}, nil
}
nativeLB := c.NativeLBByDefault
@ -449,6 +446,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", protocol, address)}}, nil
}
var servers []dynamic.Server
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
nodes, nodesExists, nodesErr := c.client.GetNodes()
if nodesErr != nil {
@ -482,27 +480,20 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
return servers, nil
}
endpoints, endpointsExists, endpointsErr := c.client.GetEndpoints(namespace, sanitizedName)
if endpointsErr != nil {
return nil, endpointsErr
}
if !endpointsExists {
return nil, fmt.Errorf("endpoints not found for %s/%s", namespace, sanitizedName)
endpointSlices, err := c.client.GetEndpointSlicesForService(namespace, sanitizedName)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
if len(endpoints.Subsets) == 0 && !c.allowEmptyServices {
return nil, fmt.Errorf("subset not found for %s/%s", namespace, sanitizedName)
}
for _, subset := range endpoints.Subsets {
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
for _, p := range subset.Ports {
if svcPort.Name == p.Name {
port = p.Port
for _, p := range endpointSlice.Ports {
if svcPort.Name == *p.Name {
port = *p.Port
break
}
}
if port == 0 {
continue
}
@ -512,15 +503,28 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L
return nil, err
}
for _, addr := range subset.Addresses {
hostPort := net.JoinHostPort(addr.IP, strconv.Itoa(int(port)))
for _, endpoint := range endpointSlice.Endpoints {
if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready {
continue
}
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s", protocol, hostPort),
})
for _, address := range endpoint.Addresses {
if _, ok := addresses[address]; ok {
continue
}
addresses[address] = struct{}{}
servers = append(servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(address, strconv.Itoa(int(port)))),
})
}
}
}
if len(servers) == 0 && !c.allowEmptyServices {
return nil, fmt.Errorf("no servers found for %s/%s", namespace, sanitizedName)
}
return servers, nil
}

View file

@ -238,7 +238,6 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc traefikv1
}
var servers []dynamic.TCPServer
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
nodes, nodesExists, nodesErr := client.GetNodes()
if nodesErr != nil {
@ -284,40 +283,47 @@ func (p *Provider) loadTCPServers(client Client, namespace string, svc traefikv1
return []dynamic.TCPServer{{Address: address}}, nil
}
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
endpointSlices, err := client.GetEndpointSlicesForService(namespace, svc.Name)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 && !p.AllowEmptyServices {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if svcPort.Name == p.Name {
port = p.Port
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
for _, p := range endpointSlice.Ports {
if svcPort.Name == *p.Name {
port = *p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
continue
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.TCPServer{
Address: net.JoinHostPort(addr.IP, strconv.Itoa(int(port))),
})
for _, endpoint := range endpointSlice.Endpoints {
if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready {
continue
}
for _, address := range endpoint.Addresses {
if _, ok := addresses[address]; ok {
continue
}
addresses[address] = struct{}{}
servers = append(servers, dynamic.TCPServer{
Address: net.JoinHostPort(address, strconv.Itoa(int(port))),
})
}
}
}
}
if len(servers) == 0 && !p.AllowEmptyServices {
return nil, fmt.Errorf("no servers found for %s/%s", namespace, svc.Name)
}
return servers, nil
}

View file

@ -4620,9 +4620,9 @@ func TestLoadIngressRoutes(t *testing.T) {
},
},
{
desc: "IngressRoute, service with multiple subsets",
desc: "IngressRoute, service with multiple endpoint addresses on endpointslice",
allowEmptyServices: true,
paths: []string{"services.yml", "with_multiple_subsets.yml"},
paths: []string{"services.yml", "with_multiple_endpointaddresses.yml"},
expected: &dynamic.Configuration{
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
@ -4648,6 +4648,66 @@ func TestLoadIngressRoutes(t *testing.T) {
"default-test-route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
{
URL: "http://10.10.0.5:80",
},
{
URL: "http://10.10.0.6:80",
},
},
PassHostHeader: Bool(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "IngressRoute, service with duplicated endpointaddresses",
allowEmptyServices: true,
paths: []string{"services.yml", "with_duplicated_endpointaddresses.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-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
Service: "default-test-route-6b204d94623b3df4370c",
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
Priority: 12,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-test-route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
{
URL: "http://10.10.0.2:8080",
},
{
URL: "http://10.10.0.3:8080",
},
@ -4726,7 +4786,7 @@ func TestLoadIngressRoutes(t *testing.T) {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-without-endpoints-subsets-80",
Name: "default-whoami-without-endpointslice-endpoints-80",
Weight: func(i int) *int { return &i }(1),
},
},
@ -4734,10 +4794,10 @@ func TestLoadIngressRoutes(t *testing.T) {
},
"default-test-mirror": {
Mirroring: &dynamic.Mirroring{
Service: "default-whoami-without-endpoints-subsets-80",
Service: "default-whoami-without-endpointslice-endpoints-80",
Mirrors: []dynamic.MirrorService{
{
Name: "default-whoami-without-endpoints-subsets-80",
Name: "default-whoami-without-endpointslice-endpoints-80",
},
{
Name: "default-test-weighted",
@ -4745,7 +4805,7 @@ func TestLoadIngressRoutes(t *testing.T) {
},
},
},
"default-whoami-without-endpoints-subsets-80": {
"default-whoami-without-endpointslice-endpoints-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
ResponseForwarding: &dynamic.ResponseForwarding{
@ -4799,6 +4859,87 @@ func TestLoadIngressRoutes(t *testing.T) {
}
}
func TestLoadIngressRoutes_multipleEndpointAddresses(t *testing.T) {
wantConf := &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-test-route-6b204d94623b3df4370c": {
EntryPoints: []string{"foo"},
Service: "default-test-route-6b204d94623b3df4370c",
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
Priority: 12,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-test-route-6b204d94623b3df4370c": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
}
wantServers := []dynamic.Server{
{
URL: "http://10.10.0.3:8080",
},
{
URL: "http://10.10.0.4:8080",
},
{
URL: "http://10.10.0.5:8080",
},
{
URL: "http://10.10.0.6:8080",
},
}
k8sObjects, crdObjects := readResources(t, []string{"services.yml", "with_multiple_endpointslices.yml"})
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...)
client := newClientImpl(kubeClient, crdClient)
stopCh := make(chan struct{})
eventCh, err := client.WatchAll(nil, stopCh)
require.NoError(t, err)
if k8sObjects != nil || crdObjects != nil {
// just wait for the first event
<-eventCh
}
p := Provider{}
conf := p.loadConfigurationFromCRD(context.Background(), client)
service, ok := conf.HTTP.Services["default-test-route-6b204d94623b3df4370c"]
require.True(t, ok)
require.NotNil(t, service)
require.NotNil(t, service.LoadBalancer)
assert.ElementsMatch(t, wantServers, service.LoadBalancer.Servers)
service.LoadBalancer.Servers = nil
assert.Equal(t, wantConf, conf)
}
func TestLoadIngressRouteUDPs(t *testing.T) {
testCases := []struct {
desc string

View file

@ -122,7 +122,6 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc traefikv1
}
var servers []dynamic.UDPServer
if service.Spec.Type == corev1.ServiceTypeNodePort && svc.NodePortLB {
nodes, nodesExists, nodesErr := client.GetNodes()
if nodesErr != nil {
@ -168,39 +167,46 @@ func (p *Provider) loadUDPServers(client Client, namespace string, svc traefikv1
return []dynamic.UDPServer{{Address: address}}, nil
}
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
if endpointsErr != nil {
return nil, endpointsErr
endpointSlices, err := client.GetEndpointSlicesForService(namespace, svc.Name)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 && !p.AllowEmptyServices {
return nil, errors.New("subset not found")
}
var port int32
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if svcPort.Name == p.Name {
port = p.Port
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
for _, p := range endpointSlice.Ports {
if svcPort.Name == *p.Name {
port = *p.Port
break
}
}
if port == 0 {
return nil, errors.New("cannot define a port")
continue
}
for _, addr := range subset.Addresses {
servers = append(servers, dynamic.UDPServer{
Address: net.JoinHostPort(addr.IP, strconv.Itoa(int(port))),
})
for _, endpoint := range endpointSlice.Endpoints {
if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready {
continue
}
for _, address := range endpoint.Addresses {
if _, ok := addresses[address]; ok {
continue
}
addresses[address] = struct{}{}
servers = append(servers, dynamic.UDPServer{
Address: net.JoinHostPort(address, strconv.Itoa(int(port))),
})
}
}
}
}
if len(servers) == 0 && !p.AllowEmptyServices {
return nil, fmt.Errorf("no servers found for %s/%s", namespace, svc.Name)
}
return servers, nil
}