Fix HTTP challenge router unexpected delayed creation

This commit is contained in:
Harold Ozouf 2021-01-28 16:16:05 +01:00 committed by GitHub
parent 9a931e4dc9
commit 2065f4c003
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 8 deletions

View file

@ -1,7 +1,7 @@
whoami: whoami:
image: traefik/whoami image: traefik/whoami
labels: labels:
- traefik.http.routers.route1.rule=PathPrefix(`/`) - traefik.http.routers.route1.rule=PathPrefix(`/foo`)
- traefik.http.routers.route1.middlewares=passtls - traefik.http.routers.route1.middlewares=passtls
- traefik.http.routers.route1.tls=true - traefik.http.routers.route1.tls=true
- traefik.http.middlewares.passtls.passtlsclientcert.pem=true - traefik.http.middlewares.passtls.passtlsclientcert.pem=true

View file

@ -50,10 +50,10 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer s.killCmd(cmd) 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) 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) c.Assert(err, checker.IsNil)
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath) certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)

View file

@ -8,12 +8,14 @@ import (
"github.com/traefik/traefik/v2/pkg/log" "github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/provider" "github.com/traefik/traefik/v2/pkg/provider"
"github.com/traefik/traefik/v2/pkg/provider/file" "github.com/traefik/traefik/v2/pkg/provider/file"
"github.com/traefik/traefik/v2/pkg/provider/traefik"
"github.com/traefik/traefik/v2/pkg/safe" "github.com/traefik/traefik/v2/pkg/safe"
) )
// ProviderAggregator aggregates providers. // ProviderAggregator aggregates providers.
type ProviderAggregator struct { type ProviderAggregator struct {
fileProvider *file.Provider internalProvider provider.Provider
fileProvider provider.Provider
providers []provider.Provider providers []provider.Provider
} }
@ -98,11 +100,15 @@ func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
return err return err
} }
if fileProvider, ok := provider.(*file.Provider); ok { switch provider.(type) {
p.fileProvider = fileProvider case *file.Provider:
} else { p.fileProvider = provider
case *traefik.Provider:
p.internalProvider = provider
default:
p.providers = append(p.providers, provider) p.providers = append(p.providers, provider)
} }
return nil return nil
} }
@ -117,12 +123,17 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po
launchProvider(configurationChan, pool, p.fileProvider) launchProvider(configurationChan, pool, p.fileProvider)
} }
if p.internalProvider != nil {
launchProvider(configurationChan, pool, p.internalProvider)
}
for _, prd := range p.providers { for _, prd := range p.providers {
prd := prd prd := prd
safe.Go(func() { safe.Go(func() {
launchProvider(configurationChan, pool, prd) launchProvider(configurationChan, pool, prd)
}) })
} }
return nil return nil
} }

View file

@ -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
}