1
0
Fork 0

Merge branch v2.10 into v3.0

This commit is contained in:
Fernandez Ludovic 2023-04-17 11:34:00 +02:00
commit 79c5f34156
40 changed files with 792 additions and 452 deletions

View file

@ -220,8 +220,8 @@ func (r *ResponseForwarding) SetDefaults() {
// Server holds the server configuration.
type Server struct {
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
Scheme string `toml:"-" json:"-" yaml:"-" file:"-"`
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
Scheme string `json:"-" toml:"-" yaml:"-" file:"-"`
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
}
// SetDefaults Default values for a Server.
@ -282,7 +282,7 @@ type Spiffe struct {
// IDs defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain).
IDs []string `description:"Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain)." json:"ids,omitempty" toml:"ids,omitempty" yaml:"ids,omitempty"`
// TrustDomain defines the allowed SPIFFE trust domain.
TrustDomain string `description:"Defines the allowed SPIFFE trust domain." json:"trustDomain,omitempty" yaml:"trustDomain,omitempty" toml:"trustDomain,omitempty"`
TrustDomain string `description:"Defines the allowed SPIFFE trust domain." json:"trustDomain,omitempty" toml:"trustDomain,omitempty" yaml:"trustDomain,omitempty"`
}
// +k8s:deepcopy-gen=true

View file

@ -382,7 +382,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
type IPAllowList struct {
// SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation).
SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"`
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
IPStrategy *IPStrategy `json:"ipStrategy,omitempty" toml:"ipStrategy,omitempty" yaml:"ipStrategy,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
}
// +k8s:deepcopy-gen=true

View file

@ -101,7 +101,7 @@ func (l *TCPServersLoadBalancer) Mergeable(loadBalancer *TCPServersLoadBalancer)
// TCPServer holds a TCP Server configuration.
type TCPServer struct {
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
Port string `toml:"-" json:"-" yaml:"-"`
Port string `json:"-" toml:"-" yaml:"-"`
TLS bool `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"`
}

View file

@ -78,5 +78,5 @@ func (l *UDPServersLoadBalancer) Mergeable(loadBalancer *UDPServersLoadBalancer)
// UDPServer defines a UDP server configuration.
type UDPServer struct {
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty" label:"-"`
Port string `toml:"-" json:"-" yaml:"-" file:"-"`
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
}

View file

@ -114,7 +114,7 @@ type ServersTransport struct {
// Spiffe holds the SPIFFE configuration.
type Spiffe struct {
IDs []string `description:"Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain)." json:"ids,omitempty" toml:"ids,omitempty" yaml:"ids,omitempty"`
TrustDomain string `description:"Defines the allowed SPIFFE trust domain." json:"trustDomain,omitempty" yaml:"trustDomain,omitempty" toml:"trustDomain,omitempty"`
TrustDomain string `description:"Defines the allowed SPIFFE trust domain." json:"trustDomain,omitempty" toml:"trustDomain,omitempty" yaml:"trustDomain,omitempty"`
}
// TCPServersTransport options to configure communication between Traefik and the servers.
@ -191,13 +191,13 @@ func (a *LifeCycle) SetDefaults() {
type Tracing struct {
ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"`
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)." json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty" export:"true"`
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
OpenTelemetry *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"openTelemetry,omitempty" toml:"openTelemetry,omitempty" yaml:"openTelemetry,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Jaeger *jaeger.Config `description:"Settings for Jaeger." json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Zipkin *zipkin.Config `description:"Settings for Zipkin." json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Datadog *datadog.Config `description:"Settings for Datadog." json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Instana *instana.Config `description:"Settings for Instana." json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Haystack *haystack.Config `description:"Settings for Haystack." json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Elastic *elastic.Config `description:"Settings for Elastic." json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
OpenTelemetry *opentelemetry.Config `description:"Settings for OpenTelemetry." json:"openTelemetry,omitempty" toml:"openTelemetry,omitempty" yaml:"openTelemetry,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
}
// SetDefaults sets the default values.
@ -210,12 +210,12 @@ func (t *Tracing) SetDefaults() {
type Providers struct {
ProvidersThrottleDuration ptypes.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"`
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"`
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
KubernetesGateway *gateway.Provider `description:"Enable Kubernetes gateway api provider with default settings." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
KubernetesGateway *gateway.Provider `description:"Enable Kubernetes gateway api provider with default settings." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Rest *rest.Provider ` description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Nomad *nomad.ProviderBuilder `description:"Enable Nomad backend with default settings." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`

View file

@ -310,6 +310,7 @@ func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPo
func (p Provider) getIPAddress(ctx context.Context, container dockerData) string {
logger := log.Ctx(ctx)
netNotFound := false
if container.ExtraConf.Docker.Network != "" {
settings := container.NetworkSettings
if settings.Networks != nil {
@ -318,7 +319,8 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
return network.Addr
}
logger.Warn().Msgf("Could not find network named '%s' for container '%s'! Maybe you're missing the project's prefix in the label? Defaulting to first available network.", container.ExtraConf.Docker.Network, container.Name)
netNotFound = true
logger.Warn().Msgf("Could not find network named %q for container %q. Maybe you're missing the project's prefix in the label?", container.ExtraConf.Docker.Network, container.Name)
}
}
@ -367,6 +369,9 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
}
for _, network := range container.NetworkSettings.Networks {
if netNotFound {
logger.Warn().Msgf("Defaulting to first available network (%q) for container %q.", network, container.Name)
}
return network.Addr
}

View file

@ -164,23 +164,62 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
for _, ns := range namespaces {
factoryCrd := traefikinformers.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, traefikinformers.WithNamespace(ns), traefikinformers.WithTweakListOptions(matchesLabelSelector))
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().MiddlewareTCPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().IngressRouteUDPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TLSOptions().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().ServersTransports().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().ServersTransportTCPs().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TLSStores().Informer().AddEventHandler(eventHandler)
factoryCrd.Traefik().V1alpha1().TraefikServices().Informer().AddEventHandler(eventHandler)
_, err := factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().MiddlewareTCPs().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().IngressRouteUDPs().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().TLSOptions().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().ServersTransports().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().ServersTransportTCPs().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().TLSStores().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryCrd.Traefik().V1alpha1().TraefikServices().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns))
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
_, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm))
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
_, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
c.factoriesCrd[ns] = factoryCrd
c.factoriesKube[ns] = factoryKube

View file

@ -33,7 +33,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/discovery"
discoveryfake "k8s.io/client-go/discovery/fake"
fakediscovery "k8s.io/client-go/discovery/fake"
"k8s.io/client-go/testing"
)
@ -50,7 +50,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
}
cs := &Clientset{tracker: o}
cs.discovery = &discoveryfake.FakeDiscovery{Fake: &cs.Fake}
cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
cs.AddReactor("*", "*", testing.ObjectReaction(o))
cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
gvr := action.GetResource()
@ -70,7 +70,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
// you want to test easier.
type Clientset struct {
testing.Fake
discovery *discoveryfake.FakeDiscovery
discovery *fakediscovery.FakeDiscovery
tracker testing.ObjectTracker
}

View file

@ -6964,7 +6964,6 @@ func TestNativeLB(t *testing.T) {
func TestCreateBasicAuthCredentials(t *testing.T) {
var k8sObjects []runtime.Object
var crdObjects []runtime.Object
yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/basic_auth_secrets.yml"))
if err != nil {
panic(err)
@ -6980,7 +6979,7 @@ func TestCreateBasicAuthCredentials(t *testing.T) {
}
kubeClient := kubefake.NewSimpleClientset(k8sObjects...)
crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...)
crdClient := traefikcrdfake.NewSimpleClientset()
client := newClientImpl(kubeClient, crdClient)
@ -6989,7 +6988,7 @@ func TestCreateBasicAuthCredentials(t *testing.T) {
eventCh, err := client.WatchAll([]string{"default"}, stopCh)
require.NoError(t, err)
if len(k8sObjects) != 0 || len(crdObjects) != 0 {
if len(k8sObjects) != 0 {
// just wait for the first event
<-eventCh
}

View file

@ -126,8 +126,7 @@ func newExternalClusterClientFromFile(file string) (*clientWrapper, error) {
return createClientFromConfig(configFromFlags)
}
// newExternalClusterClient returns a new Provider client that may run outside
// of the cluster.
// newExternalClusterClient returns a new Provider client that may run outside of the cluster.
// The endpoint parameter must not be empty.
func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrapper, error) {
if endpoint == "" {
@ -172,27 +171,54 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
}
c.factoryNamespace = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod)
c.factoryNamespace.Core().V1().Namespaces().Informer().AddEventHandler(eventHandler)
_, err := c.factoryNamespace.Core().V1().Namespaces().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
c.factoryGatewayClass = gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithTweakListOptions(labelSelectorOptions))
c.factoryGatewayClass.Gateway().V1alpha2().GatewayClasses().Informer().AddEventHandler(eventHandler)
_, err = c.factoryGatewayClass.Gateway().V1alpha2().GatewayClasses().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
// TODO manage Reference Policy
// https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.ReferencePolicy
for _, ns := range namespaces {
factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns))
factoryGateway.Gateway().V1alpha2().Gateways().Informer().AddEventHandler(eventHandler)
factoryGateway.Gateway().V1alpha2().HTTPRoutes().Informer().AddEventHandler(eventHandler)
factoryGateway.Gateway().V1alpha2().TCPRoutes().Informer().AddEventHandler(eventHandler)
factoryGateway.Gateway().V1alpha2().TLSRoutes().Informer().AddEventHandler(eventHandler)
_, err = factoryGateway.Gateway().V1alpha2().Gateways().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryGateway.Gateway().V1alpha2().HTTPRoutes().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryGateway.Gateway().V1alpha2().TCPRoutes().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryGateway.Gateway().V1alpha2().TLSRoutes().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns))
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
_, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm))
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
_, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
c.factoriesGateway[ns] = factoryGateway
c.factoriesKube[ns] = factoryKube

View file

@ -31,10 +31,6 @@ const (
defaultTimeout = 5 * time.Second
)
type marshaler interface {
Marshal() ([]byte, error)
}
// Client is a client for the Provider master.
// WatchAll starts the watch of the Provider resources and updates the stores.
// The stores can then be accessed via the Get* functions.
@ -45,7 +41,7 @@ type Client interface {
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 *netv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error
UpdateIngressStatus(ing *netv1.Ingress, ingStatus []netv1.IngressLoadBalancerIngress) error
GetServerVersion() *version.Version
}
@ -172,20 +168,35 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(matchesLabelSelector))
if supportsNetworkingV1Ingress(serverVersion) {
factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler)
_, err = factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
} else {
factoryIngress.Networking().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
_, err = factoryIngress.Networking().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
}
c.factoriesIngress[ns] = factoryIngress
factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns))
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
_, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
_, err = factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
c.factoriesKube[ns] = factoryKube
factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm))
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
_, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
c.factoriesSecret[ns] = factorySecret
}
@ -219,9 +230,15 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
c.clusterFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod)
if supportsNetworkingV1Ingress(serverVersion) {
c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
_, err = c.clusterFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
} else {
c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler)
_, err = c.clusterFactory.Networking().V1beta1().IngressClasses().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
}
c.clusterFactory.Start(stopCh)
@ -263,7 +280,7 @@ func (c *clientWrapper) GetIngresses() []*netv1.Ingress {
}
for _, ing := range list {
n, err := toNetworkingV1(ing)
n, err := convert[netv1.Ingress](ing)
if err != nil {
log.Error().Err(err).Msgf("Failed to convert ingress %s from networking/v1beta1 to networking/v1", ns)
continue
@ -277,36 +294,6 @@ func (c *clientWrapper) GetIngresses() []*netv1.Ingress {
return results
}
func toNetworkingV1(ing marshaler) (*netv1.Ingress, error) {
data, err := ing.Marshal()
if err != nil {
return nil, err
}
ni := &netv1.Ingress{}
err = ni.Unmarshal(data)
if err != nil {
return nil, err
}
return ni, nil
}
func toNetworkingV1IngressClass(ing marshaler) (*netv1.IngressClass, error) {
data, err := ing.Marshal()
if err != nil {
return nil, err
}
ni := &netv1.IngressClass{}
err = ni.Unmarshal(data)
if err != nil {
return nil, err
}
return ni, nil
}
func addServiceFromV1Beta1(ing *netv1.Ingress, old netv1beta1.Ingress) {
if old.Spec.Backend != nil {
port := netv1.ServiceBackendPort{}
@ -353,7 +340,7 @@ func addServiceFromV1Beta1(ing *netv1.Ingress, old netv1beta1.Ingress) {
}
// UpdateIngressStatus updates an Ingress with a provided status.
func (c *clientWrapper) UpdateIngressStatus(src *netv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
func (c *clientWrapper) UpdateIngressStatus(src *netv1.Ingress, ingStatus []netv1.IngressLoadBalancerIngress) 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)
}
@ -375,7 +362,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *netv1.Ingress, ingStatus []core
}
ingCopy := ing.DeepCopy()
ingCopy.Status = netv1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: ingStatus}}
ingCopy.Status = netv1.IngressStatus{LoadBalancer: netv1.IngressLoadBalancerStatus{Ingress: ingStatus}}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
@ -389,7 +376,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *netv1.Ingress, ingStatus []core
return nil
}
func (c *clientWrapper) updateIngressStatusOld(src *netv1.Ingress, ingStatus []corev1.LoadBalancerIngress) error {
func (c *clientWrapper) updateIngressStatusOld(src *netv1.Ingress, ingStatus []netv1.IngressLoadBalancerIngress) 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)
@ -397,13 +384,23 @@ func (c *clientWrapper) updateIngressStatusOld(src *netv1.Ingress, ingStatus []c
logger := log.With().Str("namespace", ing.Namespace).Str("ingress", ing.Name).Logger()
if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) {
ingresses, err := convertSlice[netv1.IngressLoadBalancerIngress](ing.Status.LoadBalancer.Ingress)
if err != nil {
return err
}
if isLoadBalancerIngressEquals(ingresses, ingStatus) {
logger.Debug().Msg("Skipping ingress status update")
return nil
}
ingressesBeta1, err := convertSlice[netv1beta1.IngressLoadBalancerIngress](ingStatus)
if err != nil {
return err
}
ingCopy := ing.DeepCopy()
ingCopy.Status = netv1beta1.IngressStatus{LoadBalancer: corev1.LoadBalancerStatus{Ingress: ingStatus}}
ingCopy.Status = netv1beta1.IngressStatus{LoadBalancer: netv1beta1.IngressLoadBalancerStatus{Ingress: ingressesBeta1}}
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
@ -417,7 +414,7 @@ func (c *clientWrapper) updateIngressStatusOld(src *netv1.Ingress, ingStatus []c
}
// isLoadBalancerIngressEquals returns true if the given slices are equal, false otherwise.
func isLoadBalancerIngressEquals(aSlice, bSlice []corev1.LoadBalancerIngress) bool {
func isLoadBalancerIngressEquals(aSlice, bSlice []netv1.IngressLoadBalancerIngress) bool {
if len(aSlice) != len(bSlice) {
return false
}
@ -483,7 +480,7 @@ func (c *clientWrapper) GetIngressClasses() ([]*netv1.IngressClass, error) {
for _, ic := range ingressClasses {
if ic.Spec.Controller == traefikDefaultIngressClassController {
icN, err := toNetworkingV1IngressClass(ic)
icN, err := convert[netv1.IngressClass](ic)
if err != nil {
log.Error().Err(err).Msgf("Failed to convert ingress class %s from networking/v1beta1 to networking/v1", ic.Name)
continue

View file

@ -51,7 +51,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
case *corev1.Endpoints:
c.endpoints = append(c.endpoints, o)
case *netv1beta1.Ingress:
ing, err := toNetworkingV1(o)
ing, err := convert[netv1.Ingress](o)
if err != nil {
panic(err)
}
@ -60,7 +60,7 @@ func newClientMock(serverVersion string, paths ...string) clientMock {
case *netv1.Ingress:
c.ingresses = append(c.ingresses, o)
case *netv1beta1.IngressClass:
ic, err := toNetworkingV1IngressClass(o)
ic, err := convert[netv1.IngressClass](o)
if err != nil {
panic(err)
}
@ -132,6 +132,6 @@ func (c clientMock) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-cha
return c.watchChan, nil
}
func (c clientMock) UpdateIngressStatus(_ *netv1.Ingress, _ []corev1.LoadBalancerIngress) error {
func (c clientMock) UpdateIngressStatus(_ *netv1.Ingress, _ []netv1.IngressLoadBalancerIngress) error {
return c.apiIngressStatusError
}

View file

@ -61,8 +61,8 @@ func TestTranslateNotFoundError(t *testing.T) {
func TestIsLoadBalancerIngressEquals(t *testing.T) {
testCases := []struct {
desc string
aSlice []corev1.LoadBalancerIngress
bSlice []corev1.LoadBalancerIngress
aSlice []netv1.IngressLoadBalancerIngress
bSlice []netv1.IngressLoadBalancerIngress
expectedEqual bool
}{
{
@ -71,28 +71,28 @@ func TestIsLoadBalancerIngressEquals(t *testing.T) {
},
{
desc: "not the same length",
bSlice: []corev1.LoadBalancerIngress{
bSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
},
expectedEqual: false,
},
{
desc: "same ordered content",
aSlice: []corev1.LoadBalancerIngress{
aSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
},
bSlice: []corev1.LoadBalancerIngress{
bSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
},
expectedEqual: true,
},
{
desc: "same unordered content",
aSlice: []corev1.LoadBalancerIngress{
aSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
{IP: "192.168.1.2", Hostname: "traefik2"},
},
bSlice: []corev1.LoadBalancerIngress{
bSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.2", Hostname: "traefik2"},
{IP: "192.168.1.1", Hostname: "traefik"},
},
@ -100,11 +100,11 @@ func TestIsLoadBalancerIngressEquals(t *testing.T) {
},
{
desc: "different ordered content",
aSlice: []corev1.LoadBalancerIngress{
aSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
{IP: "192.168.1.2", Hostname: "traefik2"},
},
bSlice: []corev1.LoadBalancerIngress{
bSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
{IP: "192.168.1.2", Hostname: "traefik"},
},
@ -112,11 +112,11 @@ func TestIsLoadBalancerIngressEquals(t *testing.T) {
},
{
desc: "different unordered content",
aSlice: []corev1.LoadBalancerIngress{
aSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.1", Hostname: "traefik"},
{IP: "192.168.1.2", Hostname: "traefik2"},
},
bSlice: []corev1.LoadBalancerIngress{
bSlice: []netv1.IngressLoadBalancerIngress{
{IP: "192.168.1.2", Hostname: "traefik3"},
{IP: "192.168.1.1", Hostname: "traefik"},
},

View file

@ -0,0 +1,70 @@
package ingress
import (
"errors"
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
netv1beta1 "k8s.io/api/networking/v1beta1"
)
type marshaler interface {
Marshal() ([]byte, error)
}
type unmarshaler interface {
Unmarshal([]byte) error
}
type LoadBalancerIngress interface {
corev1.LoadBalancerIngress | netv1beta1.IngressLoadBalancerIngress | netv1.IngressLoadBalancerIngress
}
// convertSlice converts slice of LoadBalancerIngress to slice of LoadBalancerIngress.
// O (Bar), I (Foo) => []Bar.
func convertSlice[O LoadBalancerIngress, I LoadBalancerIngress](loadBalancerIngresses []I) ([]O, error) {
var results []O
for _, loadBalancerIngress := range loadBalancerIngresses {
mar, ok := any(&loadBalancerIngress).(marshaler)
if !ok {
// All the pointer of types related to the interface LoadBalancerIngress are compatible with the interface marshaler.
continue
}
um, err := convert[O](mar)
if err != nil {
return nil, err
}
v, ok := any(*um).(O)
if !ok {
continue
}
results = append(results, v)
}
return results, nil
}
// convert must only be used with unmarshaler and marshaler compatible types.
func convert[T any](input marshaler) (*T, error) {
data, err := input.Marshal()
if err != nil {
return nil, err
}
var output T
um, ok := any(&output).(unmarshaler)
if !ok {
return nil, errors.New("the output type doesn't implement unmarshaler interface")
}
err = um.Unmarshal(data)
if err != nil {
return nil, err
}
return &output, nil
}

View file

@ -0,0 +1,151 @@
package ingress
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
netv1 "k8s.io/api/networking/v1"
netv1beta1 "k8s.io/api/networking/v1beta1"
)
func Test_convertSlice_corev1_to_networkingv1(t *testing.T) {
g := []corev1.LoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []corev1.PortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
actual, err := convertSlice[netv1.IngressLoadBalancerIngress](g)
require.NoError(t, err)
expected := []netv1.IngressLoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []netv1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
assert.Equal(t, expected, actual)
}
func Test_convertSlice_networkingv1beta1_to_networkingv1(t *testing.T) {
g := []netv1beta1.IngressLoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []netv1beta1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
actual, err := convertSlice[netv1.IngressLoadBalancerIngress](g)
require.NoError(t, err)
expected := []netv1.IngressLoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []netv1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
assert.Equal(t, expected, actual)
}
func Test_convertSlice_networkingv1_to_networkingv1beta1(t *testing.T) {
g := []netv1.IngressLoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []netv1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
actual, err := convertSlice[netv1beta1.IngressLoadBalancerIngress](g)
require.NoError(t, err)
expected := []netv1beta1.IngressLoadBalancerIngress{
{
IP: "132456",
Hostname: "foo",
Ports: []netv1beta1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
},
}
assert.Equal(t, expected, actual)
}
func Test_convert(t *testing.T) {
g := &corev1.LoadBalancerIngress{
IP: "132456",
Hostname: "foo",
Ports: []corev1.PortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
}
actual, err := convert[netv1.IngressLoadBalancerIngress](g)
require.NoError(t, err)
expected := &netv1.IngressLoadBalancerIngress{
IP: "132456",
Hostname: "foo",
Ports: []netv1.IngressPortStatus{
{
Port: 123,
Protocol: "https",
Error: ptr("test"),
},
},
}
assert.Equal(t, expected, actual)
}
func ptr[T any](v T) *T {
return &v
}

View file

@ -357,7 +357,7 @@ func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client) err
return errors.New("publishedService or ip or hostname must be defined")
}
return k8sClient.UpdateIngressStatus(ing, []corev1.LoadBalancerIngress{{IP: p.IngressEndpoint.IP, Hostname: p.IngressEndpoint.Hostname}})
return k8sClient.UpdateIngressStatus(ing, []netv1.IngressLoadBalancerIngress{{IP: p.IngressEndpoint.IP, Hostname: p.IngressEndpoint.Hostname}})
}
serviceInfo := strings.Split(p.IngressEndpoint.PublishedService, "/")
@ -382,7 +382,12 @@ func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client) err
return fmt.Errorf("missing service: %s", p.IngressEndpoint.PublishedService)
}
return k8sClient.UpdateIngressStatus(ing, service.Status.LoadBalancer.Ingress)
ingresses, err := convertSlice[netv1.IngressLoadBalancerIngress](service.Status.LoadBalancer.Ingress)
if err != nil {
return err
}
return k8sClient.UpdateIngressStatus(ing, ingresses)
}
func (p *Provider) shouldProcessIngress(ingress *netv1.Ingress, ingressClasses []*netv1.IngressClass) bool {

View file

@ -27,7 +27,7 @@ import (
// Config provides configuration settings for the open-telemetry tracer.
type Config struct {
// NOTE: as no gRPC option is implemented yet, the type is empty and is used as a boolean for upward compatibility purposes.
GRPC *struct{} `description:"gRPC specific configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
GRPC *struct{} `description:"gRPC specific configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Address string `description:"Sets the address (host:port) of the collector endpoint." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
Path string `description:"Sets the URL path of the collector endpoint." json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"`

View file

@ -107,7 +107,7 @@ func (i *InfluxDB2) SetDefaults() {
// OpenTelemetry contains specific configuration used by the OpenTelemetry Metrics exporter.
type OpenTelemetry struct {
// NOTE: as no gRPC option is implemented yet, the type is empty and is used as a boolean for upward compatibility purposes.
GRPC *struct{} `description:"gRPC specific configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" export:"true" label:"allowEmpty" file:"allowEmpty"`
GRPC *struct{} `description:"gRPC specific configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
Address string `description:"Address (host:port) of the collector endpoint." json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"`