Compare commits

...

11 Commits

Author SHA1 Message Date
Emile Vauge
7f3ae6edb0 Merge pull request #915 from containous/prepare-release-v1.1.1
Prepare release v1.1.1
2016-11-29 16:25:52 +01:00
Emile Vauge
1a993f5dfb Prepare release v1.1.1
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-11-29 14:52:55 +01:00
Emile Vauge
4e527304d0 Merge pull request #908 from containous/fix-swarm-panic
Fix Swarm panic
2016-11-29 14:47:51 +01:00
Emile Vauge
841be8d806 Fix Swarm panic 2016-11-28 16:46:37 +01:00
Ryan Leary
055cd01bb7 Fix GroupsAsSubDomains option for Mesos and Marathon (#868)
* Fix GroupsAsSubDomains option for Mesos and Marathon
* Refactor reverseStringSlice function
2016-11-28 14:59:08 +01:00
Emile Vauge
e34c364d5e Merge pull request #900 from containous/fix-k8s-panic
Fix k8s panic
2016-11-28 12:19:52 +01:00
Emile Vauge
926eb099f1 Fix k8s client panic
Signed-off-by: Emile Vauge <emile@vauge.com>
2016-11-24 19:19:10 +01:00
Emile Vauge
710508dc40 Fix digest auth doc 2016-11-24 18:17:57 +01:00
Alexander Block
b4ea68b88a Fix missing value for k8s watch request parameter (#874)
Fixes: 732
2016-11-23 23:21:09 +01:00
WTFKr0
2bf9acd95e Normalize backend even if is user-defined (#865)
Signed-off-by: WTFKr0 <thomas.kovatchitch@gmail.com>
2016-11-23 21:31:37 +01:00
Kristian Klausen
a8cb905255 consul/kv.tmpl: weight default value should be a int (#826)
* consul/kv.tmpl: weight default value should be a int

Fix #821

* Use 0 as default weight in all backends
2016-11-23 14:49:55 +01:00
17 changed files with 174 additions and 57 deletions

View File

@@ -1,5 +1,49 @@
# Change Log
## [v1.1.1](https://github.com/containous/traefik/tree/v1.1.1) (2016-11-29)
[Full Changelog](https://github.com/containous/traefik/compare/v1.1.0...v1.1.1)
**Implemented enhancements:**
- Getting "Kubernetes connection error failed to decode watch event : unexpected EOF" every minute in Traefik log [\#732](https://github.com/containous/traefik/issues/732)
**Fixed bugs:**
- 1.1.0 kubernetes panic: send on closed channel [\#877](https://github.com/containous/traefik/issues/877)
- digest auth example is incorrect [\#869](https://github.com/containous/traefik/issues/869)
- Marathon & Mesos providers' GroupsAsSubDomains option broken [\#867](https://github.com/containous/traefik/issues/867)
- 404 responses when a new Marathon leader is elected [\#653](https://github.com/containous/traefik/issues/653)
**Closed issues:**
- traefik:latest fails to auto-detect Docker containers [\#901](https://github.com/containous/traefik/issues/901)
- Panic error on bare metal Kubernetes \(installed with Kubeadm\) [\#897](https://github.com/containous/traefik/issues/897)
- api backend readOnly: what is the purpose of this setting [\#893](https://github.com/containous/traefik/issues/893)
- file backend: using external file - doesn't work [\#892](https://github.com/containous/traefik/issues/892)
- auth support for web backend [\#891](https://github.com/containous/traefik/issues/891)
- Basic auth with docker labels [\#890](https://github.com/containous/traefik/issues/890)
- file vs inline config [\#888](https://github.com/containous/traefik/issues/888)
- combine Host and HostRegexp rules [\#882](https://github.com/containous/traefik/issues/882)
- \[Question\] Traefik + Kubernetes + Let's Encrypt \(ssl not used\) [\#881](https://github.com/containous/traefik/issues/881)
- Traefik security for dashboard [\#880](https://github.com/containous/traefik/issues/880)
- Kubernetes Nginx Deployment Panic [\#879](https://github.com/containous/traefik/issues/879)
- Kubernetes Example Address already in use [\#872](https://github.com/containous/traefik/issues/872)
- ETCD Backend - frontend/backends missing [\#866](https://github.com/containous/traefik/issues/866)
- \[Swarm mode\] Dashboard does not work on RC4 [\#864](https://github.com/containous/traefik/issues/864)
- Docker v1.1.0 image does not exist [\#861](https://github.com/containous/traefik/issues/861)
- ConsulService catalog do not support multiple rules [\#859](https://github.com/containous/traefik/issues/859)
- Update official docker repo [\#858](https://github.com/containous/traefik/issues/858)
- Still a memory leak with k8s - 1.1 RC4 [\#844](https://github.com/containous/traefik/issues/844)
**Merged pull requests:**
- Fix Swarm panic [\#908](https://github.com/containous/traefik/pull/908) ([emilevauge](https://github.com/emilevauge))
- Fix k8s panic [\#900](https://github.com/containous/traefik/pull/900) ([emilevauge](https://github.com/emilevauge))
- Fix missing value for k8s watch request parameter [\#874](https://github.com/containous/traefik/pull/874) ([codablock](https://github.com/codablock))
- Fix GroupsAsSubDomains option for Mesos and Marathon [\#868](https://github.com/containous/traefik/pull/868) ([ryanleary](https://github.com/ryanleary))
- Normalize backend even if is user-defined [\#865](https://github.com/containous/traefik/pull/865) ([WTFKr0](https://github.com/WTFKr0))
- consul/kv.tmpl: weight default value should be a int [\#826](https://github.com/containous/traefik/pull/826) ([klausenbusk](https://github.com/klausenbusk))
## [v1.1.0](https://github.com/containous/traefik/tree/v1.1.0) (2016-11-17)
[Full Changelog](https://github.com/containous/traefik/compare/v1.0.0...v1.1.0)

View File

@@ -511,7 +511,7 @@ address = ":8080"
# To enable digest auth on the webui
# with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
# You can use htdigest to generate those ones
# [web.auth.basic]
# [web.auth.digest]
# users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
```

View File

@@ -61,7 +61,7 @@ func TestConsulCatalogGetAttribute(t *testing.T) {
"traefik.backend.weight=42",
},
key: "backend.weight",
defaultValue: "",
defaultValue: "0",
expected: "42",
},
{
@@ -70,8 +70,8 @@ func TestConsulCatalogGetAttribute(t *testing.T) {
"traefik.backend.wei=42",
},
key: "backend.weight",
defaultValue: "",
expected: "",
defaultValue: "0",
expected: "0",
},
}

View File

@@ -402,7 +402,7 @@ func (provider *Docker) getFrontendRule(container dockerData) string {
func (provider *Docker) getBackend(container dockerData) string {
if label, err := getLabel(container, "traefik.backend"); err == nil {
return label
return normalize(label)
}
return normalize(container.Name)
}
@@ -455,7 +455,7 @@ func (provider *Docker) getWeight(container dockerData) string {
if label, err := getLabel(container, "traefik.weight"); err == nil {
return label
}
return "1"
return "0"
}
func (provider *Docker) getSticky(container dockerData) string {
@@ -560,6 +560,10 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
if container.ContainerJSONBase.HostConfig != nil {
dockerData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode
}
if container.State != nil && container.State.Health != nil {
dockerData.Health = container.State.Health.Status
}
}
if container.Config != nil && container.Config.Labels != nil {
@@ -583,10 +587,6 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
}
if container.State != nil && container.State.Health != nil {
dockerData.Health = container.State.Health.Status
}
return dockerData
}

View File

@@ -436,7 +436,7 @@ func TestDockerGetWeight(t *testing.T) {
},
Config: &container.Config{},
},
expected: "1",
expected: "0",
},
{
container: docker.ContainerJSON{
@@ -972,7 +972,7 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1054,11 +1054,11 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
"server-test2": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1112,7 +1112,7 @@ func TestDockerLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: &types.CircuitBreaker{
@@ -1527,7 +1527,7 @@ func TestSwarmGetWeight(t *testing.T) {
},
},
},
expected: "1",
expected: "0",
networks: map[string]*docker.NetworkResource{},
},
{
@@ -2055,7 +2055,7 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -2143,11 +2143,11 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
Servers: map[string]types.Server{
"server-test1": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
"server-test2": {
URL: "http://127.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,

View File

@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/parnurzeal/gorequest"
"net/http"
"net/url"
@@ -160,7 +161,7 @@ func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan in
if err != nil {
return watchCh, errCh, fmt.Errorf("failed to create watch: %v", err)
}
go func() {
safe.Go(func() {
defer close(watchCh)
defer close(errCh)
defer close(stopIngresses)
@@ -188,7 +189,7 @@ func (c *clientImpl) WatchAll(labelSelector string, stopCh <-chan bool) (chan in
watchCh <- event
}
}
}()
})
return watchCh, errCh, nil
}
@@ -248,7 +249,7 @@ func (c *clientImpl) watch(url string, labelSelector string, stopCh <-chan bool)
return watchCh, errCh, fmt.Errorf("failed to decode version %v", err)
}
resourceVersion := generic.ResourceVersion
queryParams := map[string]string{"watch": "", "resourceVersion": resourceVersion}
queryParams := map[string]string{"watch": "true", "resourceVersion": resourceVersion}
queryData, err := makeQueryString(queryParams, labelSelector)
if err != nil {
return watchCh, errCh, fmt.Errorf("Unable to construct query args")
@@ -268,11 +269,16 @@ func (c *clientImpl) watch(url string, labelSelector string, stopCh <-chan bool)
return watchCh, errCh, fmt.Errorf("failed to do watch request: GET %q: %v", url, err)
}
go func() {
safe.Go(func() {
EndCh := make(chan bool, 1)
defer close(watchCh)
defer close(errCh)
go func() {
defer close(EndCh)
safe.Go(func() {
defer res.Body.Close()
defer func() {
EndCh <- true
}()
for {
var eventList interface{}
if err := json.NewDecoder(res.Body).Decode(&eventList); err != nil {
@@ -283,11 +289,12 @@ func (c *clientImpl) watch(url string, labelSelector string, stopCh <-chan bool)
}
watchCh <- eventList
}
}()
})
<-stopCh
go func() {
safe.Go(func() {
cancel() // cancel watch request
}()
}()
})
<-EndCh
})
return watchCh, errCh, nil
}

View File

@@ -262,7 +262,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
log.Warnf("Endpoints not found for %s/%s, falling back to Service ClusterIP", service.ObjectMeta.Namespace, service.ObjectMeta.Name)
templateObjects.Backends[r.Host+pa.Path].Servers[string(service.UID)] = types.Server{
URL: protocol + "://" + service.Spec.ClusterIP + ":" + strconv.Itoa(port.Port),
Weight: 1,
Weight: 0,
}
} else {
for _, subset := range endpoints.Subsets {
@@ -274,7 +274,7 @@ func (provider *Kubernetes) loadIngresses(k8sClient k8s.Client) (*types.Configur
}
templateObjects.Backends[r.Host+pa.Path].Servers[name] = types.Server{
URL: url,
Weight: 1,
Weight: 0,
}
}
}

View File

@@ -204,11 +204,11 @@ func TestLoadIngresses(t *testing.T) {
Servers: map[string]types.Server{
"http://10.10.0.1:8080": {
URL: "http://10.10.0.1:8080",
Weight: 1,
Weight: 0,
},
"http://10.21.0.1:8080": {
URL: "http://10.21.0.1:8080",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -218,15 +218,15 @@ func TestLoadIngresses(t *testing.T) {
Servers: map[string]types.Server{
"2": {
URL: "http://10.0.0.2:802",
Weight: 1,
Weight: 0,
},
"https://10.15.0.1:8443": {
URL: "https://10.15.0.1:8443",
Weight: 1,
Weight: 0,
},
"https://10.15.0.2:9443": {
URL: "https://10.15.0.2:9443",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -556,7 +556,7 @@ func TestGetPassHostHeader(t *testing.T) {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -665,7 +665,7 @@ func TestOnlyReferencesServicesFromOwnNamespace(t *testing.T) {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -851,7 +851,7 @@ func TestLoadNamespacedIngresses(t *testing.T) {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -861,11 +861,11 @@ func TestLoadNamespacedIngresses(t *testing.T) {
Servers: map[string]types.Server{
"2": {
URL: "http://10.0.0.2:802",
Weight: 1,
Weight: 0,
},
"3": {
URL: "https://10.0.0.3:443",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1089,7 +1089,7 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1099,11 +1099,11 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
Servers: map[string]types.Server{
"2": {
URL: "http://10.0.0.2:802",
Weight: 1,
Weight: 0,
},
"3": {
URL: "https://10.0.0.3:443",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1113,7 +1113,7 @@ func TestLoadMultipleNamespacedIngresses(t *testing.T) {
Servers: map[string]types.Server{
"17": {
URL: "http://10.0.0.4:801",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,
@@ -1227,7 +1227,7 @@ func TestHostlessIngress(t *testing.T) {
Servers: map[string]types.Server{
"1": {
URL: "http://10.0.0.1:801",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,

View File

@@ -408,7 +408,7 @@ func TestKVLoadConfig(t *testing.T) {
},
{
Key: "traefik/backends/backend.with.dot.too/servers/server.with.dot/weight",
Value: []byte("1"),
Value: []byte("0"),
},
},
},
@@ -420,7 +420,7 @@ func TestKVLoadConfig(t *testing.T) {
Servers: map[string]types.Server{
"server.with.dot": {
URL: "http://172.17.0.2:80",
Weight: 1,
Weight: 0,
},
},
CircuitBreaker: nil,

View File

@@ -3,7 +3,6 @@ package provider
import (
"errors"
"net/url"
"sort"
"strconv"
"strings"
"text/template"
@@ -419,7 +418,7 @@ func (provider *Marathon) getFrontendBackend(application marathon.Application) s
func (provider *Marathon) getSubDomain(name string) string {
if provider.GroupsAsSubDomains {
splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/")
sort.Sort(sort.Reverse(sort.StringSlice(splitedName)))
reverseStringSlice(&splitedName)
reverseName := strings.Join(splitedName, ".")
return reverseName
}

View File

@@ -1280,3 +1280,33 @@ func TestMarathonGetBackend(t *testing.T) {
}
}
}
func TestMarathonGetSubDomain(t *testing.T) {
providerGroups := &Marathon{GroupsAsSubDomains: true}
providerNoGroups := &Marathon{GroupsAsSubDomains: false}
apps := []struct {
path string
expected string
provider *Marathon
}{
{"/test", "test", providerNoGroups},
{"/test", "test", providerGroups},
{"/a/b/c/d", "d.c.b.a", providerGroups},
{"/b/a/d/c", "c.d.a.b", providerGroups},
{"/d/c/b/a", "a.b.c.d", providerGroups},
{"/c/d/a/b", "b.a.d.c", providerGroups},
{"/a/b/c/d", "a-b-c-d", providerNoGroups},
{"/b/a/d/c", "b-a-d-c", providerNoGroups},
{"/d/c/b/a", "d-c-b-a", providerNoGroups},
{"/c/d/a/b", "c-d-a-b", providerNoGroups},
}
for _, a := range apps {
actual := a.provider.getSubDomain(a.path)
if actual != a.expected {
t.Errorf("expected %q, got %q", a.expected, actual)
}
}
}

View File

@@ -7,6 +7,8 @@ import (
"text/template"
"fmt"
"time"
"github.com/BurntSushi/ty/fun"
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
@@ -20,8 +22,6 @@ import (
"github.com/mesosphere/mesos-dns/records"
"github.com/mesosphere/mesos-dns/records/state"
"github.com/mesosphere/mesos-dns/util"
"sort"
"time"
)
var _ Provider = (*Mesos)(nil)
@@ -435,7 +435,7 @@ func Ignore(f ErrorFunction) {
func (provider *Mesos) getSubDomain(name string) string {
if provider.GroupsAsSubDomains {
splitedName := strings.Split(strings.TrimPrefix(name, "/"), "/")
sort.Sort(sort.Reverse(sort.StringSlice(splitedName)))
reverseStringSlice(&splitedName)
reverseName := strings.Join(splitedName, ".")
return reverseName
}

View File

@@ -1,11 +1,12 @@
package provider
import (
"reflect"
"testing"
"github.com/containous/traefik/log"
"github.com/containous/traefik/types"
"github.com/mesosphere/mesos-dns/records/state"
"reflect"
"testing"
)
func TestMesosTaskFilter(t *testing.T) {
@@ -244,6 +245,36 @@ func TestMesosLoadConfig(t *testing.T) {
}
}
func TestMesosGetSubDomain(t *testing.T) {
providerGroups := &Mesos{GroupsAsSubDomains: true}
providerNoGroups := &Mesos{GroupsAsSubDomains: false}
apps := []struct {
path string
expected string
provider *Mesos
}{
{"/test", "test", providerNoGroups},
{"/test", "test", providerGroups},
{"/a/b/c/d", "d.c.b.a", providerGroups},
{"/b/a/d/c", "c.d.a.b", providerGroups},
{"/d/c/b/a", "a.b.c.d", providerGroups},
{"/c/d/a/b", "b.a.d.c", providerGroups},
{"/a/b/c/d", "a-b-c-d", providerNoGroups},
{"/b/a/d/c", "b-a-d-c", providerNoGroups},
{"/d/c/b/a", "d-c-b-a", providerNoGroups},
{"/c/d/a/b", "c-d-a-b", providerNoGroups},
}
for _, a := range apps {
actual := a.provider.getSubDomain(a.path)
if actual != a.expected {
t.Errorf("expected %q, got %q", a.expected, actual)
}
}
}
// test helpers
type (

View File

@@ -101,6 +101,12 @@ func normalize(name string) string {
return strings.Join(strings.FieldsFunc(name, fargs), "-")
}
func reverseStringSlice(slice *[]string) {
for i, j := 0, len(*slice)-1; i < j; i, j = i+1, j-1 {
(*slice)[i], (*slice)[j] = (*slice)[j], (*slice)[i]
}
}
// ClientTLS holds TLS specific configurations as client
// CA, Cert and Key can be either path or file contents
type ClientTLS struct {

View File

@@ -3,7 +3,7 @@
{{if ne (getAttribute "enable" $node.Service.Tags "true") "false"}}
[backends."backend-{{getBackend $node}}".servers."{{getBackendName $node $index}}"]
url = "{{getAttribute "protocol" $node.Service.Tags "http"}}://{{getBackendAddress $node}}:{{$node.Service.Port}}"
{{$weight := getAttribute "backend.weight" $node.Service.Tags ""}}
{{$weight := getAttribute "backend.weight" $node.Service.Tags "0"}}
{{with $weight}}
weight = {{$weight}}
{{end}}

View File

@@ -32,7 +32,7 @@
{{range $servers}}
[backends."{{Last $backend}}".servers."{{Last .}}"]
url = "{{Get "" . "/url"}}"
weight = {{Get "" . "/weight"}}
weight = {{Get "0" . "/weight"}}
{{end}}
{{end}}

View File

@@ -294,7 +294,7 @@
# To enable digest auth on the webui
# with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
# You can use htdigest to generate those ones
# [web.auth.basic]
# [web.auth.digest]
# users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]