From 7cc8b099d2a33d2b3586cb4a786303fbe6fab814 Mon Sep 17 00:00:00 2001 From: shreealt Date: Fri, 3 Oct 2025 19:00:05 +0530 Subject: [PATCH] Log provider namespace during startup --- pkg/provider/aggregator/aggregator.go | 13 +++++- pkg/provider/aggregator/aggregator_test.go | 49 ++++++++++++++++++++ pkg/provider/consulcatalog/consul_catalog.go | 5 ++ pkg/provider/kv/consul/consul.go | 5 ++ pkg/provider/nomad/nomad.go | 5 ++ pkg/provider/provider.go | 11 +++++ 6 files changed, 86 insertions(+), 2 deletions(-) diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index fb2b1a6dc..3e45c2119 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -2,6 +2,7 @@ package aggregator import ( "context" + "fmt" "time" "github.com/rs/zerolog/log" @@ -203,8 +204,16 @@ func (p *ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Mes log.Debug().Err(err).Msgf("Cannot marshal the provider configuration %T", prd) } - log.Info().Msgf("Starting provider %T", prd) - log.Debug().RawJSON("config", []byte(jsonConf)).Msgf("%T provider configuration", prd) + // Check if provider has namespace information. + var namespaceInfo string + if namespaceProvider, ok := prd.(provider.NamespacedProvider); ok { + if namespace := namespaceProvider.Namespace(); namespace != "" { + namespaceInfo = fmt.Sprintf(" (namespace: %s)", namespace) + } + } + + log.Info().Msgf("Starting provider %T%s", prd, namespaceInfo) + log.Debug().RawJSON("config", []byte(jsonConf)).Msgf("%T provider configuration%s", prd, namespaceInfo) if err := maybeThrottledProvide(prd, p.providersThrottleDuration)(configurationChan, pool); err != nil { log.Error().Err(err).Msgf("Cannot start the provider %T", prd) diff --git a/pkg/provider/aggregator/aggregator_test.go b/pkg/provider/aggregator/aggregator_test.go index 895dc6539..8bb609eb4 100644 --- a/pkg/provider/aggregator/aggregator_test.go +++ b/pkg/provider/aggregator/aggregator_test.go @@ -1,9 +1,13 @@ package aggregator import ( + "bytes" "testing" "time" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/provider" @@ -40,6 +44,34 @@ func TestProviderAggregator_Provide(t *testing.T) { require.NoError(t, <-errCh) } +func TestLaunchNamespacedProvider(t *testing.T) { + // Capture log output + var buf bytes.Buffer + + originalLogger := log.Logger + log.Logger = zerolog.New(&buf).Level(zerolog.InfoLevel) + + providerWithNamespace := &mockNamespacedProvider{namespace: "test-namespace"} + + aggregator := ProviderAggregator{ + internalProvider: providerWithNamespace, + } + + cfgCh := make(chan dynamic.Message) + pool := safe.NewPool(t.Context()) + + t.Cleanup(func() { + pool.Stop() + log.Logger = originalLogger + }) + + err := aggregator.Provide(cfgCh, pool) + require.NoError(t, err) + + output := buf.String() + assert.Contains(t, output, "Starting provider *aggregator.mockNamespacedProvider (namespace: test-namespace)") +} + // requireReceivedMessageFromProviders makes sure the given providers have emitted a message on the given message channel. // Providers order is not enforced. func requireReceivedMessageFromProviders(t *testing.T, cfgCh <-chan dynamic.Message, names []string) { @@ -76,3 +108,20 @@ func (p *providerMock) Provide(configurationChan chan<- dynamic.Message, pool *s return nil } + +// mockNamespacedProvider is a mock implementation of NamespacedProvider for testing. +type mockNamespacedProvider struct { + namespace string +} + +func (m *mockNamespacedProvider) Namespace() string { + return m.namespace +} + +func (m *mockNamespacedProvider) Provide(_ chan<- dynamic.Message, _ *safe.Pool) error { + return nil +} + +func (m *mockNamespacedProvider) Init() error { + return nil +} diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index 55c24edb6..d3fc9b06f 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -647,3 +647,8 @@ func repeatSend(ctx context.Context, interval time.Duration, c chan<- struct{}) } } } + +// Namespace returns the namespace of the ConsulCatalog provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/kv/consul/consul.go b/pkg/provider/kv/consul/consul.go index 2a5fdfd49..14d8e82b6 100644 --- a/pkg/provider/kv/consul/consul.go +++ b/pkg/provider/kv/consul/consul.go @@ -97,3 +97,8 @@ func (p *Provider) Init() error { return p.Provider.Init(consul.StoreName, p.name, config) } + +// Namespace returns the namespace of the Consul provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/nomad/nomad.go b/pkg/provider/nomad/nomad.go index 6f0b4774e..bdca8b6fb 100644 --- a/pkg/provider/nomad/nomad.go +++ b/pkg/provider/nomad/nomad.go @@ -571,3 +571,8 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s return eventsChanBuffered } + +// Namespace returns the namespace of the Nomad provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 2597a05e6..8ebe66055 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -12,3 +12,14 @@ type Provider interface { Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error Init() error } + +// NamespacedProvider is implemented by providers that support namespace-scoped configurations, +// where each configured namespace results in a dedicated provider instance. +// This enables clear identification of which namespace each provider instance serves during +// startup logging and operational monitoring. +type NamespacedProvider interface { + Provider + + // Namespace returns the specific namespace this provider instance is configured for. + Namespace() string +}