diff --git a/.travis.yml b/.travis.yml index 5ca18caaf..53da8682a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ deploy: on: repo: containous/traefik tags: true + condition: ${TRAVIS_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ - provider: releases api_key: ${GITHUB_TOKEN} file: dist/traefik* diff --git a/CHANGELOG.md b/CHANGELOG.md index 0776d25b6..bb9e1c4b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [v1.4.4](https://github.com/containous/traefik/tree/v1.4.4) (2017-11-21) +[All Commits](https://github.com/containous/traefik/compare/v1.4.3...v1.4.4) + +**Enhancements:** +- **[middleware]** Remove GzipHandler Fork ([#2436](https://github.com/containous/traefik/pull/2436) by [ldez](https://github.com/ldez)) + +**Bug fixes:** +- **[docker]** Fix problems about duplicated and missing Docker backends/frontends. ([#2434](https://github.com/containous/traefik/pull/2434) by [nmengin](https://github.com/nmengin)) +- **[middleware]** Fix raw path handling in strip prefix ([#2382](https://github.com/containous/traefik/pull/2382) by [marco-jantke](https://github.com/marco-jantke)) +- **[rancher]** Fix issue with label traefik.backend.loadbalancer.stickiness.cookieName ([#2423](https://github.com/containous/traefik/pull/2423) by [rawmind0](https://github.com/rawmind0)) +- http.Server log goes to Debug level. ([#2420](https://github.com/containous/traefik/pull/2420) by [ldez](https://github.com/ldez)) + +**Documentation:** +- Documentation archive ([#2405](https://github.com/containous/traefik/pull/2405) by [ldez](https://github.com/ldez)) + ## [v1.4.3](https://github.com/containous/traefik/tree/v1.4.3) (2017-11-14) [All Commits](https://github.com/containous/traefik/compare/v1.4.2...v1.4.3) diff --git a/docs/archive.md b/docs/archive.md new file mode 100644 index 000000000..166815b3d --- /dev/null +++ b/docs/archive.md @@ -0,0 +1,17 @@ +## Previous documentation + +- [Latest stable](https://docs.traefik.io) + +- [Experimental](https://master--traefik-docs.netlify.com/) + +- [v1.4 aka Roquefort](http://v1-4.archive.docs.traefik.io/) + +- [v1.3 aka Raclette](http://v1-3.archive.docs.traefik.io/) + +- [v1.2 aka Morbier](http://v1-2.archive.docs.traefik.io/) + +- [v1.1 aka Camembert](http://v1-1.archive.docs.traefik.io/) + +## More + +[Change log](https://github.com/containous/traefik/blob/master/CHANGELOG.md) \ No newline at end of file diff --git a/glide.lock b/glide.lock index 72640efa1..aff28ba38 100644 --- a/glide.lock +++ b/glide.lock @@ -1,4 +1,4 @@ -hash: fec4fec4363272870c49e10cea64cc51095ecd0987b9c020c9714d950cf38784 +hash: 6deb9adeca5f1724f9ef2b31b122f85a00cf47cf4308527d6d3ff68a6ac0e705 updated: 2017-11-17T14:21:55.148450413+01:00 imports: - name: cloud.google.com/go @@ -420,9 +420,7 @@ imports: repo: https://github.com/ijc25/Gotty.git vcs: git - name: github.com/NYTimes/gziphandler - version: 26a3f68265200656f31940bc15b191f7d10b5bbd - repo: https://github.com/containous/gziphandler.git - vcs: git + version: d6f46609c7629af3a02d791a4666866eed3cbd3e - name: github.com/ogier/pflag version: 45c278ab3607870051a2ea9040bb85fcb8557481 - name: github.com/opencontainers/go-digest diff --git a/glide.yaml b/glide.yaml index 7e6d3364f..6da32ec0f 100644 --- a/glide.yaml +++ b/glide.yaml @@ -84,9 +84,6 @@ import: vcs: git - package: github.com/abbot/go-http-auth - package: github.com/NYTimes/gziphandler - version: fork-containous - repo: https://github.com/containous/gziphandler.git - vcs: git - package: github.com/docker/leadership repo: https://github.com/containous/leadership.git vcs: git diff --git a/middlewares/stripPrefix.go b/middlewares/stripPrefix.go index 8c1499d63..e156950a1 100644 --- a/middlewares/stripPrefix.go +++ b/middlewares/stripPrefix.go @@ -16,8 +16,11 @@ type StripPrefix struct { func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) { for _, prefix := range s.Prefixes { - if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) { - r.URL.Path = "/" + strings.TrimPrefix(p, "/") + if strings.HasPrefix(r.URL.Path, prefix) { + r.URL.Path = stripPrefix(r.URL.Path, prefix) + if r.URL.RawPath != "" { + r.URL.RawPath = stripPrefix(r.URL.RawPath, prefix) + } s.serveRequest(w, r, strings.TrimSpace(prefix)) return } @@ -35,3 +38,11 @@ func (s *StripPrefix) serveRequest(w http.ResponseWriter, r *http.Request, prefi func (s *StripPrefix) SetHandler(Handler http.Handler) { s.Handler = Handler } + +func stripPrefix(s, prefix string) string { + return ensureLeadingSlash(strings.TrimPrefix(s, prefix)) +} + +func ensureLeadingSlash(str string) string { + return "/" + strings.TrimPrefix(str, "/") +} diff --git a/middlewares/stripPrefixRegex.go b/middlewares/stripPrefixRegex.go index bf2130ca8..bcd66d912 100644 --- a/middlewares/stripPrefixRegex.go +++ b/middlewares/stripPrefixRegex.go @@ -40,6 +40,9 @@ func (s *StripPrefixRegex) ServeHTTP(w http.ResponseWriter, r *http.Request) { } r.URL.Path = r.URL.Path[len(prefix.Path):] + if r.URL.RawPath != "" { + r.URL.RawPath = r.URL.RawPath[len(prefix.Path):] + } r.Header.Add(ForwardedPrefixHeader, prefix.Path) r.RequestURI = r.URL.RequestURI() s.Handler.ServeHTTP(w, r) diff --git a/middlewares/stripPrefixRegex_test.go b/middlewares/stripPrefixRegex_test.go index 038195dac..0f7a10062 100644 --- a/middlewares/stripPrefixRegex_test.go +++ b/middlewares/stripPrefixRegex_test.go @@ -10,13 +10,13 @@ import ( ) func TestStripPrefixRegex(t *testing.T) { - testPrefixRegex := []string{"/a/api/", "/b/{regex}/", "/c/{category}/{id:[0-9]+}/"} tests := []struct { path string expectedStatusCode int expectedPath string + expectedRawPath string expectedHeader string }{ { @@ -61,6 +61,13 @@ func TestStripPrefixRegex(t *testing.T) { path: "/c/api/abc/test4", expectedStatusCode: http.StatusNotFound, }, + { + path: "/a/api/a%2Fb", + expectedStatusCode: http.StatusOK, + expectedPath: "a/b", + expectedRawPath: "a%2Fb", + expectedHeader: "/a/api/", + }, } for _, test := range tests { @@ -68,9 +75,10 @@ func TestStripPrefixRegex(t *testing.T) { t.Run(test.path, func(t *testing.T) { t.Parallel() - var actualPath, actualHeader string + var actualPath, actualRawPath, actualHeader string handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actualPath = r.URL.Path + actualRawPath = r.URL.RawPath actualHeader = r.Header.Get(ForwardedPrefixHeader) }) handler := NewStripPrefixRegex(handlerPath, testPrefixRegex) @@ -82,6 +90,7 @@ func TestStripPrefixRegex(t *testing.T) { assert.Equal(t, test.expectedStatusCode, resp.Code, "Unexpected status code.") assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.") + assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader) }) } diff --git a/middlewares/stripPrefix_test.go b/middlewares/stripPrefix_test.go index 2e8778d72..dd3f48106 100644 --- a/middlewares/stripPrefix_test.go +++ b/middlewares/stripPrefix_test.go @@ -16,6 +16,7 @@ func TestStripPrefix(t *testing.T) { path string expectedStatusCode int expectedPath string + expectedRawPath string expectedHeader string }{ { @@ -94,6 +95,15 @@ func TestStripPrefix(t *testing.T) { expectedPath: "/us", expectedHeader: "/stat", }, + { + desc: "raw path is also stripped", + prefixes: []string{"/stat"}, + path: "/stat/a%2Fb", + expectedStatusCode: http.StatusOK, + expectedPath: "/a/b", + expectedRawPath: "/a%2Fb", + expectedHeader: "/stat", + }, } for _, test := range tests { @@ -101,11 +111,12 @@ func TestStripPrefix(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - var actualPath, actualHeader, requestURI string + var actualPath, actualRawPath, actualHeader, requestURI string handler := &StripPrefix{ Prefixes: test.prefixes, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actualPath = r.URL.Path + actualRawPath = r.URL.RawPath actualHeader = r.Header.Get(ForwardedPrefixHeader) requestURI = r.RequestURI }), @@ -118,8 +129,15 @@ func TestStripPrefix(t *testing.T) { assert.Equal(t, test.expectedStatusCode, resp.Code, "Unexpected status code.") assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.") + assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader) - assert.Equal(t, test.expectedPath, requestURI, "Unexpected request URI.") + + expectedURI := test.expectedPath + if test.expectedRawPath != "" { + // go HTTP uses the raw path when existent in the RequestURI + expectedURI = test.expectedRawPath + } + assert.Equal(t, expectedURI, requestURI, "Unexpected request URI.") }) } } diff --git a/mkdocs.yml b/mkdocs.yml index 1e7f3fce1..509312a49 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -100,3 +100,4 @@ pages: - 'Clustering/HA': 'user-guide/cluster.md' - 'gRPC Example': 'user-guide/grpc.md' - Benchmarks: benchmarks.md + - 'Archive': 'archive.md' diff --git a/provider/docker/docker.go b/provider/docker/docker.go index a0b7059c5..00233e7da 100644 --- a/provider/docker/docker.go +++ b/provider/docker/docker.go @@ -345,9 +345,15 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con frontends := map[string][]dockerData{} backends := map[string]dockerData{} servers := map[string][]dockerData{} + serviceNames := make(map[string]struct{}) for idx, container := range filteredContainers { - frontendName := p.getFrontendName(container, idx) - frontends[frontendName] = append(frontends[frontendName], container) + if _, exists := serviceNames[container.ServiceName]; !exists { + frontendName := p.getFrontendName(container, idx) + frontends[frontendName] = append(frontends[frontendName], container) + if len(container.ServiceName) > 0 { + serviceNames[container.ServiceName] = struct{}{} + } + } backendName := p.getBackend(container) backends[backendName] = container servers[backendName] = append(servers[backendName], container) @@ -471,9 +477,9 @@ func (p *Provider) getServicePriority(container dockerData, serviceName string) // Extract backend from labels for a given service and a given docker container func (p *Provider) getServiceBackend(container dockerData, serviceName string) string { if value, ok := getContainerServiceLabel(container, serviceName, "frontend.backend"); ok { - return value + return container.ServiceName + "-" + value } - return p.getBackend(container) + "-" + provider.Normalize(serviceName) + return strings.TrimPrefix(container.ServiceName, "/") + "-" + p.getBackend(container) + "-" + provider.Normalize(serviceName) } // Extract rule from labels for a given service and a given docker container diff --git a/provider/docker/service_test.go b/provider/docker/service_test.go index 36fef70e2..781b9a34d 100644 --- a/provider/docker/service_test.go +++ b/provider/docker/service_test.go @@ -171,19 +171,19 @@ func TestDockerGetServiceBackend(t *testing.T) { }{ { container: containerJSON(name("foo")), - expected: "foo-myservice", + expected: "foo-foo-myservice", }, { container: containerJSON(labels(map[string]string{ types.LabelBackend: "another-backend", })), - expected: "another-backend-myservice", + expected: "fake-another-backend-myservice", }, { container: containerJSON(labels(map[string]string{ "traefik.myservice.frontend.backend": "custom-backend", })), - expected: "custom-backend", + expected: "fake-custom-backend", }, } @@ -342,8 +342,8 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { ), }, expectedFrontends: map[string]*types.Frontend{ - "frontend-foo-service": { - Backend: "backend-foo-service", + "frontend-foo-foo-service": { + Backend: "backend-foo-foo-service", PassHostHeader: true, EntryPoints: []string{"http", "https"}, BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, @@ -356,7 +356,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { }, }, expectedBackends: map[string]*types.Backend{ - "backend-foo-service": { + "backend-foo-foo-service": { Servers: map[string]types.Server{ "service-0": { URL: "http://127.0.0.1:2503", @@ -402,8 +402,8 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { ), }, expectedFrontends: map[string]*types.Frontend{ - "frontend-foobar": { - Backend: "backend-foobar", + "frontend-test1-foobar": { + Backend: "backend-test1-foobar", PassHostHeader: false, Priority: 5000, EntryPoints: []string{"http", "https", "ws"}, @@ -415,8 +415,8 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { }, }, }, - "frontend-test2-anotherservice": { - Backend: "backend-test2-anotherservice", + "frontend-test2-test2-anotherservice": { + Backend: "backend-test2-test2-anotherservice", PassHostHeader: true, EntryPoints: []string{}, BasicAuth: []string{}, @@ -429,7 +429,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { }, }, expectedBackends: map[string]*types.Backend{ - "backend-foobar": { + "backend-test1-foobar": { Servers: map[string]types.Server{ "service-0": { URL: "https://127.0.0.1:2503", @@ -438,7 +438,7 @@ func TestDockerLoadDockerServiceConfig(t *testing.T) { }, CircuitBreaker: nil, }, - "backend-test2-anotherservice": { + "backend-test2-test2-anotherservice": { Servers: map[string]types.Server{ "service-0": { URL: "http://127.0.0.1:8079", diff --git a/provider/rancher/rancher.go b/provider/rancher/rancher.go index b58fd4379..6c84b9379 100644 --- a/provider/rancher/rancher.go +++ b/provider/rancher/rancher.go @@ -133,7 +133,7 @@ func (p *Provider) hasStickinessLabel(service rancherData) bool { return errStickiness == nil && len(labelStickiness) > 0 && strings.EqualFold(strings.TrimSpace(labelStickiness), "true") } -func (p *Provider) getStickinessCookieName(service rancherData, backendName string) string { +func (p *Provider) getStickinessCookieName(service rancherData) string { if label, err := getServiceLabel(service, types.LabelBackendLoadbalancerStickinessCookieName); err == nil { return label } diff --git a/server/server.go b/server/server.go index 5678ac5a5..e35dd205f 100644 --- a/server/server.go +++ b/server/server.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io/ioutil" + stdlog "log" "net" "net/http" "net/url" @@ -20,6 +21,7 @@ import ( "sync" "time" + "github.com/Sirupsen/logrus" "github.com/armon/go-proxyproto" "github.com/containous/mux" "github.com/containous/traefik/cluster" @@ -47,6 +49,10 @@ import ( "golang.org/x/net/http2" ) +var ( + httpServerLogger = stdlog.New(log.WriterLevel(logrus.DebugLevel), "", 0) +) + // Server is the reverse-proxy/load-balancer engine type Server struct { serverEntryPoints serverEntryPoints @@ -794,6 +800,7 @@ func (server *Server) prepareServer(entryPointName string, entryPoint *configura ReadTimeout: readTimeout, WriteTimeout: writeTimeout, IdleTimeout: idleTimeout, + ErrorLog: httpServerLogger, }, listener, nil