Upgrade Ingress Handling to work with networkingv1/Ingress

This commit is contained in:
Manuel Zapf 2021-03-15 11:16:04 +01:00 committed by GitHub
parent 702e301990
commit 29908098e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 1141 additions and 113 deletions

View file

@ -348,3 +348,13 @@ After deploying the new [Traefik CRDs](../reference/dynamic-configuration/kubern
Please note that the unknown fields will not be pruned when migrating from `apiextensions.k8s.io/v1beta1` to `apiextensions.k8s.io/v1` CRDs.
For more details check out the official [documentation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema).
### Kubernetes Ingress
Traefik v2.5 moves forward for the Ingress provider to support Kubernetes v1.22.
Traefik now supports only v1.14+ Kubernetes clusters, which means the support of `extensions/v1beta1` API Version ingresses has been dropped.
The `extensions/v1beta1` API Version should now be replaced either by `networking.k8s.io/v1beta1` or by `networking.k8s.io/v1` (as of Kubernetes v1.19+).
The support of the `networking.k8s.io/v1beta1` API Version will stop in Kubernetes v1.22.

View file

@ -6,6 +6,10 @@ The Kubernetes Ingress Controller.
The Traefik Kubernetes Ingress provider is a Kubernetes Ingress controller; that is to say,
it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification.
## Requirements
Traefik supports `1.14+` Kubernetes clusters.
## Routing Configuration
See the dedicated section in [routing](../routing/providers/kubernetes-ingress.md).
@ -31,9 +35,9 @@ The provider then watches for incoming ingresses events, such as the example bel
and derives the corresponding dynamic configuration from it,
which in turn creates the resulting routers, services, handlers, etc.
```yaml tab="File (YAML)"
```yaml tab="Ingress"
kind: Ingress
apiVersion: extensions/v1beta1
apiVersion: networking.k8s.io/v1beta1
metadata:
name: "foo"
namespace: production
@ -53,6 +57,32 @@ spec:
servicePort: 80
```
```yaml tab="Ingress Kubernetes v1.19+"
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: "foo"
namespace: production
spec:
rules:
- host: example.net
http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80
- path: /foo
backend:
service:
name: service1
port:
number: 80
```
## LetsEncrypt Support with the Ingress Provider
By design, Traefik is a stateless application,
@ -220,7 +250,7 @@ Value of `kubernetes.io/ingress.class` annotation that identifies Ingress object
If the parameter is set, only Ingresses containing an annotation with the same value are processed.
Otherwise, Ingresses missing the annotation, having an empty value, or the value `traefik` are processed.
!!! info "Kubernetes 1.18+"
??? info "Kubernetes 1.18+"
If the Kubernetes cluster version is 1.18+,
the new `IngressClass` resource can be leveraged to identify Ingress objects that should be processed.
@ -256,6 +286,39 @@ Otherwise, Ingresses missing the annotation, having an empty value, or the value
servicePort: 80
```
??? info "Kubernetes 1.19+"
If the Kubernetes cluster version is 1.19+,
prefer using the `networking.k8s.io/v1` [apiVersion](https://v1-19.docs.kubernetes.io/docs/setup/release/notes/#api-change) of `Ingress` and `IngressClass`.
```yaml tab="IngressClass"
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: traefik-lb
spec:
controller: traefik.io/ingress-controller
```
```yaml tab="Ingress"
apiVersion: "networking.k8s.io/v1"
kind: "Ingress"
metadata:
name: "example-ingress"
spec:
ingressClassName: "traefik-lb"
rules:
- host: "*.example.com"
http:
paths:
- path: "/example"
backend:
service:
name: "example-service"
port:
number: 80
```
```toml tab="File (TOML)"
[providers.kubernetesIngress]
ingressClass = "traefik-internal"

View file

@ -85,6 +85,33 @@ which in turn will create the resulting routers, services, handlers, etc.
servicePort: 80
```
```yaml tab="Ingress Kubernetes v1.19+"
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: myingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: example.com
http:
paths:
- path: /bar
backend:
service:
name: whoami
port:
number: 80
- path: /foo
backend:
service:
name: whoami
port:
number: 80
```
```yaml tab="Traefik"
apiVersion: v1
kind: ServiceAccount
@ -434,6 +461,33 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d
servicePort: 80
```
```yaml tab="Ingress Kubernetes v1.19+"
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: myingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
spec:
rules:
- host: example.com
http:
paths:
- path: /bar
backend:
service:
name: whoami
port:
number: 80
- path: /foo
backend:
service:
name: whoami
port:
number: 80
```
```yaml tab="Traefik"
apiVersion: v1
kind: ServiceAccount
@ -613,6 +667,34 @@ For more options, please refer to the available [annotations](#on-ingress).
servicePort: 80
```
```yaml tab="Ingress Kubernetes v1.19+"
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: myingress
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: true
spec:
rules:
- host: example.com
http:
paths:
- path: /bar
backend:
service:
name: whoami
port:
number: 80
- path: /foo
backend:
service:
name: whoami
port:
number: 80
```
```yaml tab="Traefik"
apiVersion: v1
kind: ServiceAccount
@ -732,6 +814,31 @@ For more options, please refer to the available [annotations](#on-ingress).
tls:
- secretName: supersecret
```
```yaml tab="Ingress Kubernetes v1.19+"
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: foo
namespace: production
spec:
rules:
- host: example.net
http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80
# Only selects which certificate(s) should be loaded from the secret, in order to terminate TLS.
# Doesn't enable TLS for that ingress (hence for the underlying router).
# Please see the TLS annotations on ingress made for that purpose.
tls:
- secretName: supersecret
```
```yaml tab="Secret"
apiVersion: v1
@ -777,16 +884,30 @@ and will connect via TLS automatically.
Ingresses can be created that look like the following:
```yaml
```yaml tab="Ingress"
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: cheese
spec:
backend:
serviceName: stilton
servicePort: 80
defaultBackend:
serviceName: stilton
serverPort: 80
```
```yaml tab="Ingress Kubernetes v1.19+"
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cheese
spec:
defaultBackend:
service:
name: stilton
port:
number: 80
```
This ingress follows the Global Default Backend property of ingresses.

View file

@ -1,11 +1,9 @@
package ingress
import (
"k8s.io/api/networking/v1beta1"
)
import networkingv1 "k8s.io/api/networking/v1"
func buildIngress(opts ...func(*v1beta1.Ingress)) *v1beta1.Ingress {
i := &v1beta1.Ingress{}
func buildIngress(opts ...func(*networkingv1.Ingress)) *networkingv1.Ingress {
i := &networkingv1.Ingress{}
i.Kind = "Ingress"
for _, opt := range opts {
opt(i)
@ -13,15 +11,15 @@ func buildIngress(opts ...func(*v1beta1.Ingress)) *v1beta1.Ingress {
return i
}
func iNamespace(value string) func(*v1beta1.Ingress) {
return func(i *v1beta1.Ingress) {
func iNamespace(value string) func(*networkingv1.Ingress) {
return func(i *networkingv1.Ingress) {
i.Namespace = value
}
}
func iRules(opts ...func(*v1beta1.IngressSpec)) func(*v1beta1.Ingress) {
return func(i *v1beta1.Ingress) {
s := &v1beta1.IngressSpec{}
func iRules(opts ...func(*networkingv1.IngressSpec)) func(*networkingv1.Ingress) {
return func(i *networkingv1.Ingress) {
s := &networkingv1.IngressSpec{}
for _, opt := range opts {
opt(s)
}
@ -29,9 +27,9 @@ func iRules(opts ...func(*v1beta1.IngressSpec)) func(*v1beta1.Ingress) {
}
}
func iRule(opts ...func(*v1beta1.IngressRule)) func(*v1beta1.IngressSpec) {
return func(spec *v1beta1.IngressSpec) {
r := &v1beta1.IngressRule{}
func iRule(opts ...func(*networkingv1.IngressRule)) func(*networkingv1.IngressSpec) {
return func(spec *networkingv1.IngressSpec) {
r := &networkingv1.IngressRule{}
for _, opt := range opts {
opt(r)
}
@ -39,24 +37,24 @@ func iRule(opts ...func(*v1beta1.IngressRule)) func(*v1beta1.IngressSpec) {
}
}
func iHost(name string) func(*v1beta1.IngressRule) {
return func(rule *v1beta1.IngressRule) {
func iHost(name string) func(*networkingv1.IngressRule) {
return func(rule *networkingv1.IngressRule) {
rule.Host = name
}
}
func iTLSes(opts ...func(*v1beta1.IngressTLS)) func(*v1beta1.Ingress) {
return func(i *v1beta1.Ingress) {
func iTLSes(opts ...func(*networkingv1.IngressTLS)) func(*networkingv1.Ingress) {
return func(i *networkingv1.Ingress) {
for _, opt := range opts {
iTLS := v1beta1.IngressTLS{}
iTLS := networkingv1.IngressTLS{}
opt(&iTLS)
i.Spec.TLS = append(i.Spec.TLS, iTLS)
}
}
}
func iTLS(secret string, hosts ...string) func(*v1beta1.IngressTLS) {
return func(i *v1beta1.IngressTLS) {
func iTLS(secret string, hosts ...string) func(*networkingv1.IngressTLS) {
return func(i *networkingv1.IngressTLS) {
i.SecretName = secret
i.Hosts = hosts
}

View file

@ -13,11 +13,12 @@ import (
"github.com/traefik/traefik/v2/pkg/log"
traefikversion "github.com/traefik/traefik/v2/pkg/version"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
kubeerror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
@ -54,12 +55,12 @@ func (reh *resourceEventHandler) OnDelete(obj interface{}) {
// The stores can then be accessed via the Get* functions.
type Client interface {
WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error)
GetIngresses() []*networkingv1beta1.Ingress
GetIngresses() []*networkingv1.Ingress
GetIngressClasses() ([]*networkingv1beta1.IngressClass, 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)
UpdateIngressStatus(ing *networkingv1beta1.Ingress, ingStatus []corev1.LoadBalancerIngress) error
UpdateIngressStatus(ing *networkingv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error
GetServerVersion() (*version.Version, error)
}
@ -167,9 +168,20 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
opts.LabelSelector = c.ingressLabelSelector
}
serverVersion, err := c.GetServerVersion()
if err != nil {
return nil, fmt.Errorf("failed to get server version: %w", err)
}
for _, ns := range namespaces {
factoryIngress := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(matchesLabelSelector))
factoryIngress.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
if supportsNetworkingV1Ingress(serverVersion) {
factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler)
} else {
factoryIngress.Networking().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
}
c.factoriesIngress[ns] = factoryIngress
factoryKube := informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, informers.WithNamespace(ns))
@ -208,12 +220,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
}
}
serverVersion, err := c.GetServerVersion()
if err != nil {
log.WithoutContext().Errorf("Failed to get server version: %v", err)
return eventCh, nil
}
if supportsIngressClass(serverVersion) {
c.clusterFactory = informers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler)
@ -230,42 +236,59 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
}
// GetIngresses returns all Ingresses for observed namespaces in the cluster.
func (c *clientWrapper) GetIngresses() []*networkingv1beta1.Ingress {
var results []*networkingv1beta1.Ingress
func (c *clientWrapper) GetIngresses() []*networkingv1.Ingress {
var results []*networkingv1.Ingress
serverVersion, err := c.GetServerVersion()
if err != nil {
log.Errorf("Failed to get server version: %v", err)
return results
}
isNetworkingV1Supported := supportsNetworkingV1Ingress(serverVersion)
for ns, factory := range c.factoriesIngress {
// extensions
ings, err := factory.Extensions().V1beta1().Ingresses().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
}
for _, ing := range ings {
n, err := extensionsToNetworking(ing)
if isNetworkingV1Supported {
// networking
listNew, err := factory.Networking().V1().Ingresses().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to convert ingress %s from extensions/v1beta1 to networking/v1beta1: %v", ns, err)
log.WithoutContext().Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
continue
}
results = append(results, n)
results = append(results, listNew...)
continue
}
// networking
// networking beta
list, err := factory.Networking().V1beta1().Ingresses().Lister().List(labels.Everything())
if err != nil {
log.Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
log.WithoutContext().Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
continue
}
for _, ing := range list {
n, err := toNetworkingV1(ing)
if err != nil {
log.WithoutContext().Errorf("Failed to convert ingress %s from networking/v1beta1 to networking/v1: %v", ns, err)
continue
}
addServiceFromV1Beta1(n, *ing)
results = append(results, n)
}
results = append(results, list...)
}
return results
}
func extensionsToNetworking(ing marshaler) (*networkingv1beta1.Ingress, error) {
func toNetworkingV1(ing marshaler) (*networkingv1.Ingress, error) {
data, err := ing.Marshal()
if err != nil {
return nil, err
}
ni := &networkingv1beta1.Ingress{}
ni := &networkingv1.Ingress{}
err = ni.Unmarshal(data)
if err != nil {
return nil, err
@ -274,16 +297,95 @@ func extensionsToNetworking(ing marshaler) (*networkingv1beta1.Ingress, error) {
return ni, nil
}
func addServiceFromV1Beta1(ing *networkingv1.Ingress, old networkingv1beta1.Ingress) {
if old.Spec.Backend != nil {
port := networkingv1.ServiceBackendPort{}
if old.Spec.Backend.ServicePort.Type == intstr.Int {
port.Number = old.Spec.Backend.ServicePort.IntVal
} else {
port.Name = old.Spec.Backend.ServicePort.StrVal
}
if old.Spec.Backend.ServiceName != "" {
ing.Spec.DefaultBackend = &networkingv1.IngressBackend{
Service: &networkingv1.IngressServiceBackend{
Name: old.Spec.Backend.ServiceName,
Port: port,
},
}
}
}
for rc, rule := range ing.Spec.Rules {
if rule.HTTP == nil {
continue
}
for pc, path := range rule.HTTP.Paths {
if path.Backend.Service == nil {
oldBackend := old.Spec.Rules[rc].HTTP.Paths[pc].Backend
port := networkingv1.ServiceBackendPort{}
if oldBackend.ServicePort.Type == intstr.Int {
port.Number = oldBackend.ServicePort.IntVal
} else {
port.Name = oldBackend.ServicePort.StrVal
}
svc := networkingv1.IngressServiceBackend{
Name: oldBackend.ServiceName,
Port: port,
}
ing.Spec.Rules[rc].HTTP.Paths[pc].Backend.Service = &svc
}
}
}
}
// UpdateIngressStatus updates an Ingress with a provided status.
func (c *clientWrapper) UpdateIngressStatus(src *networkingv1beta1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
func (c *clientWrapper) UpdateIngressStatus(src *networkingv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
if !c.isWatchedNamespace(src.Namespace) {
return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", src.Namespace, src.Name)
}
if src.GetObjectKind().GroupVersionKind().Group != "networking.k8s.io" {
serverVersion, err := c.GetServerVersion()
if err != nil {
log.WithoutContext().Errorf("Failed to get server version: %v", err)
return err
}
if !supportsNetworkingV1Ingress(serverVersion) {
return c.updateIngressStatusOld(src, ingStatus)
}
ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Networking().V1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
if err != nil {
return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
}
logger := log.WithoutContext().WithField("namespace", ing.Namespace).WithField("ingress", ing.Name)
if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) {
logger.Debug("Skipping ingress status update")
return nil
}
ingCopy := ing.DeepCopy()
ingCopy.Status = networkingv1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: ingStatus}}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
_, err = c.clientset.NetworkingV1().Ingresses(ingCopy.Namespace).UpdateStatus(ctx, ingCopy, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err)
}
logger.Info("Updated ingress status")
return nil
}
func (c *clientWrapper) updateIngressStatusOld(src *networkingv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Networking().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
if err != nil {
return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
@ -306,35 +408,6 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1beta1.Ingress, ingS
if err != nil {
return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err)
}
logger.Info("Updated ingress status")
return nil
}
func (c *clientWrapper) updateIngressStatusOld(src *networkingv1beta1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Extensions().V1beta1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name)
if err != nil {
return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
}
logger := log.WithoutContext().WithField("namespace", ing.Namespace).WithField("ingress", ing.Name)
if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) {
logger.Debug("Skipping ingress status update")
return nil
}
ingCopy := ing.DeepCopy()
ingCopy.Status = extensionsv1beta1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: ingStatus}}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
_, err = c.clientset.ExtensionsV1beta1().Ingresses(ingCopy.Namespace).UpdateStatus(ctx, ingCopy, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err)
}
logger.Info("Updated ingress status")
return nil
}
@ -488,3 +561,11 @@ func filterIngressClassByName(ingressClassName string, ics []*networkingv1beta1.
return ingressClasses
}
// Ingress in networking.k8s.io/v1 is supported starting 1.19.
// thus, we query it in K8s starting 1.19.
func supportsNetworkingV1Ingress(serverVersion *version.Version) bool {
ingressNetworkingVersion := version.Must(version.NewVersion("1.19"))
return serverVersion.GreaterThanOrEqual(ingressNetworkingVersion)
}

View file

@ -7,14 +7,14 @@ import (
"github.com/hashicorp/go-version"
"github.com/traefik/traefik/v2/pkg/provider/kubernetes/k8s"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
)
var _ Client = (*clientMock)(nil)
type clientMock struct {
ingresses []*networkingv1beta1.Ingress
ingresses []*networkingv1.Ingress
services []*corev1.Service
secrets []*corev1.Secret
endpoints []*corev1.Endpoints
@ -51,13 +51,14 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
case *corev1.Endpoints:
c.endpoints = append(c.endpoints, o)
case *networkingv1beta1.Ingress:
c.ingresses = append(c.ingresses, o)
case *extensionsv1beta1.Ingress:
ing, err := extensionsToNetworking(o)
ing, err := toNetworkingV1(o)
if err != nil {
panic(err)
}
addServiceFromV1Beta1(ing, *o)
c.ingresses = append(c.ingresses, ing)
case *networkingv1.Ingress:
c.ingresses = append(c.ingresses, o)
case *networkingv1beta1.IngressClass:
c.ingressClasses = append(c.ingressClasses, o)
default:
@ -69,7 +70,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
return c
}
func (c clientMock) GetIngresses() []*networkingv1beta1.Ingress {
func (c clientMock) GetIngresses() []*networkingv1.Ingress {
return c.ingresses
}
@ -125,6 +126,6 @@ func (c clientMock) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-cha
return c.watchChan, nil
}
func (c clientMock) UpdateIngressStatus(_ *networkingv1beta1.Ingress, _ []corev1.LoadBalancerIngress) error {
func (c clientMock) UpdateIngressStatus(_ *networkingv1.Ingress, _ []corev1.LoadBalancerIngress) error {
return c.apiIngressStatusError
}

View file

@ -8,9 +8,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/api/networking/v1beta1"
kubeerror "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/version"
fakediscovery "k8s.io/client-go/discovery/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
)
@ -149,6 +153,11 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
discovery, _ := kubeClient.Discovery().(*fakediscovery.FakeDiscovery)
discovery.FakedServerVersion = &version.Info{
GitVersion: "v1.19",
}
client := newClientImpl(kubeClient)
stopCh := make(chan struct{})
@ -180,3 +189,72 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
require.NoError(t, err)
assert.False(t, found)
}
func TestClientUsesCorrectServerVersion(t *testing.T) {
ingressV1Beta := &v1beta1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "ingress-v1beta",
},
}
ingressV1 := &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Namespace: "default",
Name: "ingress-v1",
},
}
kubeClient := kubefake.NewSimpleClientset(ingressV1Beta, ingressV1)
discovery, _ := kubeClient.Discovery().(*fakediscovery.FakeDiscovery)
discovery.FakedServerVersion = &version.Info{
GitVersion: "v1.18.12+foobar",
}
stopCh := make(chan struct{})
client := newClientImpl(kubeClient)
eventCh, err := client.WatchAll(nil, stopCh)
require.NoError(t, err)
select {
case event := <-eventCh:
ingress, ok := event.(*v1beta1.Ingress)
require.True(t, ok)
assert.Equal(t, "ingress-v1beta", ingress.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for ingress")
}
select {
case <-eventCh:
assert.Fail(t, "received more than one event")
case <-time.After(50 * time.Millisecond):
}
discovery.FakedServerVersion = &version.Info{
GitVersion: "v1.19",
}
eventCh, err = client.WatchAll(nil, stopCh)
require.NoError(t, err)
select {
case event := <-eventCh:
ingress, ok := event.(*networkingv1.Ingress)
require.True(t, ok)
assert.Equal(t, "ingress-v1", ingress.Name)
case <-time.After(50 * time.Millisecond):
assert.Fail(t, "expected to receive event for ingress")
}
select {
case <-eventCh:
assert.Fail(t, "received more than one event")
case <-time.After(50 * time.Millisecond):
}
}

View file

@ -0,0 +1,24 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 80
---
kind: Endpoints
apiVersion: v1
metadata:
name: defaultservice
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,12 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: defaultbackend
namespace: testing
spec:
defaultBackend:
service:
name: defaultservice
port:
number: 8080

View file

@ -0,0 +1,22 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1
---
kind: Service
apiVersion: v1
metadata:
name: defaultservice
namespace: testing
spec:
ports:
- port: 8080
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,18 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
annotations:
traefik.ingress.kubernetes.io/router.pathmatcher: Path
spec:
rules:
- http:
paths:
- path: /bar
pathType: ""
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,16 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
rules:
- http:
paths:
- path: /bar
pathType: Exact
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,18 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
annotations:
traefik.ingress.kubernetes.io/router.pathmatcher: Path
spec:
rules:
- http:
paths:
- path: /bar
pathType: ImplementationSpecific
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,17 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,16 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
ingressClassName: traefik-lb
rules:
- http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,6 @@
apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
name: traefik-lb
spec:
controller: traefik.io/ingress-controller

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,16 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
ingressClassName: traefik-lb
rules:
- http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,12 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- name: foobar
port: 4711

View file

@ -0,0 +1,16 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
rules:
- http:
paths:
- path: /bar
pathType: Prefix
backend:
service:
name: service1
port:
name: foobar

View file

@ -0,0 +1,12 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- name: foobar
port: 4711
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,17 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
annotations:
traefik.ingress.kubernetes.io/router.pathmatcher: Path
spec:
rules:
- http:
paths:
- path: /bar
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -0,0 +1,11 @@
kind: Endpoints
apiVersion: v1
metadata:
name: service1
namespace: testing
subsets:
- addresses:
- ip: 10.10.0.1
ports:
- port: 8080

View file

@ -0,0 +1,16 @@
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: ""
namespace: testing
spec:
rules:
- http:
paths:
- path: /bar
pathType: Prefix
backend:
service:
name: service1
port:
number: 80

View file

@ -0,0 +1,10 @@
kind: Service
apiVersion: v1
metadata:
name: service1
namespace: testing
spec:
ports:
- port: 80
clusterIP: 10.0.0.1

View file

@ -23,9 +23,9 @@ import (
"github.com/traefik/traefik/v2/pkg/safe"
"github.com/traefik/traefik/v2/pkg/tls"
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/intstr"
)
const (
@ -226,17 +226,17 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
log.FromContext(ctx).Errorf("Error configuring TLS: %v", err)
}
if len(ingress.Spec.Rules) == 0 && ingress.Spec.Backend != nil {
if len(ingress.Spec.Rules) == 0 && ingress.Spec.DefaultBackend != nil {
if _, ok := conf.HTTP.Services["default-backend"]; ok {
log.FromContext(ctx).Error("The default backend already exists.")
continue
}
service, err := loadService(client, ingress.Namespace, *ingress.Spec.Backend)
service, err := loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend)
if err != nil {
log.FromContext(ctx).
WithField("serviceName", ingress.Spec.Backend.ServiceName).
WithField("servicePort", ingress.Spec.Backend.ServicePort.String()).
WithField("serviceName", ingress.Spec.DefaultBackend.Service.Name).
WithField("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()).
Errorf("Cannot create service: %v", err)
continue
}
@ -272,13 +272,19 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
service, err := loadService(client, ingress.Namespace, pa.Backend)
if err != nil {
log.FromContext(ctx).
WithField("serviceName", pa.Backend.ServiceName).
WithField("servicePort", pa.Backend.ServicePort.String()).
WithField("serviceName", pa.Backend.Service.Name).
WithField("servicePort", pa.Backend.Service.Port.String()).
Errorf("Cannot create service: %v", err)
continue
}
serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.ServiceName + "-" + pa.Backend.ServicePort.String())
portString := pa.Backend.Service.Port.Name
if len(pa.Backend.Service.Port.Name) == 0 {
portString = fmt.Sprint(pa.Backend.Service.Port.Number)
}
serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.Service.Name + "-" + portString)
conf.HTTP.Services[serviceName] = service
routerKey := strings.TrimPrefix(provider.Normalize(ingress.Name+"-"+ingress.Namespace+"-"+rule.Host+pa.Path), "-")
@ -316,7 +322,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
return conf
}
func (p *Provider) updateIngressStatus(ing *networkingv1beta1.Ingress, k8sClient Client) error {
func (p *Provider) updateIngressStatus(ing *networkingv1.Ingress, k8sClient Client) error {
// Only process if an EndpointIngress has been configured.
if p.IngressEndpoint == nil {
return nil
@ -355,7 +361,7 @@ func (p *Provider) updateIngressStatus(ing *networkingv1beta1.Ingress, k8sClient
return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress)
}
func (p *Provider) shouldProcessIngress(ingress *networkingv1beta1.Ingress, ingressClasses []*networkingv1beta1.IngressClass) bool {
func (p *Provider) shouldProcessIngress(ingress *networkingv1.Ingress, ingressClasses []*networkingv1beta1.IngressClass) bool {
// configuration through the new kubernetes ingressClass
if ingress.Spec.IngressClassName != nil {
for _, ic := range ingressClasses {
@ -379,7 +385,7 @@ func buildHostRule(host string) string {
return "Host(`" + host + "`)"
}
func getCertificates(ctx context.Context, ingress *networkingv1beta1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
func getCertificates(ctx context.Context, ingress *networkingv1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
for _, t := range ingress.Spec.TLS {
if t.SecretName == "" {
log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
@ -464,8 +470,8 @@ func getTLSConfig(tlsConfigs map[string]*tls.CertAndStores) []*tls.CertAndStores
return configs
}
func loadService(client Client, namespace string, backend networkingv1beta1.IngressBackend) (*dynamic.Service, error) {
service, exists, err := client.GetService(namespace, backend.ServiceName)
func loadService(client Client, namespace string, backend networkingv1.IngressBackend) (*dynamic.Service, error) {
service, exists, err := client.GetService(namespace, backend.Service.Name)
if err != nil {
return nil, err
}
@ -478,8 +484,7 @@ func loadService(client Client, namespace string, backend networkingv1beta1.Ingr
var portSpec corev1.ServicePort
var match bool
for _, p := range service.Spec.Ports {
if (backend.ServicePort.Type == intstr.Int && backend.ServicePort.IntVal == p.Port) ||
(backend.ServicePort.Type == intstr.String && backend.ServicePort.StrVal == p.Name) {
if backend.Service.Port.Number == p.Port || (backend.Service.Port.Name == p.Name && len(p.Name) > 0) {
portName = p.Name
portSpec = p
match = true
@ -520,7 +525,7 @@ func loadService(client Client, namespace string, backend networkingv1beta1.Ingr
return svc, nil
}
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, backend.ServiceName)
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, backend.Service.Name)
if endpointsErr != nil {
return nil, endpointsErr
}
@ -584,7 +589,7 @@ func makeRouterKeyWithHash(key, rule string) (string, error) {
return dupKey, nil
}
func loadRouter(rule networkingv1beta1.IngressRule, pa networkingv1beta1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
func loadRouter(rule networkingv1.IngressRule, pa networkingv1.HTTPIngressPath, rtConfig *RouterConfig, serviceName string) *dynamic.Router {
var rules []string
if len(rule.Host) > 0 {
rules = []string{buildHostRule(rule.Host)}
@ -593,11 +598,11 @@ func loadRouter(rule networkingv1beta1.IngressRule, pa networkingv1beta1.HTTPIng
if len(pa.Path) > 0 {
matcher := defaultPathMatcher
if pa.PathType == nil || *pa.PathType == "" || *pa.PathType == networkingv1beta1.PathTypeImplementationSpecific {
if pa.PathType == nil || *pa.PathType == "" || *pa.PathType == networkingv1.PathTypeImplementationSpecific {
if rtConfig != nil && rtConfig.Router != nil && rtConfig.Router.PathMatcher != "" {
matcher = rtConfig.Router.PathMatcher
}
} else if *pa.PathType == networkingv1beta1.PathTypeExact {
} else if *pa.PathType == networkingv1.PathTypeExact {
matcher = "Path"
}

View file

@ -14,7 +14,7 @@ import (
"github.com/traefik/traefik/v2/pkg/tls"
"github.com/traefik/traefik/v2/pkg/types"
corev1 "k8s.io/api/core/v1"
"k8s.io/api/networking/v1beta1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -1302,6 +1302,271 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
},
},
},
{
desc: "v19 Ingress with prefix pathType",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with no pathType",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "Path(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with empty pathType",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "Path(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with exact pathType",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "Path(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with implementationSpecific pathType",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "Path(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with ingress annotation",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with ingressClass",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "PathPrefix(`/bar`)",
Service: "testing-service1-80",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-80": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with named port",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"testing-bar": {
Rule: "PathPrefix(`/bar`)",
Service: "testing-service1-foobar",
},
},
Services: map[string]*dynamic.Service{
"testing-service1-foobar": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:4711",
},
},
},
},
},
},
},
},
{
desc: "v19 Ingress with missing ingressClass",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{},
Services: map[string]*dynamic.Service{},
},
},
},
{
desc: "v19 Ingress with defaultbackend",
serverVersion: "v1.19",
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{},
HTTP: &dynamic.HTTPConfiguration{
Middlewares: map[string]*dynamic.Middleware{},
Routers: map[string]*dynamic.Router{
"default-router": {
Rule: "PathPrefix(`/`)",
Priority: math.MinInt32,
Service: "default-backend",
},
},
Services: map[string]*dynamic.Service{
"default-backend": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: Bool(true),
Servers: []dynamic.Server{
{
URL: "http://10.10.0.1:8080",
},
},
},
},
},
},
},
},
}
for _, test := range testCases {
@ -1375,7 +1640,7 @@ func TestGetCertificates(t *testing.T) {
testCases := []struct {
desc string
ingress *v1beta1.Ingress
ingress *networkingv1.Ingress
client Client
result map[string]*tls.CertAndStores
errResult string