forked from Ivasoft/traefik
Compare commits
6 Commits
v1.0.alpha
...
v1.0.alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ded2682d2 | ||
|
|
4042938556 | ||
|
|
0e683cc535 | ||
|
|
4923da7f4d | ||
|
|
11781087ca | ||
|
|
3063251d43 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ traefik.toml
|
||||
*.test
|
||||
vendor/
|
||||
static/
|
||||
glide.lock
|
||||
@@ -16,13 +16,14 @@ It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/
|
||||
|
||||
## Features
|
||||
|
||||
- It's fast
|
||||
- No dependency hell, single binary made with go
|
||||
- Simple json Rest API
|
||||
- Simple TOML file configuration
|
||||
- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
|
||||
- Watchers for backends, can listen change in backends to apply a new configuration automatically
|
||||
- Hot-reloading of configuration. No need to restart the process
|
||||
- Graceful shutdown http connections during hot-reloads
|
||||
- Graceful shutdown http connections
|
||||
- Circuit breakers on backends
|
||||
- Round Robin, rebalancer load-balancers
|
||||
- Rest Metrics
|
||||
|
||||
1
cmd.go
1
cmd.go
@@ -126,7 +126,6 @@ func init() {
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Filename, "marathon.filename", "", "Override default configuration template. For advanced users :)")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Endpoint, "marathon.endpoint", "http://127.0.0.1:8080", "Marathon server endpoint. You can also specify multiple endpoint for Marathon")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.Domain, "marathon.domain", "", "Default domain used")
|
||||
traefikCmd.PersistentFlags().StringVar(&arguments.Marathon.NetworkInterface, "marathon.networkInterface", "eth0", "Network interface used to call Marathon web services. Needed in case of multiple network interfaces")
|
||||
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.consul, "consul", false, "Enable Consul backend")
|
||||
traefikCmd.PersistentFlags().BoolVar(&arguments.Consul.Watch, "consul.watch", true, "Watch provider")
|
||||
|
||||
@@ -151,7 +151,6 @@ Flags:
|
||||
--marathon.domain string Default domain used
|
||||
--marathon.endpoint string Marathon server endpoint. You can also specify multiple endpoint for Marathon (default "http://127.0.0.1:8080")
|
||||
--marathon.filename string Override default configuration template. For advanced users :)
|
||||
--marathon.networkInterface string Network interface used to call Marathon web services. Needed in case of multiple network interfaces (default "eth0")
|
||||
--marathon.watch Watch provider (default true)
|
||||
--maxIdleConnsPerHost int If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used
|
||||
--providersThrottleDuration duration Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time. (default 2s)
|
||||
@@ -640,12 +639,6 @@ Træfɪk can be configured to use Marathon as a backend configuration:
|
||||
#
|
||||
endpoint = "http://127.0.0.1:8080"
|
||||
|
||||
# Network interface used to call Marathon web services. Needed in case of multiple network interfaces.
|
||||
# Optional
|
||||
# Default: "eth0"
|
||||
#
|
||||
networkInterface = "eth0"
|
||||
|
||||
# Enable watch Marathon changes
|
||||
#
|
||||
# Optional
|
||||
|
||||
40
middlewares/handlerSwitcher.go
Normal file
40
middlewares/handlerSwitcher.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HandlerSwitcher allows hot switching of http.ServeMux
|
||||
type HandlerSwitcher struct {
|
||||
handler *mux.Router
|
||||
handlerLock *sync.Mutex
|
||||
}
|
||||
|
||||
// NewHandlerSwitcher builds a new instance of HandlerSwitcher
|
||||
func NewHandlerSwitcher(newHandler *mux.Router) (hs *HandlerSwitcher) {
|
||||
return &HandlerSwitcher{
|
||||
handler: newHandler,
|
||||
handlerLock: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (hs *HandlerSwitcher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
hs.handlerLock.Lock()
|
||||
handlerBackup := hs.handler
|
||||
hs.handlerLock.Unlock()
|
||||
handlerBackup.ServeHTTP(rw, r)
|
||||
}
|
||||
|
||||
// GetHandler returns the current http.ServeMux
|
||||
func (hs *HandlerSwitcher) GetHandler() (newHandler *mux.Router) {
|
||||
return hs.handler
|
||||
}
|
||||
|
||||
// UpdateHandler safely updates the current http.ServeMux with a new one
|
||||
func (hs *HandlerSwitcher) UpdateHandler(newHandler *mux.Router) {
|
||||
hs.handlerLock.Lock()
|
||||
hs.handler = newHandler
|
||||
defer hs.handlerLock.Unlock()
|
||||
}
|
||||
@@ -17,13 +17,12 @@ import (
|
||||
|
||||
// Marathon holds configuration of the Marathon provider.
|
||||
type Marathon struct {
|
||||
BaseProvider `mapstructure:",squash"`
|
||||
Endpoint string
|
||||
Domain string
|
||||
NetworkInterface string
|
||||
Basic *MarathonBasic
|
||||
TLS *tls.Config
|
||||
marathonClient marathon.Marathon
|
||||
BaseProvider `mapstructure:",squash"`
|
||||
Endpoint string
|
||||
Domain string
|
||||
Basic *MarathonBasic
|
||||
TLS *tls.Config
|
||||
marathonClient marathon.Marathon
|
||||
}
|
||||
|
||||
// MarathonBasic holds basic authentication specific configurations
|
||||
@@ -42,7 +41,7 @@ type lightMarathonClient interface {
|
||||
func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage) error {
|
||||
config := marathon.NewDefaultConfig()
|
||||
config.URL = provider.Endpoint
|
||||
config.EventsInterface = provider.NetworkInterface
|
||||
config.EventsTransport = marathon.EventsTransportSSE
|
||||
if provider.Basic != nil {
|
||||
config.HTTPBasicAuthUser = provider.Basic.HTTPBasicAuthUser
|
||||
config.HTTPBasicPassword = provider.Basic.HTTPBasicPassword
|
||||
@@ -61,7 +60,7 @@ func (provider *Marathon) Provide(configurationChan chan<- types.ConfigMessage)
|
||||
update := make(marathon.EventsChannel, 5)
|
||||
if provider.Watch {
|
||||
if err := client.AddEventsListener(update, marathon.EVENTS_APPLICATIONS); err != nil {
|
||||
log.Errorf("Failed to register for subscriptions, %s", err)
|
||||
log.Errorf("Failed to register for events, %s", err)
|
||||
} else {
|
||||
go func() {
|
||||
for {
|
||||
|
||||
41
server.go
41
server.go
@@ -48,7 +48,7 @@ type Server struct {
|
||||
|
||||
type serverEntryPoint struct {
|
||||
httpServer *manners.GracefulServer
|
||||
httpRouter *mux.Router
|
||||
httpRouter *middlewares.HandlerSwitcher
|
||||
}
|
||||
|
||||
// NewServer returns an initialized Server.
|
||||
@@ -82,7 +82,7 @@ func (server *Server) Start() {
|
||||
// Stop stops the server
|
||||
func (server *Server) Stop() {
|
||||
for _, serverEntryPoint := range server.serverEntryPoints {
|
||||
serverEntryPoint.httpServer.Close()
|
||||
serverEntryPoint.httpServer.BlockingClose()
|
||||
}
|
||||
server.stopChan <- true
|
||||
}
|
||||
@@ -142,22 +142,23 @@ func (server *Server) listenConfigurations() {
|
||||
server.serverLock.Lock()
|
||||
for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints {
|
||||
currentServerEntryPoint := server.serverEntryPoints[newServerEntryPointName]
|
||||
server.currentConfigurations = newConfigurations
|
||||
currentServerEntryPoint.httpRouter = newServerEntryPoint.httpRouter
|
||||
oldServer := currentServerEntryPoint.httpServer
|
||||
newsrv, err := server.prepareServer(currentServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], oldServer, server.loggerMiddleware, metrics)
|
||||
if err != nil {
|
||||
log.Fatal("Error preparing server: ", err)
|
||||
}
|
||||
go server.startServer(newsrv, server.globalConfiguration)
|
||||
currentServerEntryPoint.httpServer = newsrv
|
||||
server.serverEntryPoints[newServerEntryPointName] = currentServerEntryPoint
|
||||
time.Sleep(1 * time.Second)
|
||||
if oldServer != nil {
|
||||
log.Info("Stopping old server")
|
||||
oldServer.Close()
|
||||
if currentServerEntryPoint.httpServer == nil {
|
||||
newsrv, err := server.prepareServer(newServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], nil, server.loggerMiddleware, metrics)
|
||||
if err != nil {
|
||||
log.Fatal("Error preparing server: ", err)
|
||||
}
|
||||
go server.startServer(newsrv, server.globalConfiguration)
|
||||
currentServerEntryPoint.httpServer = newsrv
|
||||
currentServerEntryPoint.httpRouter = newServerEntryPoint.httpRouter
|
||||
server.serverEntryPoints[newServerEntryPointName] = currentServerEntryPoint
|
||||
log.Infof("Created new Handler: %p", newServerEntryPoint.httpRouter.GetHandler())
|
||||
} else {
|
||||
handlerSwitcher := currentServerEntryPoint.httpRouter
|
||||
handlerSwitcher.UpdateHandler(newServerEntryPoint.httpRouter.GetHandler())
|
||||
log.Infof("Created new Handler: %p", newServerEntryPoint.httpRouter.GetHandler())
|
||||
}
|
||||
}
|
||||
server.currentConfigurations = newConfigurations
|
||||
server.serverLock.Unlock()
|
||||
} else {
|
||||
log.Error("Error loading new configuration, aborted ", err)
|
||||
@@ -264,7 +265,7 @@ func (server *Server) startServer(srv *manners.GracefulServer, globalConfigurati
|
||||
log.Info("Server stopped")
|
||||
}
|
||||
|
||||
func (server *Server) prepareServer(router *mux.Router, entryPoint *EntryPoint, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) {
|
||||
func (server *Server) prepareServer(router http.Handler, entryPoint *EntryPoint, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) {
|
||||
log.Info("Preparing server")
|
||||
// middlewares
|
||||
var negroni = negroni.New()
|
||||
@@ -303,7 +304,7 @@ func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration)
|
||||
for entryPointName := range globalConfiguration.EntryPoints {
|
||||
router := server.buildDefaultHTTPRouter()
|
||||
serverEntryPoints[entryPointName] = serverEntryPoint{
|
||||
httpRouter: router,
|
||||
httpRouter: middlewares.NewHandlerSwitcher(router),
|
||||
}
|
||||
}
|
||||
return serverEntryPoints
|
||||
@@ -332,7 +333,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||
if _, ok := serverEntryPoints[entryPointName]; !ok {
|
||||
return nil, errors.New("Undefined entrypoint: " + entryPointName)
|
||||
}
|
||||
newRoute := serverEntryPoints[entryPointName].httpRouter.NewRoute().Name(frontendName)
|
||||
newRoute := serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)
|
||||
for routeName, route := range frontend.Routes {
|
||||
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
||||
route, err := getRoute(newRoute, route.Rule, route.Value)
|
||||
@@ -443,7 +444,7 @@ func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *En
|
||||
if len(entryPoint.Redirect.EntryPoint) > 0 {
|
||||
regex = "^(?:https?:\\/\\/)?([\\da-z\\.-]+)(?::\\d+)(.*)$"
|
||||
if server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint] == nil {
|
||||
return nil, errors.New("Unkown entrypoint " + entryPoint.Redirect.EntryPoint)
|
||||
return nil, errors.New("Unknown entrypoint " + entryPoint.Redirect.EntryPoint)
|
||||
}
|
||||
protocol := "http"
|
||||
if server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].TLS != nil {
|
||||
|
||||
@@ -206,12 +206,6 @@
|
||||
#
|
||||
# endpoint = "http://127.0.0.1:8080"
|
||||
|
||||
# Network interface used to call Marathon web services. Needed in case of multiple network interfaces.
|
||||
# Optional
|
||||
# Default: "eth0"
|
||||
#
|
||||
# networkInterface = "eth0"
|
||||
|
||||
# Enable watch Marathon changes
|
||||
#
|
||||
# Optional
|
||||
|
||||
Reference in New Issue
Block a user