Merge v1.4.0-rc2 into master

This commit is contained in:
Fernandez Ludovic 2017-09-08 23:35:49 +02:00
commit 9fba37b409
64 changed files with 829 additions and 289 deletions

View file

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

View file

@ -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)"

View file

@ -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:
@ -497,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.

View file

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

View file

@ -79,7 +79,7 @@ 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 |
@ -89,6 +89,7 @@ Labels can be used on task containers to override default behaviour:
| `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:
@ -103,7 +104,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",

View file

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

View file

@ -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,4 +225,98 @@ 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) {
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, "nginx1")
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)
}

View file

@ -1,6 +1,9 @@
defaultEntryPoints = ["http"]
logLevel = "DEBUG"
[web]
address = ":8080"
[entryPoints]
[entryPoints.http]
address = ":8000"

View file

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

View file

@ -11,7 +11,7 @@ consul:
- "8301/udp"
- "8302"
- "8302/udp"
nginx:
nginx1:
image: nginx:alpine
nginx2:
image: nginx:alpine

View file

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

View file

@ -18,6 +18,7 @@ var (
func init() {
logger = logrus.StandardLogger().WithFields(logrus.Fields{})
logrus.SetOutput(os.Stdout)
}
// Context sets the Context of the logger

View file

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

View file

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

View file

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

View file

@ -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()
@ -330,6 +396,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 +451,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,

View file

@ -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)
}
}
@ -610,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 {
@ -625,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{
@ -665,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{
@ -702,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{
@ -739,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)
@ -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)
}
})
}
}

View file

@ -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 := p.label(i, types.LabelFrontendAuthBasic)
if label != "" {
return strings.Split(label, ",")
}
return []string{}
}
func (p *Provider) getLoadBalancerSticky(instances []ecsInstance) string {
if len(instances) > 0 {
label := p.label(instances[0], types.LabelBackendLoadbalancerSticky)

View file

@ -521,3 +521,34 @@ func TestTaskChunking(t *testing.T) {
}
}
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)
})
}
}

View file

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

View file

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

View file

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

View file

@ -17,7 +17,6 @@ find_dirs() {
find . -not \( \
\( \
-path './integration/*' \
-o -path './.glide/*' \
-o -path './vendor/*' \
-o -path './.git/*' \
\) \

View file

@ -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=()

View file

@ -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=()

View file

@ -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=()

View file

@ -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[@]}")

View file

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

View file

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

View file

@ -40,6 +40,9 @@
"{{.}}",
{{end}}]
{{end}}
basicAuth = [{{range getBasicAuth .Attributes}}
"{{.}}",
{{end}}]
[frontends."frontend-{{.ServiceName}}".routes."route-host-{{.ServiceName}}"]
rule = "{{getFrontendRule .}}"
{{end}}

View file

@ -18,6 +18,9 @@
priority = {{ getPriority .}}
entryPoints = [{{range getEntryPoints .}}
"{{.}}",
{{end}}]
basicAuth = [{{range getBasicAuth .}}
"{{.}}",
{{end}}]
[frontends.frontend-{{ $serviceName }}.routes.route-frontend-{{ $serviceName }}]
rule = "{{getFrontendRule .}}"