1
0
Fork 0

Support multiple namespaces for Consul and ConsulCatalog providers

Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
Romain 2022-06-03 12:00:09 +02:00 committed by GitHub
parent f352c34136
commit f90e3817e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 531 additions and 152 deletions

View file

@ -4,30 +4,80 @@ import (
"errors"
"github.com/kvtools/valkeyrie/store"
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/provider"
"github.com/traefik/traefik/v2/pkg/provider/kv"
)
// providerName is the Consul provider name.
const providerName = "consul"
var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
// ProviderBuilder is responsible for constructing namespaced instances of the Consul provider.
type ProviderBuilder struct {
kv.Provider `export:"true"`
// Deprecated: use Namespaces instead.
Namespace string `description:"Sets the namespace used to discover the configuration (Consul Enterprise only)." json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"`
Namespaces []string `description:"Sets the namespaces used to discover the configuration (Consul Enterprise only)." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty"`
}
// SetDefaults sets the default values.
func (p *Provider) SetDefaults() {
func (p *ProviderBuilder) SetDefaults() {
p.Provider.SetDefaults()
p.Endpoints = []string{"127.0.0.1:8500"}
}
// BuildProviders builds Consul provider instances for the given namespaces configuration.
func (p *ProviderBuilder) BuildProviders() []*Provider {
// We can warn about that, because we've already made sure before that
// Namespace and Namespaces are mutually exclusive.
if p.Namespace != "" {
log.WithoutContext().Warnf("Namespace option is deprecated, please use the Namespaces option instead.")
}
if len(p.Namespaces) == 0 {
return []*Provider{{
Provider: p.Provider,
name: providerName,
// p.Namespace could very well be empty.
namespace: p.Namespace,
}}
}
var providers []*Provider
for _, namespace := range p.Namespaces {
providers = append(providers, &Provider{
Provider: p.Provider,
name: providerName + "-" + namespace,
namespace: namespace,
})
}
return providers
}
// Provider holds configurations of the provider.
type Provider struct {
kv.Provider
name string
namespace string
}
// Init the provider.
func (p *Provider) Init() error {
// Wildcard namespace allows fetching KV values from any namespace for recursive requests (see https://www.consul.io/api/kv#ns).
// As we are not supporting multiple namespaces at the same time, wildcard namespace is not allowed.
if p.Namespace == "*" {
if p.namespace == "*" {
return errors.New("wildcard namespace is not supported")
}
return p.Provider.Init(store.CONSUL, "consul")
// In case they didn't initialize with BuildProviders.
if p.name == "" {
p.name = providerName
}
return p.Provider.Init(store.CONSUL, p.name, p.namespace)
}

View file

@ -0,0 +1,59 @@
package consul
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNamespaces(t *testing.T) {
testCases := []struct {
desc string
namespace string
namespaces []string
expectedNamespaces []string
}{
{
desc: "no defined namespaces",
expectedNamespaces: []string{""},
},
{
desc: "deprecated: use of defined namespace",
namespace: "test-ns",
expectedNamespaces: []string{"test-ns"},
},
{
desc: "use of 1 defined namespaces",
namespaces: []string{"test-ns"},
expectedNamespaces: []string{"test-ns"},
},
{
desc: "use of multiple defined namespaces",
namespaces: []string{"test-ns1", "test-ns2", "test-ns3", "test-ns4"},
expectedNamespaces: []string{"test-ns1", "test-ns2", "test-ns3", "test-ns4"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
pb := &ProviderBuilder{
Namespace: test.namespace,
Namespaces: test.namespaces,
}
assert.Equal(t, test.expectedNamespaces, extractNSFromProvider(pb.BuildProviders()))
})
}
}
func extractNSFromProvider(providers []*Provider) []string {
res := make([]string, len(providers))
for i, p := range providers {
res[i] = p.namespace
}
return res
}

View file

@ -21,5 +21,5 @@ func (p *Provider) SetDefaults() {
// Init the provider.
func (p *Provider) Init() error {
return p.Provider.Init(store.ETCDV3, "etcd")
return p.Provider.Init(store.ETCDV3, "etcd", "")
}

View file

@ -30,12 +30,12 @@ type Provider struct {
Username string `description:"KV Username" json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"`
Password string `description:"KV Password" json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"`
Token string `description:"KV Token" json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
Namespace string `description:"KV Namespace" json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"`
TLS *types.ClientTLS `description:"Enable TLS support" json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true" `
name string
namespace string
storeType store.Backend
kvClient store.Store
name string
}
// SetDefaults sets the default values.
@ -44,11 +44,12 @@ func (p *Provider) SetDefaults() {
}
// Init the provider.
func (p *Provider) Init(storeType store.Backend, name string) error {
func (p *Provider) Init(storeType store.Backend, name, namespace string) error {
ctx := log.With(context.Background(), log.Str(log.ProviderName, name))
p.storeType = storeType
p.name = name
p.namespace = namespace
p.storeType = storeType
kvClient, err := p.createKVClient(ctx)
if err != nil {
@ -167,7 +168,7 @@ func (p *Provider) createKVClient(ctx context.Context) (store.Store, error) {
Username: p.Username,
Password: p.Password,
Token: p.Token,
Namespace: p.Namespace,
Namespace: p.namespace,
}
if p.TLS != nil {

View file

@ -21,5 +21,5 @@ func (p *Provider) SetDefaults() {
// Init the provider.
func (p *Provider) Init() error {
return p.Provider.Init(store.REDIS, "redis")
return p.Provider.Init(store.REDIS, "redis", "")
}

View file

@ -21,5 +21,5 @@ func (p *Provider) SetDefaults() {
// Init the provider.
func (p *Provider) Init() error {
return p.Provider.Init(store.ZK, "zookeeper")
return p.Provider.Init(store.ZK, "zookeeper", "")
}