Compare commits

..

7 Commits

Author SHA1 Message Date
Marcus Stong
be209ed30c #216: sets passHostHeader to true by default (#351) 2016-05-10 13:43:24 +02:00
Emile Vauge
4a4ba2791d Merge pull request #357 from s7anley/generate-fix
Create static folder on first generate
2016-05-09 23:47:23 +02:00
Ján Koščo
c61d9776e7 Create static folder on first generate 2016-05-09 23:13:22 +02:00
Vincent Demeester
b5716abd3e Merge pull request #347 from keis/consul-catalog-replace-space-in-tags
Replace whitespace in tags in consul_catalog
2016-05-07 17:26:17 +02:00
David Keijser
b9bb78d04b Normalise tags in backend name of consul_catalog
Another fun thing consul lets you do is use spaces in your tags. This
means when including tags in backend name it's possible to generate
invalid names.
2016-05-07 16:30:38 +02:00
Vincent Demeester
8a39ee65cd Merge pull request #341 from errm/k8s-namespacing
Adds option to namespace k8s ingresses
2016-05-07 13:30:35 +02:00
Ed Robinson
301a463aeb Adds option to namespace k8s ingresses
If the flag kubernetes.namespaces is set...
Then we only select ingresses from that/those namespace(s)

This allows multiple instances of traefik to
independently load balance for each namespace.
This could be for logical or security reasons.

Addresses #336
2016-05-06 11:36:53 +01:00
17 changed files with 601 additions and 32 deletions

1
cmd.go
View File

