Compare commits

...

14 commits

Author SHA1 Message Date
fd015c5a9b
Merge branch 'master' of github.com:traefik/traefik
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
2024-06-25 06:38:43 +05:30
27ab7b5c2e
Merge branch 'v3.0' of github.com:traefik/traefik 2024-06-25 06:38:29 +05:30
Romain
6f1bd54d86
Fix some documentation links
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
2024-06-24 11:22:03 +02:00
Romain
983940ae60
KubernetesGateway provider out of experimental
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
2024-06-24 10:36:03 +02:00
Kevin Pollet
6d8407893d
Bump Gateway API to v1.1.0
Co-authored-by: Romain <rtribotte@users.noreply.github.com>
2024-06-22 05:46:03 +02:00
Jesper Noordsij
a8a92eb2a5
Migrate to EndpointSlices API 2024-06-21 14:56:03 +02:00
mmatur
61defcdd66
Merge current v3.0 into master 2024-06-21 09:15:28 +02:00
mmatur
ec638a741e
Merge current v2.11 into v3.0 2024-06-21 08:55:31 +02:00
Michael
097e71ad24
fix: readme badge 2024-06-21 08:54:03 +02:00
Emile Vauge
eabcb3e1c0
Update maintainers 2024-06-19 17:18:03 +02:00
Kevin Pollet
53a8bd76f2
Prepare release v3.0.3 2024-06-18 16:10:06 +02:00
kevinpollet
0e89c48e38
Merge branch v2.11 into v3.0 2024-06-18 14:05:42 +02:00
Romain
385ff5055c
Prepare release v2.11.5 2024-06-18 12:00:04 +02:00
Kevin Pollet
b4f99ae3ac
Support HTTPRoute method and query param matching 2024-06-18 09:48:04 +02:00
111 changed files with 20953 additions and 13490 deletions

View file

@ -1,3 +1,19 @@
## [v3.0.3](https://github.com/traefik/traefik/tree/v3.0.3) (2024-06-18)
[All Commits](https://github.com/traefik/traefik/compare/v3.0.2...v3.0.3)
**Misc:**
- Merge v2.11 into v3.0 ([#10823](https://github.com/traefik/traefik/pull/10823) by [kevinpollet](https://github.com/kevinpollet))
- Merge v2.11 into v3.0 ([#10810](https://github.com/traefik/traefik/pull/10810) by [mmatur](https://github.com/mmatur))
## [v2.11.5](https://github.com/traefik/traefik/tree/v2.11.5) (2024-06-18)
[All Commits](https://github.com/traefik/traefik/compare/v2.11.4...v2.11.5)
**Bug fixes:**
- **[acme]** Update go-acme/lego to v4.17.4 ([#10803](https://github.com/traefik/traefik/pull/10803) by [ldez](https://github.com/ldez))
**Documentation:**
- Update the supported versions table ([#10798](https://github.com/traefik/traefik/pull/10798) by [nmengin](https://github.com/nmengin))
## [v3.0.2](https://github.com/traefik/traefik/tree/v3.0.2) (2024-06-10)
[All Commits](https://github.com/traefik/traefik/compare/v3.0.1...v3.0.2)

View file

@ -7,7 +7,7 @@
</picture>
</p>
[![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/traefik)
[![Build Status SemaphoreCI](https://traefik-oss.semaphoreci.com/badges/traefik/branches/master.svg?style=shields)](https://traefik-oss.semaphoreci.com/projects/traefik)
[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://doc.traefik.io/traefik)
[![Go Report Card](https://goreportcard.com/badge/traefik/traefik)](https://goreportcard.com/report/traefik/traefik)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/traefik/traefik/blob/master/LICENSE.md)

View file

@ -15,7 +15,7 @@ Let's see how.
### General
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
This [documentation](../../ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
### Method 1: `Docker` and `make`

View file

@ -9,7 +9,6 @@ description: "Traefik Proxy is an open source software with a thriving community
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
* Manuel Zapf [@SantoDE](https://github.com/SantoDE)
* Ludovic Fernandez [@ldez](https://github.com/ldez)
* Julien Salleyron [@juliens](https://github.com/juliens)
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
* Michaël Matur [@mmatur](https://github.com/mmatur)
@ -33,6 +32,7 @@ People who have had an incredibly positive impact on the project, and are now fo
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
* Marco Jantke [@mjantke](https://github.com/mjeri)
* Ludovic Fernandez [@ldez](https://github.com/ldez)
## Maintainer's Guidelines

View file

@ -35,12 +35,18 @@ rules:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io

View file

@ -135,7 +135,7 @@ It is now unsupported and would prevent Traefik to start.
##### Remediation
The `http3` option should be removed from the static configuration experimental section.
To configure `http3`, please checkout the [entrypoint configuration documentation](https://doc.traefik.io/traefik/v3.0/routing/entrypoints/#http3_1).
To configure `http3`, please checkout the [entrypoint configuration documentation](../routing/entrypoints.md#http3_1).
### Consul provider

View file

@ -29,7 +29,7 @@ core:
defaultRuleSyntax: v2
```
This snippet in the static configuration makes the [v2 format](https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/?ref=traefik.io#configure-the-default-syntax-in-static-configuration "Link to configure default syntax in static config") the default rule matchers syntax.
This snippet in the static configuration makes the [v2 format](../migration/v2-to-v3-details.md#configure-the-default-syntax-in-static-configuration "Link to configure default syntax in static config") the default rule matchers syntax.
Start Traefik v3 with this new configuration to test it.

View file

@ -553,7 +553,7 @@ The following ciphers have been removed from the default list:
- `TLS_RSA_WITH_AES_128_GCM_SHA256`
- `TLS_RSA_WITH_AES_256_GCM_SHA384`
To enable these ciphers, please set the option `CipherSuites` in your [TLS configuration](https://doc.traefik.io/traefik/https/tls/#cipher-suites) or set the environment variable `GODEBUG=tlsrsakex=1`.
To enable these ciphers, please set the option `CipherSuites` in your [TLS configuration](../https/tls.md#cipher-suites) or set the environment variable `GODEBUG=tlsrsakex=1`.
### Minimum TLS Version
@ -562,7 +562,7 @@ To enable these ciphers, please set the option `CipherSuites` in your [TLS confi
> This change can be reverted with the `tls10server=1 GODEBUG` setting.
> (https://go.dev/doc/go1.22#crypto/tls)
To enable TLS 1.0, please set the option `MinVersion` to `VersionTLS10` in your [TLS configuration](https://doc.traefik.io/traefik/https/tls/#cipher-suites) or set the environment variable `GODEBUG=tls10server=1`.
To enable TLS 1.0, please set the option `MinVersion` to `VersionTLS10` in your [TLS configuration](../https/tls.md#cipher-suites) or set the environment variable `GODEBUG=tls10server=1`.
## v2.11.1

View file

@ -0,0 +1,53 @@
---
title: "Traefik Migration Documentation"
description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions. Read the technical documentation."
---
# Migration: Steps needed between the versions
## v3.0 to v3.1
### Kubernetes Provider RBACs
Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses.
Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) provider RBACs),
the `endpoints` right has to be removed and the following `endpointslices` right has to be added.
```yaml
...
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
...
```
#### Gateway API: KubernetesGateway Provider
In v3.1, the KubernetesGateway Provider is no longer an experimental feature.
It can be enabled without the associated `experimental.kubernetesgateway` option, which is now deprecated.
??? example "An example of the experimental `kubernetesgateway` option"
```yaml tab="File (YAML)"
experimental:
kubernetesgateway: true
```
```toml tab="File (TOML)"
[experimental]
kubernetesgateway=true
```
```bash tab="CLI"
--experimental.kubernetesgateway=true
```
##### Remediation
The `kubernetesgateway` option should be removed from the experimental section of the static configuration.
To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md).

View file

@ -58,7 +58,7 @@ For this reason, users can run multiple instances of Traefik at the same time to
When using a single instance of Traefik with Let's Encrypt, you should encounter no issues. However, this could be a single point of failure.
Unfortunately, it is not possible to run multiple instances of Traefik Proxy 2.0 with Let's Encrypt enabled, because there is no way to ensure that the correct instance of Traefik will receive the challenge request and subsequent responses.
Previous versions of Traefik used a [KV store](https://doc.traefik.io/traefik/v1.7/configuration/acme/#storage) to attempt to achieve this, but due to sub-optimal performance that feature was dropped in 2.0.
Early versions (v1.x) of Traefik used a [KV store](https://doc.traefik.io/traefik/v1.7/configuration/acme/#storage) to attempt to achieve this, but due to sub-optimal performance that feature was dropped in 2.0.
If you need Let's Encrypt with HA in a Kubernetes environment, we recommend using [Traefik Enterprise](https://traefik.io/traefik-enterprise/), which includes distributed Let's Encrypt as a supported feature.
@ -183,7 +183,7 @@ _Optional, Default: ""_
A label selector can be defined to filter on specific resource objects only,
this applies only to Traefik [Custom Resources](../routing/providers/kubernetes-crd.md#custom-resource-definition-crd)
and has no effect on Kubernetes `Secrets`, `Endpoints` and `Services`.
and has no effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
If left empty, Traefik processes all resource objects in the configured namespaces.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.

View file

@ -80,7 +80,7 @@ When using a single instance of Traefik Proxy with Let's Encrypt, you should enc
However, this could be a single point of failure.
Unfortunately, it is not possible to run multiple instances of Traefik 2.0 with Let's Encrypt enabled,
because there is no way to ensure that the correct instance of Traefik receives the challenge request, and subsequent responses.
Previous versions of Traefik used a [KV store](https://doc.traefik.io/traefik/v1.7/configuration/acme/#storage) to attempt to achieve this,
Early versions (v1.x) of Traefik used a [KV store](https://doc.traefik.io/traefik/v1.7/configuration/acme/#storage) to attempt to achieve this,
but due to sub-optimal performance that feature was dropped in 2.0.
If you need Let's Encrypt with high availability in a Kubernetes environment,

View file

@ -150,8 +150,8 @@ Below is the list of the currently supported providers in Traefik.
!!! info "More Providers"
The current version of Traefik does not yet support every provider that Traefik v1.7 did.
See the [previous version (v1.7)](https://doc.traefik.io/traefik/v1.7/) for more providers.
The current version of Traefik does not yet support every provider that Traefik v2.11 did.
See the [previous version (v2.11)](https://doc.traefik.io/traefik/v2.11/) for more information.
### Configuration Reload Frequency

View file

@ -8,13 +8,19 @@ rules:
- ""
resources:
- services
- endpoints
- secrets
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io

View file

@ -15,12 +15,18 @@ rules:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
- apiGroups:
- gateway.networking.k8s.io
resources:

View file

@ -211,7 +211,7 @@ WriteTimeout is the maximum duration before timing out writes of the response. I
Timeout defines how long to wait on an idle session before releasing the related resources. (Default: ```3```)
`--experimental.kubernetesgateway`:
Allow the Kubernetes gateway api provider usage. (Default: ```false```)
(Deprecated) Allow the Kubernetes gateway api provider usage. (Default: ```false```)
`--experimental.localplugins.<name>`:
Local plugins configuration. (Default: ```false```)

View file

@ -211,7 +211,7 @@ WriteTimeout is the maximum duration before timing out writes of the response. I
Timeout defines how long to wait on an idle session before releasing the related resources. (Default: ```3```)
`TRAEFIK_EXPERIMENTAL_KUBERNETESGATEWAY`:
Allow the Kubernetes gateway api provider usage. (Default: ```false```)
(Deprecated) Allow the Kubernetes gateway api provider usage. (Default: ```false```)
`TRAEFIK_EXPERIMENTAL_LOCALPLUGINS_<NAME>`:
Local plugins configuration. (Default: ```false```)

View file

@ -29,12 +29,18 @@ which in turn will create the resulting routers, services, handlers, etc.
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
@ -427,12 +433,19 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
@ -612,12 +625,19 @@ For more options, please refer to the available [annotations](#on-ingress).
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
@ -832,7 +852,7 @@ TLS certificates can be managed in Secrets objects.
whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP.
One alternative is to use an `ExternalName` service to forward requests to the Kubernetes service through DNS.
To do so, one must [allow external name services](https://doc.traefik.io/traefik/providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services").
To do so, one must [allow external name services](../providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services").
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
Although Traefik will connect directly to the endpoints (pods),

View file

@ -172,6 +172,7 @@ nav:
- 'HTTP Challenge': 'user-guides/docker-compose/acme-http/index.md'
- 'DNS Challenge': 'user-guides/docker-compose/acme-dns/index.md'
- 'Migration':
- 'Traefik v3 minor migrations': 'migration/v3.md'
- 'Traefik v2 to v3':
- 'Migration guide': 'migration/v2-to-v3.md'
- 'Configuration changes for v3': 'migration/v2-to-v3-details.md'

42
go.mod
View file

@ -1,6 +1,6 @@
module github.com/traefik/traefik/v3
go 1.22
go 1.22.4
require (
github.com/BurntSushi/toml v1.4.0
@ -22,7 +22,7 @@ require (
github.com/golang/protobuf v1.5.4
github.com/google/go-github/v28 v28.1.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/gorilla/websocket v1.5.1
github.com/hashicorp/consul/api v1.26.1
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-multierror v1.1.1
@ -79,7 +79,7 @@ require (
go.opentelemetry.io/otel/sdk v1.27.0
go.opentelemetry.io/otel/sdk/metric v1.27.0
go.opentelemetry.io/otel/trace v1.27.0
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
golang.org/x/mod v0.18.0
golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0
@ -88,14 +88,14 @@ require (
golang.org/x/tools v0.22.0
google.golang.org/grpc v1.64.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.29.2
k8s.io/apiextensions-apiserver v0.28.3
k8s.io/apimachinery v0.29.2
k8s.io/client-go v0.29.2
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
k8s.io/api v0.30.0
k8s.io/apiextensions-apiserver v0.30.0
k8s.io/apimachinery v0.30.0
k8s.io/client-go v0.30.0
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22
mvdan.cc/xurls/v2 v2.5.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
sigs.k8s.io/controller-runtime v0.18.0
sigs.k8s.io/gateway-api v1.1.0
)
require (
@ -165,9 +165,9 @@ require (
github.com/distribution/reference v0.5.0 // indirect
github.com/dnsimple/dnsimple-go v1.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/exoscale/egoscale v0.102.3 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@ -178,11 +178,11 @@ require (
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.2.4 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.20.0 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-resty/resty/v2 v2.11.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
@ -316,9 +316,9 @@ require (
github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
github.com/zeebo/errs v1.2.2 // indirect
go.etcd.io/etcd/api/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect
go.etcd.io/etcd/client/v3 v3.5.9 // indirect
go.etcd.io/etcd/api/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
go.etcd.io/etcd/client/v3 v3.5.10 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/contrib/propagators/aws v1.27.0 // indirect
@ -347,8 +347,8 @@ require (
gopkg.in/ns1/ns1-go.v2 v2.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect
nhooyr.io/websocket v1.8.7 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect

96
go.sum
View file

@ -151,7 +151,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@ -282,8 +281,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -295,8 +294,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc=
github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/exoscale/egoscale v0.102.3 h1:DYqN2ipoLKpiFoprRGQkp2av/Ze7sUYYlGhi1N62tfY=
github.com/exoscale/egoscale v0.102.3/go.mod h1:RPf2Gah6up+6kAEayHTQwqapzXlm93f0VQas/UEGU5c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -349,26 +348,22 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ=
github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
@ -519,8 +514,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf h1:C1GPyPJrOlJlIrcaBBiBpDsqZena2Ks8spa5xZqr1XQ=
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf/go.mod h1:zXqxTI6jXDdKnlf8s+nT+3c8LrwUEy3yNpO4XJL90lA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@ -874,8 +869,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@ -1168,12 +1163,12 @@ github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtC
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs=
go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k=
go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE=
go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4=
go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E=
go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA=
go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -1221,18 +1216,15 @@ go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naR
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw=
@ -1240,7 +1232,6 @@ go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyB
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@ -1280,8 +1271,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -1507,7 +1498,6 @@ golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4X
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
@ -1517,8 +1507,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
@ -1650,20 +1638,20 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs=
k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y=
k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY=
k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 h1:ao5hUqGhsqdm+bYbjH/pRkCs0unBGe9UyDahzs9zQzQ=
k8s.io/utils v0.0.0-20240423183400-0849a56e8f22/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8=
mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
@ -1671,10 +1659,10 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs=
sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c=
sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME=
sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw=
sigs.k8s.io/gateway-api v1.1.0 h1:DsLDXCi6jR+Xz8/xd0Z1PYl2Pn0TyaFMOPPZIj4inDM=
sigs.k8s.io/gateway-api v1.1.0/go.mod h1:ZH4lHrL2sDi0FHZ9jjneb8kKnGzFWyrTya35sWUTrRs=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -15,12 +15,19 @@ rules:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- discovery.k8s.io
resources:
- endpointslices
verbs:
- get
- list
- watch
- apiGroups:
- gateway.networking.k8s.io
resources:

View file

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"slices"
@ -18,6 +19,7 @@ import (
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/version"
"gopkg.in/yaml.v3"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/util/sets"
kclientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
@ -27,10 +29,12 @@ import (
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
conformanceV1alpha1 "sigs.k8s.io/gateway-api/conformance/apis/v1alpha1"
"sigs.k8s.io/gateway-api/conformance"
v1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
"sigs.k8s.io/gateway-api/conformance/tests"
"sigs.k8s.io/gateway-api/conformance/utils/config"
ksuite "sigs.k8s.io/gateway-api/conformance/utils/suite"
"sigs.k8s.io/gateway-api/pkg/features"
)
const (
@ -84,7 +88,7 @@ func (s *K8sConformanceSuite) SetupSuite() {
s.k3sContainer, err = k3s.RunContainer(ctx,
testcontainers.WithImage(k3sImage),
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.0.0.yml"),
k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.1.0.yml"),
k3s.WithManifest("./fixtures/k8s-conformance/01-rbac.yml"),
k3s.WithManifest("./fixtures/k8s-conformance/02-traefik.yml"),
network.WithNetwork(nil, s.network),
@ -122,15 +126,19 @@ func (s *K8sConformanceSuite) SetupSuite() {
s.T().Fatalf("Error initializing Kubernetes REST client: %v", err)
}
if err = gatev1alpha2.AddToScheme(s.kubeClient.Scheme()); err != nil {
if err = gatev1alpha2.Install(s.kubeClient.Scheme()); err != nil {
s.T().Fatal(err)
}
if err = gatev1beta1.AddToScheme(s.kubeClient.Scheme()); err != nil {
if err = gatev1beta1.Install(s.kubeClient.Scheme()); err != nil {
s.T().Fatal(err)
}
if err = gatev1.AddToScheme(s.kubeClient.Scheme()); err != nil {
if err = gatev1.Install(s.kubeClient.Scheme()); err != nil {
s.T().Fatal(err)
}
if err = apiextensionsv1.AddToScheme(s.kubeClient.Scheme()); err != nil {
s.T().Fatal(err)
}
}
@ -169,81 +177,54 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
err = try.GetRequest("http://"+k3sContainerIP+":9000/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"web"`))
require.NoError(s.T(), err)
opts := ksuite.Options{
cSuite, err := ksuite.NewConformanceTestSuite(ksuite.ConformanceOptions{
Client: s.kubeClient,
Clientset: s.clientSet,
GatewayClassName: "traefik",
Debug: true,
CleanupBaseResources: true,
TimeoutConfig: config.TimeoutConfig{
CreateTimeout: 5 * time.Second,
DeleteTimeout: 5 * time.Second,
GetTimeout: 5 * time.Second,
GatewayMustHaveAddress: 5 * time.Second,
GatewayMustHaveCondition: 5 * time.Second,
GatewayStatusMustHaveListeners: 10 * time.Second,
GatewayListenersMustHaveCondition: 5 * time.Second,
GWCMustBeAccepted: 60 * time.Second, // Pod creation in k3s cluster can be long.
HTTPRouteMustNotHaveParents: 5 * time.Second,
HTTPRouteMustHaveCondition: 5 * time.Second,
TLSRouteMustHaveCondition: 5 * time.Second,
RouteMustHaveParents: 5 * time.Second,
ManifestFetchTimeout: 5 * time.Second,
MaxTimeToConsistency: 5 * time.Second,
NamespacesMustBeReady: 60 * time.Second, // Pod creation in k3s cluster can be long.
RequestTimeout: 5 * time.Second,
LatestObservedGenerationSet: 5 * time.Second,
RequiredConsecutiveSuccesses: 0,
},
SupportedFeatures: sets.New(ksuite.SupportGateway,
ksuite.SupportGatewayPort8080,
ksuite.SupportHTTPRoute,
ksuite.SupportHTTPRouteQueryParamMatching,
ksuite.SupportHTTPRouteMethodMatching,
ksuite.SupportHTTPRoutePortRedirect,
ksuite.SupportHTTPRouteSchemeRedirect,
ksuite.SupportHTTPRouteHostRewrite,
ksuite.SupportHTTPRoutePathRewrite,
ksuite.SupportHTTPRoutePathRedirect,
),
ExemptFeatures: sets.New(
ksuite.SupportHTTPRouteRequestTimeout,
ksuite.SupportHTTPRouteBackendTimeout,
ksuite.SupportHTTPRouteResponseHeaderModification,
ksuite.SupportHTTPRouteRequestMirror,
ksuite.SupportHTTPRouteRequestMultipleMirrors,
),
TimeoutConfig: config.DefaultTimeoutConfig(),
ManifestFS: []fs.FS{&conformance.Manifests},
EnableAllSupportedFeatures: false,
RunTest: *k8sConformanceRunTest,
// Until the feature are all supported, following tests are skipped.
SkipTests: []string{
tests.HTTPRouteMethodMatching.ShortName,
tests.HTTPRouteQueryParamMatching.ShortName,
},
}
cSuite, err := ksuite.NewExperimentalConformanceTestSuite(ksuite.ExperimentalConformanceOptions{
Options: opts,
Implementation: conformanceV1alpha1.Implementation{
Implementation: v1.Implementation{
Organization: "traefik",
Project: "traefik",
URL: "https://traefik.io/",
Version: version.Version,
Contact: []string{"@traefik/maintainers"},
},
ConformanceProfiles: sets.New(ksuite.HTTPConformanceProfileName),
ConformanceProfiles: sets.New(ksuite.GatewayHTTPConformanceProfileName),
SupportedFeatures: sets.New(
features.SupportGateway,
features.SupportGatewayPort8080,
features.SupportHTTPRoute,
features.SupportHTTPRouteQueryParamMatching,
features.SupportHTTPRouteMethodMatching,
features.SupportHTTPRoutePortRedirect,
features.SupportHTTPRouteSchemeRedirect,
features.SupportHTTPRouteHostRewrite,
features.SupportHTTPRoutePathRewrite,
features.SupportHTTPRoutePathRedirect,
),
ExemptFeatures: sets.New(
features.SupportHTTPRouteRequestTimeout,
features.SupportHTTPRouteBackendTimeout,
features.SupportHTTPRouteResponseHeaderModification,
features.SupportHTTPRouteRequestMirror,
features.SupportHTTPRouteRequestMultipleMirrors,
),
})
require.NoError(s.T(), err)
cSuite.Setup(s.T())
cSuite.Setup(s.T(), tests.ConformanceTests)
err = cSuite.Run(s.T(), tests.ConformanceTests)
require.NoError(s.T(), err)
report, err := cSuite.Report()
require.NoError(s.T(), err, "failed generating conformance report")
report.GatewayAPIVersion = "1.0.0"
rawReport, err := yaml.Marshal(report)
require.NoError(s.T(), err)
s.T().Logf("Conformance report:\n%s", string(rawReport))

View file

@ -1,7 +1,7 @@
version: "3.8"
services:
server:
image: rancher/k3s:v1.20.15-k3s1
image: rancher/k3s:v1.21.14-k3s1
privileged: true
command:
- server
@ -26,7 +26,7 @@ services:
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
node:
image: rancher/k3s:v1.20.15-k3s1
image: rancher/k3s:v1.21.14-k3s1
privileged: true
environment:
K3S_TOKEN: somethingtotallyrandom

View file

@ -459,6 +459,7 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool {
type experimental struct {
HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"`
}
func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
@ -469,11 +470,17 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
if e.HTTP3 != nil {
logger.Error().Msg("HTTP3 is not an experimental feature in v3 and the associated enablement has been removed." +
"Please remove its usage from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#http3-experimental-configuration")
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3-details/#http3")
return true
}
if e.KubernetesGateway != nil {
logger.Error().Msg("KubernetesGateway provider is not an experimental feature starting with v3.1." +
"Please remove its usage from the static configuration." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v3/#gateway-api-kubernetesgateway-provider")
}
return false
}

View file

@ -19,6 +19,7 @@ func TestDeprecationNotice(t *testing.T) {
tests := []struct {
desc string
config configuration
wantCompatible bool
}{
{
desc: "Docker provider swarmMode option is incompatible",
@ -196,6 +197,15 @@ func TestDeprecationNotice(t *testing.T) {
},
},
},
{
desc: "Experimental KubernetesGateway enablement configuration is compatible",
config: configuration{
Experimental: &experimental{
KubernetesGateway: ptr(true),
},
},
wantCompatible: true,
},
{
desc: "Tracing SpanNameLimit option is incompatible",
config: configuration{
@ -278,7 +288,8 @@ func TestDeprecationNotice(t *testing.T) {
})
logger := log.With().Logger().Hook(testHook)
assert.True(t, test.config.deprecationNotice(logger))
assert.Equal(t, !test.wantCompatible, test.config.deprecationNotice(logger))
assert.True(t, gotLog)
assert.Equal(t, zerolog.ErrorLevel, gotLevel)
})

View file

@ -7,5 +7,6 @@ type Experimental struct {
Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"`
LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"`
KubernetesGateway bool `description:"Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
// Deprecated: KubernetesGateway provider is not an experimental feature starting with v3.1. Please remove its usage from the static configuration.
KubernetesGateway bool `description:"(Deprecated) Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"`
}

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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
addressType: IPv4
ports:
- name: websecure
port: 8443
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
addressType: IPv4
ports:
- name: websecure2
port: 8443
endpoints:
- addresses:
- 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:
- addresses:
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
addressType: IPv6
ports:
- name: web
port: 8080
endpoints:
- addresses:
- "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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: myapp2
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
addressType: IPv4
ports:
- name: websecure
port: 443
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
addressType: IPv4
ports:
- name: myapp3
port: 8083
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.9
- ip: 10.10.0.10
ports:
addressType: IPv4
ports:
- name: myapp4
port: 8084
endpoints:
- addresses:
- 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:
- addresses:
- ip: "fd00:10:244:0:1::3"
- ip: "2001:db8:85a3:8d3:1319:8a2e:370:7348"
ports:
addressType: IPv6
ports:
- name: myapp-ipv6
port: 8080
endpoints:
- addresses:
- "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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: myapp2
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
addressType: IPv4
ports:
- name: myapp3
port: 8083
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.9
- ip: 10.10.0.10
ports:
addressType: IPv4
ports:
- name: myapp4
port: 8084
endpoints:
- addresses:
- 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:
- addresses:
- ip: "fd00:10:244:0:1::3"
ports:
addressType: IPv6
ports:
- name: myapp-ipv6
port: 8080
endpoints:
- addresses:
- "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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: myapp
port: 8000
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
addressType: IPv4
ports:
- name: web
port: 80
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 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,14 +503,27 @@ 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
}
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, hostPort),
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,39 +283,46 @@ 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
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 && !p.AllowEmptyServices {
return nil, errors.New("subset not found")
endpointSlices, err := client.GetEndpointSlicesForService(namespace, svc.Name)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
for _, subset := range endpoints.Subsets {
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 {
return nil, errors.New("cannot define a port")
continue
}
for _, addr := range subset.Addresses {
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(addr.IP, strconv.Itoa(int(port))),
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
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 && !p.AllowEmptyServices {
return nil, errors.New("subset not found")
endpointSlices, err := client.GetEndpointSlicesForService(namespace, svc.Name)
if err != nil {
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
for _, subset := range endpoints.Subsets {
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 {
return nil, errors.New("cannot define a port")
continue
}
for _, addr := range subset.Addresses {
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(addr.IP, strconv.Itoa(int(port))),
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

@ -11,9 +11,11 @@ import (
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/types"
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"
ktypes "k8s.io/apimachinery/pkg/types"
kinformers "k8s.io/client-go/informers"
kclientset "k8s.io/client-go/kubernetes"
@ -61,9 +63,9 @@ type Client interface {
ListTLSRoutes() ([]*gatev1alpha2.TLSRoute, error)
ListNamespaces(selector labels.Selector) ([]string, error)
ListReferenceGrants(namespace string) ([]*gatev1beta1.ReferenceGrant, error)
ListEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
GetService(namespace, name string) (*corev1.Service, bool, error)
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error)
}
type clientWrapper struct {
@ -222,7 +224,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
}
@ -543,16 +545,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) {
// ListEndpointSlicesForService returns the EndpointSlices for the given service name in the given namespace.
func (c *clientWrapper) ListEndpointSlicesForService(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)
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 endpoint, exist, err
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

@ -0,0 +1,50 @@
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners: # Use GatewayClass defaults for listener definition.
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- method: GET
path:
type: PathPrefix
value: /foo
backendRefs:
- name: whoami
port: 80
weight: 1
kind: Service
group: ""

View file

@ -0,0 +1,56 @@
---
kind: GatewayClass
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway-class
spec:
controllerName: traefik.io/gateway-controller
---
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: my-gateway
namespace: default
spec:
gatewayClassName: my-gateway-class
listeners: # Use GatewayClass defaults for listener definition.
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: http-app-1
namespace: default
spec:
parentRefs:
- name: my-gateway
kind: Gateway
group: gateway.networking.k8s.io
hostnames:
- "foo.com"
rules:
- matches:
- queryParams:
- type: Exact
name: foo
value: bar
- type: RegularExpression
name: baz
value: buz
path:
type: PathPrefix
value: /foo
backendRefs:
- name: whoami
port: 80
weight: 1
kind: Service
group: ""

View file

@ -17,21 +17,26 @@ 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:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: web
port: 80
- name: web2
port: 8000
port: 8080
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
apiVersion: v1
@ -53,21 +58,26 @@ spec:
task: whoami
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoami-bar
name: whoami-bar-abc
namespace: bar
labels:
kubernetes.io/service-name: whoami-bar
subsets:
- addresses:
- ip: 10.10.0.11
- ip: 10.10.0.12
ports:
addressType: IPv4
ports:
- name: web
port: 80
- name: web2
port: 8000
endpoints:
- addresses:
- 10.10.0.11
- 10.10.0.12
conditions:
ready: true
---
apiVersion: v1
@ -86,19 +96,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:
- addresses:
- ip: 10.10.0.3
- ip: 10.10.0.4
ports:
addressType: IPv4
ports:
- name: web
port: 8080
endpoints:
- addresses:
- 10.10.0.3
- 10.10.0.4
conditions:
ready: true
---
apiVersion: v1
@ -117,19 +132,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:
- addresses:
- ip: 10.10.0.5
- ip: 10.10.0.6
ports:
addressType: IPv4
ports:
- name: websecure
port: 8443
endpoints:
- addresses:
- 10.10.0.5
- 10.10.0.6
conditions:
ready: true
---
apiVersion: v1
@ -148,19 +168,24 @@ spec:
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:
- addresses:
- ip: 10.10.0.7
- ip: 10.10.0.8
ports:
addressType: IPv4
ports:
- name: websecure2
port: 8443
endpoints:
- addresses:
- 10.10.0.7
- 10.10.0.8
conditions:
ready: true
---
apiVersion: v1
@ -201,23 +226,28 @@ spec:
port: 443
---
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:
- addresses:
- ip: 10.10.0.9
- ip: 10.10.0.10
ports:
addressType: IPv4
ports:
- name: tcp-1
protocol: TCP
port: 9000
- name: tcp-2
protocol: TCP
port: 10000
endpoints:
- addresses:
- 10.10.0.9
- 10.10.0.10
conditions:
ready: true
---
apiVersion: v1
@ -236,23 +266,28 @@ spec:
name: tcp-2
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: whoamitcp-bar
name: whoamitcp-bar-abc
namespace: bar
labels:
kubernetes.io/service-name: whoamitcp-bar
subsets:
- addresses:
- ip: 10.10.0.13
- ip: 10.10.0.14
ports:
addressType: IPv4
ports:
- name: tcp-1
protocol: TCP
port: 9000
- name: tcp-2
protocol: TCP
port: 10000
endpoints:
- addresses:
- 10.10.0.13
- 10.10.0.14
conditions:
ready: true
---
apiVersion: v1

View file

@ -370,6 +370,10 @@ func (p *Provider) loadHTTPRouteFilterExtensionRef(namespace string, extensionRe
}
func (p *Provider) loadHTTPServers(namespace string, backendRef gatev1.HTTPBackendRef) (*dynamic.ServersLoadBalancer, error) {
if backendRef.Port == nil {
return nil, errors.New("port is required for Kubernetes Service reference")
}
service, exists, err := p.client.GetService(namespace, string(backendRef.Name))
if err != nil {
return nil, fmt.Errorf("getting service: %w", err)
@ -378,58 +382,60 @@ func (p *Provider) loadHTTPServers(namespace string, backendRef gatev1.HTTPBacke
return nil, errors.New("service not found")
}
var portSpec corev1.ServicePort
var match bool
var svcPort *corev1.ServicePort
for _, p := range service.Spec.Ports {
if backendRef.Port == nil || p.Port == int32(*backendRef.Port) {
portSpec = p
match = true
if p.Port == int32(*backendRef.Port) {
svcPort = &p
break
}
}
if !match {
return nil, errors.New("service port not found")
if svcPort == nil {
return nil, fmt.Errorf("service port %d not found", *backendRef.Port)
}
endpoints, endpointsExists, err := p.client.GetEndpoints(namespace, string(backendRef.Name))
endpointSlices, err := p.client.ListEndpointSlicesForService(namespace, string(backendRef.Name))
if err != nil {
return nil, fmt.Errorf("getting endpoints: %w", err)
return nil, fmt.Errorf("getting endpointslices: %w", err)
}
if !endpointsExists {
return nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, errors.New("subset not found")
if len(endpointSlices) == 0 {
return nil, errors.New("endpointslices not found")
}
lb := &dynamic.ServersLoadBalancer{}
lb.SetDefaults()
protocol := getProtocol(*svcPort)
addresses := map[string]struct{}{}
for _, endpointSlice := range endpointSlices {
var port int32
var portStr string
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
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
}
protocol := getProtocol(portSpec)
for _, endpoint := range endpointSlice.Endpoints {
if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready {
continue
}
portStr = strconv.FormatInt(int64(port), 10)
for _, addr := range subset.Addresses {
for _, address := range endpoint.Addresses {
if _, ok := addresses[address]; ok {
continue
}
addresses[address] = struct{}{}
lb.Servers = append(lb.Servers, dynamic.Server{
URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(addr.IP, portStr)),
URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(address, strconv.Itoa(int(port)))),
})
}
}
}
return lb, nil
}
@ -469,11 +475,11 @@ func buildHostRule(hostnames []gatev1.Hostname) (string, int) {
// The current priority computing is rather naive but aims to fulfill Conformance tests suite requirement.
// The priority is computed to match the following precedence order:
//
// * "Exact" path match. (+100000)
// * "Prefix" path match with largest number of characters. (+10000) PathRegex (+1000)
// * Method match. (not implemented)
// * Largest number of header matches. (+100 each) or with PathRegex (+10 each)
// * Largest number of query param matches. (not implemented)
// * "Exact" path match (+100000).
// * "Prefix" path match with largest number of characters (+10000 + nb_characters*100).
// * Method match (+1000).
// * Largest number of header matches (+100 each).
// * Largest number of query param matches (+10 each).
//
// In case of multiple matches for a route, the maximum priority among all matches is retain.
func buildMatchRule(hostnames []gatev1.Hostname, match gatev1.HTTPRouteMatch) (string, int) {
@ -489,10 +495,19 @@ func buildMatchRule(hostnames []gatev1.Hostname, match gatev1.HTTPRouteMatch) (s
matchRules = append(matchRules, pathRule)
priority += pathPriority
if match.Method != nil {
matchRules = append(matchRules, fmt.Sprintf("Method(`%s`)", *match.Method))
priority += 1000
}
headerRules, headersPriority := buildHeaderRules(match.Headers)
matchRules = append(matchRules, headerRules...)
priority += headersPriority
queryParamRules, queryParamsPriority := buildQueryParamRules(match.QueryParams)
matchRules = append(matchRules, queryParamRules...)
priority += queryParamsPriority
matchRulesStr := strings.Join(matchRules, " && ")
hostRule, hostPriority := buildHostRule(hostnames)
@ -525,7 +540,7 @@ func buildPathRule(pathMatch gatev1.HTTPPathMatch) (string, int) {
return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", pv), 10000 + len(pathValue)*100
case gatev1.PathMatchRegularExpression:
return fmt.Sprintf("PathRegexp(`%s`)", pathValue), 1000 + len(pathValue)*100
return fmt.Sprintf("PathRegexp(`%s`)", pathValue), 10000 + len(pathValue)*100
default:
return "PathPrefix(`/`)", 1
@ -533,18 +548,38 @@ func buildPathRule(pathMatch gatev1.HTTPPathMatch) (string, int) {
}
func buildHeaderRules(headers []gatev1.HTTPHeaderMatch) ([]string, int) {
var rules []string
var priority int
var (
rules []string
priority int
)
for _, header := range headers {
typ := ptr.Deref(header.Type, gatev1.HeaderMatchExact)
switch typ {
case gatev1.HeaderMatchExact:
rules = append(rules, fmt.Sprintf("Header(`%s`,`%s`)", header.Name, header.Value))
priority += 100
case gatev1.HeaderMatchRegularExpression:
rules = append(rules, fmt.Sprintf("HeaderRegexp(`%s`,`%s`)", header.Name, header.Value))
priority += 10
}
priority += 100
}
return rules, priority
}
func buildQueryParamRules(queryParams []gatev1.HTTPQueryParamMatch) ([]string, int) {
var (
rules []string
priority int
)
for _, qp := range queryParams {
typ := ptr.Deref(qp.Type, gatev1.QueryParamMatchExact)
switch typ {
case gatev1.QueryParamMatchExact:
rules = append(rules, fmt.Sprintf("Query(`%s`,`%s`)", qp.Name, qp.Value))
case gatev1.QueryParamMatchRegularExpression:
rules = append(rules, fmt.Sprintf("QueryRegexp(`%s`,`%s`)", qp.Name, qp.Value))
}
priority += 10
}
return rules, priority

View file

@ -1288,7 +1288,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
"default-http-app-1-my-gateway-web-2-d23f7039bc8036fb918c": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && PathRegexp(`^/buzz/[0-9]+$`)",
Priority: 2408,
Priority: 11408,
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-2-wrr",
},
@ -1354,6 +1354,128 @@ func TestLoadHTTPRoutes(t *testing.T) {
TLS: &dynamic.TLSConfiguration{},
},
},
{
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{
"default-http-app-1-my-gateway-web-0-74ad70a7cf090becdd3c": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && (Path(`/foo`) || PathPrefix(`/foo/`)) && Method(`GET`)",
Priority: 11408,
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-0-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-http-app-1-my-gateway-web-0-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-80",
Weight: ptr.To(1),
},
},
},
},
"default-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
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{
"default-http-app-1-my-gateway-web-0-bb7b03c9610e982fd627": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && (Path(`/foo`) || PathPrefix(`/foo/`)) && Query(`foo`,`bar`) && QueryRegexp(`baz`,`buz`)",
Priority: 10428,
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-0-wrr",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-http-app-1-my-gateway-web-0-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
Name: "default-whoami-80",
Weight: ptr.To(1),
},
},
},
},
"default-whoami-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:80",
},
{
URL: "http://10.10.0.2:80",
},
},
PassHostHeader: ptr.To(true),
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{},
},
},
{
desc: "HTTPRoute with Same namespace selector",
paths: []string{"services.yml", "httproute/with_namespace_same.yml"},

View file

@ -206,82 +206,71 @@ func (p *Provider) loadTCPServices(namespace string, backendRefs []gatev1.Backen
return nil, nil, fmt.Errorf("unsupported BackendRef %s/%s/%s", *backendRef.Group, *backendRef.Kind, backendRef.Name)
}
svc := dynamic.TCPService{
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
if backendRef.Port == nil {
return nil, nil, errors.New("port is required for Kubernetes Service reference")
}
service, exists, err := p.client.GetService(namespace, string(backendRef.Name))
if err != nil {
return nil, nil, err
return nil, nil, fmt.Errorf("getting service: %w", err)
}
if !exists {
return nil, nil, errors.New("service not found")
}
if len(service.Spec.Ports) > 1 && backendRef.Port == nil {
// If the port is unspecified and the backend is a Service
// object consisting of multiple port definitions, the route
// must be dropped from the Gateway. The controller should
// raise the "ResolvedRefs" condition on the Gateway with the
// "DroppedRoutes" reason. The gateway status for this route
// should be updated with a condition that describes the error
// more specifically.
log.Error().Msg("A multiple ports Kubernetes Service cannot be used if unspecified backendRef.Port")
var svcPort *corev1.ServicePort
for _, p := range service.Spec.Ports {
if p.Port == int32(*backendRef.Port) {
svcPort = &p
break
}
}
if svcPort == nil {
return nil, nil, fmt.Errorf("service port %d not found", *backendRef.Port)
}
endpointSlices, err := p.client.ListEndpointSlicesForService(namespace, string(backendRef.Name))
if err != nil {
return nil, nil, fmt.Errorf("getting endpointslices: %w", err)
}
if len(endpointSlices) == 0 {
return nil, nil, errors.New("endpointslices not found")
}
svc := dynamic.TCPService{LoadBalancer: &dynamic.TCPServersLoadBalancer{}}
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 {
continue
}
var portSpec corev1.ServicePort
var match bool
for _, p := range service.Spec.Ports {
if backendRef.Port == nil || p.Port == int32(*backendRef.Port) {
portSpec = p
match = true
break
}
for _, endpoint := range endpointSlice.Endpoints {
if endpoint.Conditions.Ready == nil || !*endpoint.Conditions.Ready {
continue
}
if !match {
return nil, nil, errors.New("service port not found")
for _, address := range endpoint.Addresses {
if _, ok := addresses[address]; ok {
continue
}
endpoints, endpointsExists, endpointsErr := p.client.GetEndpoints(namespace, string(backendRef.Name))
if endpointsErr != nil {
return nil, nil, endpointsErr
}
if !endpointsExists {
return nil, nil, errors.New("endpoints not found")
}
if len(endpoints.Subsets) == 0 {
return nil, nil, errors.New("subset not found")
}
var port int32
var portStr string
for _, subset := range endpoints.Subsets {
for _, p := range subset.Ports {
if portSpec.Name == p.Name {
port = p.Port
break
}
}
if port == 0 {
return nil, nil, errors.New("cannot define a port")
}
portStr = strconv.FormatInt(int64(port), 10)
for _, addr := range subset.Addresses {
addresses[address] = struct{}{}
svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, dynamic.TCPServer{
Address: net.JoinHostPort(addr.IP, portStr),
Address: net.JoinHostPort(address, strconv.Itoa(int(port))),
})
}
}
}
serviceName := provider.Normalize(service.Namespace + "-" + service.Name + "-" + portStr)
serviceName := provider.Normalize(service.Namespace + "-" + service.Name + "-" + strconv.Itoa(int(svcPort.Port)))
services[serviceName] = &svc
wrrSvc.Weighted.Services = append(wrrSvc.Weighted.Services, dynamic.TCPWRRService{Name: serviceName, Weight: &weight})

View file

@ -16,10 +16,12 @@ import (
"github.com/traefik/traefik/v3/pkg/types"
traefikversion "github.com/traefik/traefik/v3/pkg/version"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
netv1 "k8s.io/api/networking/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"
@ -41,7 +43,7 @@ type Client interface {
GetService(namespace, name string) (*corev1.Service, bool, error)
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
GetNodes() ([]*corev1.Node, bool, error)
GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error)
GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
UpdateIngressStatus(ing *netv1.Ingress, ingStatus []netv1.IngressLoadBalancerIngress) error
}
@ -185,7 +187,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
}
@ -340,15 +342,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

@ -6,6 +6,7 @@ import (
"github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
netv1 "k8s.io/api/networking/v1"
)
@ -15,13 +16,13 @@ type clientMock struct {
ingresses []*netv1.Ingress
services []*corev1.Service
secrets []*corev1.Secret
endpoints []*corev1.Endpoints
endpointSlices []*discoveryv1.EndpointSlice
nodes []*corev1.Node
ingressClasses []*netv1.IngressClass
apiServiceError error
apiSecretError error
apiEndpointsError error
apiEndpointSlicesError error
apiNodesError error
apiIngressStatusError error
@ -43,8 +44,8 @@ func newClientMock(path string) clientMock {
c.services = append(c.services, o)
case *corev1.Secret:
c.secrets = append(c.secrets, o)
case *corev1.Endpoints:
c.endpoints = append(c.endpoints, o)
case *discoveryv1.EndpointSlice:
c.endpointSlices = append(c.endpointSlices, o)
case *corev1.Node:
c.nodes = append(c.nodes, o)
case *netv1.Ingress:
@ -76,18 +77,19 @@ func (c clientMock) GetService(namespace, name string) (*corev1.Service, bool, e
return nil, false, c.apiServiceError
}
func (c clientMock) GetEndpoints(namespace, name string) (*corev1.Endpoints, bool, error) {
if c.apiEndpointsError != nil {
return nil, false, c.apiEndpointsError
func (c clientMock) GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) {
if c.apiEndpointSlicesError != nil {
return nil, c.apiEndpointSlicesError
}
for _, endpoints := range c.endpoints {
if endpoints.Namespace == namespace && endpoints.Name == name {
return endpoints, true, nil
var result []*discoveryv1.EndpointSlice
for _, endpointSlice := range c.endpointSlices {
if endpointSlice.Namespace == namespace && endpointSlice.Labels[discoveryv1.LabelServiceName] == serviceName {
result = append(result, endpointSlice)
}
}
return &corev1.Endpoints{}, false, nil
return result, nil
}
func (c clientMock) GetNodes() ([]*corev1.Node, bool, error) {

View file

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
netv1 "k8s.io/api/networking/v1"
kerror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -188,10 +189,10 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
assert.False(t, found)
}
func TestClientIgnoresEmptyEndpointUpdates(t *testing.T) {
emptyEndpoint := &corev1.Endpoints{
func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) {
emptyEndpointSlice := &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "empty-endpoint",
Name: "empty-endpointslice",
Namespace: "test",
ResourceVersion: "1244",
Annotations: map[string]string{
@ -200,25 +201,31 @@ func TestClientIgnoresEmptyEndpointUpdates(t *testing.T) {
},
}
filledEndpoint := &corev1.Endpoints{
samplePortName := "testing"
samplePortNumber := int32(1337)
samplePortProtocol := corev1.ProtocolTCP
sampleAddressReady := true
filledEndpointSlice := &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "filled-endpoint",
Name: "filled-endpointslice",
Namespace: "test",
ResourceVersion: "1234",
},
Subsets: []corev1.EndpointSubset{{
Addresses: []corev1.EndpointAddress{{
IP: "10.13.37.1",
}},
Ports: []corev1.EndpointPort{{
Name: "testing",
Port: 1337,
Protocol: "tcp",
AddressType: discoveryv1.AddressTypeIPv4,
Endpoints: []discoveryv1.Endpoint{{
Addresses: []string{"10.13.37.1"},
Conditions: discoveryv1.EndpointConditions{
Ready: &sampleAddressReady,
},
}},
Ports: []discoveryv1.EndpointPort{{
Name: &samplePortName,
Port: &samplePortNumber,
Protocol: &samplePortProtocol,
}},
}
kubeClient := kubefake.NewSimpleClientset(emptyEndpoint, filledEndpoint)
kubeClient := kubefake.NewSimpleClientset(emptyEndpointSlice, filledEndpointSlice)
discovery, _ := kubeClient.Discovery().(*discoveryfake.FakeDiscovery)
discovery.FakedServerVersion = &kversion.Info{
@ -234,50 +241,72 @@ func TestClientIgnoresEmptyEndpointUpdates(t *testing.T) {
select {
case event := <-eventCh:
ep, ok := event.(*corev1.Endpoints)
ep, ok := event.(*discoveryv1.EndpointSlice)
require.True(t, ok)
assert.True(t, ep.Name == "empty-endpoint" || ep.Name == "filled-endpoint")
assert.True(t, ep.Name == "empty-endpointslice" || ep.Name == "filled-endpointslice")
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for endpoints")
assert.Fail(t, "expected to receive event for endpointslices")
}
emptyEndpoint, err = kubeClient.CoreV1().Endpoints("test").Get(context.TODO(), "empty-endpoint", metav1.GetOptions{})
emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "empty-endpointslice", metav1.GetOptions{})
assert.NoError(t, err)
// Update endpoint annotation and resource version (apparently not done by fake client itself)
// to show an update that should not trigger an update event on our eventCh.
// This reflects the behavior of kubernetes controllers which use endpoint annotations for leader election.
emptyEndpoint.Annotations["test-annotation"] = "___"
emptyEndpoint.ResourceVersion = "1245"
_, err = kubeClient.CoreV1().Endpoints("test").Update(context.TODO(), emptyEndpoint, metav1.UpdateOptions{})
emptyEndpointSlice.Annotations["test-annotation"] = "___"
emptyEndpointSlice.ResourceVersion = "1245"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), emptyEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {
case event := <-eventCh:
ep, ok := event.(*corev1.Endpoints)
ep, ok := event.(*discoveryv1.EndpointSlice)
require.True(t, ok)
assert.Fail(t, "didn't expect to receive event for empty endpoint update", ep.Name)
assert.Fail(t, "didn't expect to receive event for empty endpointslice update", ep.Name)
case <-time.After(50 * time.Millisecond):
}
filledEndpoint, err = kubeClient.CoreV1().Endpoints("test").Get(context.TODO(), "filled-endpoint", metav1.GetOptions{})
filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "filled-endpointslice", metav1.GetOptions{})
assert.NoError(t, err)
filledEndpoint.Subsets[0].Addresses[0].IP = "10.13.37.2"
filledEndpoint.ResourceVersion = "1235"
_, err = kubeClient.CoreV1().Endpoints("test").Update(context.TODO(), filledEndpoint, metav1.UpdateOptions{})
filledEndpointSlice.Endpoints[0].Addresses[0] = "10.13.37.2"
filledEndpointSlice.ResourceVersion = "1235"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {
case event := <-eventCh:
ep, ok := event.(*corev1.Endpoints)
ep, ok := event.(*discoveryv1.EndpointSlice)
require.True(t, ok)
assert.Equal(t, "filled-endpoint", ep.Name)
assert.Equal(t, "filled-endpointslice", ep.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for filled endpoint")
assert.Fail(t, "expected to receive event for filled endpointslice")
}
select {
case <-eventCh:
assert.Fail(t, "received more than one event")
case <-time.After(50 * time.Millisecond):
}
newPortNumber := int32(42)
filledEndpointSlice.Ports[0].Port = &newPortNumber
filledEndpointSlice.ResourceVersion = "1236"
_, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{})
require.NoError(t, err)
select {
case event := <-eventCh:
ep, ok := event.(*discoveryv1.EndpointSlice)
require.True(t, ok)
assert.Equal(t, "filled-endpointslice", ep.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for filled endpointslice")
}
select {

View file

@ -1,32 +1,42 @@
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: tchouk
port: 8089
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: toto
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.11.0.1
- ip: 10.11.0.2
ports:
addressType: IPv4
ports:
- name: tchouk
port: 8089
endpoints:
- addresses:
- 10.11.0.1
- 10.11.0.2
conditions:
ready: true
---
kind: Ingress

View file

@ -50,35 +50,41 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiversion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.30.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.41.0.1
ports:
- port: 8080
- 10.30.0.1
- 10.41.0.1
conditions:
ready: true
---
kind: Endpoints
apiversion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service2
name: service2-abc
namespace: testing
labels:
kubernetes.io/service-name: service2
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -40,18 +40,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -37,18 +37,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -30,18 +30,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -36,18 +36,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -52,15 +52,20 @@ spec:
externalName: "2001:0db8:3c4d:0015:0000:0000:1a2f:2a3b"
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service-bar
name: service-bar-abc
namespace: testing
labels:
kubernetes.io/service-name: service-bar
subsets:
- addresses:
- ip: "2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b"
ports:
addressType: IPv6
ports:
- name: http
port: 8080
endpoints:
- addresses:
- "2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b"
conditions:
ready: true

View file

@ -30,18 +30,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8443
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8443
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -31,20 +31,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- name: https
port: 8443
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- name: https
port: 8443
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -31,20 +31,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- name: https-foo
port: 8443
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- name: https-foo
port: 8443
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -29,18 +29,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -33,23 +33,42 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: tchouk
port: 8089
endpoints:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
- ip: 10.10.0.3
ports:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1-def
namespace: testing
labels:
kubernetes.io/service-name: service1
addressType: IPv4
ports:
- name: carotte
port: 8090
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
- 10.10.0.3
conditions:
ready: true

View file

@ -53,18 +53,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -41,18 +41,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -37,18 +37,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -31,14 +31,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -36,27 +36,38 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 80
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: defaultservice
name: defaultservice-abc
namespace: testing
labels:
kubernetes.io/service-name: defaultservice
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -30,14 +30,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -28,14 +28,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -30,14 +30,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -30,14 +30,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -31,14 +31,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -37,15 +37,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -64,14 +64,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -29,14 +29,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -64,14 +64,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -29,15 +29,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- name: foobar
port: 4711
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -31,14 +31,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -31,15 +31,20 @@ spec:
type: ClusterIP
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: example-com
name: example-com-abc
namespace: testing
labels:
kubernetes.io/service-name: example-com
subsets:
- addresses:
- ip: 10.11.0.1
ports:
addressType: IPv4
ports:
- name: http
port: 80
endpoints:
- addresses:
- 10.11.0.1
conditions:
ready: true

View file

@ -30,8 +30,14 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
addressType: IPv4
ports: null
endpoints: []

View file

@ -40,24 +40,43 @@ spec:
type: ClusterIP
---
apiVersion: v1
kind: Endpoints
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
subsets:
- addresses:
- ip: 10.0.0.1
nodeName: admin.whoami.service1
ports:
labels:
kubernetes.io/service-name: service1
addressType: IPv4
ports:
- name: http-admin
port: 8079
protocol: TCP
endpoints:
- addresses:
- ip: 10.0.0.1
nodeName: whoami.service1
# targetRef:
ports:
- 10.0.0.1
conditions:
ready: true
nodeName: admin.whoami.service1
---
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1-def
namespace: testing
labels:
kubernetes.io/service-name: service1
addressType: IPv4
ports:
- name: http
port: 8080
protocol: TCP
endpoints:
- addresses:
- 10.0.0.1
conditions:
ready: true
nodeName: whoami.service1

View file

@ -33,18 +33,23 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true

View file

@ -33,24 +33,23 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -33,24 +33,23 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -28,14 +28,20 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- 10.10.0.1
conditions:
ready: true

View file

@ -38,18 +38,21 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true

View file

@ -40,18 +40,23 @@ spec:
clusterIP: 10.0.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- name: carotte
port: 8090
- name: tchouk
port: 8089
endpoints:
- addresses:
- 10.10.0.1
- 10.10.0.2
conditions:
ready: true

View file

@ -52,35 +52,41 @@ spec:
clusterIP: 10.1.0.1
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service1
name: service1-abc
namespace: testing
labels:
kubernetes.io/service-name: service1
subsets:
- addresses:
- ip: 10.10.0.1
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.1
ports:
- port: 8080
- 10.10.0.1
- 10.21.0.1
conditions:
ready: true
---
kind: Endpoints
apiVersion: v1
kind: EndpointSlice
apiVersion: discovery.k8s.io/v1
metadata:
name: service2
name: service2-abc
namespace: testing
labels:
kubernetes.io/service-name: service2
subsets:
- addresses:
- ip: 10.10.0.2
ports:
addressType: IPv4
ports:
- port: 8080
name: ""
endpoints:
- addresses:
- ip: 10.21.0.2
ports:
- port: 8080
- 10.10.0.2
- 10.21.0.2
conditions:
ready: true

Some files were not shown because too many files have changed in this diff Show more