Support custom headers when fetching configuration through HTTP

This commit is contained in:
Kevin Pollet 2022-10-14 15:10:10 +02:00 committed by GitHub
parent 188ef84c4f
commit 33f0aed5ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 86 additions and 28 deletions

View file

@ -76,6 +76,26 @@ providers:
--providers.http.pollTimeout=5s
```
### `headers`
_Optional_
Defines custom headers to be sent to the endpoint.
```yaml tab="File (YAML)"
providers:
headers:
name: value
```
```toml tab="File (TOML)"
[providers.http.headers]
name = "value"
```
```bash tab="CLI"
--providers.http.headers.name=value
### `tls`
_Optional_

View file

@ -645,6 +645,9 @@ Enable HTTP backend with default settings. (Default: ```false```)
`--providers.http.endpoint`:
Load configuration from this endpoint.
`--providers.http.headers.<name>`:
Define custom headers to be sent to the endpoint.
`--providers.http.pollinterval`:
Polling interval for endpoint. (Default: ```5```)

View file

@ -645,6 +645,9 @@ Enable HTTP backend with default settings. (Default: ```false```)
`TRAEFIK_PROVIDERS_HTTP_ENDPOINT`:
Load configuration from this endpoint.
`TRAEFIK_PROVIDERS_HTTP_HEADERS_<NAME>`:
Define custom headers to be sent to the endpoint.
`TRAEFIK_PROVIDERS_HTTP_POLLINTERVAL`:
Polling interval for endpoint. (Default: ```5```)

View file

@ -252,6 +252,9 @@
endpoint = "foobar"
pollInterval = "42s"
pollTimeout = "42s"
[providers.http.headers]
name0 = "foobar"
name1 = "foobar"
[providers.http.tls]
ca = "foobar"
caOptional = true

View file

@ -280,6 +280,9 @@ providers:
endpoint: foobar
pollInterval: 42s
pollTimeout: 42s
headers:
name0: foobar
name1: foobar
tls:
ca: foobar
caOptional: true

View file

@ -24,10 +24,12 @@ var _ provider.Provider = (*Provider)(nil)
// Provider is a provider.Provider implementation that queries an HTTP(s) endpoint for a configuration.
type Provider struct {
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint"`
PollInterval ptypes.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty" export:"true"`
PollTimeout ptypes.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty" export:"true"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint"`
PollInterval ptypes.Duration `description:"Polling interval for endpoint." json:"pollInterval,omitempty" toml:"pollInterval,omitempty" yaml:"pollInterval,omitempty" export:"true"`
PollTimeout ptypes.Duration `description:"Polling timeout for endpoint." json:"pollTimeout,omitempty" toml:"pollTimeout,omitempty" yaml:"pollTimeout,omitempty" export:"true"`
Headers map[string]string `description:"Define custom headers to be sent to the endpoint." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"`
TLS *types.ClientTLS `description:"Enable TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
httpClient *http.Client
lastConfigurationHash uint64
}
@ -139,9 +141,18 @@ func (p *Provider) updateConfiguration(configurationChan chan<- dynamic.Message)
// fetchConfigurationData fetches the configuration data from the configured endpoint.
func (p *Provider) fetchConfigurationData() ([]byte, error) {
res, err := p.httpClient.Get(p.Endpoint)
req, err := http.NewRequest(http.MethodGet, p.Endpoint, http.NoBody)
if err != nil {
return nil, err
return nil, fmt.Errorf("create fetch request: %w", err)
}
for k, v := range p.Headers {
req.Header.Set(k, v)
}
res, err := p.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("do fetch request: %w", err)
}
defer res.Body.Close()

View file

@ -69,35 +69,53 @@ func TestProvider_SetDefaults(t *testing.T) {
func TestProvider_fetchConfigurationData(t *testing.T) {
tests := []struct {
desc string
handler func(rw http.ResponseWriter, req *http.Request)
expData []byte
expErr bool
desc string
statusCode int
headers map[string]string
expData []byte
expErr require.ErrorAssertionFunc
}{
{
desc: "should return the fetched configuration data",
expData: []byte("{}"),
handler: func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusOK)
_, _ = fmt.Fprintf(rw, "{}")
},
desc: "should return the fetched configuration data",
statusCode: http.StatusOK,
expData: []byte("{}"),
expErr: require.NoError,
},
{
desc: "should return an error if endpoint does not return an OK status code",
expErr: true,
handler: func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(http.StatusNoContent)
desc: "should send configured headers",
statusCode: http.StatusOK,
headers: map[string]string{
"Foo": "bar",
"Bar": "baz",
},
expData: []byte("{}"),
expErr: require.NoError,
},
{
desc: "should return an error if endpoint does not return an OK status code",
statusCode: http.StatusInternalServerError,
expErr: require.Error,
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(test.handler))
defer server.Close()
handlerCalled := false
srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
handlerCalled = true
for k, v := range test.headers {
assert.Equal(t, v, req.Header.Get(k))
}
rw.WriteHeader(test.statusCode)
_, _ = rw.Write([]byte("{}"))
}))
defer srv.Close()
provider := Provider{
Endpoint: server.URL,
Endpoint: srv.URL,
Headers: test.headers,
PollInterval: ptypes.Duration(1 * time.Second),
PollTimeout: ptypes.Duration(1 * time.Second),
}
@ -106,12 +124,9 @@ func TestProvider_fetchConfigurationData(t *testing.T) {
require.NoError(t, err)
configData, err := provider.fetchConfigurationData()
if test.expErr {
require.Error(t, err)
return
}
test.expErr(t, err)
require.NoError(t, err)
assert.True(t, handlerCalled)
assert.Equal(t, test.expData, configData)
})
}