@@ -172,6 +172,7 @@ func init() {
traefikCmd.PersistentFlags().BoolVar(&arguments.kubernetes, "kubernetes", false, "Enable Kubernetes backend")
traefikCmd.PersistentFlags().StringVar(&arguments.Kubernetes.Endpoint, "kubernetes.endpoint", "127.0.0.1:8080", "Kubernetes server endpoint")
traefikCmd.PersistentFlags().StringSliceVar(&arguments.Kubernetes.Namespaces, "kubernetes.namespaces", []string{}, "Kubernetes namespaces")
_ = viper.BindPFlag("configFile", traefikCmd.PersistentFlags().Lookup("configFile"))
_ = viper.BindPFlag("graceTimeOut", traefikCmd.PersistentFlags().Lookup("graceTimeOut"))

View File

@@ -648,6 +648,7 @@ Træfɪk can be configured to use Kubernetes Ingress as a backend configuration:
# Optional
#
# endpoint = "http://localhost:8080"
# namespaces = ["default","production"]
```
You can find here an example [ingress](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.ingress.yaml) and [replication controller](https://raw.githubusercontent.com/containous/traefik/master/examples/k8s.rc.yaml).

View File

@@ -3,6 +3,7 @@ Copyright
*/
//go:generate rm -vf autogen/gen.go
//go:generate mkdir -p static
//go:generate go-bindata -pkg autogen -o autogen/gen.go ./static/... ./templates/...
//go:generate mkdir -p vendor/github.com/docker/docker/autogen/dockerversion

View File

@@ -133,9 +133,11 @@ func (provider *ConsulCatalog) getBackendAddress(node *api.ServiceEntry) string
func (provider *ConsulCatalog) getBackendName(node *api.ServiceEntry, index int) string {
serviceName := node.Service.Service + "--" + node.Service.Address + "--" + strconv.Itoa(node.Service.Port)
if len(node.Service.Tags) > 0 {
serviceName += "--" + strings.Join(node.Service.Tags, "--")
for _, tag := range node.Service.Tags {
serviceName += "--" + normalize(tag)
}
serviceName = strings.Replace(serviceName, ".", "-", -1)
serviceName = strings.Replace(serviceName, "=", "-", -1)

View File

@@ -154,6 +154,17 @@ func TestConsulCatalogGetBackendName(t *testing.T) {
},
expected: "api--10-0-0-1--80--traefik-weight-42--traefik-enable-true--1",
},
{
node: &api.ServiceEntry{
Service: &api.AgentService{
Service: "api",
Address: "10.0.0.1",
Port: 80,
Tags: []string{"a funny looking tag"},
},
},
expected: "api--10-0-0-1--80--a-funny-looking-tag--2",
},
}
for i, e := range services {
@@ -225,7 +236,8 @@ func TestConsulCatalogBuildConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-test": {
Backend: "backend-test",
Backend: "backend-test",
PassHostHeader: true,
Routes: map[string]types.Route{
"route-host-test": {
Rule: "Host:test.localhost",

View File

@@ -279,7 +279,7 @@ func (provider *Docker) getPassHostHeader(container dockertypes.ContainerJSON) s
if passHostHeader, err := getLabel(container, "traefik.frontend.passHostHeader"); err == nil {
return passHostHeader
}
return "false"
return "true"
}
func (provider *Docker) getEntryPoints(container dockertypes.ContainerJSON) []string {

View File

@@ -401,7 +401,6 @@ func TestDockerGetProtocol(t *testing.T) {
func TestDockerGetPassHostHeader(t *testing.T) {
provider := &Docker{}
containers := []struct {
container docker.ContainerJSON
expected string
@@ -413,7 +412,7 @@ func TestDockerGetPassHostHeader(t *testing.T) {
},
Config: &container.Config{},
},
expected: "false",
expected: "true",
},
{
container: docker.ContainerJSON{
@@ -422,11 +421,11 @@ func TestDockerGetPassHostHeader(t *testing.T) {
},
Config: &container.Config{
Labels: map[string]string{
"traefik.frontend.passHostHeader": "true",
"traefik.frontend.passHostHeader": "false",
},
},
},
expected: "true",
expected: "false",
},
}
@@ -744,8 +743,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test-docker-localhost": {
Backend: "backend-test",
EntryPoints: []string{},
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test-docker-localhost": {
Rule: "Host:test.docker.localhost",
@@ -816,8 +816,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
Backend: "backend-foobar",
EntryPoints: []string{"http", "https"},
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
Rule: "Host:test1.docker.localhost",
@@ -825,8 +826,9 @@ func TestDockerLoadDockerConfig(t *testing.T) {
},
},
"frontend-Host-test2-docker-localhost": {
Backend: "backend-foobar",
EntryPoints: []string{},
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test2-docker-localhost": {
Rule: "Host:test2.docker.localhost",

View File

@@ -49,7 +49,7 @@ func NewClient(baseURL string, caCert []byte, token string) (Client, error) {
}, nil
}
// GetIngresses returns all services in the cluster
// GetIngresses returns all ingresses in the cluster
func (c *clientImpl) GetIngresses(predicate func(Ingress) bool) ([]Ingress, error) {
getURL := c.endpointURL + extentionsEndpoint + defaultIngress

View File

@@ -21,8 +21,10 @@ const (
// Kubernetes holds configurations of the Kubernetes provider.
type Kubernetes struct {
BaseProvider `mapstructure:",squash"`
Endpoint string
BaseProvider `mapstructure:",squash"`
Endpoint string
disablePassHostHeaders bool
Namespaces []string
}
func (provider *Kubernetes) createClient() (k8s.Client, error) {
@@ -123,7 +125,15 @@ func (provider *Kubernetes) Provide(configurationChan chan<- types.ConfigMessage
func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configuration, error) {
ingresses, err := k8sClient.GetIngresses(func(ingress k8s.Ingress) bool {
return true
if len(provider.Namespaces) == 0 {
return true
}
for _, n := range provider.Namespaces {
if ingress.ObjectMeta.Namespace == n {
return true
}
}
return false
})
if err != nil {
log.Errorf("Error retrieving ingresses: %+v", err)
@@ -133,6 +143,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
map[string]*types.Backend{},
map[string]*types.Frontend{},
}
PassHostHeader := provider.getPassHostHeader()
for _, i := range ingresses {
for _, r := range i.Spec.Rules {
for _, pa := range r.HTTP.Paths {
@@ -143,8 +154,9 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path]; !exists {
templateObjects.Frontends[r.Host+pa.Path] = &types.Frontend{
Backend: r.Host + pa.Path,
Routes: make(map[string]types.Route),
Backend: r.Host + pa.Path,
PassHostHeader: PassHostHeader,
Routes: make(map[string]types.Route),
}
}
if _, exists := templateObjects.Frontends[r.Host+pa.Path].Routes[r.Host]; !exists {
@@ -190,6 +202,13 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
return &templateObjects, nil
}
func (provider *Kubernetes) getPassHostHeader() bool {
if provider.disablePassHostHeaders {
return false
}
return true
}
func (provider *Kubernetes) loadConfig(templateObjects types.Configuration) *types.Configuration {
var FuncMap = template.FuncMap{}
configuration, err := provider.getConfiguration("templates/kubernetes.tmpl", FuncMap, templateObjects)

View File

@@ -139,7 +139,8 @@ func TestLoadIngresses(t *testing.T) {
},
Frontends: map[string]*types.Frontend{
"foo/bar": {
Backend: "foo/bar",
Backend: "foo/bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"/bar": {
Rule: "PathPrefixStrip:/bar",
@@ -150,7 +151,8 @@ func TestLoadIngresses(t *testing.T) {
},
},
"bar": {
Backend: "bar",
Backend: "bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"bar": {
Rule: "Host:bar",
@@ -167,6 +169,526 @@ func TestLoadIngresses(t *testing.T) {
}
}
func TestGetPassHostHeader(t *testing.T) {
ingresses := []k8s.Ingress{{
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "foo",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/bar",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
},
},
}}
services := []k8s.Service{
{
ObjectMeta: k8s.ObjectMeta{
Name: "service1",
UID: "1",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.1",
Ports: []k8s.ServicePort{
{
Name: "http",
Port: 801,
},
},
},
},
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
watchChan: watchChan,
}
provider := Kubernetes{disablePassHostHeaders: true}
actual, err := provider.loadIngresses(client)
if err != nil {
t.Fatalf("error %+v", err)
}
expected := &types.Configuration{
Backends: map[string]*types.Backend{
"foo/bar": {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
},
Frontends: map[string]*types.Frontend{
"foo/bar": {
Backend: "foo/bar",
Routes: map[string]types.Route{
"/bar": {
Rule: "PathPrefixStrip:/bar",
},
"foo": {
Rule: "Host:foo",
},
},
},
},
}
actualJSON, _ := json.Marshal(actual)
expectedJSON, _ := json.Marshal(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON))
}
}
func TestLoadNamespacedIngresses(t *testing.T) {
ingresses := []k8s.Ingress{
{
ObjectMeta: k8s.ObjectMeta{
Namespace: "awesome",
},
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "foo",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/bar",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
{
Host: "bar",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Backend: k8s.IngressBackend{
ServiceName: "service3",
ServicePort: k8s.FromInt(443),
},
},
{
Backend: k8s.IngressBackend{
ServiceName: "service2",
ServicePort: k8s.FromInt(802),
},
},
},
},
},
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Namespace: "not-awesome",
},
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "baz",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/baz",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
},
},
},
}
services := []k8s.Service{
{
ObjectMeta: k8s.ObjectMeta{
Name: "service1",
UID: "1",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.1",
Ports: []k8s.ServicePort{
{
Name: "http",
Port: 801,
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Name: "service2",
UID: "2",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.2",
Ports: []k8s.ServicePort{
{
Port: 802,
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Name: "service3",
UID: "3",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.3",
Ports: []k8s.ServicePort{
{
Name: "http",
Port: 443,
},
},
},
},
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
watchChan: watchChan,
}
provider := Kubernetes{
Namespaces: []string{"awesome"},
}
actual, err := provider.loadIngresses(client)
if err != nil {
t.Fatalf("error %+v", err)
}
expected := &types.Configuration{
Backends: map[string]*types.Backend{
"foo/bar": {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
"bar": {
Servers: map[string]types.Server{
"2": {
URL: "http://10.0.0.2:802",
Weight: 1,
},
"3": {
URL: "https://10.0.0.3:443",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
},
Frontends: map[string]*types.Frontend{
"foo/bar": {
Backend: "foo/bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"/bar": {
Rule: "PathPrefixStrip:/bar",
},
"foo": {
Rule: "Host:foo",
},
},
},
"bar": {
Backend: "bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"bar": {
Rule: "Host:bar",
},
},
},
},
}
actualJSON, _ := json.Marshal(actual)
expectedJSON, _ := json.Marshal(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON))
}
}
func TestLoadMultipleNamespacedIngresses(t *testing.T) {
ingresses := []k8s.Ingress{
{
ObjectMeta: k8s.ObjectMeta{
Namespace: "awesome",
},
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "foo",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/bar",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
{
Host: "bar",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Backend: k8s.IngressBackend{
ServiceName: "service3",
ServicePort: k8s.FromInt(443),
},
},
{
Backend: k8s.IngressBackend{
ServiceName: "service2",
ServicePort: k8s.FromInt(802),
},
},
},
},
},
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Namespace: "somewhat-awesome",
},
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "awesome",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/quix",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Namespace: "not-awesome",
},
Spec: k8s.IngressSpec{
Rules: []k8s.IngressRule{
{
Host: "baz",
IngressRuleValue: k8s.IngressRuleValue{
HTTP: &k8s.HTTPIngressRuleValue{
Paths: []k8s.HTTPIngressPath{
{
Path: "/baz",
Backend: k8s.IngressBackend{
ServiceName: "service1",
ServicePort: k8s.FromInt(801),
},
},
},
},
},
},
},
},
},
}
services := []k8s.Service{
{
ObjectMeta: k8s.ObjectMeta{
Name: "service1",
UID: "1",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.1",
Ports: []k8s.ServicePort{
{
Name: "http",
Port: 801,
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Name: "service2",
UID: "2",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.2",
Ports: []k8s.ServicePort{
{
Port: 802,
},
},
},
},
{
ObjectMeta: k8s.ObjectMeta{
Name: "service3",
UID: "3",
},
Spec: k8s.ServiceSpec{
ClusterIP: "10.0.0.3",
Ports: []k8s.ServicePort{
{
Name: "http",
Port: 443,
},
},
},
},
}
watchChan := make(chan interface{})
client := clientMock{
ingresses: ingresses,
services: services,
watchChan: watchChan,
}
provider := Kubernetes{
Namespaces: []string{"awesome", "somewhat-awesome"},
}
actual, err := provider.loadIngresses(client)
if err != nil {
t.Fatalf("error %+v", err)
}
expected := &types.Configuration{
Backends: map[string]*types.Backend{
"foo/bar": {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
"bar": {
Servers: map[string]types.Server{
"2": {
URL: "http://10.0.0.2:802",
Weight: 1,
},
"3": {
URL: "https://10.0.0.3:443",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
"awesome/quix": {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
},
},
CircuitBreaker: nil,
LoadBalancer: nil,
},
},
Frontends: map[string]*types.Frontend{
"foo/bar": {
Backend: "foo/bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"/bar": {
Rule: "PathPrefixStrip:/bar",
},
"foo": {
Rule: "Host:foo",
},
},
},
"bar": {
Backend: "bar",
PassHostHeader: true,
Routes: map[string]types.Route{
"bar": {
Rule: "Host:bar",
},
},
},
"awesome/quix": {
Backend: "awesome/quix",
PassHostHeader: true,
Routes: map[string]types.Route{
"/quix": {
Rule: "PathPrefixStrip:/quix",
},
"awesome": {
Rule: "Host:awesome",
},
},
},
},
}
actualJSON, _ := json.Marshal(actual)
expectedJSON, _ := json.Marshal(expected)
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %+v, got %+v", string(expectedJSON), string(actualJSON))
}
}
type clientMock struct {
ingresses []k8s.Ingress
services []k8s.Service
@@ -174,7 +696,13 @@ type clientMock struct {
}
func (c clientMock) GetIngresses(predicate func(k8s.Ingress) bool) ([]k8s.Ingress, error) {
return c.ingresses, nil
var ingresses []k8s.Ingress
for _, ingress := range c.ingresses {
if predicate(ingress) {
ingresses = append(ingresses, ingress)
}
}
return ingresses, nil
}
func (c clientMock) WatchIngresses(predicate func(k8s.Ingress) bool, stopCh <-chan bool) (chan interface{}, chan error, error) {
return c.watchChan, make(chan error), nil

View File

@@ -430,7 +430,7 @@ func TestKVLoadConfig(t *testing.T) {
Frontends: map[string]*types.Frontend{
"frontend.with.dot": {
Backend: "backend.with.dot.too",
PassHostHeader: false,
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
"route.with.dot": {

View File

@@ -316,7 +316,7 @@ func (provider *Marathon) getPassHostHeader(application marathon.Application) st
if passHostHeader, err := provider.getLabel(application, "traefik.frontend.passHostHeader"); err == nil {
return passHostHeader
}
return "false"
return "true"
}
func (provider *Marathon) getEntryPoints(application marathon.Application) []string {

View File

@@ -82,8 +82,9 @@ func TestMarathonLoadConfig(t *testing.T) {
},
expectedFrontends: map[string]*types.Frontend{
`frontend-test`: {
Backend: "backend-test",
EntryPoints: []string{},
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
Routes: map[string]types.Route{
`route-host-test`: {
Rule: "Host:test.docker.localhost",
@@ -780,15 +781,15 @@ func TestMarathonGetPassHostHeader(t *testing.T) {
}{
{
application: marathon.Application{},
expected: "false",
expected: "true",
},
{
application: marathon.Application{
Labels: map[string]string{
"traefik.frontend.passHostHeader": "true",
"traefik.frontend.passHostHeader": "false",
},
},
expected: "true",
expected: "false",
},
}

View File

@@ -29,7 +29,7 @@
{{range .Services}}
[frontends.frontend-{{.ServiceName}}]
backend = "backend-{{.ServiceName}}"
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "false"}}
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}}
{{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}}
{{with $entryPoints}}
entrypoints = [{{range getEntryPoints $entryPoints}}

View File

@@ -9,6 +9,7 @@
[frontends]{{range $frontendName, $frontend := .Frontends}}
[frontends."{{$frontendName}}"]
backend = "{{$frontend.Backend}}"
passHostHeader = {{$frontend.PassHostHeader}}
{{range $routeName, $route := $frontend.Routes}}
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
rule = "{{$route.Rule}}"

View File

@@ -39,7 +39,7 @@
{{$entryPoints := SplitGet . "/entrypoints"}}
[frontends."{{$frontend}}"]
backend = "{{Get "" . "/backend"}}"
passHostHeader = {{Get "false" . "/passHostHeader"}}
passHostHeader = {{Get "true" . "/passHostHeader"}}
entryPoints = [{{range $entryPoints}}
"{{.}}",
{{end}}]

View File

@@ -343,6 +343,7 @@
# Optional
#
# endpoint = "http://localhost:8080"
# namespaces = ["default"]
################################################################
# Consul KV configuration backend