2017-07-06 17:28:13 +03:00
package integration
2015-09-27 16:59:51 +03:00
import (
2019-01-15 16:28:04 +03:00
"bytes"
"encoding/json"
2017-05-17 16:22:44 +03:00
"fmt"
2015-09-27 16:59:51 +03:00
"net/http"
2018-08-24 17:20:03 +03:00
"net/http/httptest"
2017-09-26 11:22:03 +03:00
"os"
2017-05-17 16:22:44 +03:00
"strings"
2017-09-26 11:22:03 +03:00
"syscall"
2015-09-27 16:59:51 +03:00
"time"
2019-01-15 16:28:04 +03:00
"github.com/containous/traefik/config"
2017-05-17 16:22:44 +03:00
"github.com/containous/traefik/integration/try"
2016-04-02 13:40:21 +03:00
"github.com/go-check/check"
2015-09-27 16:59:51 +03:00
checker "github.com/vdemeester/shakers"
)
2015-11-04 01:06:31 +03:00
// SimpleSuite
type SimpleSuite struct { BaseSuite }
2015-09-27 16:59:51 +03:00
func ( s * SimpleSuite ) TestInvalidConfigShouldFail ( c * check . C ) {
2017-07-10 15:58:31 +03:00
cmd , output := s . cmdTraefik ( withConfigFile ( "fixtures/invalid_configuration.toml" ) )
2016-04-19 20:23:08 +03:00
2017-05-17 16:22:44 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2016-04-19 20:23:08 +03:00
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
err = try . Do ( 500 * time . Millisecond , func ( ) error {
expected := "Near line 0 (last key parsed ''): bare keys cannot contain '{'"
2017-07-10 15:58:31 +03:00
actual := output . String ( )
2017-05-17 16:22:44 +03:00
if ! strings . Contains ( actual , expected ) {
2018-07-03 11:02:03 +03:00
return fmt . Errorf ( "got %s, wanted %s" , actual , expected )
2017-05-17 16:22:44 +03:00
}
return nil
} )
c . Assert ( err , checker . IsNil )
2015-09-27 16:59:51 +03:00
}
func ( s * SimpleSuite ) TestSimpleDefaultConfig ( c * check . C ) {
2017-07-10 15:58:31 +03:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/simple_default.toml" ) )
2017-05-17 16:22:44 +03:00
2015-09-27 16:59:51 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2015-11-04 01:06:31 +03:00
defer cmd . Process . Kill ( )
2015-09-27 16:59:51 +03:00
// TODO validate : run on 80
2016-03-15 20:57:56 +03:00
// Expected a 404 as we did not configure anything
2017-05-17 16:22:44 +03:00
err = try . GetRequest ( "http://127.0.0.1:8000/" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
2016-03-15 20:57:56 +03:00
c . Assert ( err , checker . IsNil )
2015-11-04 01:06:31 +03:00
}
func ( s * SimpleSuite ) TestWithWebConfig ( c * check . C ) {
2017-07-10 15:58:31 +03:00
cmd , _ := s . cmdTraefik ( withConfigFile ( "fixtures/simple_web.toml" ) )
2017-05-17 16:22:44 +03:00
2015-11-04 01:06:31 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2015-11-04 01:06:31 +03:00
c . Assert ( err , checker . IsNil )
2015-09-27 16:59:51 +03:00
}
2016-06-02 16:17:04 +03:00
func ( s * SimpleSuite ) TestPrintHelp ( c * check . C ) {
2017-07-10 15:58:31 +03:00
cmd , output := s . cmdTraefik ( "--help" )
2016-06-02 16:17:04 +03:00
2017-05-17 16:22:44 +03:00
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
2016-06-02 16:17:04 +03:00
defer cmd . Process . Kill ( )
2017-05-17 16:22:44 +03:00
err = try . Do ( 500 * time . Millisecond , func ( ) error {
expected := "Usage:"
notExpected := "panic:"
2017-07-10 15:58:31 +03:00
actual := output . String ( )
2017-05-17 16:22:44 +03:00
if strings . Contains ( actual , notExpected ) {
2018-07-03 11:02:03 +03:00
return fmt . Errorf ( "got %s" , actual )
2017-05-17 16:22:44 +03:00
}
if ! strings . Contains ( actual , expected ) {
2018-07-03 11:02:03 +03:00
return fmt . Errorf ( "got %s, wanted %s" , actual , expected )
2017-05-17 16:22:44 +03:00
}
return nil
} )
c . Assert ( err , checker . IsNil )
2016-06-02 16:17:04 +03:00
}
2017-09-26 11:22:03 +03:00
func ( s * SimpleSuite ) TestRequestAcceptGraceTimeout ( c * check . C ) {
s . createComposeProject ( c , "reqacceptgrace" )
s . composeProject . Start ( c )
whoami := "http://" + s . composeProject . Container ( c , "whoami" ) . NetworkSettings . IPAddress + ":80"
file := s . adaptFile ( c , "fixtures/reqacceptgrace.toml" , struct {
Server string
} { whoami } )
defer os . Remove ( file )
cmd , display := s . traefikCmd ( withConfigFile ( file ) )
defer display ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// Wait for Traefik to turn ready.
err = try . GetRequest ( "http://127.0.0.1:8000/" , 2 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
c . Assert ( err , checker . IsNil )
// Make sure exposed service is ready.
err = try . GetRequest ( "http://127.0.0.1:8000/service" , 3 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
2018-03-22 20:18:03 +03:00
// Check that /ping endpoint is responding with 200.
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 3 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
2017-09-26 11:22:03 +03:00
// Send SIGTERM to Traefik.
proc , err := os . FindProcess ( cmd . Process . Pid )
c . Assert ( err , checker . IsNil )
err = proc . Signal ( syscall . SIGTERM )
c . Assert ( err , checker . IsNil )
// Give Traefik time to process the SIGTERM and send a request half-way
// into the request accepting grace period, by which requests should
// still get served.
time . Sleep ( 5 * time . Second )
resp , err := http . Get ( "http://127.0.0.1:8000/service" )
c . Assert ( err , checker . IsNil )
defer resp . Body . Close ( )
c . Assert ( resp . StatusCode , checker . Equals , http . StatusOK )
2018-03-22 20:18:03 +03:00
// ping endpoint should now return a Service Unavailable.
resp , err = http . Get ( "http://127.0.0.1:8001/ping" )
c . Assert ( err , checker . IsNil )
defer resp . Body . Close ( )
c . Assert ( resp . StatusCode , checker . Equals , http . StatusServiceUnavailable )
2017-09-26 11:22:03 +03:00
// Expect Traefik to shut down gracefully once the request accepting grace
// period has elapsed.
waitErr := make ( chan error )
go func ( ) {
waitErr <- cmd . Wait ( )
} ( )
select {
case err := <- waitErr :
c . Assert ( err , checker . IsNil )
case <- time . After ( 10 * time . Second ) :
// By now we are ~5 seconds out of the request accepting grace period
// (start + 5 seconds sleep prior to the mid-grace period request +
// 10 seconds timeout = 15 seconds > 10 seconds grace period).
// Something must have gone wrong if we still haven't terminated at
// this point.
c . Fatal ( "Traefik did not terminate in time" )
}
}
2017-11-09 18:12:04 +03:00
func ( s * SimpleSuite ) TestApiOnSameEntryPoint ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
2018-11-27 19:42:04 +03:00
cmd , output := s . traefikCmd ( "--entryPoints=Name:http Address::8000" , "--api.entryPoint=http" , "--global.debug" , "--providers.docker" )
2017-11-09 18:12:04 +03:00
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
// TODO validate : run on 80
// Expected a 404 as we did not configure anything
err = try . GetRequest ( "http://127.0.0.1:8000/test" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
c . Assert ( err , checker . IsNil )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8000/api/providers/docker" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2017-11-09 18:12:04 +03:00
c . Assert ( err , checker . IsNil )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8000/api/providers/docker/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2017-11-09 18:12:04 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
2017-11-30 14:18:03 +03:00
func ( s * SimpleSuite ) TestStatsWithMultipleEntryPoint ( c * check . C ) {
2019-01-18 17:18:04 +03:00
c . Skip ( "Stats is missing" )
2017-11-30 14:18:03 +03:00
s . createComposeProject ( c , "stats" )
s . composeProject . Start ( c )
whoami1 := "http://" + s . composeProject . Container ( c , "whoami1" ) . NetworkSettings . IPAddress + ":80"
whoami2 := "http://" + s . composeProject . Container ( c , "whoami2" ) . NetworkSettings . IPAddress + ":80"
file := s . adaptFile ( c , "fixtures/simple_stats.toml" , struct {
Server1 string
Server2 string
} { whoami1 , whoami2 } )
cmd , output := s . traefikCmd ( withConfigFile ( file ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
err = try . GetRequest ( "http://127.0.0.1:8080/api" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2017-11-30 14:18:03 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8080/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8080/health" , 1 * time . Second , try . BodyContains ( ` "total_status_code_count": { "200":2} ` ) )
c . Assert ( err , checker . IsNil )
}
2017-11-09 18:12:04 +03:00
func ( s * SimpleSuite ) TestNoAuthOnPing ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
cmd , output := s . traefikCmd ( withConfigFile ( "./fixtures/simple_auth.toml" ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8001/api/providers" , 2 * time . Second , try . StatusCodeIs ( http . StatusUnauthorized ) )
2017-11-09 18:12:04 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
2017-11-30 18:10:02 +03:00
func ( s * SimpleSuite ) TestDefaultEntrypointHTTP ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
2018-11-27 19:42:04 +03:00
cmd , output := s . traefikCmd ( "--entryPoints=Name:http Address::8000" , "--global.debug" , "--providers.docker" , "--api" )
2017-11-30 18:10:02 +03:00
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/docker/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2017-11-30 18:10:02 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
func ( s * SimpleSuite ) TestWithUnexistingEntrypoint ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
2018-11-27 19:42:04 +03:00
cmd , output := s . traefikCmd ( "--entryPoints=Name:http Address::8000" , "--global.debug" , "--providers.docker" , "--api" )
2017-11-30 18:10:02 +03:00
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/docker/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2017-11-30 18:10:02 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
2018-01-19 16:30:04 +03:00
func ( s * SimpleSuite ) TestMetricsPrometheusDefaultEntrypoint ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
2019-01-18 17:18:04 +03:00
cmd , output := s . traefikCmd ( "--entryPoints=Name:http Address::8000" , "--api" , "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0" , "--providers.docker" , "--global.debug" )
2018-01-19 16:30:04 +03:00
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/docker/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2018-01-19 16:30:04 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
c . Assert ( err , checker . IsNil )
}
2018-02-16 13:04:04 +03:00
func ( s * SimpleSuite ) TestMultipleProviderSameBackendName ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
2018-08-24 17:20:03 +03:00
2018-02-16 13:04:04 +03:00
ipWhoami01 := s . composeProject . Container ( c , "whoami1" ) . NetworkSettings . IPAddress
ipWhoami02 := s . composeProject . Container ( c , "whoami2" ) . NetworkSettings . IPAddress
file := s . adaptFile ( c , "fixtures/multiple_provider.toml" , struct { IP string } {
IP : ipWhoami02 ,
} )
defer os . Remove ( file )
2018-08-24 17:20:03 +03:00
2018-02-16 13:04:04 +03:00
cmd , output := s . traefikCmd ( withConfigFile ( file ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2018-11-14 12:18:03 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/routers" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2018-02-16 13:04:04 +03:00
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . BodyContains ( ipWhoami01 ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/file" , 1 * time . Second , try . BodyContains ( ipWhoami02 ) )
c . Assert ( err , checker . IsNil )
}
2018-08-24 17:20:03 +03:00
func ( s * SimpleSuite ) TestIPStrategyWhitelist ( c * check . C ) {
s . createComposeProject ( c , "whitelist" )
s . composeProject . Start ( c )
cmd , output := s . traefikCmd ( withConfigFile ( "fixtures/simple_whitelist.toml" ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
2019-01-18 17:18:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/docker/routers" , 2 * time . Second , try . BodyContains ( "override" ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/docker/routers" , 2 * time . Second , try . BodyContains ( "override.remoteaddr.whitelist.docker.local" ) )
2018-08-24 17:20:03 +03:00
c . Assert ( err , checker . IsNil )
testCases := [ ] struct {
desc string
xForwardedFor string
host string
expectedStatusCode int
} {
2019-01-18 17:18:04 +03:00
// {
// desc: "default client ip strategy accept",
// xForwardedFor: "8.8.8.8,127.0.0.1",
// host: "no.override.whitelist.docker.local",
// expectedStatusCode: 200,
// },
// FIXME add clientipstrategy and forwarded headers on entrypoint
// {
// desc: "default client ip strategy reject",
// xForwardedFor: "8.8.8.10,127.0.0.1",
// host: "no.override.whitelist.docker.local",
// expectedStatusCode: 403,
// },
2018-08-24 17:20:03 +03:00
{
desc : "override remote addr reject" ,
xForwardedFor : "8.8.8.8,8.8.8.8" ,
host : "override.remoteaddr.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override depth accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,127.0.0.1" ,
host : "override.depth.whitelist.docker.local" ,
expectedStatusCode : 200 ,
} ,
{
desc : "override depth reject" ,
xForwardedFor : "10.0.0.1,8.8.8.8,127.0.0.1" ,
host : "override.depth.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs reject" ,
xForwardedFor : "10.0.0.3,10.0.0.1,10.0.0.2" ,
host : "override.excludedips.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,10.0.0.2" ,
host : "override.excludedips.whitelist.docker.local" ,
expectedStatusCode : 200 ,
} ,
}
for _ , test := range testCases {
req := httptest . NewRequest ( http . MethodGet , "http://127.0.0.1:8000" , nil )
req . Header . Set ( "X-Forwarded-For" , test . xForwardedFor )
req . Host = test . host
req . RequestURI = ""
err = try . Request ( req , 1 * time . Second , try . StatusCodeIs ( test . expectedStatusCode ) )
if err != nil {
c . Fatalf ( "Error while %s: %v" , test . desc , err )
}
}
}
2018-10-30 14:34:00 +03:00
2018-10-17 15:22:03 +03:00
func ( s * SimpleSuite ) TestKeepTrailingSlash ( c * check . C ) {
file := s . adaptFile ( c , "fixtures/keep_trailing_slash.toml" , struct {
KeepTrailingSlash bool
} { true } )
defer os . Remove ( file )
cmd , output := s . traefikCmd ( withConfigFile ( file ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
oldCheckRedirect := http . DefaultClient . CheckRedirect
http . DefaultClient . CheckRedirect = func ( req * http . Request , via [ ] * http . Request ) error {
return http . ErrUseLastResponse
}
err = try . GetRequest ( "http://127.0.0.1:8000/test/foo/" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
c . Assert ( err , checker . IsNil )
http . DefaultClient . CheckRedirect = oldCheckRedirect
}
2019-01-15 16:28:04 +03:00
func ( s * SimpleSuite ) TestMultiprovider ( c * check . C ) {
s . createComposeProject ( c , "base" )
s . composeProject . Start ( c )
server := "http://" + s . composeProject . Container ( c , "whoami1" ) . NetworkSettings . IPAddress
file := s . adaptFile ( c , "fixtures/multiprovider.toml" , struct {
Server string
} { Server : server } )
defer os . Remove ( file )
cmd , output := s . traefikCmd ( withConfigFile ( file ) )
defer output ( c )
err := cmd . Start ( )
c . Assert ( err , checker . IsNil )
defer cmd . Process . Kill ( )
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/file/services" , 1000 * time . Millisecond , try . BodyContains ( "service" ) )
c . Assert ( err , checker . IsNil )
config := config . Configuration {
Routers : map [ string ] * config . Router {
"router1" : {
EntryPoints : [ ] string { "http" } ,
Middlewares : [ ] string { "file.customheader" } ,
Service : "file.service" ,
Rule : "PathPrefix:/" ,
} ,
} ,
}
json , err := json . Marshal ( config )
c . Assert ( err , checker . IsNil )
request , err := http . NewRequest ( http . MethodPut , "http://127.0.0.1:8080/api/providers/rest" , bytes . NewReader ( json ) )
c . Assert ( err , checker . IsNil )
response , err := http . DefaultClient . Do ( request )
c . Assert ( err , checker . IsNil )
c . Assert ( response . StatusCode , checker . Equals , http . StatusOK )
err = try . GetRequest ( "http://127.0.0.1:8080/api/providers/rest/routers" , 1000 * time . Millisecond , try . BodyContains ( "PathPrefix:/" ) )
c . Assert ( err , checker . IsNil )
err = try . GetRequest ( "http://127.0.0.1:8000/" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) , try . BodyContains ( "CustomValue" ) )
c . Assert ( err , checker . IsNil )
}