From 03ba8396f338269fbdf11290c146b2c63e4c2e38 Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Wed, 6 Sep 2017 09:36:02 +0200 Subject: [PATCH 01/17] Add test for SSL TERMINATION in Websocket --- .../fixtures/websocket/config_https.toml | 27 +++++++++ integration/websocket_test.go | 58 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 integration/fixtures/websocket/config_https.toml diff --git a/integration/fixtures/websocket/config_https.toml b/integration/fixtures/websocket/config_https.toml new file mode 100644 index 000000000..59a5df545 --- /dev/null +++ b/integration/fixtures/websocket/config_https.toml @@ -0,0 +1,27 @@ +defaultEntryPoints = ["wss"] + +logLevel = "DEBUG" + +[entryPoints] + [entryPoints.wss] + address = ":8000" + [entryPoints.wss.tls] + [[entryPoints.wss.tls.certificates]] + CertFile = "resources/tls/local.cert" + KeyFile = "resources/tls/local.key" + +[web] + address = ":8080" + +[file] + +[backends] + [backends.backend1] + [backends.backend1.servers.server1] + url = "{{ .WebsocketServer }}" + +[frontends] + [frontends.frontend1] + backend = "backend1" + [frontends.frontend1.routes.test_1] + rule = "Path:/ws" diff --git a/integration/websocket_test.go b/integration/websocket_test.go index 2bccf0b5f..d2a4dd988 100644 --- a/integration/websocket_test.go +++ b/integration/websocket_test.go @@ -1,6 +1,9 @@ package integration import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" "net" "net/http" "net/http/httptest" @@ -232,3 +235,58 @@ func (suite *WebsocketSuite) TestWrongOriginIgnoredByServer(c *check.C) { c.Assert(string(msg), checker.Equals, "OK") } + +func (suite *WebsocketSuite) TestSSLTermination(c *check.C) { + var upgrader = gorillawebsocket.Upgrader{} // use default options + + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + defer c.Close() + for { + mt, message, err := c.ReadMessage() + if err != nil { + break + } + err = c.WriteMessage(mt, message) + if err != nil { + break + } + } + })) + file := suite.adaptFile(c, "fixtures/websocket/config_https.toml", struct { + WebsocketServer string + }{ + WebsocketServer: srv.URL, + }) + + defer os.Remove(file) + cmd, _ := suite.cmdTraefik(withConfigFile(file), "--debug") + + err := cmd.Start() + c.Assert(err, check.IsNil) + defer cmd.Process.Kill() + + // wait for traefik + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.BodyContains("127.0.0.1")) + c.Assert(err, checker.IsNil) + + //Add client self-signed cert + roots := x509.NewCertPool() + certContent, err := ioutil.ReadFile("./resources/tls/local.cert") + roots.AppendCertsFromPEM(certContent) + gorillawebsocket.DefaultDialer.TLSClientConfig = &tls.Config{ + RootCAs: roots, + } + conn, _, err := gorillawebsocket.DefaultDialer.Dial("wss://127.0.0.1:8000/ws", nil) + c.Assert(err, checker.IsNil) + + err = conn.WriteMessage(gorillawebsocket.TextMessage, []byte("OK")) + c.Assert(err, checker.IsNil) + + _, msg, err := conn.ReadMessage() + c.Assert(err, checker.IsNil) + c.Assert(string(msg), checker.Equals, "OK") +} From cd6c58a372fdd819cc01ad7a43c31c0748d614fb Mon Sep 17 00:00:00 2001 From: Manuel Zapf Date: Wed, 6 Sep 2017 10:50:04 +0200 Subject: [PATCH 02/17] fix rancher api environment get --- provider/rancher/api.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/provider/rancher/api.go b/provider/rancher/api.go index 96a975df2..30d447064 100644 --- a/provider/rancher/api.go +++ b/provider/rancher/api.go @@ -125,14 +125,14 @@ func (p *Provider) apiProvide(configurationChan chan<- types.ConfigMessage, pool return nil } -func listRancherEnvironments(client *rancher.RancherClient) []*rancher.Project { +func listRancherEnvironments(client *rancher.RancherClient) []*rancher.Environment { - // Rancher Environment in frontend UI is actually project in API + // Rancher Environment in frontend UI is actually a stack // https://forums.rancher.com/t/api-key-for-all-environments/279/9 - var environmentList = []*rancher.Project{} + var environmentList = []*rancher.Environment{} - environments, err := client.Project.List(nil) + environments, err := client.Environment.List(nil) if err != nil { log.Errorf("Cannot get Rancher Environments %+v", err) @@ -193,12 +193,13 @@ func listRancherContainer(client *rancher.RancherClient) []*rancher.Container { return containerList } -func parseAPISourcedRancherData(environments []*rancher.Project, services []*rancher.Service, containers []*rancher.Container) []rancherData { +func parseAPISourcedRancherData(environments []*rancher.Environment, services []*rancher.Service, containers []*rancher.Container) []rancherData { var rancherDataList []rancherData for _, environment := range environments { for _, service := range services { + if service.EnvironmentId != environment.Id { continue } From 5157a6ad47cd1229eb6aa9664064d30114f623f4 Mon Sep 17 00:00:00 2001 From: Marco Jantke Date: Wed, 6 Sep 2017 11:58:03 +0200 Subject: [PATCH 03/17] Send traefik logs to stdout --- log/logger.go | 1 + 1 file changed, 1 insertion(+) diff --git a/log/logger.go b/log/logger.go index 448713c37..c60b08acb 100644 --- a/log/logger.go +++ b/log/logger.go @@ -18,6 +18,7 @@ var ( func init() { logger = logrus.StandardLogger().WithFields(logrus.Fields{}) + logrus.SetOutput(os.Stdout) } // Context sets the Context of the logger From a43cf8d2b874e8e7a952dfea1ac8c0687cb17c52 Mon Sep 17 00:00:00 2001 From: Charlie O'Leary Date: Thu, 7 Sep 2017 01:08:04 -0700 Subject: [PATCH 04/17] Fix IAM policy sid. --- docs/configuration/backends/ecs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index 72c7768bc..15484a196 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -103,7 +103,7 @@ Træfik needs the following policy to read ECS information: "Version": "2012-10-17", "Statement": [ { - "Sid": "Traefik ECS read access", + "Sid": "TraefikECSReadAccess", "Effect": "Allow", "Action": [ "ecs:ListClusters", From 8339139400e5dfd1fed6cb3545d611d8311ab381 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 7 Sep 2017 10:54:03 +0200 Subject: [PATCH 05/17] Access log default values --- middlewares/accesslog/logger_formatters.go | 26 ++-- .../accesslog/logger_formatters_test.go | 114 ++++++++++++++++++ 2 files changed, 128 insertions(+), 12 deletions(-) create mode 100644 middlewares/accesslog/logger_formatters_test.go diff --git a/middlewares/accesslog/logger_formatters.go b/middlewares/accesslog/logger_formatters.go index a19dffc46..fcc63e6c9 100644 --- a/middlewares/accesslog/logger_formatters.go +++ b/middlewares/accesslog/logger_formatters.go @@ -9,7 +9,10 @@ import ( ) // default format for time presentation -const commonLogTimeFormat = "02/Jan/2006:15:04:05 -0700" +const ( + commonLogTimeFormat = "02/Jan/2006:15:04:05 -0700" + defaultValue = "-" +) // CommonLogFormatter provides formatting in the Traefik common log format type CommonLogFormatter struct{} @@ -21,27 +24,26 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { timestamp := entry.Data[StartUTC].(time.Time).Format(commonLogTimeFormat) elapsedMillis := entry.Data[Duration].(time.Duration).Nanoseconds() / 1000000 - _, err := fmt.Fprintf(b, "%s - %s [%s] \"%s %s %s\" %d %d %s %s %d %s %s %dms\n", + _, err := fmt.Fprintf(b, "%s - %s [%s] \"%s %s %s\" %v %v %s %s %v %s %s %dms\n", entry.Data[ClientHost], entry.Data[ClientUsername], timestamp, entry.Data[RequestMethod], entry.Data[RequestPath], entry.Data[RequestProtocol], - entry.Data[OriginStatus], - entry.Data[OriginContentSize], - toLogString(entry.Data["request_Referer"]), - toLogString(entry.Data["request_User-Agent"]), - entry.Data[RequestCount], - toLogString(entry.Data[FrontendName]), - toLogString(entry.Data[BackendURL]), + toLog(entry.Data[OriginStatus]), + toLog(entry.Data[OriginContentSize]), + toLog(entry.Data["request_Referer"]), + toLog(entry.Data["request_User-Agent"]), + toLog(entry.Data[RequestCount]), + toLog(entry.Data[FrontendName]), + toLog(entry.Data[BackendURL]), elapsedMillis) return b.Bytes(), err } -func toLogString(v interface{}) string { - defaultValue := "-" +func toLog(v interface{}) interface{} { if v == nil { return defaultValue } @@ -54,7 +56,7 @@ func toLogString(v interface{}) string { return quoted(s.String(), defaultValue) default: - return defaultValue + return v } } diff --git a/middlewares/accesslog/logger_formatters_test.go b/middlewares/accesslog/logger_formatters_test.go new file mode 100644 index 000000000..051d73850 --- /dev/null +++ b/middlewares/accesslog/logger_formatters_test.go @@ -0,0 +1,114 @@ +package accesslog + +import ( + "net/http" + "testing" + "time" + + "github.com/Sirupsen/logrus" + "github.com/stretchr/testify/assert" +) + +func TestCommonLogFormatter_Format(t *testing.T) { + clf := CommonLogFormatter{} + + testCases := []struct { + name string + data map[string]interface{} + expectedLog string + }{ + { + name: "OriginStatus & OriginContentSize are nil", + data: map[string]interface{}{ + StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + Duration: 123 * time.Second, + ClientHost: "10.0.0.1", + ClientUsername: "Client", + RequestMethod: http.MethodGet, + RequestPath: "/foo", + RequestProtocol: "http", + OriginStatus: nil, + OriginContentSize: nil, + "request_Referer": "", + "request_User-Agent": "", + RequestCount: 0, + FrontendName: "", + BackendURL: "", + }, + expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - - - 0 - - 123000ms +`, + }, + { + name: "all data", + data: map[string]interface{}{ + StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), + Duration: 123 * time.Second, + ClientHost: "10.0.0.1", + ClientUsername: "Client", + RequestMethod: http.MethodGet, + RequestPath: "/foo", + RequestProtocol: "http", + OriginStatus: 123, + OriginContentSize: 132, + "request_Referer": "referer", + "request_User-Agent": "agent", + RequestCount: nil, + FrontendName: "foo", + BackendURL: "http://10.0.0.2/toto", + }, + expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" 123 132 "referer" "agent" - "foo" "http://10.0.0.2/toto" 123000ms +`, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + entry := &logrus.Entry{Data: test.data} + + raw, err := clf.Format(entry) + assert.NoError(t, err) + + assert.Equal(t, test.expectedLog, string(raw)) + }) + } + +} + +func Test_toLog(t *testing.T) { + + testCases := []struct { + name string + value interface{} + expectedLog interface{} + }{ + { + name: "", + value: 1, + expectedLog: 1, + }, + { + name: "", + value: "foo", + expectedLog: `"foo"`, + }, + { + name: "", + value: nil, + expectedLog: "-", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + lg := toLog(test.value) + + assert.Equal(t, test.expectedLog, lg) + }) + } +} From adcb99d330bfe885a89d98805c8a0f807abe50db Mon Sep 17 00:00:00 2001 From: Keith Bremner Date: Thu, 7 Sep 2017 10:16:03 +0100 Subject: [PATCH 06/17] Update cluster.md --- docs/user-guide/cluster.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user-guide/cluster.md b/docs/user-guide/cluster.md index 6d21a6b0a..c512c4f35 100644 --- a/docs/user-guide/cluster.md +++ b/docs/user-guide/cluster.md @@ -1,6 +1,6 @@ # Clustering / High Availability (beta) -This guide explains how tu use Træfik in high availability mode. +This guide explains how to use Træfik in high availability mode. In order to deploy and configure multiple Træfik instances, without copying the same configuration file on each instance, we will use a distributed Key-Value store. ## Prerequisites From 924e82ab0c2095b010ddb86200ae8f006d5724f9 Mon Sep 17 00:00:00 2001 From: Chulki Lee Date: Thu, 7 Sep 2017 02:40:03 -0700 Subject: [PATCH 07/17] doc: add notes on server urls with path --- docs/basics.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/basics.md b/docs/basics.md index da89f62bb..39a623575 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -381,7 +381,10 @@ To use a different port for the healthcheck: ### Servers -Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing). +Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing). + +!!! note + Paths in `url` are ignored. Use `Modifier` to specify paths instead. Here is an example of backends and servers definition: From 8c5514612f4509403715b10712c68f961b13285f Mon Sep 17 00:00:00 2001 From: Chulki Lee Date: Thu, 7 Sep 2017 03:02:03 -0700 Subject: [PATCH 08/17] Fix whitespaces --- .github/ISSUE_TEMPLATE.md | 2 +- CONTRIBUTING.md | 10 +- MAINTAINER.md | 2 +- README.md | 6 +- contrib/scripts/dumpcerts.sh | 16 ++-- docs/basics.md | 6 +- docs/benchmarks.md | 4 +- docs/configuration/backends/boltdb.md | 2 +- docs/configuration/backends/consul.md | 2 +- docs/configuration/backends/docker.md | 2 +- docs/configuration/backends/dynamodb.md | 2 +- docs/configuration/backends/file.md | 2 +- docs/configuration/backends/rancher.md | 2 +- docs/configuration/backends/web.md | 2 +- docs/configuration/backends/zookeeper.md | 2 +- docs/configuration/commons.md | 28 +++--- docs/index.md | 2 +- docs/user-guide/cluster.md | 2 +- docs/user-guide/kv-config.md | 2 +- docs/user-guide/swarm-mode.md | 10 +- examples/compose-marathon.yml | 2 +- examples/k8s.namespace.yaml | 2 +- examples/whoami-group.json | 2 +- integration/fixtures/access_log_config.toml | 92 +++++++++---------- integration/fixtures/acme/README.md | 2 +- integration/fixtures/consul/simple.toml | 4 +- integration/fixtures/docker/simple.toml | 2 +- integration/fixtures/https/rootcas/https.toml | 2 +- .../https/rootcas/https_with_file.toml | 2 +- integration/fixtures/https/rootcas/local.crt | 2 +- integration/fixtures/simple_default.toml | 2 +- integration/resources/compose/consul.yml | 12 +-- integration/resources/compose/consul_tls.yml | 4 +- integration/resources/compose/etcd.yml | 10 +- integration/resources/tls/consul_config.json | 2 +- webui/src/app/traefik.scss | 4 +- 36 files changed, 126 insertions(+), 126 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 9bf7b6189..782643358 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -23,7 +23,7 @@ If you intend to ask a support question: DO NOT FILE AN ISSUE. HOW TO WRITE A GOOD ISSUE? - Respect the issue template as more as possible. -- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I. +- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I. - The title must be short and descriptive. - Explain the conditions which led you to write this issue: the context. - The context should lead to something, an idea or a problem that you’re facing. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 916c81b16..62834fe9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,7 +52,7 @@ GOHOSTOS="linux" GOOS="linux" GOPATH="/home//go" GORACE="" -## more go env's will be listed +## more go env's will be listed ``` ##### Build Træfik @@ -63,7 +63,7 @@ Once your environment is set up and the Træfik repository cloned you can build cd ~/go/src/github.com/containous/traefik # Get go-bindata. Please note, the ellipses are required -go get github.com/jteeuwen/go-bindata/... +go get github.com/jteeuwen/go-bindata/... # Start build go generate @@ -73,7 +73,7 @@ go build ./cmd/traefik # run other commands like tests ``` -You will find the Træfik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`. +You will find the Træfik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`. ### Setting up `glide` and `glide-vc` for dependency management @@ -180,7 +180,7 @@ INFO - Cleaning site directory Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests. For end-user related support questions, refer to one of the following: -- the Traefik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) +- the Traefik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) - [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag) ### Title @@ -190,7 +190,7 @@ The title must be short and descriptive. (~60 characters) ### Description - Respect the issue template as much as possible. [template](.github/ISSUE_TEMPLATE.md) -- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I. +- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I. - Explain the conditions which led you to write this issue: the context. - The context should lead to something, an idea or a problem that you’re facing. - Remain clear and concise. diff --git a/MAINTAINER.md b/MAINTAINER.md index 8e001551c..3009f42ee 100644 --- a/MAINTAINER.md +++ b/MAINTAINER.md @@ -37,7 +37,7 @@ If you want to preserve commits you must add `bot/merge-method-rebase` before `s The status `status/4-merge-in-progress` is only for the bot. -If the bot is not able to perform the merge, the label `bot/need-human-merge` is added. +If the bot is not able to perform the merge, the label `bot/need-human-merge` is added. In this case you must solve conflicts/CI/... and after you only need to remove `bot/need-human-merge`. diff --git a/README.md b/README.md index f2a9d6857..2bfae92db 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Run it and forget it! You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers. If you are looking for a more comprehensive and real use-case example, you can also check [Play-With-Docker](http://training.play-with-docker.com/traefik-load-balancing/) to see how to load balance between multiple nodes. Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/). -You will learn Træfik basics in less than 10 minutes. +You will learn Træfik basics in less than 10 minutes. [![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k) @@ -134,13 +134,13 @@ git clone https://github.com/containous/traefik ## Documentation You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io). -A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io). +A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io). ## Support To get basic support, you can: -- join the Træfik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) +- join the Træfik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) - use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag) If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: . diff --git a/contrib/scripts/dumpcerts.sh b/contrib/scripts/dumpcerts.sh index 327943727..51af73c2c 100755 --- a/contrib/scripts/dumpcerts.sh +++ b/contrib/scripts/dumpcerts.sh @@ -6,22 +6,22 @@ # # Usage - dumpcerts.sh /etc/traefik/acme.json /etc/ssl/ # -# Dependencies - +# Dependencies - # util-linux # openssl # jq # The MIT License (MIT) -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -78,7 +78,7 @@ We need to read this file to explode the JSON bundle... exiting. ${USAGE}" >&2 exit 2 -fi +fi if [ ! -d "${certdir}" ]; then @@ -88,7 +88,7 @@ We need a directory in which to explode the JSON bundle... exiting. ${USAGE}" >&2 exit 4 -fi +fi jq=$(command -v jq) || exit_jq @@ -126,7 +126,7 @@ trap 'umask ${oldumask}' EXIT # # openssl: # echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----" \ -# | openssl rsa -inform pem -out "${pdir}/letsencrypt.key" +# | openssl rsa -inform pem -out "${pdir}/letsencrypt.key" # # and sed: # echo "-----BEGIN RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key" @@ -142,7 +142,7 @@ echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY----- # Process the certificates for each of the domains in acme.json for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' acme.json); do - # Traefik stores a cert bundle for each domain. Within this cert + # Traefik stores a cert bundle for each domain. Within this cert # bundle there is both proper the certificate and the Let's Encrypt CA echo "Extracting cert bundle for ${domain}" cert=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate | diff --git a/docs/basics.md b/docs/basics.md index 39a623575..67da3acbf 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -355,7 +355,7 @@ requests periodically carried out by Traefik. The check is defined by a path appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds). Each backend must respond to the health check within 5 seconds. -By default, the port of the backend server is used, however, this may be overridden. +By default, the port of the backend server is used, however, this may be overridden. A recovering backend returning 200 OK responses again is being returned to the LB rotation pool. @@ -500,9 +500,9 @@ Usage: traefik [command] [--flag=flag_argument] ``` -List of Træfik available commands with description :                                                              +List of Træfik available commands with description : -- `version` : Print version  +- `version` : Print version - `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it. - `bug`: The easiest way to submit a pre-filled issue. - `healthcheck`: Calls traefik `/ping` to check health. diff --git a/docs/benchmarks.md b/docs/benchmarks.md index a3e13ec3e..d320015c6 100644 --- a/docs/benchmarks.md +++ b/docs/benchmarks.md @@ -65,8 +65,8 @@ http { keepalive_requests 10000; types_hash_max_size 2048; - open_file_cache max=200000 inactive=300s; - open_file_cache_valid 300s; + open_file_cache max=200000 inactive=300s; + open_file_cache_valid 300s; open_file_cache_min_uses 2; open_file_cache_errors on; diff --git a/docs/configuration/backends/boltdb.md b/docs/configuration/backends/boltdb.md index ce44ea67e..e9e64d799 100644 --- a/docs/configuration/backends/boltdb.md +++ b/docs/configuration/backends/boltdb.md @@ -33,4 +33,4 @@ prefix = "/traefik" # Optional # filename = "boltdb.tmpl" -``` \ No newline at end of file +``` diff --git a/docs/configuration/backends/consul.md b/docs/configuration/backends/consul.md index c92fd59e4..734f83dc0 100644 --- a/docs/configuration/backends/consul.md +++ b/docs/configuration/backends/consul.md @@ -113,4 +113,4 @@ Additional settings can be defined using Consul Catalog tags: | `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). | | `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.frontend.priority=10` | Override default frontend priority | -| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | \ No newline at end of file +| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index 98ebd9164..12de434a2 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -168,4 +168,4 @@ exposedbydefault = false !!! warning when running inside a container, Træfik will need network access through: - `docker network connect ` \ No newline at end of file + `docker network connect ` diff --git a/docs/configuration/backends/dynamodb.md b/docs/configuration/backends/dynamodb.md index 44885dd1e..da4eb62e2 100644 --- a/docs/configuration/backends/dynamodb.md +++ b/docs/configuration/backends/dynamodb.md @@ -53,7 +53,7 @@ SecretAccessKey = "123" Endpoint = "http://localhost:8080" ``` -Items in the `dynamodb` table must have three attributes: +Items in the `dynamodb` table must have three attributes: - `id` (string): The id is the primary key. - `name`(string): The name is used as the name of the frontend or backend. diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index b6ddcbdf4..08347757d 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -148,7 +148,7 @@ filename = "rules.toml" ## Multiple .toml Files You could have multiple `.toml` files in a directory: - + ```toml [file] directory = "/path/to/config/" diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index f462d548c..784bc51a2 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -101,7 +101,7 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" !!! note If Traefik needs access to the Rancher API, you need to set the `endpoint`, `accesskey` and `secretkey` parameters. - + To enable traefik to fetch information about the Environment it's deployed in only, you need to create an `Environment API Key`. This can be found within the API Key advanced options. diff --git a/docs/configuration/backends/web.md b/docs/configuration/backends/web.md index 0989deb7a..b435294b8 100644 --- a/docs/configuration/backends/web.md +++ b/docs/configuration/backends/web.md @@ -86,7 +86,7 @@ You can enable Traefik to export internal metrics to different monitoring system - DataDog ```toml -# DataDog metrics exporter type +# DataDog metrics exporter type [web.metrics.datadog] Address = "localhost:8125" Pushinterval = "10s" diff --git a/docs/configuration/backends/zookeeper.md b/docs/configuration/backends/zookeeper.md index c35e3e37e..4d834497d 100644 --- a/docs/configuration/backends/zookeeper.md +++ b/docs/configuration/backends/zookeeper.md @@ -35,4 +35,4 @@ prefix = "traefik" # filename = "zookeeper.tmpl" ``` -Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. \ No newline at end of file +Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on traefik KV structure. diff --git a/docs/configuration/commons.md b/docs/configuration/commons.md index 900ee6a1e..af3fa76be 100644 --- a/docs/configuration/commons.md +++ b/docs/configuration/commons.md @@ -154,7 +154,7 @@ logLevel = "ERROR" ### Access Logs -Access logs are written when `[accessLog]` is defined. +Access logs are written when `[accessLog]` is defined. By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields. To enable access logs using the default settings just add the `[accessLog]` entry. @@ -197,7 +197,7 @@ This allows the logs to be rotated and processed by an external program, such as Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes. In the example below, if a 503 status is returned from the frontend "website", the custom error page at http://2.3.4.5/503.html is returned with the actual status code set in the HTTP header. -Note, the `503.html` page itself is not hosted on traefik, but some other infrastructure. +Note, the `503.html` page itself is not hosted on traefik, but some other infrastructure. ```toml [frontends] @@ -275,13 +275,13 @@ The configured status code ranges are inclusive; that is, in the above example, # If zero, no timeout exists. # Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw # values (digits). If no units are provided, the value is parsed assuming seconds. -# +# # Optional # Default: "0s" -# +# # readTimeout = "5s" -# writeTimeout is the maximum duration before timing out writes of the response. It covers the time from the end of +# writeTimeout is the maximum duration before timing out writes of the response. It covers the time from the end of # the request header read to the end of the response write. # If zero, no timeout exists. # Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw @@ -289,7 +289,7 @@ The configured status code ranges are inclusive; that is, in the above example, # # Optional # Default: "0s" -# +# # writeTimeout = "5s" # idleTimeout is the maximum duration an idle (keep-alive) connection will remain idle before closing itself. @@ -310,30 +310,30 @@ The configured status code ranges are inclusive; that is, in the above example, ```toml [forwardingTimeouts] -# dialTimeout is the amount of time to wait until a connection to a backend server can be established. +# dialTimeout is the amount of time to wait until a connection to a backend server can be established. # If zero, no timeout exists. # Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw # values (digits). If no units are provided, the value is parsed assuming seconds. -# +# # Optional # Default: "30s" -# +# # dialTimeout = "30s" -# responseHeaderTimeout is the amount of time to wait for a server's response headers after fully writing the request (including its body, if any). +# responseHeaderTimeout is the amount of time to wait for a server's response headers after fully writing the request (including its body, if any). # If zero, no timeout exists. # Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw # values (digits). If no units are provided, the value is parsed assuming seconds. # # Optional # Default: "0s" -# +# # responseHeaderTimeout = "0s" ``` ### Idle Timeout (deprecated) -Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `IdleTimeout`. +Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `IdleTimeout`. In the case both settings are configured, the deprecated option will be overwritten. `IdleTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. @@ -344,7 +344,7 @@ If no units are provided, the value is parsed assuming seconds. ```toml # IdleTimeout -# +# # DEPRECATED - see [respondingTimeouts] section. # # Optional @@ -388,7 +388,7 @@ filename = "my_custom_config_template.tpml" The template files can be written using functions provided by: -- [go template](https://golang.org/pkg/text/template/) +- [go template](https://golang.org/pkg/text/template/) - [sprig library](https://masterminds.github.io/sprig/) Example: diff --git a/docs/index.md b/docs/index.md index e91c27659..0be52991c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -71,7 +71,7 @@ Run it and forget it! You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers. Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com). -You will learn Træfik basics in less than 10 minutes. +You will learn Træfik basics in less than 10 minutes. [![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k) diff --git a/docs/user-guide/cluster.md b/docs/user-guide/cluster.md index c512c4f35..cf06a9702 100644 --- a/docs/user-guide/cluster.md +++ b/docs/user-guide/cluster.md @@ -18,4 +18,4 @@ Once your Træfik configuration is uploaded on your KV store, you can start each A Træfik cluster is based on a manager/worker model. When starting, Træfik will elect a manager. If this instance fails, another manager will be automatically elected. - + diff --git a/docs/user-guide/kv-config.md b/docs/user-guide/kv-config.md index de18cd925..012f757d1 100644 --- a/docs/user-guide/kv-config.md +++ b/docs/user-guide/kv-config.md @@ -36,7 +36,7 @@ consul: - "8301" - "8301/udp" - "8302" - - "8302/udp" + - "8302/udp" whoami1: image: emilevauge/whoami diff --git a/docs/user-guide/swarm-mode.md b/docs/user-guide/swarm-mode.md index ea77b1a03..55cf754e3 100644 --- a/docs/user-guide/swarm-mode.md +++ b/docs/user-guide/swarm-mode.md @@ -303,11 +303,11 @@ Wait, I thought we added the sticky flag to `whoami1`? Traefik relies on a cook First you need to add `whoami1.traefik` to your hosts file: ```shell -if [ -n "$(grep whoami1.traefik /etc/hosts)" ]; -then - echo "whoami1.traefik already exists (make sure the ip is current)"; -else - sudo -- sh -c -e "echo '$(docker-machine ip manager)\twhoami1.traefik' >> /etc/hosts"; +if [ -n "$(grep whoami1.traefik /etc/hosts)" ]; +then + echo "whoami1.traefik already exists (make sure the ip is current)"; +else + sudo -- sh -c -e "echo '$(docker-machine ip manager)\twhoami1.traefik' >> /etc/hosts"; fi ``` diff --git a/examples/compose-marathon.yml b/examples/compose-marathon.yml index 8c30b1033..7b3a1bc1f 100644 --- a/examples/compose-marathon.yml +++ b/examples/compose-marathon.yml @@ -56,4 +56,4 @@ services: - "mesos-slave:172.17.0.1" environment: - MARATHON_ZK=zk://zookeeper:2181/marathon - - MARATHON_MASTER=zk://zookeeper:2181/mesos \ No newline at end of file + - MARATHON_MASTER=zk://zookeeper:2181/mesos diff --git a/examples/k8s.namespace.yaml b/examples/k8s.namespace.yaml index 64e53ec0d..abc1ad232 100755 --- a/examples/k8s.namespace.yaml +++ b/examples/k8s.namespace.yaml @@ -3,4 +3,4 @@ apiVersion: v1 metadata: name: kube-system labels: - name: kube-system \ No newline at end of file + name: kube-system diff --git a/examples/whoami-group.json b/examples/whoami-group.json index 9c21a8dae..ee43b62b4 100644 --- a/examples/whoami-group.json +++ b/examples/whoami-group.json @@ -37,4 +37,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/integration/fixtures/access_log_config.toml b/integration/fixtures/access_log_config.toml index 2a3e0ed08..154e58d30 100644 --- a/integration/fixtures/access_log_config.toml +++ b/integration/fixtures/access_log_config.toml @@ -1,46 +1,46 @@ -################################################################ -# Global configuration -################################################################ -traefikLogsFile = "traefik.log" -accessLogsFile = "access.log" -logLevel = "ERROR" -defaultEntryPoints = ["http"] -[entryPoints] - [entryPoints.http] - address = ":8000" - -################################################################ -# Web configuration backend -################################################################ -[web] -address = ":7888" - -################################################################ -# File configuration backend -################################################################ -[file] - -################################################################ -# rules -################################################################ - [backends] - [backends.backend1] - [backends.backend1.servers.server1] - url = "http://127.0.0.1:8081" - [backends.backend2] - [backends.backend2.LoadBalancer] - method = "drr" - [backends.backend2.servers.server1] - url = "http://127.0.0.1:8082" - [backends.backend2.servers.server2] - url = "http://127.0.0.1:8083" - [frontends] - [frontends.frontend1] - backend = "backend1" - [frontends.frontend1.routes.test_1] - rule = "Path: /test1" - [frontends.frontend2] - backend = "backend2" - passHostHeader = true - [frontends.frontend2.routes.test_2] - rule = "Path: /test2" \ No newline at end of file +################################################################ +# Global configuration +################################################################ +traefikLogsFile = "traefik.log" +accessLogsFile = "access.log" +logLevel = "ERROR" +defaultEntryPoints = ["http"] +[entryPoints] + [entryPoints.http] + address = ":8000" + +################################################################ +# Web configuration backend +################################################################ +[web] +address = ":7888" + +################################################################ +# File configuration backend +################################################################ +[file] + +################################################################ +# rules +################################################################ + [backends] + [backends.backend1] + [backends.backend1.servers.server1] + url = "http://127.0.0.1:8081" + [backends.backend2] + [backends.backend2.LoadBalancer] + method = "drr" + [backends.backend2.servers.server1] + url = "http://127.0.0.1:8082" + [backends.backend2.servers.server2] + url = "http://127.0.0.1:8083" + [frontends] + [frontends.frontend1] + backend = "backend1" + [frontends.frontend1.routes.test_1] + rule = "Path: /test1" + [frontends.frontend2] + backend = "backend2" + passHostHeader = true + [frontends.frontend2.routes.test_2] + rule = "Path: /test2" diff --git a/integration/fixtures/acme/README.md b/integration/fixtures/acme/README.md index e9eedd4c0..44878dbc0 100644 --- a/integration/fixtures/acme/README.md +++ b/integration/fixtures/acme/README.md @@ -34,4 +34,4 @@ sudo openssl genrsa -out "$SSL_DIR/wildcard.key" 2048 sudo openssl req -new -subj "$(echo -n "$SUBJ" | tr "\n" "/")" -key "$SSL_DIR/wildcard.key" -out "$SSL_DIR/wildcard.csr" -passin pass:$PASSPHRASE sudo openssl x509 -req -days 3650 -in "$SSL_DIR/wildcard.csr" -signkey "$SSL_DIR/wildcard.key" -out "$SSL_DIR/wildcard.crt" sudo rm -f "$SSL_DIR/wildcard.csr" -``` \ No newline at end of file +``` diff --git a/integration/fixtures/consul/simple.toml b/integration/fixtures/consul/simple.toml index 317edc354..e4bbb1a44 100644 --- a/integration/fixtures/consul/simple.toml +++ b/integration/fixtures/consul/simple.toml @@ -11,6 +11,6 @@ logLevel = "DEBUG" endpoint = "{{.ConsulHost}}:8500" watch = true prefix = "traefik" - + [web] - address = ":8081" \ No newline at end of file + address = ":8081" diff --git a/integration/fixtures/docker/simple.toml b/integration/fixtures/docker/simple.toml index d3c3052ab..14ec46557 100644 --- a/integration/fixtures/docker/simple.toml +++ b/integration/fixtures/docker/simple.toml @@ -12,4 +12,4 @@ logLevel = "DEBUG" endpoint = "{{.DockerHost}}" domain = "docker.localhost" -exposedbydefault = true \ No newline at end of file +exposedbydefault = true diff --git a/integration/fixtures/https/rootcas/https.toml b/integration/fixtures/https/rootcas/https.toml index 4623351cd..8466b2f57 100644 --- a/integration/fixtures/https/rootcas/https.toml +++ b/integration/fixtures/https/rootcas/https.toml @@ -38,4 +38,4 @@ fblo6RBxUQ== [frontends.frontend1] backend = "backend1" [frontends.frontend1.routes.test_1] - rule = "Path: /ping" \ No newline at end of file + rule = "Path: /ping" diff --git a/integration/fixtures/https/rootcas/https_with_file.toml b/integration/fixtures/https/rootcas/https_with_file.toml index 294ba4a75..a95571a36 100644 --- a/integration/fixtures/https/rootcas/https_with_file.toml +++ b/integration/fixtures/https/rootcas/https_with_file.toml @@ -22,4 +22,4 @@ RootCAs = [ "fixtures/https/rootcas/local.crt"] [frontends.frontend1] backend = "backend1" [frontends.frontend1.routes.test_1] - rule = "Path: /ping" \ No newline at end of file + rule = "Path: /ping" diff --git a/integration/fixtures/https/rootcas/local.crt b/integration/fixtures/https/rootcas/local.crt index 017a30876..07457e03e 100644 --- a/integration/fixtures/https/rootcas/local.crt +++ b/integration/fixtures/https/rootcas/local.crt @@ -11,4 +11,4 @@ AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9 tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM fblo6RBxUQ== ------END CERTIFICATE----- \ No newline at end of file +-----END CERTIFICATE----- diff --git a/integration/fixtures/simple_default.toml b/integration/fixtures/simple_default.toml index d61410d7c..ff256c8a2 100644 --- a/integration/fixtures/simple_default.toml +++ b/integration/fixtures/simple_default.toml @@ -2,4 +2,4 @@ defaultEntryPoints = ["http"] [entryPoints] [entryPoints.http] - address = ":8000" \ No newline at end of file + address = ":8000" diff --git a/integration/resources/compose/consul.yml b/integration/resources/compose/consul.yml index 065769fbb..af10fad2e 100644 --- a/integration/resources/compose/consul.yml +++ b/integration/resources/compose/consul.yml @@ -10,16 +10,16 @@ consul: - "8301" - "8301/udp" - "8302" - - "8302/udp" - + - "8302/udp" + whoami1: image: emilevauge/whoami - + whoami2: image: emilevauge/whoami - + whoami3: image: emilevauge/whoami - + whoami4: - image: emilevauge/whoami \ No newline at end of file + image: emilevauge/whoami diff --git a/integration/resources/compose/consul_tls.yml b/integration/resources/compose/consul_tls.yml index 41a666799..3cd3f31c8 100644 --- a/integration/resources/compose/consul_tls.yml +++ b/integration/resources/compose/consul_tls.yml @@ -9,6 +9,6 @@ consul: - "8301" - "8301/udp" - "8302" - - "8302/udp" + - "8302/udp" volumes: - - ../tls:/configs \ No newline at end of file + - ../tls:/configs diff --git a/integration/resources/compose/etcd.yml b/integration/resources/compose/etcd.yml index 4023b18bc..0452454fd 100644 --- a/integration/resources/compose/etcd.yml +++ b/integration/resources/compose/etcd.yml @@ -1,14 +1,14 @@ etcd: image: containous/docker-etcd - + whoami1: image: emilevauge/whoami - + whoami2: image: emilevauge/whoami - + whoami3: image: emilevauge/whoami - + whoami4: - image: emilevauge/whoami \ No newline at end of file + image: emilevauge/whoami diff --git a/integration/resources/tls/consul_config.json b/integration/resources/tls/consul_config.json index 005d381a7..bd3aa403e 100644 --- a/integration/resources/tls/consul_config.json +++ b/integration/resources/tls/consul_config.json @@ -6,4 +6,4 @@ "cert_file": "/configs/consul.cert", "key_file": "/configs/consul.key", "verify_outgoing": true -} \ No newline at end of file +} diff --git a/webui/src/app/traefik.scss b/webui/src/app/traefik.scss index 9aa01ff24..08b3e5729 100644 --- a/webui/src/app/traefik.scss +++ b/webui/src/app/traefik.scss @@ -26,9 +26,9 @@ } table { - table-layout: fixed; + table-layout: fixed; } td, th { word-wrap: break-word; -} \ No newline at end of file +} From b705e64a8ad00902f9af09b1bd8977baba842f7e Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Sep 2017 15:28:02 +0200 Subject: [PATCH 09/17] Add Basic auth for consul catalog --- docs/configuration/backends/consul.md | 1 + integration/consul_catalog_test.go | 39 ++++++++++++++++++++++++++ provider/consul/consul_catalog.go | 9 ++++++ provider/consul/consul_catalog_test.go | 38 +++++++++++++++++++++++++ templates/consul_catalog.tmpl | 3 ++ 5 files changed, 90 insertions(+) diff --git a/docs/configuration/backends/consul.md b/docs/configuration/backends/consul.md index 734f83dc0..5b1c7b430 100644 --- a/docs/configuration/backends/consul.md +++ b/docs/configuration/backends/consul.md @@ -114,3 +114,4 @@ Additional settings can be defined using Consul Catalog tags: | `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.frontend.priority=10` | Override default frontend priority | | `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | diff --git a/integration/consul_catalog_test.go b/integration/consul_catalog_test.go index 16de26030..9fad9e974 100644 --- a/integration/consul_catalog_test.go +++ b/integration/consul_catalog_test.go @@ -202,3 +202,42 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultTrueSimpleServiceMultipleNode(c err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) c.Assert(err, checker.IsNil) } + +func (s *ConsulCatalogSuite) TestBasicAuthSimpleService(c *check.C) { + cmd, output := s.cmdTraefik( + withConfigFile("fixtures/consul_catalog/simple.toml"), + "--consulCatalog", + "--consulCatalog.exposedByDefault=true", + "--consulCatalog.endpoint="+s.consulIP+":8500", + "--consulCatalog.domain=consul.localhost") + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + defer func() { + s.displayTraefikLog(c, output) + }() + + nginx := s.composeProject.Container(c, "nginx") + + err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{ + "traefik.frontend.auth.basic=test:$2a$06$O5NksJPAcgrC9MuANkSoE.Xe9DSg7KcLLFYNr1Lj6hPcMmvgwxhme,test2:$2y$10$xP1SZ70QbZ4K2bTGKJOhpujkpcLxQcB3kEPF6XAV19IdcqsZTyDEe", + }) + c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) + defer s.deregisterService("test", nginx.NetworkSettings.IPAddress) + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "test.consul.localhost" + + err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody()) + c.Assert(err, checker.IsNil) + + req.SetBasicAuth("test", "test") + err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) + c.Assert(err, checker.IsNil) + + req.SetBasicAuth("test2", "test2") + err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) + c.Assert(err, checker.IsNil) +} diff --git a/provider/consul/consul_catalog.go b/provider/consul/consul_catalog.go index b058ee7e5..86d87eeda 100644 --- a/provider/consul/consul_catalog.go +++ b/provider/consul/consul_catalog.go @@ -330,6 +330,14 @@ func (p *CatalogProvider) getAttribute(name string, tags []string, defaultValue return p.getTag(p.getPrefixedName(name), tags, defaultValue) } +func (p *CatalogProvider) getBasicAuth(tags []string) []string { + list := p.getAttribute("frontend.auth.basic", tags, "") + if list != "" { + return strings.Split(list, ",") + } + return []string{} +} + func (p *CatalogProvider) hasTag(name string, tags []string) bool { // Very-very unlikely that a Consul tag would ever start with '=!=' tag := p.getTag(name, tags, "=!=") @@ -377,6 +385,7 @@ func (p *CatalogProvider) buildConfig(catalog []catalogUpdate) *types.Configurat "getBackendName": p.getBackendName, "getBackendAddress": p.getBackendAddress, "getAttribute": p.getAttribute, + "getBasicAuth": p.getBasicAuth, "getTag": p.getTag, "hasTag": p.hasTag, "getEntryPoints": p.getEntryPoints, diff --git a/provider/consul/consul_catalog_test.go b/provider/consul/consul_catalog_test.go index 21eff493e..23f3f1422 100644 --- a/provider/consul/consul_catalog_test.go +++ b/provider/consul/consul_catalog_test.go @@ -348,6 +348,7 @@ func TestConsulCatalogBuildConfig(t *testing.T) { "random.foo=bar", "traefik.backend.maxconn.amount=1000", "traefik.backend.maxconn.extractorfunc=client.ip", + "traefik.frontend.auth.basic=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", }, }, Nodes: []*api.ServiceEntry{ @@ -380,6 +381,7 @@ func TestConsulCatalogBuildConfig(t *testing.T) { Rule: "Host:test.localhost", }, }, + BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, }, }, expectedBackends: map[string]*types.Backend{ @@ -411,6 +413,7 @@ func TestConsulCatalogBuildConfig(t *testing.T) { t.Fatalf("expected %#v, got %#v", c.expectedBackends, actualConfig.Backends) } if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) { + t.Fatalf("expected %#v, got %#v", c.expectedFrontends["frontend-test"].BasicAuth, actualConfig.Frontends["frontend-test"].BasicAuth) t.Fatalf("expected %#v, got %#v", c.expectedFrontends, actualConfig.Frontends) } } @@ -853,3 +856,38 @@ func TestConsulCatalogFilterEnabled(t *testing.T) { }) } } + +func TestConsulCatalogGetBasicAuth(t *testing.T) { + cases := []struct { + desc string + tags []string + expected []string + }{ + { + desc: "label missing", + tags: []string{}, + expected: []string{}, + }, + { + desc: "label existing", + tags: []string{ + "traefik.frontend.auth.basic=user:password", + }, + expected: []string{"user:password"}, + }, + } + + for _, c := range cases { + c := c + t.Run(c.desc, func(t *testing.T) { + t.Parallel() + provider := &CatalogProvider{ + Prefix: "traefik", + } + actual := provider.getBasicAuth(c.tags) + if !reflect.DeepEqual(actual, c.expected) { + t.Errorf("actual %q, expected %q", actual, c.expected) + } + }) + } +} diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index 1787232f2..edcf9a7a1 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -40,6 +40,9 @@ "{{.}}", {{end}}] {{end}} + basicAuth = [{{range getBasicAuth .Attributes}} + "{{.}}", + {{end}}] [frontends."frontend-{{.ServiceName}}".routes."route-host-{{.ServiceName}}"] rule = "{{getFrontendRule .}}" {{end}} From c19cce69fa7c9b94a10ac1958eadece5e31cf875 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 7 Sep 2017 17:34:03 +0200 Subject: [PATCH 10/17] Add basic auth for ecs --- docs/configuration/backends/ecs.md | 23 ++++--- provider/ecs/ecs.go | 9 +++ provider/ecs/ecs_test.go | 106 +++++++++++++++++++---------- templates/ecs.tmpl | 3 + 4 files changed, 93 insertions(+), 48 deletions(-) diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index 15484a196..04a707a4b 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -78,17 +78,18 @@ SecretAccessKey = "123" Labels can be used on task containers to override default behaviour: -| Label | Description | -|----------------------------------------------|------------------------------------------------------------------------------------------| -| `traefik.protocol=https` | override the default `http` protocol | -| `traefik.weight=10` | assign this weight to the container | -| `traefik.enable=false` | disable this container in Træfik | -| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions | -| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). | -| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. | -| `traefik.frontend.priority=10` | override default frontend priority | -| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | +| Label | Description | +|---------------------------------------------------|------------------------------------------------------------------------------------------| +| `traefik.protocol=https` | override the default `http` protocol | +| `traefik.weight=10` | assign this weight to the container | +| `traefik.enable=false` | disable this container in Træfik | +| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions | +| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). | +| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. | +| `traefik.frontend.priority=10` | override default frontend priority | +| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | If `AccessKeyID`/`SecretAccessKey` is not given credentials will be resolved in the following order: diff --git a/provider/ecs/ecs.go b/provider/ecs/ecs.go index b092ec8f9..e48ebe5bc 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -182,6 +182,7 @@ func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types var ecsFuncMap = template.FuncMap{ "filterFrontends": p.filterFrontends, "getFrontendRule": p.getFrontendRule, + "getBasicAuth": p.getBasicAuth, "getLoadBalancerSticky": p.getLoadBalancerSticky, "getLoadBalancerMethod": p.getLoadBalancerMethod, } @@ -469,6 +470,14 @@ func (p *Provider) getFrontendRule(i ecsInstance) string { return "Host:" + strings.ToLower(strings.Replace(i.Name, "_", "-", -1)) + "." + p.Domain } +func (p *Provider) getBasicAuth(i ecsInstance) []string { + label := i.label(types.LabelFrontendAuthBasic) + if label != "" { + return strings.Split(label, ",") + } + return []string{} +} + func (p *Provider) getLoadBalancerSticky(instances []ecsInstance) string { if len(instances) > 0 { label := instances[0].label(types.LabelBackendLoadbalancerSticky) diff --git a/provider/ecs/ecs_test.go b/provider/ecs/ecs_test.go index 1264d3713..c3cd7bc82 100644 --- a/provider/ecs/ecs_test.go +++ b/provider/ecs/ecs_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ecs" "github.com/containous/traefik/types" + "github.com/stretchr/testify/assert" ) func makeEcsInstance(containerDef *ecs.ContainerDefinition) ecsInstance { @@ -74,10 +75,10 @@ func TestEcsProtocol(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.Protocol() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.Protocol() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -93,10 +94,10 @@ func TestEcsHost(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.Host() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.Host() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -112,10 +113,10 @@ func TestEcsPort(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.Port() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.Port() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -137,10 +138,10 @@ func TestEcsWeight(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.Weight() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.Weight() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -162,10 +163,10 @@ func TestEcsPassHostHeader(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.PassHostHeader() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.PassHostHeader() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -187,10 +188,10 @@ func TestEcsPriority(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.Priority() - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.Priority() + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -218,10 +219,10 @@ func TestEcsEntryPoints(t *testing.T) { }, } - for i, c := range cases { - value := c.instanceInfo.EntryPoints() - if !reflect.DeepEqual(value, c.expected) { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + for i, test := range cases { + value := test.instanceInfo.EntryPoints() + if !reflect.DeepEqual(value, test.expected) { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -299,13 +300,13 @@ func TestFilterInstance(t *testing.T) { }, } - for i, c := range cases { + for i, test := range cases { provider := &Provider{ - ExposedByDefault: c.exposedByDefault, + ExposedByDefault: test.exposedByDefault, } - value := provider.filterInstance(c.instanceInfo) - if value != c.expected { - t.Fatalf("Should have been %v, got %v (case %d)", c.expected, value, i) + value := provider.filterInstance(test.instanceInfo) + if value != test.expected { + t.Fatalf("Should have been %v, got %v (case %d)", test.expected, value, i) } } } @@ -330,9 +331,9 @@ func TestTaskChunking(t *testing.T) { {1001, []int{100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1}}, } - for _, c := range cases { + for _, test := range cases { var tasks []*string - for v := 0; v < c.count; v++ { + for v := 0; v < test.count; v++ { tasks = append(tasks, &testval) } @@ -343,8 +344,39 @@ func TestTaskChunking(t *testing.T) { outCount = append(outCount, len(el)) } - if !reflect.DeepEqual(outCount, c.expectedLengths) { - t.Errorf("Chunking %d elements, expected %#v, got %#v", c.count, c.expectedLengths, outCount) + if !reflect.DeepEqual(outCount, test.expectedLengths) { + t.Errorf("Chunking %d elements, expected %#v, got %#v", test.count, test.expectedLengths, outCount) } } } + +func TestEcsGetBasicAuth(t *testing.T) { + cases := []struct { + desc string + instance ecsInstance + expected []string + }{ + { + desc: "label missing", + instance: simpleEcsInstance(map[string]*string{}), + expected: []string{}, + }, + { + desc: "label existing", + instance: simpleEcsInstance(map[string]*string{ + types.LabelFrontendAuthBasic: aws.String("user:password"), + }), + expected: []string{"user:password"}, + }, + } + + for _, test := range cases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{} + actual := provider.getBasicAuth(test.instance) + assert.Equal(t, test.expected, actual) + }) + } +} diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 99c758d24..36e9e29b4 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -18,6 +18,9 @@ priority = {{ .Priority }} entryPoints = [{{range .EntryPoints }} "{{.}}", + {{end}}] + basicAuth = [{{range getBasicAuth .}} + "{{.}}", {{end}}] [frontends.frontend-{{ $serviceName }}.routes.route-frontend-{{ $serviceName }}] rule = "{{getFrontendRule .}}" From 3b6afdf80cf72f8eee7bb4ade883e8ca8e54a071 Mon Sep 17 00:00:00 2001 From: Emile Vauge Date: Thu, 7 Sep 2017 20:14:03 +0200 Subject: [PATCH 11/17] Fix error in prepareServer --- server/server.go | 1 + server/server_test.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/server.go b/server/server.go index 0cc19e053..0f5379d34 100644 --- a/server/server.go +++ b/server/server.go @@ -647,6 +647,7 @@ func (server *Server) prepareServer(entryPointName string, entryPoint *configura listener, err := net.Listen("tcp", entryPoint.Address) if err != nil { log.Error("Error opening listener ", err) + return nil, nil, err } if entryPoint.ProxyProtocol { diff --git a/server/server_test.go b/server/server_test.go index 876837b3b..8d47c7831 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -96,7 +96,7 @@ func TestPrepareServerTimeouts(t *testing.T) { t.Parallel() entryPointName := "http" - entryPoint := &configuration.EntryPoint{Address: "localhost:8080"} + entryPoint := &configuration.EntryPoint{Address: "localhost:0"} router := middlewares.NewHandlerSwitcher(mux.NewRouter()) srv := NewServer(test.globalConfig) @@ -504,14 +504,14 @@ func TestServerEntrypointWhitelistConfig(t *testing.T) { { desc: "no whitelist middleware if no config on entrypoint", entrypoint: &configuration.EntryPoint{ - Address: ":8080", + Address: ":0", }, wantMiddleware: false, }, { desc: "whitelist middleware should be added if configured on entrypoint", entrypoint: &configuration.EntryPoint{ - Address: ":8080", + Address: ":0", WhitelistSourceRange: []string{ "127.0.0.1/32", }, From 286d882f1eb4dc4dd702dc583af81dcf9ae5ee83 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 8 Sep 2017 10:26:03 +0200 Subject: [PATCH 12/17] Remove old glide elements for integration tests. --- Makefile | 2 +- script/test-unit | 1 - script/validate-gofmt | 2 +- script/validate-golint | 2 +- script/validate-govet | 2 +- script/validate-misspell | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index fd89ccfe3..3d027bcdf 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ TRAEFIK_ENVS := \ -e CI \ -e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container. -SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/' | grep -v '^integration/vendor/') +SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/') BIND_DIR := "dist" TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)" diff --git a/script/test-unit b/script/test-unit index 4de9cd3f0..22697447d 100755 --- a/script/test-unit +++ b/script/test-unit @@ -17,7 +17,6 @@ find_dirs() { find . -not \( \ \( \ -path './integration/*' \ - -o -path './.glide/*' \ -o -path './vendor/*' \ -o -path './.git/*' \ \) \ diff --git a/script/validate-gofmt b/script/validate-gofmt index 099488443..95ab6603c 100755 --- a/script/validate-gofmt +++ b/script/validate-gofmt @@ -3,7 +3,7 @@ source "$(dirname "$BASH_SOURCE")/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^\(integration/\)\?vendor/' || true) ) +files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) ) unset IFS badFiles=() diff --git a/script/validate-golint b/script/validate-golint index 45f103a41..1006ba205 100755 --- a/script/validate-golint +++ b/script/validate-golint @@ -3,7 +3,7 @@ source "$(dirname "$BASH_SOURCE")/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^\(integration/\)\?vendor/\|autogen' || true) ) +files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/\|autogen' || true) ) unset IFS errors=() diff --git a/script/validate-govet b/script/validate-govet index af4a6f56a..6d526ad74 100755 --- a/script/validate-govet +++ b/script/validate-govet @@ -3,7 +3,7 @@ source "$(dirname "$BASH_SOURCE")/.validate" IFS=$'\n' -files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^\(integration/\)\?vendor/' || true) ) +files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) ) unset IFS errors=() diff --git a/script/validate-misspell b/script/validate-misspell index e21161223..f4620b496 100755 --- a/script/validate-misspell +++ b/script/validate-misspell @@ -3,7 +3,7 @@ source "$(dirname "$BASH_SOURCE")/.validate" IFS=$'\n' -src=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^\(integration/\)\?vendor/\|autogen' || true) ) +src=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/\|autogen' || true) ) docs=( $(validate_diff --diff-filter=ACMR --name-only -- 'docs/*.md') ) unset IFS files=("${src[@]}" "${docs[@]}") From 651d993d9cc0ba35e9352fbca5d06ab409e85220 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 8 Sep 2017 11:22:03 +0200 Subject: [PATCH 13/17] prometheus, HTTP method and utf8 --- middlewares/metrics.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/middlewares/metrics.go b/middlewares/metrics.go index c76c7f0ad..bd91f63d9 100644 --- a/middlewares/metrics.go +++ b/middlewares/metrics.go @@ -4,7 +4,9 @@ import ( "net/http" "strconv" "time" + "unicode/utf8" + "github.com/containous/traefik/log" "github.com/containous/traefik/metrics" gokitmetrics "github.com/go-kit/kit/metrics" ) @@ -17,7 +19,7 @@ type MetricsWrapper struct { } // NewMetricsWrapper return a MetricsWrapper struct with -// a given Metrics implementation e.g Prometheuss +// a given Metrics implementation func NewMetricsWrapper(registry metrics.Registry, service string) *MetricsWrapper { var metricsWrapper = MetricsWrapper{ registry: registry, @@ -32,7 +34,7 @@ func (m *MetricsWrapper) ServeHTTP(rw http.ResponseWriter, r *http.Request, next prw := &responseRecorder{rw, http.StatusOK} next(prw, r) - reqLabels := []string{"service", m.serviceName, "code", strconv.Itoa(prw.statusCode), "method", r.Method} + reqLabels := []string{"service", m.serviceName, "code", strconv.Itoa(prw.statusCode), "method", getMethod(r)} m.registry.ReqsCounter().With(reqLabels...).Add(1) reqDurationLabels := []string{"service", m.serviceName, "code", strconv.Itoa(prw.statusCode)} @@ -48,6 +50,14 @@ func NewMetricsRetryListener(retryMetrics retryMetrics, backendName string) Retr return &MetricsRetryListener{retryMetrics: retryMetrics, backendName: backendName} } +func getMethod(r *http.Request) string { + if !utf8.ValidString(r.Method) { + log.Warnf("Invalid HTTP method encoding: %s", r.Method) + return "NON_UTF8_HTTP_METHOD" + } + return r.Method +} + // MetricsRetryListener is an implementation of the RetryListener interface to // record RequestMetrics about retry attempts. type MetricsRetryListener struct { From 16fc3675db74e0efcaedd39f4c45e0cc51c910b1 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 8 Sep 2017 14:50:04 +0200 Subject: [PATCH 14/17] Force GOARM to v6. --- script/crossbinary-default | 12 +++++++----- script/crossbinary-others | 17 +++++++++++------ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/script/crossbinary-default b/script/crossbinary-default index 5509c7a2c..83a197438 100755 --- a/script/crossbinary-default +++ b/script/crossbinary-default @@ -18,17 +18,19 @@ if [ -z "$DATE" ]; then DATE=$(date -u '+%Y-%m-%d_%I:%M:%S%p') fi +echo "Building ${VERSION} ${CODENAME} ${DATE}" + GIT_REPO_URL='github.com/containous/traefik/version' GO_BUILD_CMD="go build -ldflags" -GO_BUILD_OPT="-s -w -X ${GIT_REPO_URL}.Version=$VERSION -X ${GIT_REPO_URL}.Codename=$CODENAME -X ${GIT_REPO_URL}.BuildDate=$DATE" +GO_BUILD_OPT="-s -w -X ${GIT_REPO_URL}.Version=${VERSION} -X ${GIT_REPO_URL}.Codename=${CODENAME} -X ${GIT_REPO_URL}.BuildDate=${DATE}" # Build 386 amd64 binaries OS_PLATFORM_ARG=(linux windows darwin) OS_ARCH_ARG=(amd64) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do - echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 $GO_BUILD_CMD "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ + echo "Building binary for ${OS}/${ARCH}..." + GOARCH=${ARCH} GOOS=${OS} CGO_ENABLED=0 ${GO_BUILD_CMD} "${GO_BUILD_OPT}" -o "dist/traefik_${OS}-${ARCH}" ./cmd/traefik/ done done @@ -37,7 +39,7 @@ OS_PLATFORM_ARG=(linux) OS_ARCH_ARG=(arm64) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do - echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 $GO_BUILD_CMD "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ + echo "Building binary for ${OS}/${ARCH}..." + GOARCH=${ARCH} GOOS=${OS} CGO_ENABLED=0 ${GO_BUILD_CMD} "${GO_BUILD_OPT}" -o "dist/traefik_${OS}-${ARCH}" ./cmd/traefik/ done done diff --git a/script/crossbinary-others b/script/crossbinary-others index 8b9fb2eaa..51d9a01b2 100755 --- a/script/crossbinary-others +++ b/script/crossbinary-others @@ -18,9 +18,11 @@ if [ -z "$DATE" ]; then DATE=$(date -u '+%Y-%m-%d_%I:%M:%S%p') fi +echo "Building ${VERSION} ${CODENAME} ${DATE}" + GIT_REPO_URL='github.com/containous/traefik/version' GO_BUILD_CMD="go build -ldflags" -GO_BUILD_OPT="-s -w -X ${GIT_REPO_URL}.Version=$VERSION -X ${GIT_REPO_URL}.Codename=$CODENAME -X ${GIT_REPO_URL}.BuildDate=$DATE" +GO_BUILD_OPT="-s -w -X ${GIT_REPO_URL}.Version=${VERSION} -X ${GIT_REPO_URL}.Codename=${CODENAME} -X ${GIT_REPO_URL}.BuildDate=${DATE}" # Build arm binaries OS_PLATFORM_ARG=(linux windows darwin) @@ -28,7 +30,7 @@ OS_ARCH_ARG=(386) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 $GO_BUILD_CMD "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ + GOARCH=${ARCH} GOOS=${OS} CGO_ENABLED=0 ${GO_BUILD_CMD} "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ done done @@ -38,18 +40,21 @@ OS_ARCH_ARG=(386 amd64) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do # Get rid of existing binaries - rm -f dist/traefik_$OS-$ARCH + rm -f dist/traefik_${OS}-${ARCH} echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 $GO_BUILD_CMD "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ + GOARCH=${ARCH} GOOS=${OS} CGO_ENABLED=0 ${GO_BUILD_CMD} "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ done done # Build arm binaries OS_PLATFORM_ARG=(linux) OS_ARCH_ARG=(arm) +ARM_ARG=(6) for OS in ${OS_PLATFORM_ARG[@]}; do for ARCH in ${OS_ARCH_ARG[@]}; do - echo "Building binary for $OS/$ARCH..." - GOARCH=$ARCH GOOS=$OS CGO_ENABLED=0 $GO_BUILD_CMD "$GO_BUILD_OPT" -o "dist/traefik_$OS-$ARCH" ./cmd/traefik/ + for ARM in ${ARM_ARG[@]}; do + echo "Building binary for $OS/${ARCH}32v${ARM}..." + GOARCH=${ARCH} GOOS=${OS} GOARM=${ARM} CGO_ENABLED=0 ${GO_BUILD_CMD} "$GO_BUILD_OPT" -o "dist/traefik_$OS-${ARCH}" ./cmd/traefik/ + done done done From ecf31097ea9fc4b525e38a9fb8782b3baa185707 Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Fri, 8 Sep 2017 16:14:03 +0200 Subject: [PATCH 15/17] Upgrade oxy for websocket bug --- glide.lock | 4 ++-- glide.yaml | 2 +- vendor/github.com/vulcand/oxy/forward/fwd.go | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index a279883b3..a6c21d9fc 100644 --- a/glide.lock +++ b/glide.lock @@ -1,4 +1,4 @@ -hash: fd8e1bbabefc71e0fc1551dcf67688d0b5ad1f23168a5c0256b8c98dda1b1fb3 +hash: 7b973a5de5444f7f29252ae78529ee50569174b2c17f4aa89bf15aaf2553c6b7 updated: 2017-08-25T11:52:16.848940186+02:00 imports: - name: cloud.google.com/go @@ -479,7 +479,7 @@ imports: - name: github.com/urfave/negroni version: 490e6a555d47ca891a89a150d0c1ef3922dfffe9 - name: github.com/vulcand/oxy - version: 7baa97f97557ff96be2798972dc831c7ba0a46e7 + version: 6c94d2888dba2b1a15a89b8a2ca515fc85e07477 repo: https://github.com/containous/oxy.git vcs: git subpackages: diff --git a/glide.yaml b/glide.yaml index 0fda26a56..2d9c37413 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,7 +12,7 @@ import: - package: github.com/cenk/backoff - package: github.com/containous/flaeg - package: github.com/vulcand/oxy - version: 7baa97f97557ff96be2798972dc831c7ba0a46e7 + version: 6c94d2888dba2b1a15a89b8a2ca515fc85e07477 repo: https://github.com/containous/oxy.git vcs: git subpackages: diff --git a/vendor/github.com/vulcand/oxy/forward/fwd.go b/vendor/github.com/vulcand/oxy/forward/fwd.go index f7fafbfb8..04974ad4d 100644 --- a/vendor/github.com/vulcand/oxy/forward/fwd.go +++ b/vendor/github.com/vulcand/oxy/forward/fwd.go @@ -128,6 +128,7 @@ func New(setters ...optSetter) (*Forwarder, error) { if f.httpForwarder.roundTripper == nil { f.httpForwarder.roundTripper = http.DefaultTransport } + f.websocketForwarder.TLSClientConfig = f.httpForwarder.roundTripper.(*http.Transport).TLSClientConfig if f.httpForwarder.rewriter == nil { h, err := os.Hostname() if err != nil { @@ -317,7 +318,11 @@ func (f *websocketForwarder) copyRequest(req *http.Request, u *url.URL) (outReq } if requestURI, err := url.ParseRequestURI(outReq.RequestURI); err == nil { - outReq.URL.Path = requestURI.Path + if requestURI.RawPath != "" { + outReq.URL.Path = requestURI.RawPath + } else { + outReq.URL.Path = requestURI.Path + } outReq.URL.RawQuery = requestURI.RawQuery } From f80a6ef2a665729da7dd5b408736235e09f1acd5 Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Fri, 8 Sep 2017 20:50:04 +0200 Subject: [PATCH 16/17] Fix consul catalog refresh problems --- integration/consul_catalog_test.go | 95 +++++++++- .../fixtures/consul_catalog/simple.toml | 3 + .../resources/compose/consul_catalog.yml | 2 +- provider/consul/consul_catalog.go | 98 ++++++++-- provider/consul/consul_catalog_test.go | 172 +++++++++--------- 5 files changed, 259 insertions(+), 111 deletions(-) diff --git a/integration/consul_catalog_test.go b/integration/consul_catalog_test.go index 9fad9e974..e0aa011c6 100644 --- a/integration/consul_catalog_test.go +++ b/integration/consul_catalog_test.go @@ -66,6 +66,30 @@ func (s *ConsulCatalogSuite) registerService(name string, address string, port i return err } +func (s *ConsulCatalogSuite) registerAgentService(name string, address string, port int, tags []string) error { + agent := s.consulClient.Agent() + err := agent.ServiceRegister( + &api.AgentServiceRegistration{ + ID: address, + Tags: tags, + Name: name, + Address: address, + Port: port, + Check: &api.AgentServiceCheck{ + HTTP: "http://" + address, + Interval: "10s", + }, + }, + ) + return err +} + +func (s *ConsulCatalogSuite) deregisterAgentService(address string) error { + agent := s.consulClient.Agent() + err := agent.ServiceDeregister(address) + return err +} + func (s *ConsulCatalogSuite) deregisterService(name string, address string) error { catalog := s.consulClient.Catalog() _, err := catalog.Deregister( @@ -104,7 +128,7 @@ func (s *ConsulCatalogSuite) TestSingleService(c *check.C) { c.Assert(err, checker.IsNil) defer cmd.Process.Kill() - nginx := s.composeProject.Container(c, "nginx") + nginx := s.composeProject.Container(c, "nginx1") err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{}) c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) @@ -114,7 +138,7 @@ func (s *ConsulCatalogSuite) TestSingleService(c *check.C) { c.Assert(err, checker.IsNil) req.Host = "test.consul.localhost" - err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) + err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) c.Assert(err, checker.IsNil) } @@ -129,7 +153,7 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultFalseSingleService(c *check.C) c.Assert(err, checker.IsNil) defer cmd.Process.Kill() - nginx := s.composeProject.Container(c, "nginx") + nginx := s.composeProject.Container(c, "nginx1") err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{}) c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) @@ -154,7 +178,7 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultFalseSimpleServiceMultipleNode( c.Assert(err, checker.IsNil) defer cmd.Process.Kill() - nginx := s.composeProject.Container(c, "nginx") + nginx := s.composeProject.Container(c, "nginx1") nginx2 := s.composeProject.Container(c, "nginx2") err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{}) @@ -184,14 +208,14 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultTrueSimpleServiceMultipleNode(c c.Assert(err, checker.IsNil) defer cmd.Process.Kill() - nginx := s.composeProject.Container(c, "nginx") + nginx := s.composeProject.Container(c, "nginx1") nginx2 := s.composeProject.Container(c, "nginx2") - err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{}) + err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"}) c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) defer s.deregisterService("test", nginx.NetworkSettings.IPAddress) - err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{}) + err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"}) c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress) @@ -201,6 +225,61 @@ func (s *ConsulCatalogSuite) TestExposedByDefaultTrueSimpleServiceMultipleNode(c err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2")) + c.Assert(err, checker.IsNil) + +} + +func (s *ConsulCatalogSuite) TestRefreshConfigWithMultipleNodeWithoutHealthCheck(c *check.C) { + cmd, _ := s.cmdTraefik( + withConfigFile("fixtures/consul_catalog/simple.toml"), + "--consulCatalog", + "--consulCatalog.exposedByDefault=true", + "--consulCatalog.endpoint="+s.consulIP+":8500", + "--consulCatalog.domain=consul.localhost") + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + nginx := s.composeProject.Container(c, "nginx1") + nginx2 := s.composeProject.Container(c, "nginx2") + + err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"}) + c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) + defer s.deregisterService("test", nginx.NetworkSettings.IPAddress) + + err = s.registerAgentService("test", nginx.NetworkSettings.IPAddress, 80, []string{"name=nginx1"}) + c.Assert(err, checker.IsNil, check.Commentf("Error registering agent service")) + + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + c.Assert(err, checker.IsNil) + req.Host = "test.consul.localhost" + + err = try.Request(req, 5*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody()) + c.Assert(err, checker.IsNil) + + err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1")) + c.Assert(err, checker.IsNil) + + err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"}) + c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) + + err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2")) + c.Assert(err, checker.IsNil) + + s.deregisterService("test", nginx2.NetworkSettings.IPAddress) + + err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1")) + c.Assert(err, checker.IsNil) + + err = s.registerService("test", nginx2.NetworkSettings.IPAddress, 80, []string{"name=nginx2"}) + c.Assert(err, checker.IsNil, check.Commentf("Error registering service")) + defer s.deregisterService("test", nginx2.NetworkSettings.IPAddress) + + err = try.GetRequest("http://127.0.0.1:8080/api/providers/consul_catalog/backends", 60*time.Second, try.BodyContains("nginx1", "nginx2")) + c.Assert(err, checker.IsNil) + } func (s *ConsulCatalogSuite) TestBasicAuthSimpleService(c *check.C) { @@ -218,7 +297,7 @@ func (s *ConsulCatalogSuite) TestBasicAuthSimpleService(c *check.C) { s.displayTraefikLog(c, output) }() - nginx := s.composeProject.Container(c, "nginx") + nginx := s.composeProject.Container(c, "nginx1") err = s.registerService("test", nginx.NetworkSettings.IPAddress, 80, []string{ "traefik.frontend.auth.basic=test:$2a$06$O5NksJPAcgrC9MuANkSoE.Xe9DSg7KcLLFYNr1Lj6hPcMmvgwxhme,test2:$2y$10$xP1SZ70QbZ4K2bTGKJOhpujkpcLxQcB3kEPF6XAV19IdcqsZTyDEe", diff --git a/integration/fixtures/consul_catalog/simple.toml b/integration/fixtures/consul_catalog/simple.toml index 55fb8e989..45bca454f 100644 --- a/integration/fixtures/consul_catalog/simple.toml +++ b/integration/fixtures/consul_catalog/simple.toml @@ -1,6 +1,9 @@ defaultEntryPoints = ["http"] logLevel = "DEBUG" +[web] + address = ":8080" + [entryPoints] [entryPoints.http] address = ":8000" diff --git a/integration/resources/compose/consul_catalog.yml b/integration/resources/compose/consul_catalog.yml index 54f874f2d..b755d2ad5 100644 --- a/integration/resources/compose/consul_catalog.yml +++ b/integration/resources/compose/consul_catalog.yml @@ -11,7 +11,7 @@ consul: - "8301/udp" - "8302" - "8302/udp" -nginx: +nginx1: image: nginx:alpine nginx2: image: nginx:alpine diff --git a/provider/consul/consul_catalog.go b/provider/consul/consul_catalog.go index 86d87eeda..a6fe6f447 100644 --- a/provider/consul/consul_catalog.go +++ b/provider/consul/consul_catalog.go @@ -77,7 +77,7 @@ func (a nodeSorter) Less(i int, j int) bool { return lentr.Service.Port < rentr.Service.Port } -func getChangedKeys(currState map[string][]string, prevState map[string][]string) ([]string, []string) { +func getChangedServiceKeys(currState map[string]Service, prevState map[string]Service) ([]string, []string) { currKeySet := fun.Set(fun.Keys(currState).([]string)).(map[string]bool) prevKeySet := fun.Set(fun.Keys(prevState).([]string)).(map[string]bool) @@ -87,13 +87,36 @@ func getChangedKeys(currState map[string][]string, prevState map[string][]string return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string) } +func getChangedServiceNodeKeys(currState map[string]Service, prevState map[string]Service) ([]string, []string) { + var addedNodeKeys []string + var removedNodeKeys []string + for key, value := range currState { + if prevValue, ok := prevState[key]; ok { + addedKeys, removedKeys := getChangedHealthyKeys(value.Nodes, prevValue.Nodes) + addedNodeKeys = append(addedKeys) + removedNodeKeys = append(removedKeys) + } + } + return addedNodeKeys, removedNodeKeys +} + +func getChangedHealthyKeys(currState []string, prevState []string) ([]string, []string) { + currKeySet := fun.Set(currState).(map[string]bool) + prevKeySet := fun.Set(prevState).(map[string]bool) + + addedKeys := fun.Difference(currKeySet, prevKeySet).(map[string]bool) + removedKeys := fun.Difference(prevKeySet, currKeySet).(map[string]bool) + + return fun.Keys(addedKeys).([]string), fun.Keys(removedKeys).([]string) +} + func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan<- map[string][]string) { health := p.client.Health() catalog := p.client.Catalog() safe.Go(func() { // variable to hold previous state - var flashback map[string][]string + var flashback []string options := &api.QueryOptions{WaitTime: DefaultWatchWaitTime} @@ -105,14 +128,20 @@ func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan< } // Listening to changes that leads to `passing` state or degrades from it. - // The call is used just as a trigger for further actions - // (intentionally there is no interest in the received data). - _, meta, err := health.State("passing", options) + healthyState, meta, err := health.State("passing", options) if err != nil { log.WithError(err).Error("Failed to retrieve health checks") return } + var current []string + if healthyState != nil { + for _, healthy := range healthyState { + current = append(current, healthy.ServiceID) + } + + } + // If LastIndex didn't change then it means `Get` returned // because of the WaitTime and the key didn't changed. if options.WaitIndex == meta.LastIndex { @@ -132,30 +161,38 @@ func (p *CatalogProvider) watchHealthState(stopCh <-chan struct{}, watchCh chan< // A critical note is that the return of a blocking request is no guarantee of a change. // It is possible that there was an idempotent write that does not affect the result of the query. // Thus it is required to do extra check for changes... - addedKeys, removedKeys := getChangedKeys(data, flashback) + addedKeys, removedKeys := getChangedHealthyKeys(current, flashback) if len(addedKeys) > 0 { log.WithField("DiscoveredServices", addedKeys).Debug("Health State change detected.") watchCh <- data - flashback = data + flashback = current } if len(removedKeys) > 0 { log.WithField("MissingServices", removedKeys).Debug("Health State change detected.") watchCh <- data - flashback = data + flashback = current } } } }) } +// Service represent a Consul service. +type Service struct { + Name string + Tags []string + Nodes []string +} + func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh chan<- map[string][]string) { catalog := p.client.Catalog() safe.Go(func() { + current := make(map[string]Service) // variable to hold previous state - var flashback map[string][]string + var flashback map[string]Service options := &api.QueryOptions{WaitTime: DefaultWatchWaitTime} @@ -179,26 +216,55 @@ func (p *CatalogProvider) watchCatalogServices(stopCh <-chan struct{}, watchCh c options.WaitIndex = meta.LastIndex if data != nil { + + for key, value := range data { + nodes, _, err := catalog.Service(key, "", options) + if err != nil { + log.Errorf("Failed to get detail of service %s: %s", key, err) + return + } + nodesID := getServiceIds(nodes) + if service, ok := current[key]; ok { + service.Tags = value + service.Nodes = nodesID + } else { + service := Service{ + Name: key, + Tags: value, + Nodes: nodesID, + } + current[key] = service + } + } // A critical note is that the return of a blocking request is no guarantee of a change. // It is possible that there was an idempotent write that does not affect the result of the query. // Thus it is required to do extra check for changes... - addedKeys, removedKeys := getChangedKeys(data, flashback) + addedServiceKeys, removedServiceKeys := getChangedServiceKeys(current, flashback) - if len(addedKeys) > 0 { - log.WithField("DiscoveredServices", addedKeys).Debug("Catalog Services change detected.") + addedServiceNodeKeys, removedServiceNodeKeys := getChangedServiceNodeKeys(current, flashback) + + if len(addedServiceKeys) > 0 || len(addedServiceNodeKeys) > 0 { + log.WithField("DiscoveredServices", addedServiceKeys).Debug("Catalog Services change detected.") watchCh <- data - flashback = data + flashback = current } - if len(removedKeys) > 0 { - log.WithField("MissingServices", removedKeys).Debug("Catalog Services change detected.") + if len(removedServiceKeys) > 0 || len(removedServiceNodeKeys) > 0 { + log.WithField("MissingServices", removedServiceKeys).Debug("Catalog Services change detected.") watchCh <- data - flashback = data + flashback = current } } } }) } +func getServiceIds(services []*api.CatalogService) []string { + var serviceIds []string + for _, service := range services { + serviceIds = append(serviceIds, service.ServiceID) + } + return serviceIds +} func (p *CatalogProvider) healthyNodes(service string) (catalogUpdate, error) { health := p.client.Health() diff --git a/provider/consul/consul_catalog_test.go b/provider/consul/consul_catalog_test.go index 23f3f1422..7cd3c25eb 100644 --- a/provider/consul/consul_catalog_test.go +++ b/provider/consul/consul_catalog_test.go @@ -613,8 +613,8 @@ func TestConsulCatalogNodeSorter(t *testing.T) { func TestConsulCatalogGetChangedKeys(t *testing.T) { type Input struct { - currState map[string][]string - prevState map[string][]string + currState map[string]Service + prevState map[string]Service } type Output struct { @@ -628,37 +628,37 @@ func TestConsulCatalogGetChangedKeys(t *testing.T) { }{ { input: Input{ - currState: map[string][]string{ - "foo-service": {"v1"}, - "bar-service": {"v1"}, - "baz-service": {"v1"}, - "qux-service": {"v1"}, - "quux-service": {"v1"}, - "quuz-service": {"v1"}, - "corge-service": {"v1"}, - "grault-service": {"v1"}, - "garply-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + currState: map[string]Service{ + "foo-service": {Name: "v1"}, + "bar-service": {Name: "v1"}, + "baz-service": {Name: "v1"}, + "qux-service": {Name: "v1"}, + "quux-service": {Name: "v1"}, + "quuz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "grault-service": {Name: "v1"}, + "garply-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, - prevState: map[string][]string{ - "foo-service": {"v1"}, - "bar-service": {"v1"}, - "baz-service": {"v1"}, - "qux-service": {"v1"}, - "quux-service": {"v1"}, - "quuz-service": {"v1"}, - "corge-service": {"v1"}, - "grault-service": {"v1"}, - "garply-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + prevState: map[string]Service{ + "foo-service": {Name: "v1"}, + "bar-service": {Name: "v1"}, + "baz-service": {Name: "v1"}, + "qux-service": {Name: "v1"}, + "quux-service": {Name: "v1"}, + "quuz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "grault-service": {Name: "v1"}, + "garply-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, }, output: Output{ @@ -668,34 +668,34 @@ func TestConsulCatalogGetChangedKeys(t *testing.T) { }, { input: Input{ - currState: map[string][]string{ - "foo-service": {"v1"}, - "bar-service": {"v1"}, - "baz-service": {"v1"}, - "qux-service": {"v1"}, - "quux-service": {"v1"}, - "quuz-service": {"v1"}, - "corge-service": {"v1"}, - "grault-service": {"v1"}, - "garply-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + currState: map[string]Service{ + "foo-service": {Name: "v1"}, + "bar-service": {Name: "v1"}, + "baz-service": {Name: "v1"}, + "qux-service": {Name: "v1"}, + "quux-service": {Name: "v1"}, + "quuz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "grault-service": {Name: "v1"}, + "garply-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, - prevState: map[string][]string{ - "foo-service": {"v1"}, - "bar-service": {"v1"}, - "baz-service": {"v1"}, - "corge-service": {"v1"}, - "grault-service": {"v1"}, - "garply-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + prevState: map[string]Service{ + "foo-service": {Name: "v1"}, + "bar-service": {Name: "v1"}, + "baz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "grault-service": {Name: "v1"}, + "garply-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, }, output: Output{ @@ -705,33 +705,33 @@ func TestConsulCatalogGetChangedKeys(t *testing.T) { }, { input: Input{ - currState: map[string][]string{ - "foo-service": {"v1"}, - "qux-service": {"v1"}, - "quux-service": {"v1"}, - "quuz-service": {"v1"}, - "corge-service": {"v1"}, - "grault-service": {"v1"}, - "garply-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + currState: map[string]Service{ + "foo-service": {Name: "v1"}, + "qux-service": {Name: "v1"}, + "quux-service": {Name: "v1"}, + "quuz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "grault-service": {Name: "v1"}, + "garply-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, - prevState: map[string][]string{ - "foo-service": {"v1"}, - "bar-service": {"v1"}, - "baz-service": {"v1"}, - "qux-service": {"v1"}, - "quux-service": {"v1"}, - "quuz-service": {"v1"}, - "corge-service": {"v1"}, - "waldo-service": {"v1"}, - "fred-service": {"v1"}, - "plugh-service": {"v1"}, - "xyzzy-service": {"v1"}, - "thud-service": {"v1"}, + prevState: map[string]Service{ + "foo-service": {Name: "v1"}, + "bar-service": {Name: "v1"}, + "baz-service": {Name: "v1"}, + "qux-service": {Name: "v1"}, + "quux-service": {Name: "v1"}, + "quuz-service": {Name: "v1"}, + "corge-service": {Name: "v1"}, + "waldo-service": {Name: "v1"}, + "fred-service": {Name: "v1"}, + "plugh-service": {Name: "v1"}, + "xyzzy-service": {Name: "v1"}, + "thud-service": {Name: "v1"}, }, }, output: Output{ @@ -742,7 +742,7 @@ func TestConsulCatalogGetChangedKeys(t *testing.T) { } for _, c := range cases { - addedKeys, removedKeys := getChangedKeys(c.input.currState, c.input.prevState) + addedKeys, removedKeys := getChangedServiceKeys(c.input.currState, c.input.prevState) if !reflect.DeepEqual(fun.Set(addedKeys), fun.Set(c.output.addedKeys)) { t.Fatalf("Added keys comparison results: got %q, want %q", addedKeys, c.output.addedKeys) From 6d28c52f592945eaa0d8d4acdbea748f26f9bd13 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 8 Sep 2017 21:28:02 +0200 Subject: [PATCH 17/17] Prepare release v1.4.0-rc2 --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3978aa04..29773a40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Change Log +## [v1.4.0-rc2](https://github.com/containous/traefik/tree/v1.4.0-rc2) (2017-09-08) +[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.4.0-rc2) + +**Enhancements:** +- **[authentication,consul]** Add Basic auth for consul catalog ([#2027](https://github.com/containous/traefik/pull/2027) by [mmatur](https://github.com/mmatur)) +- **[authentication,ecs]** Add basic auth for ecs ([#2026](https://github.com/containous/traefik/pull/2026) by [mmatur](https://github.com/mmatur)) +- **[logs]** Send traefik logs to stdout instead stderr ([#2054](https://github.com/containous/traefik/pull/2054) by [marco-jantke](https://github.com/marco-jantke)) +- **[websocket]** Add test for SSL TERMINATION in Websocket IT ([#2063](https://github.com/containous/traefik/pull/2063) by [Juliens](https://github.com/Juliens)) + +**Bug fixes:** +- **[consul]** Fix consul catalog refresh problems ([#2089](https://github.com/containous/traefik/pull/2089) by [Juliens](https://github.com/Juliens)) +- **[logs,middleware]** Access log default values ([#2061](https://github.com/containous/traefik/pull/2061) by [ldez](https://github.com/ldez)) +- **[metrics]** prometheus, HTTP method and utf8 ([#2081](https://github.com/containous/traefik/pull/2081) by [ldez](https://github.com/ldez)) +- **[rancher]** fix rancher api environment get ([#2053](https://github.com/containous/traefik/pull/2053) by [SantoDE](https://github.com/SantoDE)) +- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2088](https://github.com/containous/traefik/pull/2088) by [Juliens](https://github.com/Juliens)) +- Fix error in prepareServer ([#2076](https://github.com/containous/traefik/pull/2076) by [emilevauge](https://github.com/emilevauge)) + +**Documentation:** +- **[acme,provider]** Fix whitespaces ([#2075](https://github.com/containous/traefik/pull/2075) by [chulkilee](https://github.com/chulkilee)) +- **[ecs]** Fix IAM policy sid. ([#2066](https://github.com/containous/traefik/pull/2066) by [charlieoleary](https://github.com/charlieoleary)) +- **[k8s]** Fix invalid service yaml example ([#2059](https://github.com/containous/traefik/pull/2059) by [kairen](https://github.com/kairen)) +- **[mesos]** fix: documentation Mesos. ([#2029](https://github.com/containous/traefik/pull/2029) by [ldez](https://github.com/ldez)) +- Update cluster.md ([#2073](https://github.com/containous/traefik/pull/2073) by [kmbremner](https://github.com/kmbremner)) +- Enhance documentation. ([#2048](https://github.com/containous/traefik/pull/2048) by [ldez](https://github.com/ldez)) +- doc: add notes on server urls with path ([#2045](https://github.com/containous/traefik/pull/2045) by [chulkilee](https://github.com/chulkilee)) +- Enhance security headers doc. ([#2042](https://github.com/containous/traefik/pull/2042) by [ldez](https://github.com/ldez)) +- HTTPS for images, video and links in docs. ([#2041](https://github.com/containous/traefik/pull/2041) by [ldez](https://github.com/ldez)) +- Fix error pages configuration. ([#2038](https://github.com/containous/traefik/pull/2038) by [ldez](https://github.com/ldez)) + ## [v1.4.0-rc1](https://github.com/containous/traefik/tree/v1.4.0-rc1) (2017-08-28) [All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0-rc1) @@ -143,6 +172,12 @@ - Merge current v1.3 to master ([#1643](https://github.com/containous/traefik/pull/1643) by [ldez](https://github.com/ldez)) - Merge v1.3.0-rc2 master ([#1613](https://github.com/containous/traefik/pull/1613) by [emilevauge](https://github.com/emilevauge)) +## [v1.3.8](https://github.com/containous/traefik/tree/v1.3.8) (2017-09-07) +[All Commits](https://github.com/containous/traefik/compare/v1.3.7...v1.3.8) + +**Bug fixes:** +- **[middleware]** Compress and Webscocket ([#2079](https://github.com/containous/traefik/pull/2079) by [ldez](https://github.com/ldez)) + ## [v1.3.7](https://github.com/containous/traefik/tree/v1.3.7) (2017-08-25) [All Commits](https://github.com/containous/traefik/compare/v1.3.6...v1.3.7)