diff --git a/integration/resources/compose/tlsclientheaders.yml b/integration/resources/compose/tlsclientheaders.yml index f315de749..938ca3cbe 100644 --- a/integration/resources/compose/tlsclientheaders.yml +++ b/integration/resources/compose/tlsclientheaders.yml @@ -1,7 +1,7 @@ whoami: image: traefik/whoami labels: - - traefik.http.routers.route1.rule=PathPrefix(`/`) + - traefik.http.routers.route1.rule=PathPrefix(`/foo`) - traefik.http.routers.route1.middlewares=passtls - traefik.http.routers.route1.tls=true - traefik.http.middlewares.passtls.passtlsclientcert.pem=true diff --git a/integration/tls_client_headers_test.go b/integration/tls_client_headers_test.go index 9842b777d..f55d2ff99 100644 --- a/integration/tls_client_headers_test.go +++ b/integration/tls_client_headers_test.go @@ -50,10 +50,10 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) { c.Assert(err, checker.IsNil) defer s.killCmd(cmd) - err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/`)")) + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("PathPrefix(`/foo`)")) c.Assert(err, checker.IsNil) - request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil) + request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443/foo", nil) c.Assert(err, checker.IsNil) certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath) diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index 9632af47f..38410f0a8 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -8,13 +8,15 @@ import ( "github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/provider/file" + "github.com/traefik/traefik/v2/pkg/provider/traefik" "github.com/traefik/traefik/v2/pkg/safe" ) // ProviderAggregator aggregates providers. type ProviderAggregator struct { - fileProvider *file.Provider - providers []provider.Provider + internalProvider provider.Provider + fileProvider provider.Provider + providers []provider.Provider } // NewProviderAggregator returns an aggregate of all the providers configured in the static configuration. @@ -98,11 +100,15 @@ func (p *ProviderAggregator) AddProvider(provider provider.Provider) error { return err } - if fileProvider, ok := provider.(*file.Provider); ok { - p.fileProvider = fileProvider - } else { + switch provider.(type) { + case *file.Provider: + p.fileProvider = provider + case *traefik.Provider: + p.internalProvider = provider + default: p.providers = append(p.providers, provider) } + return nil } @@ -117,12 +123,17 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po launchProvider(configurationChan, pool, p.fileProvider) } + if p.internalProvider != nil { + launchProvider(configurationChan, pool, p.internalProvider) + } + for _, prd := range p.providers { prd := prd safe.Go(func() { launchProvider(configurationChan, pool, prd) }) } + return nil } diff --git a/pkg/provider/aggregator/aggregator_test.go b/pkg/provider/aggregator/aggregator_test.go new file mode 100644 index 000000000..1613ac193 --- /dev/null +++ b/pkg/provider/aggregator/aggregator_test.go @@ -0,0 +1,79 @@ +package aggregator + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/traefik/traefik/v2/pkg/config/dynamic" + "github.com/traefik/traefik/v2/pkg/provider" + "github.com/traefik/traefik/v2/pkg/safe" +) + +func TestProviderAggregator_Provide(t *testing.T) { + aggregator := ProviderAggregator{ + internalProvider: &providerMock{"internal"}, + fileProvider: &providerMock{"file"}, + providers: []provider.Provider{ + &providerMock{"salad"}, + &providerMock{"tomato"}, + &providerMock{"onion"}, + }, + } + + cfgCh := make(chan dynamic.Message) + errCh := make(chan error) + pool := safe.NewPool(context.Background()) + + t.Cleanup(pool.Stop) + + go func() { + errCh <- aggregator.Provide(cfgCh, pool) + }() + + // Make sure the file provider is always called first, followed by the internal provider. + requireReceivedMessageFromProviders(t, cfgCh, []string{"file"}) + requireReceivedMessageFromProviders(t, cfgCh, []string{"internal"}) + + // Check if all providers have been called, the order doesn't matter. + requireReceivedMessageFromProviders(t, cfgCh, []string{"salad", "tomato", "onion"}) + + require.NoError(t, <-errCh) +} + +// 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) { + t.Helper() + + var msg dynamic.Message + var receivedMessagesFrom []string + + for range names { + select { + case <-time.After(10 * time.Millisecond): + case msg = <-cfgCh: + receivedMessagesFrom = append(receivedMessagesFrom, msg.ProviderName) + } + } + + require.ElementsMatch(t, names, receivedMessagesFrom) +} + +type providerMock struct { + Name string +} + +func (p *providerMock) Init() error { + return nil +} + +func (p *providerMock) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { + configurationChan <- dynamic.Message{ + ProviderName: p.Name, + Configuration: &dynamic.Configuration{}, + } + + return nil +}