2015-09-11 16:37:13 +02:00
package main
import (
2015-09-12 15:10:03 +02:00
"github.com/BurntSushi/toml"
"github.com/codegangsta/negroni"
2015-09-15 21:38:54 +02:00
"github.com/emilevauge/traefik/middlewares"
2015-09-11 16:37:13 +02:00
"github.com/gorilla/mux"
"github.com/mailgun/oxy/forward"
"github.com/mailgun/oxy/roundrobin"
2015-09-12 15:10:03 +02:00
"github.com/op/go-logging"
"github.com/thoas/stats"
2015-09-11 16:37:13 +02:00
"github.com/tylerb/graceful"
2015-09-12 15:10:03 +02:00
"github.com/unrolled/render"
"gopkg.in/alecthomas/kingpin.v2"
2015-09-11 16:37:13 +02:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"syscall"
"time"
2015-09-19 13:02:59 +02:00
fmtlog "log"
"github.com/mailgun/oxy/cbreaker"
2015-09-11 16:37:13 +02:00
)
2015-09-12 01:55:10 +02:00
var (
2015-09-12 15:10:03 +02:00
globalConfigFile = kingpin . Arg ( "conf" , "Main configration file." ) . Default ( "traefik.toml" ) . String ( )
2015-09-12 01:55:10 +02:00
currentConfiguration = new ( Configuration )
2015-09-12 15:10:03 +02:00
metrics = stats . New ( )
log = logging . MustGetLogger ( "traefik" )
2015-09-17 16:29:44 +02:00
oxyLogger = & OxyLogger { }
2015-09-12 15:10:03 +02:00
templatesRenderer = render . New ( render . Options {
Directory : "templates" ,
Asset : Asset ,
2015-09-12 01:55:10 +02:00
AssetNames : AssetNames ,
} )
)
2015-09-11 16:37:13 +02:00
func main ( ) {
2015-09-12 01:55:10 +02:00
kingpin . Parse ( )
2015-09-19 13:02:59 +02:00
fmtlog . SetFlags ( fmtlog . Lshortfile | fmtlog . LstdFlags )
2015-09-11 16:37:13 +02:00
var srv * graceful . Server
var configurationRouter * mux . Router
2015-09-21 18:05:56 +02:00
var configurationChan = make ( chan * Configuration , 10 )
2015-09-16 19:08:01 +02:00
defer close ( configurationChan )
2015-09-11 16:37:13 +02:00
var providers = [ ] Provider { }
var format = logging . MustStringFormatter ( "%{color}%{time:15:04:05.000} %{shortfile:20.20s} %{level:8.8s} %{id:03x} ▶%{color:reset} %{message}" )
var sigs = make ( chan os . Signal , 1 )
signal . Notify ( sigs , syscall . SIGINT , syscall . SIGTERM )
// load global configuration
2015-09-12 01:55:10 +02:00
gloablConfiguration := LoadFileConfig ( * globalConfigFile )
2015-09-11 16:37:13 +02:00
2015-09-12 15:20:56 +02:00
loggerMiddleware := middlewares . NewLogger ( gloablConfiguration . AccessLogsFile )
defer loggerMiddleware . Close ( )
2015-09-11 16:37:13 +02:00
// logging
backends := [ ] logging . Backend { }
level , err := logging . LogLevel ( gloablConfiguration . LogLevel )
if err != nil {
log . Fatal ( "Error getting level" , err )
}
2015-09-11 16:48:52 +02:00
2015-09-12 15:10:03 +02:00
if len ( gloablConfiguration . TraefikLogsFile ) > 0 {
fi , err := os . OpenFile ( gloablConfiguration . TraefikLogsFile , os . O_RDWR | os . O_CREATE | os . O_APPEND , 0666 )
2015-09-12 15:20:56 +02:00
defer fi . Close ( )
2015-09-11 16:37:13 +02:00
if err != nil {
log . Fatal ( "Error opening file" , err )
2015-09-12 15:10:03 +02:00
} else {
2015-09-11 16:37:13 +02:00
logBackend := logging . NewLogBackend ( fi , "" , 0 )
logBackendFormatter := logging . NewBackendFormatter ( logBackend , logging . GlogFormatter )
backends = append ( backends , logBackendFormatter )
}
}
2015-09-12 15:10:03 +02:00
if gloablConfiguration . TraefikLogsStdout {
2015-09-11 16:37:13 +02:00
logBackend := logging . NewLogBackend ( os . Stdout , "" , 0 )
logBackendFormatter := logging . NewBackendFormatter ( logBackend , format )
backends = append ( backends , logBackendFormatter )
}
logging . SetBackend ( backends ... )
2015-09-21 10:50:46 +02:00
logging . SetLevel ( level , "traefik" )
2015-09-11 16:37:13 +02:00
configurationRouter = LoadDefaultConfig ( gloablConfiguration )
// listen new configurations from providers
go func ( ) {
for {
configuration := <- configurationChan
log . Info ( "Configuration receveived %+v" , configuration )
if configuration == nil {
log . Info ( "Skipping empty configuration" )
2015-09-12 15:10:03 +02:00
} else if reflect . DeepEqual ( currentConfiguration , configuration ) {
2015-09-11 16:37:13 +02:00
log . Info ( "Skipping same configuration" )
} else {
2015-09-19 13:02:59 +02:00
newConfigurationRouter , err := LoadConfig ( configuration , gloablConfiguration )
if ( err == nil ) {
currentConfiguration = configuration
configurationRouter = newConfigurationRouter
srv . Stop ( time . Duration ( gloablConfiguration . GraceTimeOut ) * time . Second )
time . Sleep ( 3 * time . Second )
} else {
log . Error ( "Error loading new configuration, aborted " , err )
}
2015-09-11 16:37:13 +02:00
}
}
} ( )
// configure providers
2015-09-12 15:10:03 +02:00
if gloablConfiguration . Docker != nil {
2015-09-11 16:37:13 +02:00
providers = append ( providers , gloablConfiguration . Docker )
}
2015-09-12 15:10:03 +02:00
if gloablConfiguration . Marathon != nil {
2015-09-11 16:37:13 +02:00
providers = append ( providers , gloablConfiguration . Marathon )
}
2015-09-12 15:10:03 +02:00
if gloablConfiguration . File != nil {
if len ( gloablConfiguration . File . Filename ) == 0 {
2015-09-11 16:37:13 +02:00
// no filename, setting to global config file
2015-09-12 01:55:10 +02:00
gloablConfiguration . File . Filename = * globalConfigFile
2015-09-11 16:37:13 +02:00
}
providers = append ( providers , gloablConfiguration . File )
}
2015-09-12 15:10:03 +02:00
if gloablConfiguration . Web != nil {
2015-09-11 16:37:13 +02:00
providers = append ( providers , gloablConfiguration . Web )
}
2015-09-21 18:05:56 +02:00
if gloablConfiguration . Consul != nil {
providers = append ( providers , gloablConfiguration . Consul )
}
2015-09-11 16:37:13 +02:00
// start providers
for _ , provider := range providers {
log . Notice ( "Starting provider %v %+v" , reflect . TypeOf ( provider ) , provider )
currentProvider := provider
go func ( ) {
currentProvider . Provide ( configurationChan )
} ( )
}
goAway := false
go func ( ) {
sig := <- sigs
log . Notice ( "I have to go... %+v" , sig )
goAway = true
srv . Stop ( time . Duration ( gloablConfiguration . GraceTimeOut ) * time . Second )
} ( )
for {
if goAway {
break
}
2015-09-12 15:10:03 +02:00
// middlewares
var negroni = negroni . New ( )
negroni . Use ( metrics )
2015-09-12 15:20:56 +02:00
negroni . Use ( loggerMiddleware )
2015-09-19 13:02:59 +02:00
//negroni.Use(middlewares.NewCircuitBreaker(oxyLogger))
2015-09-12 19:22:44 +02:00
//negroni.Use(middlewares.NewRoutes(configurationRouter))
2015-09-12 15:10:03 +02:00
negroni . UseHandler ( configurationRouter )
2015-09-11 16:37:13 +02:00
srv = & graceful . Server {
2015-09-12 15:10:03 +02:00
Timeout : time . Duration ( gloablConfiguration . GraceTimeOut ) * time . Second ,
2015-09-11 16:37:13 +02:00
NoSignalHandling : true ,
Server : & http . Server {
Addr : gloablConfiguration . Port ,
2015-09-12 15:10:03 +02:00
Handler : negroni ,
2015-09-11 16:37:13 +02:00
} ,
}
go func ( ) {
2015-09-12 15:10:03 +02:00
if len ( gloablConfiguration . CertFile ) > 0 && len ( gloablConfiguration . KeyFile ) > 0 {
2015-09-11 16:48:52 +02:00
srv . ListenAndServeTLS ( gloablConfiguration . CertFile , gloablConfiguration . KeyFile )
} else {
srv . ListenAndServe ( )
}
2015-09-11 16:37:13 +02:00
} ( )
log . Notice ( "Started" )
<- srv . StopChan ( )
log . Notice ( "Stopped" )
}
}
2015-09-19 13:02:59 +02:00
func LoadConfig ( configuration * Configuration , gloablConfiguration * GlobalConfiguration ) ( * mux . Router , error ) {
2015-09-11 16:37:13 +02:00
router := mux . NewRouter ( )
2015-09-12 15:10:03 +02:00
router . NotFoundHandler = http . HandlerFunc ( notFoundHandler )
2015-09-11 16:37:13 +02:00
backends := map [ string ] http . Handler { }
2015-09-15 16:09:21 +02:00
for frontendName , frontend := range configuration . Frontends {
log . Debug ( "Creating frontend %s" , frontendName )
2015-09-19 13:02:59 +02:00
fwd , _ := forward . New ( forward . Logger ( oxyLogger ) )
2015-09-17 16:29:44 +02:00
newRoute := router . NewRoute ( ) . Name ( frontendName )
2015-09-15 16:09:21 +02:00
for routeName , route := range frontend . Routes {
2015-09-19 13:02:59 +02:00
log . Debug ( "Creating route %s %s:%s" , routeName , route . Rule , route . Value )
2015-09-15 18:35:32 +02:00
newRouteReflect := Invoke ( newRoute , route . Rule , route . Value )
2015-09-12 13:20:54 +02:00
newRoute = newRouteReflect [ 0 ] . Interface ( ) . ( * mux . Route )
2015-09-11 16:37:13 +02:00
}
2015-09-15 16:09:21 +02:00
if backends [ frontend . Backend ] == nil {
log . Debug ( "Creating backend %s" , frontend . Backend )
2015-09-11 16:37:13 +02:00
lb , _ := roundrobin . New ( fwd )
2015-09-17 16:29:44 +02:00
rb , _ := roundrobin . NewRebalancer ( lb , roundrobin . RebalancerLogger ( oxyLogger ) )
2015-09-15 16:09:21 +02:00
for serverName , server := range configuration . Backends [ frontend . Backend ] . Servers {
2015-09-19 13:02:59 +02:00
if url , err := url . Parse ( server . Url ) ; err != nil {
return nil , err
} else {
log . Debug ( "Creating server %s %s" , serverName , url . String ( ) )
rb . UpsertServer ( url , roundrobin . Weight ( server . Weight ) )
}
2015-09-11 16:37:13 +02:00
}
2015-09-19 13:02:59 +02:00
backends [ frontend . Backend ] = rb
2015-09-12 15:10:03 +02:00
} else {
2015-09-15 16:09:21 +02:00
log . Debug ( "Reusing backend %s" , frontend . Backend )
2015-09-11 16:37:13 +02:00
}
2015-09-17 16:29:44 +02:00
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(gloablConfiguration.Replay)), stream.Logger(oxyLogger))
2015-09-19 13:02:59 +02:00
var negroni = negroni . New ( )
negroni . Use ( middlewares . NewCircuitBreaker ( backends [ frontend . Backend ] , cbreaker . Logger ( oxyLogger ) ) )
newRoute . Handler ( negroni )
2015-09-12 13:20:54 +02:00
err := newRoute . GetError ( )
if err != nil {
log . Error ( "Error building route " , err )
2015-09-11 16:37:13 +02:00
}
}
2015-09-19 13:02:59 +02:00
return router , nil
2015-09-11 16:37:13 +02:00
}
func Invoke ( any interface { } , name string , args ... interface { } ) [ ] reflect . Value {
inputs := make ( [ ] reflect . Value , len ( args ) )
2015-09-15 22:32:09 +02:00
for i := range args {
2015-09-11 16:37:13 +02:00
inputs [ i ] = reflect . ValueOf ( args [ i ] )
}
return reflect . ValueOf ( any ) . MethodByName ( name ) . Call ( inputs )
}
func LoadFileConfig ( file string ) * GlobalConfiguration {
configuration := NewGlobalConfiguration ( )
if _ , err := toml . DecodeFile ( file , configuration ) ; err != nil {
2015-09-11 19:25:49 +02:00
log . Fatal ( "Error reading file " , err )
2015-09-11 16:37:13 +02:00
}
log . Debug ( "Global configuration loaded %+v" , configuration )
return configuration
2015-09-17 16:29:44 +02:00
}