Support custom headers when fetching configuration through HTTP
This commit is contained in:
parent
188ef84c4f
commit
33f0aed5ea
7 changed files with 86 additions and 28 deletions
|
@ -76,6 +76,26 @@ providers:
|
||||||
--providers.http.pollTimeout=5s
|
--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`
|
### `tls`
|
||||||
|
|
||||||
_Optional_
|
_Optional_
|
||||||
|
|
|
@ -645,6 +645,9 @@ Enable HTTP backend with default settings. (Default: ```false```)
|
||||||
`--providers.http.endpoint`:
|
`--providers.http.endpoint`:
|
||||||
Load configuration from this endpoint.
|
Load configuration from this endpoint.
|
||||||
|
|
||||||
|
`--providers.http.headers.<name>`:
|
||||||
|
Define custom headers to be sent to the endpoint.
|
||||||
|
|
||||||
`--providers.http.pollinterval`:
|
`--providers.http.pollinterval`:
|
||||||
Polling interval for endpoint. (Default: ```5```)
|
Polling interval for endpoint. (Default: ```5```)
|
||||||
|
|
||||||
|
|
|
@ -645,6 +645,9 @@ Enable HTTP backend with default settings. (Default: ```false```)
|
||||||
`TRAEFIK_PROVIDERS_HTTP_ENDPOINT`:
|
`TRAEFIK_PROVIDERS_HTTP_ENDPOINT`:
|
||||||
Load configuration from this endpoint.
|
Load configuration from this endpoint.
|
||||||
|
|
||||||
|
`TRAEFIK_PROVIDERS_HTTP_HEADERS_<NAME>`:
|
||||||
|
Define custom headers to be sent to the endpoint.
|
||||||
|
|
||||||
`TRAEFIK_PROVIDERS_HTTP_POLLINTERVAL`:
|
`TRAEFIK_PROVIDERS_HTTP_POLLINTERVAL`:
|
||||||
Polling interval for endpoint. (Default: ```5```)
|
Polling interval for endpoint. (Default: ```5```)
|
||||||
|
|
||||||
|
|
|
@ -252,6 +252,9 @@
|
||||||
endpoint = "foobar"
|
endpoint = "foobar"
|
||||||
pollInterval = "42s"
|
pollInterval = "42s"
|
||||||
pollTimeout = "42s"
|
pollTimeout = "42s"
|
||||||
|
[providers.http.headers]
|
||||||
|
name0 = "foobar"
|
||||||
|
name1 = "foobar"
|
||||||
[providers.http.tls]
|
[providers.http.tls]
|
||||||
ca = "foobar"
|
ca = "foobar"
|
||||||
caOptional = true
|
caOptional = true
|
||||||
|
|
|
@ -280,6 +280,9 @@ providers:
|
||||||
endpoint: foobar
|
endpoint: foobar
|
||||||
pollInterval: 42s
|
pollInterval: 42s
|
||||||
pollTimeout: 42s
|
pollTimeout: 42s
|
||||||
|
headers:
|
||||||
|
name0: foobar
|
||||||
|
name1: foobar
|
||||||
tls:
|
tls:
|
||||||
ca: foobar
|
ca: foobar
|
||||||
caOptional: true
|
caOptional: true
|
||||||
|
|
|
@ -24,10 +24,12 @@ var _ provider.Provider = (*Provider)(nil)
|
||||||
|
|
||||||
// Provider is a provider.Provider implementation that queries an HTTP(s) endpoint for a configuration.
|
// Provider is a provider.Provider implementation that queries an HTTP(s) endpoint for a configuration.
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
Endpoint string `description:"Load configuration from this endpoint." json:"endpoint" toml:"endpoint" yaml:"endpoint"`
|
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"`
|
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"`
|
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"`
|
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
|
httpClient *http.Client
|
||||||
lastConfigurationHash uint64
|
lastConfigurationHash uint64
|
||||||
}
|
}
|
||||||
|
@ -139,9 +141,18 @@ func (p *Provider) updateConfiguration(configurationChan chan<- dynamic.Message)
|
||||||
|
|
||||||
// fetchConfigurationData fetches the configuration data from the configured endpoint.
|
// fetchConfigurationData fetches the configuration data from the configured endpoint.
|
||||||
func (p *Provider) fetchConfigurationData() ([]byte, error) {
|
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 {
|
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()
|
defer res.Body.Close()
|
||||||
|
|
|
@ -69,35 +69,53 @@ func TestProvider_SetDefaults(t *testing.T) {
|
||||||
|
|
||||||
func TestProvider_fetchConfigurationData(t *testing.T) {
|
func TestProvider_fetchConfigurationData(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
handler func(rw http.ResponseWriter, req *http.Request)
|
statusCode int
|
||||||
expData []byte
|
headers map[string]string
|
||||||
expErr bool
|
expData []byte
|
||||||
|
expErr require.ErrorAssertionFunc
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "should return the fetched configuration data",
|
desc: "should return the fetched configuration data",
|
||||||
expData: []byte("{}"),
|
statusCode: http.StatusOK,
|
||||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
expData: []byte("{}"),
|
||||||
rw.WriteHeader(http.StatusOK)
|
expErr: require.NoError,
|
||||||
_, _ = fmt.Fprintf(rw, "{}")
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "should return an error if endpoint does not return an OK status code",
|
desc: "should send configured headers",
|
||||||
expErr: true,
|
statusCode: http.StatusOK,
|
||||||
handler: func(rw http.ResponseWriter, req *http.Request) {
|
headers: map[string]string{
|
||||||
rw.WriteHeader(http.StatusNoContent)
|
"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 {
|
for _, test := range tests {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
server := httptest.NewServer(http.HandlerFunc(test.handler))
|
handlerCalled := false
|
||||||
defer server.Close()
|
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{
|
provider := Provider{
|
||||||
Endpoint: server.URL,
|
Endpoint: srv.URL,
|
||||||
|
Headers: test.headers,
|
||||||
PollInterval: ptypes.Duration(1 * time.Second),
|
PollInterval: ptypes.Duration(1 * time.Second),
|
||||||
PollTimeout: 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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
configData, err := provider.fetchConfigurationData()
|
configData, err := provider.fetchConfigurationData()
|
||||||
if test.expErr {
|
test.expErr(t, err)
|
||||||
require.Error(t, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
assert.True(t, handlerCalled)
|
||||||
assert.Equal(t, test.expData, configData)
|
assert.Equal(t, test.expData, configData)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue