Merge branch v2.3 into master
This commit is contained in:
commit
2112de6f15
36 changed files with 864 additions and 188 deletions
|
@ -21,7 +21,6 @@ import (
|
|||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
|
@ -66,13 +65,14 @@ type Client interface {
|
|||
|
||||
// TODO: add tests for the clientWrapper (and its methods) itself.
|
||||
type clientWrapper struct {
|
||||
csCrd *versioned.Clientset
|
||||
csKube *kubernetes.Clientset
|
||||
csCrd versioned.Interface
|
||||
csKube kubernetes.Interface
|
||||
|
||||
factoriesCrd map[string]externalversions.SharedInformerFactory
|
||||
factoriesKube map[string]informers.SharedInformerFactory
|
||||
factoriesCrd map[string]externalversions.SharedInformerFactory
|
||||
factoriesKube map[string]informers.SharedInformerFactory
|
||||
factoriesSecret map[string]informers.SharedInformerFactory
|
||||
|
||||
labelSelector labels.Selector
|
||||
labelSelector string
|
||||
|
||||
isNamespaceAll bool
|
||||
watchedNamespaces []string
|
||||
|
@ -100,12 +100,13 @@ func createClientFromConfig(c *rest.Config) (*clientWrapper, error) {
|
|||
return newClientImpl(csKube, csCrd), nil
|
||||
}
|
||||
|
||||
func newClientImpl(csKube *kubernetes.Clientset, csCrd *versioned.Clientset) *clientWrapper {
|
||||
func newClientImpl(csKube kubernetes.Interface, csCrd versioned.Interface) *clientWrapper {
|
||||
return &clientWrapper{
|
||||
csCrd: csCrd,
|
||||
csKube: csKube,
|
||||
factoriesCrd: make(map[string]externalversions.SharedInformerFactory),
|
||||
factoriesKube: make(map[string]informers.SharedInformerFactory),
|
||||
csCrd: csCrd,
|
||||
csKube: csKube,
|
||||
factoriesCrd: make(map[string]externalversions.SharedInformerFactory),
|
||||
factoriesKube: make(map[string]informers.SharedInformerFactory),
|
||||
factoriesSecret: make(map[string]informers.SharedInformerFactory),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,16 +161,25 @@ func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrappe
|
|||
// WatchAll starts namespace-specific controllers for all relevant kinds.
|
||||
func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) {
|
||||
eventCh := make(chan interface{}, 1)
|
||||
eventHandler := c.newResourceEventHandler(eventCh)
|
||||
eventHandler := &resourceEventHandler{ev: eventCh}
|
||||
|
||||
if len(namespaces) == 0 {
|
||||
namespaces = []string{metav1.NamespaceAll}
|
||||
c.isNamespaceAll = true
|
||||
}
|
||||
|
||||
c.watchedNamespaces = namespaces
|
||||
|
||||
notOwnedByHelm := func(opts *metav1.ListOptions) {
|
||||
opts.LabelSelector = "owner!=helm"
|
||||
}
|
||||
|
||||
matchesLabelSelector := func(opts *metav1.ListOptions) {
|
||||
opts.LabelSelector = c.labelSelector
|
||||
}
|
||||
|
||||
for _, ns := range namespaces {
|
||||
factoryCrd := externalversions.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, externalversions.WithNamespace(ns))
|
||||
factoryCrd := externalversions.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, externalversions.WithNamespace(ns), externalversions.WithTweakListOptions(matchesLabelSelector))
|
||||
factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler)
|
||||
factoryCrd.Traefik().V1alpha1().Middlewares().Informer().AddEventHandler(eventHandler)
|
||||
factoryCrd.Traefik().V1alpha1().IngressRouteTCPs().Informer().AddEventHandler(eventHandler)
|
||||
|
@ -180,18 +190,21 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
|||
factoryCrd.Traefik().V1alpha1().TraefikServices().Informer().AddEventHandler(eventHandler)
|
||||
|
||||
factoryKube := informers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, informers.WithNamespace(ns))
|
||||
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
|
||||
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
|
||||
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
|
||||
factoryKube.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
|
||||
|
||||
factorySecret := informers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, informers.WithNamespace(ns), informers.WithTweakListOptions(notOwnedByHelm))
|
||||
factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
|
||||
|
||||
c.factoriesCrd[ns] = factoryCrd
|
||||
c.factoriesKube[ns] = factoryKube
|
||||
c.factoriesSecret[ns] = factorySecret
|
||||
}
|
||||
|
||||
for _, ns := range namespaces {
|
||||
c.factoriesCrd[ns].Start(stopCh)
|
||||
c.factoriesKube[ns].Start(stopCh)
|
||||
c.factoriesSecret[ns].Start(stopCh)
|
||||
}
|
||||
|
||||
for _, ns := range namespaces {
|
||||
|
@ -206,6 +219,12 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
|
|||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
||||
}
|
||||
}
|
||||
|
||||
for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventCh, nil
|
||||
|
@ -215,7 +234,7 @@ func (c *clientWrapper) GetIngressRoutes() []*v1alpha1.IngressRoute {
|
|||
var result []*v1alpha1.IngressRoute
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(c.labelSelector)
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list ingress routes in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -229,7 +248,7 @@ func (c *clientWrapper) GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP {
|
|||
var result []*v1alpha1.IngressRouteTCP
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRouteTCPs().Lister().List(c.labelSelector)
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRouteTCPs().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list tcp ingress routes in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -243,7 +262,7 @@ func (c *clientWrapper) GetIngressRouteUDPs() []*v1alpha1.IngressRouteUDP {
|
|||
var result []*v1alpha1.IngressRouteUDP
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRouteUDPs().Lister().List(c.labelSelector)
|
||||
ings, err := factory.Traefik().V1alpha1().IngressRouteUDPs().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list udp ingress routes in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -257,7 +276,7 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
|
|||
var result []*v1alpha1.Middleware
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
middlewares, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(c.labelSelector)
|
||||
middlewares, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list middlewares in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -283,7 +302,7 @@ func (c *clientWrapper) GetTraefikServices() []*v1alpha1.TraefikService {
|
|||
var result []*v1alpha1.TraefikService
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
ings, err := factory.Traefik().V1alpha1().TraefikServices().Lister().List(c.labelSelector)
|
||||
ings, err := factory.Traefik().V1alpha1().TraefikServices().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list Traefik services in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -298,7 +317,7 @@ func (c *clientWrapper) GetServersTransports() []*v1alpha1.ServersTransport {
|
|||
var result []*v1alpha1.ServersTransport
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
serversTransports, err := factory.Traefik().V1alpha1().ServersTransports().Lister().List(c.labelSelector)
|
||||
serversTransports, err := factory.Traefik().V1alpha1().ServersTransports().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list servers transport in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -313,7 +332,7 @@ func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
|
|||
var result []*v1alpha1.TLSOption
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(c.labelSelector)
|
||||
options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list tls options in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -328,7 +347,7 @@ func (c *clientWrapper) GetTLSStores() []*v1alpha1.TLSStore {
|
|||
var result []*v1alpha1.TLSStore
|
||||
|
||||
for ns, factory := range c.factoriesCrd {
|
||||
stores, err := factory.Traefik().V1alpha1().TLSStores().Lister().List(c.labelSelector)
|
||||
stores, err := factory.Traefik().V1alpha1().TLSStores().Lister().List(labels.Everything())
|
||||
if err != nil {
|
||||
log.Errorf("Failed to list tls stores in namespace %s: %v", ns, err)
|
||||
}
|
||||
|
@ -366,7 +385,7 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
|
|||
return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name)
|
||||
}
|
||||
|
||||
secret, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
|
||||
secret, err := c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name)
|
||||
exist, err := translateNotFoundError(err)
|
||||
return secret, exist, err
|
||||
}
|
||||
|
@ -384,31 +403,6 @@ func (c *clientWrapper) lookupNamespace(ns string) string {
|
|||
return ns
|
||||
}
|
||||
|
||||
func (c *clientWrapper) newResourceEventHandler(events chan<- interface{}) cache.ResourceEventHandler {
|
||||
return &cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool {
|
||||
// Ignore Ingresses that do not match our custom label selector.
|
||||
switch v := obj.(type) {
|
||||
case *v1alpha1.IngressRoute:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
case *v1alpha1.IngressRouteTCP:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
case *v1alpha1.TraefikService:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
case *v1alpha1.TLSOption:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
case *v1alpha1.TLSStore:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
case *v1alpha1.Middleware:
|
||||
return c.labelSelector.Matches(labels.Set(v.GetLabels()))
|
||||
default:
|
||||
return true
|
||||
}
|
||||
},
|
||||
Handler: &resourceEventHandler{ev: events},
|
||||
}
|
||||
}
|
||||
|
||||
// eventHandlerFunc will pass the obj on to the events channel or drop it.
|
||||
// This is so passing the events along won't block in the case of high volume.
|
||||
// The events are only used for signaling anyway so dropping a few is ok.
|
||||
|
|
65
pkg/provider/kubernetes/crd/client_test.go
Normal file
65
pkg/provider/kubernetes/crd/client_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
package crd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
crdfake "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
)
|
||||
|
||||
func TestClientIgnoresHelmOwnedSecrets(t *testing.T) {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "secret",
|
||||
},
|
||||
}
|
||||
helmSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "helm-secret",
|
||||
Labels: map[string]string{
|
||||
"owner": "helm",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
kubeClient := kubefake.NewSimpleClientset(helmSecret, secret)
|
||||
crdClient := crdfake.NewSimpleClientset()
|
||||
|
||||
client := newClientImpl(kubeClient, crdClient)
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
|
||||
eventCh, err := client.WatchAll(nil, stopCh)
|
||||
require.NoError(t, err)
|
||||
|
||||
select {
|
||||
case event := <-eventCh:
|
||||
secret, ok := event.(*corev1.Secret)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.NotEqual(t, "helm-secret", secret.Name)
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
assert.Fail(t, "expected to receive event for secret")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-eventCh:
|
||||
assert.Fail(t, "received more than one event")
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
}
|
||||
|
||||
_, found, err := client.GetSecret("default", "secret")
|
||||
require.NoError(t, err)
|
||||
assert.True(t, found)
|
||||
|
||||
_, found, err = client.GetSecret("default", "helm-secret")
|
||||
require.NoError(t, err)
|
||||
assert.False(t, found)
|
||||
}
|
|
@ -49,12 +49,12 @@ type Provider struct {
|
|||
lastConfiguration safe.Safe
|
||||
}
|
||||
|
||||
func (p *Provider) newK8sClient(ctx context.Context, labelSelector string) (*clientWrapper, error) {
|
||||
labelSel, err := labels.Parse(labelSelector)
|
||||
func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
|
||||
_, err := labels.Parse(p.LabelSelector)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid label selector: %q", labelSelector)
|
||||
return nil, fmt.Errorf("invalid label selector: %q", p.LabelSelector)
|
||||
}
|
||||
log.FromContext(ctx).Infof("label selector is: %q", labelSel)
|
||||
log.FromContext(ctx).Infof("label selector is: %q", p.LabelSelector)
|
||||
|
||||
withEndpoint := ""
|
||||
if p.Endpoint != "" {
|
||||
|
@ -74,11 +74,12 @@ func (p *Provider) newK8sClient(ctx context.Context, labelSelector string) (*cli
|
|||
client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
client.labelSelector = labelSel
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, err
|
||||
client.labelSelector = p.LabelSelector
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Init the provider.
|
||||
|
@ -92,8 +93,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
|||
ctxLog := log.With(context.Background(), log.Str(log.ProviderName, providerName))
|
||||
logger := log.FromContext(ctxLog)
|
||||
|
||||
logger.Debugf("Using label selector: %q", p.LabelSelector)
|
||||
k8sClient, err := p.newK8sClient(ctxLog, p.LabelSelector)
|
||||
k8sClient, err := p.newK8sClient(ctxLog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue