func prepareServer(router *mux.Router, globalConfiguration *GlobalConfiguration, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) { log.Info("Preparing server") // middlewares var negroni = negroni.New() for _, middleware := range middlewares { negroni.Use(middleware) } negroni.UseHandler(router) tlsConfig, err := createTLSConfig(globalConfiguration.Certificates) if err != nil { log.Fatalf("Error creating TLS config %s", err) return nil, err } if oldServer == nil { return manners.NewWithServer( &http.Server{ Addr: globalConfiguration.Port, Handler: negroni, TLSConfig: tlsConfig, }), nil } server, err := oldServer.HijackListener(&http.Server{ Addr: globalConfiguration.Port, Handler: negroni, TLSConfig: tlsConfig, }, tlsConfig) if err != nil { log.Fatalf("Error hijacking server %s", err) return nil, err } return server, nil }
func prepareServer(router *mux.Router, globalConfiguration *GlobalConfiguration, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) *manners.GracefulServer { log.Info("Preparing server") // middlewares var negroni = negroni.New() for _, middleware := range middlewares { negroni.Use(middleware) } negroni.UseHandler(router) if oldServer == nil { return manners.NewWithServer( &http.Server{ Addr: globalConfiguration.Port, Handler: negroni, }) } else { server, err := oldServer.HijackListener(&http.Server{ Addr: globalConfiguration.Port, Handler: negroni, }, nil) if err != nil { log.Fatalf("Error hijacking server %s", err) return nil } else { return server } } }
func (server *Server) loadEntryPointConfig(entryPointName string, entryPoint *EntryPoint) (http.Handler, error) { regex := entryPoint.Redirect.Regex replacement := entryPoint.Redirect.Replacement 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) } protocol := "http" if server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].TLS != nil { protocol = "https" } r, _ := regexp.Compile("(:\\d+)") match := r.FindStringSubmatch(server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address) if len(match) == 0 { return nil, errors.New("Bad Address format: " + server.globalConfiguration.EntryPoints[entryPoint.Redirect.EntryPoint].Address) } replacement = protocol + "://$1" + match[0] + "$2" } rewrite, err := middlewares.NewRewrite(regex, replacement, true) if err != nil { return nil, err } log.Debugf("Creating entryPoint redirect %s -> %s : %s -> %s", entryPointName, entryPoint.Redirect.EntryPoint, regex, replacement) negroni := negroni.New() negroni.Use(rewrite) return negroni, nil }
func (server *Server) prepareServer(router *mux.Router, entryPoint *EntryPoint, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) { log.Info("Preparing server") // middlewares var negroni = negroni.New() for _, middleware := range middlewares { negroni.Use(middleware) } negroni.UseHandler(router) tlsConfig, err := server.createTLSConfig(entryPoint.TLS) if err != nil { log.Fatalf("Error creating TLS config %s", err) return nil, err } if oldServer == nil { return manners.NewWithServer( &http.Server{ Addr: entryPoint.Address, Handler: negroni, TLSConfig: tlsConfig, }), nil } gracefulServer, err := oldServer.HijackListener(&http.Server{ Addr: entryPoint.Address, Handler: negroni, TLSConfig: tlsConfig, }, tlsConfig) if err != nil { log.Fatalf("Error hijacking server %s", err) return nil, err } return gracefulServer, nil }
// initNegroni initializes Negroni with all the middleware and starts it. func initNegroni(routes Routes, config ServiceConfig) (*RestServiceInfo, error) { var err error // Create negroni negroni := negroni.New() // Add content-negotiation middleware. // This is an example of using a middleware. // This will modify the response header to the // negotiated content type, and can then be used as // ct := w.Header().Get("Content-Type") // where w is http.ResponseWriter negroni.Use(NewNegotiator()) // Unmarshal data from the content-type format // into a map negroni.Use(NewUnmarshaller()) pubKeyLocation := config.Common.Api.AuthPublic if pubKeyLocation != "" { log.Infof("Reading public key from %s", pubKeyLocation) config.Common.PublicKey, err = ioutil.ReadFile(pubKeyLocation) } if err != nil { return nil, err } // We use the public key of root server to check the token. authMiddleware := AuthMiddleware{PublicKey: config.Common.PublicKey} negroni.Use(authMiddleware) timeoutMillis := getTimeoutMillis(config.Common) var dur time.Duration var readWriteDur time.Duration timeoutStr := fmt.Sprintf("%dms", timeoutMillis) dur, _ = time.ParseDuration(timeoutStr) timeoutStr = fmt.Sprintf("%dms", timeoutMillis+ReadWriteTimeoutDelta) readWriteDur, _ = time.ParseDuration(timeoutStr) router := newRouter(routes) timeoutHandler := http.TimeoutHandler(router, dur, TimeoutMessage) negroni.UseHandler(timeoutHandler) hostPort := config.Common.Api.GetHostPort() svcInfo, err := RunNegroni(negroni, hostPort, readWriteDur) return svcInfo, err }
func main() { c := cors.New(cors.Options{ AllowedOrigins: []string{"http://localhost:5000"}, }) fmt.Printf("milestone api activated on port :4000\n\r") services.SetupRabbitMq() router := routes.InitRoutes() negroni := negroni.Classic() negroni.Use(c) negroni.UseHandler(router) negroni.Run(":4400") //http.ListenAndServe(":4400", router) }
func LoadConfig(configuration *Configuration, gloablConfiguration *GlobalConfiguration) (*mux.Router, error) { router := mux.NewRouter() router.NotFoundHandler = http.HandlerFunc(notFoundHandler) backends := map[string]http.Handler{} for frontendName, frontend := range configuration.Frontends { log.Debug("Creating frontend %s", frontendName) fwd, _ := forward.New(forward.Logger(oxyLogger)) newRoute := router.NewRoute().Name(frontendName) for routeName, route := range frontend.Routes { log.Debug("Creating route %s %s:%s", routeName, route.Rule, route.Value) newRouteReflect := Invoke(newRoute, route.Rule, route.Value) newRoute = newRouteReflect[0].Interface().(*mux.Route) } if backends[frontend.Backend] == nil { log.Debug("Creating backend %s", frontend.Backend) lb, _ := roundrobin.New(fwd) rb, _ := roundrobin.NewRebalancer(lb, roundrobin.RebalancerLogger(oxyLogger)) for serverName, server := range configuration.Backends[frontend.Backend].Servers { 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)) } } backends[frontend.Backend] = rb } else { log.Debug("Reusing backend %s", frontend.Backend) } // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(gloablConfiguration.Replay)), stream.Logger(oxyLogger)) var negroni = negroni.New() negroni.Use(middlewares.NewCircuitBreaker(backends[frontend.Backend], cbreaker.Logger(oxyLogger))) newRoute.Handler(negroni) err := newRoute.GetError() if err != nil { log.Error("Error building route ", err) } } return router, nil }
// LoadConfig returns a new gorrilla.mux Route from the specified global configuration and the dynamic // provider configurations. func LoadConfig(configurations configs, globalConfiguration *GlobalConfiguration) (*mux.Router, error) { router := mux.NewRouter() router.NotFoundHandler = http.HandlerFunc(notFoundHandler) backends := map[string]http.Handler{} for _, configuration := range configurations { for frontendName, frontend := range configuration.Frontends { log.Debugf("Creating frontend %s", frontendName) fwd, _ := forward.New(forward.Logger(oxyLogger), forward.PassHostHeader(frontend.PassHostHeader)) newRoute := router.NewRoute().Name(frontendName) for routeName, route := range frontend.Routes { log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value) newRouteReflect, err := invoke(newRoute, route.Rule, route.Value) if err != nil { return nil, err } newRoute = newRouteReflect[0].Interface().(*mux.Route) } if backends[frontend.Backend] == nil { log.Debugf("Creating backend %s", frontend.Backend) var lb http.Handler rr, _ := roundrobin.New(fwd) if configuration.Backends[frontend.Backend] == nil { return nil, errors.New("Backend not found: " + frontend.Backend) } lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { configuration.Backends[frontend.Backend].LoadBalancer = &types.LoadBalancer{Method: "wrr"} } switch lbMethod { case types.Drr: log.Infof("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) lb = rebalancer for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Infof("Creating server %s %s", serverName, url.String()) rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)) } case types.Wrr: log.Infof("Creating load-balancer wrr") lb = middlewares.NewWebsocketUpgrader(rr) for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Infof("Creating server %s %s", serverName, url.String()) rr.UpsertServer(url, roundrobin.Weight(server.Weight)) } } var negroni = negroni.New() if configuration.Backends[frontend.Backend].CircuitBreaker != nil { log.Infof("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger))) } else { negroni.UseHandler(lb) } backends[frontend.Backend] = negroni } else { log.Infof("Reusing backend %s", frontend.Backend) } // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(globalConfiguration.Replay)), stream.Logger(oxyLogger)) newRoute.Handler(backends[frontend.Backend]) err := newRoute.GetError() if err != nil { log.Errorf("Error building route: %s", err) } } } return router, nil }
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic // provider configurations. func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]serverEntryPoint, error) { serverEntryPoints := server.buildEntryPoints(globalConfiguration) redirectHandlers := make(map[string]http.Handler) backends := map[string]http.Handler{} for _, configuration := range configurations { frontendNames := sortedFrontendNamesForConfig(configuration) for _, frontendName := range frontendNames { frontend := configuration.Frontends[frontendName] log.Debugf("Creating frontend %s", frontendName) fwd, _ := forward.New(forward.Logger(oxyLogger), forward.PassHostHeader(frontend.PassHostHeader)) // default endpoints if not defined in frontends if len(frontend.EntryPoints) == 0 { frontend.EntryPoints = globalConfiguration.DefaultEntryPoints } for _, entryPointName := range frontend.EntryPoints { log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName) if _, ok := serverEntryPoints[entryPointName]; !ok { return nil, errors.New("Undefined entrypoint: " + entryPointName) } newRoute := serverEntryPoints[entryPointName].httpRouter.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) if err != nil { return nil, err } newRoute = route } entryPoint := globalConfiguration.EntryPoints[entryPointName] if entryPoint.Redirect != nil { if redirectHandlers[entryPointName] != nil { newRoute.Handler(redirectHandlers[entryPointName]) } else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil { return nil, err } else { newRoute.Handler(handler) redirectHandlers[entryPointName] = handler } } else { if backends[frontend.Backend] == nil { log.Debugf("Creating backend %s", frontend.Backend) var lb http.Handler rr, _ := roundrobin.New(fwd) if configuration.Backends[frontend.Backend] == nil { return nil, errors.New("Undefined backend: " + frontend.Backend) } lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { configuration.Backends[frontend.Backend].LoadBalancer = &types.LoadBalancer{Method: "wrr"} } switch lbMethod { case types.Drr: log.Debugf("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) lb = rebalancer for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)) } case types.Wrr: log.Debugf("Creating load-balancer wrr") lb = middlewares.NewWebsocketUpgrader(rr) for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) rr.UpsertServer(url, roundrobin.Weight(server.Weight)) } } var negroni = negroni.New() if configuration.Backends[frontend.Backend].CircuitBreaker != nil { log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger))) } else { negroni.UseHandler(lb) } backends[frontend.Backend] = negroni } else { log.Debugf("Reusing backend %s", frontend.Backend) } server.wireFrontendBackend(frontend.Routes, newRoute, backends[frontend.Backend]) } err := newRoute.GetError() if err != nil { log.Errorf("Error building route: %s", err) } } } } return serverEntryPoints, nil }
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic // provider configurations. func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]*serverEntryPoint, error) { serverEntryPoints := server.buildEntryPoints(globalConfiguration) redirectHandlers := make(map[string]http.Handler) backends := map[string]http.Handler{} backend2FrontendMap := map[string]string{} for _, configuration := range configurations { frontendNames := sortedFrontendNamesForConfig(configuration) frontend: for _, frontendName := range frontendNames { frontend := configuration.Frontends[frontendName] log.Debugf("Creating frontend %s", frontendName) fwd, err := forward.New(forward.Logger(oxyLogger), forward.PassHostHeader(frontend.PassHostHeader)) if err != nil { log.Errorf("Error creating forwarder for frontend %s: %v", frontendName, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } saveBackend := middlewares.NewSaveBackend(fwd) if len(frontend.EntryPoints) == 0 { log.Errorf("No entrypoint defined for frontend %s, defaultEntryPoints:%s", frontendName, globalConfiguration.DefaultEntryPoints) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } for _, entryPointName := range frontend.EntryPoints { log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName) if _, ok := serverEntryPoints[entryPointName]; !ok { log.Errorf("Undefined entrypoint '%s' for frontend %s", entryPointName, frontendName) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)} for routeName, route := range frontend.Routes { err := getRoute(newServerRoute, &route) if err != nil { log.Errorf("Error creating route for frontend %s: %v", frontendName, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } log.Debugf("Creating route %s %s", routeName, route.Rule) } entryPoint := globalConfiguration.EntryPoints[entryPointName] if entryPoint.Redirect != nil { if redirectHandlers[entryPointName] != nil { newServerRoute.route.Handler(redirectHandlers[entryPointName]) } else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil { log.Errorf("Error loading entrypoint configuration for frontend %s: %v", frontendName, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } else { newServerRoute.route.Handler(handler) redirectHandlers[entryPointName] = handler } } else { if backends[frontend.Backend] == nil { log.Debugf("Creating backend %s", frontend.Backend) var lb http.Handler rr, _ := roundrobin.New(saveBackend) if configuration.Backends[frontend.Backend] == nil { log.Errorf("Undefined backend '%s' for frontend %s", frontend.Backend, frontendName) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { log.Errorf("Error loading load balancer method '%+v' for frontend %s: %v", configuration.Backends[frontend.Backend].LoadBalancer, frontendName, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } stickysession := configuration.Backends[frontend.Backend].LoadBalancer.Sticky cookiename := "_TRAEFIK_BACKEND" var sticky *roundrobin.StickySession if stickysession { sticky = roundrobin.NewStickySession(cookiename) } switch lbMethod { case types.Drr: log.Debugf("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) if stickysession { log.Debugf("Sticky session with cookie %v", cookiename) rebalancer, _ = roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger), roundrobin.RebalancerStickySession(sticky)) } lb = rebalancer for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { log.Errorf("Error parsing server URL %s: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } backend2FrontendMap[url.String()] = frontendName log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) if err := rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } } case types.Wrr: log.Debugf("Creating load-balancer wrr") if stickysession { log.Debugf("Sticky session with cookie %v", cookiename) rr, _ = roundrobin.New(saveBackend, roundrobin.EnableStickySession(sticky)) } lb = rr for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { log.Errorf("Error parsing server URL %s: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } backend2FrontendMap[url.String()] = frontendName log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) if err := rr.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { log.Errorf("Error adding server %s to load balancer: %v", server.URL, err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } } } maxConns := configuration.Backends[frontend.Backend].MaxConn if maxConns != nil && maxConns.Amount != 0 { extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc) if err != nil { log.Errorf("Error creating connlimit: %v", err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } log.Debugf("Creating loadd-balancer connlimit") lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger)) if err != nil { log.Errorf("Error creating connlimit: %v", err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } } // retry ? if globalConfiguration.Retry != nil { retries := len(configuration.Backends[frontend.Backend].Servers) if globalConfiguration.Retry.Attempts > 0 { retries = globalConfiguration.Retry.Attempts } lb = middlewares.NewRetry(retries, lb) log.Debugf("Creating retries max attempts %d", retries) } var negroni = negroni.New() if configuration.Backends[frontend.Backend].CircuitBreaker != nil { log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) cbreaker, err := middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger)) if err != nil { log.Errorf("Error creating circuit breaker: %v", err) log.Errorf("Skipping frontend %s...", frontendName) continue frontend } negroni.Use(cbreaker) } else { negroni.UseHandler(lb) } backends[frontend.Backend] = negroni } else { log.Debugf("Reusing backend %s", frontend.Backend) } if frontend.Priority > 0 { newServerRoute.route.Priority(frontend.Priority) } server.wireFrontendBackend(newServerRoute, backends[frontend.Backend]) } err := newServerRoute.route.GetError() if err != nil { log.Errorf("Error building route: %s", err) } } } } middlewares.SetBackend2FrontendMap(&backend2FrontendMap) //sort routes for _, serverEntryPoint := range serverEntryPoints { serverEntryPoint.httpRouter.GetHandler().SortRoutes() } return serverEntryPoints, nil }
func main() { kingpin.Parse() fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) var srv *graceful.Server var configurationRouter *mux.Router var configurationChan = make(chan *Configuration, 10) defer close(configurationChan) 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 gloablConfiguration := LoadFileConfig(*globalConfigFile) loggerMiddleware := middlewares.NewLogger(gloablConfiguration.AccessLogsFile) defer loggerMiddleware.Close() // logging backends := []logging.Backend{} level, err := logging.LogLevel(gloablConfiguration.LogLevel) if err != nil { log.Fatal("Error getting level", err) } if len(gloablConfiguration.TraefikLogsFile) > 0 { fi, err := os.OpenFile(gloablConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) defer fi.Close() if err != nil { log.Fatal("Error opening file", err) } else { logBackend := logging.NewLogBackend(fi, "", 0) logBackendFormatter := logging.NewBackendFormatter(logBackend, logging.GlogFormatter) backends = append(backends, logBackendFormatter) } } if gloablConfiguration.TraefikLogsStdout { logBackend := logging.NewLogBackend(os.Stdout, "", 0) logBackendFormatter := logging.NewBackendFormatter(logBackend, format) backends = append(backends, logBackendFormatter) } logging.SetBackend(backends...) logging.SetLevel(level, "traefik") 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") } else if reflect.DeepEqual(currentConfiguration, configuration) { log.Info("Skipping same configuration") } else { 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) } } } }() // configure providers if gloablConfiguration.Docker != nil { providers = append(providers, gloablConfiguration.Docker) } if gloablConfiguration.Marathon != nil { providers = append(providers, gloablConfiguration.Marathon) } if gloablConfiguration.File != nil { if len(gloablConfiguration.File.Filename) == 0 { // no filename, setting to global config file gloablConfiguration.File.Filename = *globalConfigFile } providers = append(providers, gloablConfiguration.File) } if gloablConfiguration.Web != nil { providers = append(providers, gloablConfiguration.Web) } if gloablConfiguration.Consul != nil { providers = append(providers, gloablConfiguration.Consul) } // 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 } // middlewares var negroni = negroni.New() negroni.Use(metrics) negroni.Use(loggerMiddleware) //negroni.Use(middlewares.NewCircuitBreaker(oxyLogger)) //negroni.Use(middlewares.NewRoutes(configurationRouter)) negroni.UseHandler(configurationRouter) srv = &graceful.Server{ Timeout: time.Duration(gloablConfiguration.GraceTimeOut) * time.Second, NoSignalHandling: true, Server: &http.Server{ Addr: gloablConfiguration.Port, Handler: negroni, }, } go func() { if len(gloablConfiguration.CertFile) > 0 && len(gloablConfiguration.KeyFile) > 0 { srv.ListenAndServeTLS(gloablConfiguration.CertFile, gloablConfiguration.KeyFile) } else { srv.ListenAndServe() } }() log.Notice("Started") <-srv.StopChan() log.Notice("Stopped") } }
// Provide allows the provider to provide configurations to traefik // using the given configuration channel. func (provider *WebProvider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, _ types.Constraints) error { systemRouter := mux.NewRouter() // health route systemRouter.Methods("GET").Path("/health").HandlerFunc(provider.getHealthHandler) // ping route systemRouter.Methods("GET").Path("/ping").HandlerFunc(provider.getPingHandler) // API routes systemRouter.Methods("GET").Path("/api").HandlerFunc(provider.getConfigHandler) systemRouter.Methods("GET").Path("/api/version").HandlerFunc(provider.getVersionHandler) systemRouter.Methods("GET").Path("/api/providers").HandlerFunc(provider.getConfigHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}").HandlerFunc(provider.getProviderHandler) systemRouter.Methods("PUT").Path("/api/providers/{provider}").HandlerFunc(func(response http.ResponseWriter, request *http.Request) { if provider.ReadOnly { response.WriteHeader(http.StatusForbidden) fmt.Fprintf(response, "REST API is in read-only mode") return } vars := mux.Vars(request) if vars["provider"] != "web" { response.WriteHeader(http.StatusBadRequest) fmt.Fprintf(response, "Only 'web' provider can be updated through the REST API") return } configuration := new(types.Configuration) body, _ := ioutil.ReadAll(request.Body) err := json.Unmarshal(body, configuration) if err == nil { configurationChan <- types.ConfigMessage{ProviderName: "web", Configuration: configuration} provider.getConfigHandler(response, request) } else { log.Errorf("Error parsing configuration %+v", err) http.Error(response, fmt.Sprintf("%+v", err), http.StatusBadRequest) } }) systemRouter.Methods("GET").Path("/api/providers/{provider}/backends").HandlerFunc(provider.getBackendsHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}").HandlerFunc(provider.getBackendHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers").HandlerFunc(provider.getServersHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/backends/{backend}/servers/{server}").HandlerFunc(provider.getServerHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends").HandlerFunc(provider.getFrontendsHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}").HandlerFunc(provider.getFrontendHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}/routes").HandlerFunc(provider.getRoutesHandler) systemRouter.Methods("GET").Path("/api/providers/{provider}/frontends/{frontend}/routes/{route}").HandlerFunc(provider.getRouteHandler) // Expose dashboard systemRouter.Methods("GET").Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) { http.Redirect(response, request, "/dashboard/", 302) }) systemRouter.Methods("GET").PathPrefix("/dashboard/").Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: autogen.Asset, AssetInfo: autogen.AssetInfo, AssetDir: autogen.AssetDir, Prefix: "static"}))) // expvars if provider.server.globalConfiguration.Debug { systemRouter.Methods("GET").Path("/debug/vars").HandlerFunc(expvarHandler) } go func() { var err error var negroni = negroni.New() if provider.Auth != nil { authMiddleware, err := middlewares.NewAuthenticator(provider.Auth) if err != nil { log.Fatal("Error creating Auth: ", err) } negroni.Use(authMiddleware) } negroni.UseHandler(systemRouter) if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 { err = http.ListenAndServeTLS(provider.Address, provider.CertFile, provider.KeyFile, negroni) } else { err = http.ListenAndServe(provider.Address, negroni) } if err != nil { log.Fatal("Error creating server: ", err) } }() return nil }
// InitializeService initializes the service with the // provided config and starts it. The channel returned // allows the calller to wait for a message from the running // service. Messages are of type ServiceMessage above. // It can be used for launching service from tests, etc. func InitializeService(service Service, config ServiceConfig) (chan ServiceMessage, string, error) { err := service.SetConfig(config) if err != nil { return nil, "", err } err = service.Initialize() if err != nil { return nil, "", err } // Create negroni negroni := negroni.New() // Add authentication middleware negroni.Use(NewAuth()) // Add content-negotiation middleware. // This is an example of using a middleware. // This will modify the response header to the // negotiated content type, and can then be used as // ct := w.Header().Get("Content-Type") // where w is http.ResponseWriter negroni.Use(NewNegotiator()) // Unmarshal data from the content-type format // into a map negroni.Use(NewUnmarshaller()) routes := service.Routes() router := newRouter(routes) timeoutMillis := config.Common.Api.RestTimeoutMillis var dur time.Duration var readWriteDur time.Duration if timeoutMillis <= 0 { timeoutMillis = DefaultRestTimeout dur = DefaultRestTimeout * time.Millisecond readWriteDur = (DefaultRestTimeout + ReadWriteTimeoutDelta) * time.Millisecond log.Printf("%s: Invalid timeout %d, defaulting to %d\n", service.Name(), timeoutMillis, dur) } else { timeoutStr := fmt.Sprintf("%dms", timeoutMillis) dur, _ = time.ParseDuration(timeoutStr) timeoutStr = fmt.Sprintf("%dms", timeoutMillis+ReadWriteTimeoutDelta) readWriteDur, _ = time.ParseDuration(timeoutStr) } log.Printf("%s: Creating TimeoutHandler with %v\n", service.Name(), dur) timeoutHandler := http.TimeoutHandler(router, dur, TimeoutMessage) negroni.UseHandler(timeoutHandler) hostPort := config.Common.Api.GetHostPort() log.Println("About to start...") ch, addr, err := RunNegroni(negroni, hostPort, readWriteDur) if err == nil { if addr != hostPort { log.Printf("Requested address %s, real %s\n", hostPort, addr) idx := strings.LastIndex(addr, ":") config.Common.Api.Host = addr[0:idx] port, _ := strconv.Atoi(addr[idx+1:]) port64 := uint64(port) config.Common.Api.Port = port64 // Also register this with root service url := fmt.Sprintf("%s/config/%s/port", config.Common.Api.RootServiceUrl, service.Name()) result := make(map[string]interface{}) portMsg := PortUpdateMessage{Port: port64} client, err := NewRestClient("", timeoutMillis) if err != nil { return ch, addr, err } err = client.Post(url, portMsg, &result) } } return ch, addr, err }
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic // provider configurations. func (server *Server) loadConfig(configurations configs, globalConfiguration GlobalConfiguration) (map[string]*serverEntryPoint, error) { serverEntryPoints := server.buildEntryPoints(globalConfiguration) redirectHandlers := make(map[string]http.Handler) backends := map[string]http.Handler{} for _, configuration := range configurations { frontendNames := sortedFrontendNamesForConfig(configuration) for _, frontendName := range frontendNames { frontend := configuration.Frontends[frontendName] log.Debugf("Creating frontend %s", frontendName) fwd, _ := forward.New(forward.Logger(oxyLogger), forward.PassHostHeader(frontend.PassHostHeader)) // default endpoints if not defined in frontends if len(frontend.EntryPoints) == 0 { frontend.EntryPoints = globalConfiguration.DefaultEntryPoints } if len(frontend.EntryPoints) == 0 { log.Errorf("No entrypoint defined for frontend %s, defaultEntryPoints:%s. Skipping it", frontendName, globalConfiguration.DefaultEntryPoints) continue } for _, entryPointName := range frontend.EntryPoints { log.Debugf("Wiring frontend %s to entryPoint %s", frontendName, entryPointName) if _, ok := serverEntryPoints[entryPointName]; !ok { return nil, errors.New("Undefined entrypoint: " + entryPointName) } newServerRoute := &serverRoute{route: serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)} for routeName, route := range frontend.Routes { err := getRoute(newServerRoute, &route) if err != nil { return nil, err } log.Debugf("Creating route %s %s", routeName, route.Rule) } entryPoint := globalConfiguration.EntryPoints[entryPointName] if entryPoint.Redirect != nil { if redirectHandlers[entryPointName] != nil { newServerRoute.route.Handler(redirectHandlers[entryPointName]) } else if handler, err := server.loadEntryPointConfig(entryPointName, entryPoint); err != nil { return nil, err } else { newServerRoute.route.Handler(handler) redirectHandlers[entryPointName] = handler } } else { if backends[frontend.Backend] == nil { log.Debugf("Creating backend %s", frontend.Backend) var lb http.Handler rr, _ := roundrobin.New(fwd) if configuration.Backends[frontend.Backend] == nil { return nil, errors.New("Undefined backend: " + frontend.Backend) } lbMethod, err := types.NewLoadBalancerMethod(configuration.Backends[frontend.Backend].LoadBalancer) if err != nil { configuration.Backends[frontend.Backend].LoadBalancer = &types.LoadBalancer{Method: "wrr"} } switch lbMethod { case types.Drr: log.Debugf("Creating load-balancer drr") rebalancer, _ := roundrobin.NewRebalancer(rr, roundrobin.RebalancerLogger(oxyLogger)) lb = rebalancer for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) if err := rebalancer.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { return nil, err } } case types.Wrr: log.Debugf("Creating load-balancer wrr") lb = rr for serverName, server := range configuration.Backends[frontend.Backend].Servers { url, err := url.Parse(server.URL) if err != nil { return nil, err } log.Debugf("Creating server %s at %s with weight %d", serverName, url.String(), server.Weight) if err := rr.UpsertServer(url, roundrobin.Weight(server.Weight)); err != nil { return nil, err } } } maxConns := configuration.Backends[frontend.Backend].MaxConn if maxConns != nil && maxConns.Amount != 0 { extractFunc, err := utils.NewExtractor(maxConns.ExtractorFunc) if err != nil { return nil, err } log.Debugf("Creating loadd-balancer connlimit") lb, err = connlimit.New(lb, extractFunc, maxConns.Amount, connlimit.Logger(oxyLogger)) if err != nil { return nil, err } } // retry ? if globalConfiguration.Retry != nil { retries := len(configuration.Backends[frontend.Backend].Servers) if globalConfiguration.Retry.Attempts > 0 { retries = globalConfiguration.Retry.Attempts } maxMem := int64(2 * 1024 * 1024) if globalConfiguration.Retry.MaxMem > 0 { maxMem = globalConfiguration.Retry.MaxMem } lb, err = stream.New(lb, stream.Logger(oxyLogger), stream.Retry("IsNetworkError() && Attempts() < "+strconv.Itoa(retries)), stream.MemRequestBodyBytes(maxMem), stream.MaxRequestBodyBytes(maxMem), stream.MemResponseBodyBytes(maxMem), stream.MaxResponseBodyBytes(maxMem)) log.Debugf("Creating retries max attempts %d", retries) if err != nil { return nil, err } } var negroni = negroni.New() if configuration.Backends[frontend.Backend].CircuitBreaker != nil { log.Debugf("Creating circuit breaker %s", configuration.Backends[frontend.Backend].CircuitBreaker.Expression) negroni.Use(middlewares.NewCircuitBreaker(lb, configuration.Backends[frontend.Backend].CircuitBreaker.Expression, cbreaker.Logger(oxyLogger))) } else { negroni.UseHandler(lb) } backends[frontend.Backend] = negroni } else { log.Debugf("Reusing backend %s", frontend.Backend) } server.wireFrontendBackend(newServerRoute, backends[frontend.Backend]) } err := newServerRoute.route.GetError() if err != nil { log.Errorf("Error building route: %s", err) } } } } return serverEntryPoints, nil }
func main() { kingpin.Parse() fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) var srv *graceful.Server var configurationRouter *mux.Router var configurationChan = make(chan *Configuration, 10) defer close(configurationChan) var providers = []Provider{} var sigs = make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) // load global configuration gloablConfiguration := LoadFileConfig(*globalConfigFile) loggerMiddleware := middlewares.NewLogger(gloablConfiguration.AccessLogsFile) defer loggerMiddleware.Close() // logging level, err := log.ParseLevel(strings.ToLower(gloablConfiguration.LogLevel)) if err != nil { log.Fatal("Error getting level", err) } log.SetLevel(level) if len(gloablConfiguration.TraefikLogsFile) > 0 { fi, err := os.OpenFile(gloablConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) defer fi.Close() if err != nil { log.Fatal("Error opening file", err) } else { log.SetOutput(fi) log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true}) } } else { log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) } configurationRouter = LoadDefaultConfig(gloablConfiguration) // listen new configurations from providers go func() { for { configuration := <-configurationChan log.Infof("Configuration receveived %+v", configuration) if configuration == nil { log.Info("Skipping empty configuration") } else if reflect.DeepEqual(currentConfiguration, configuration) { log.Info("Skipping same configuration") } else { 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) } } } }() // configure providers if gloablConfiguration.Docker != nil { providers = append(providers, gloablConfiguration.Docker) } if gloablConfiguration.Marathon != nil { providers = append(providers, gloablConfiguration.Marathon) } if gloablConfiguration.File != nil { if len(gloablConfiguration.File.Filename) == 0 { // no filename, setting to global config file gloablConfiguration.File.Filename = *globalConfigFile } providers = append(providers, gloablConfiguration.File) } if gloablConfiguration.Web != nil { providers = append(providers, gloablConfiguration.Web) } if gloablConfiguration.Consul != nil { providers = append(providers, gloablConfiguration.Consul) } // start providers for _, provider := range providers { log.Infof("Starting provider %v %+v", reflect.TypeOf(provider), provider) currentProvider := provider go func() { currentProvider.Provide(configurationChan) }() } goAway := false go func() { sig := <-sigs log.Infof("I have to go... %+v", sig) goAway = true srv.Stop(time.Duration(gloablConfiguration.GraceTimeOut) * time.Second) }() for { if goAway { break } // middlewares var negroni = negroni.New() negroni.Use(metrics) negroni.Use(loggerMiddleware) //negroni.Use(middlewares.NewCircuitBreaker(oxyLogger)) //negroni.Use(middlewares.NewRoutes(configurationRouter)) negroni.UseHandler(configurationRouter) srv = &graceful.Server{ Timeout: time.Duration(gloablConfiguration.GraceTimeOut) * time.Second, NoSignalHandling: true, Server: &http.Server{ Addr: gloablConfiguration.Port, Handler: negroni, }, } go func() { if len(gloablConfiguration.CertFile) > 0 && len(gloablConfiguration.KeyFile) > 0 { err := srv.ListenAndServeTLS(gloablConfiguration.CertFile, gloablConfiguration.KeyFile) if err != nil { netOpError, ok := err.(*net.OpError) if ok && netOpError.Err.Error() != "use of closed network connection" { log.Fatal("Error creating server: ", err) } } } else { err := srv.ListenAndServe() if err != nil { netOpError, ok := err.(*net.OpError) if ok && netOpError.Err.Error() != "use of closed network connection" { log.Fatal("Error creating server: ", err) } } } }() log.Info("Started") <-srv.StopChan() log.Info("Stopped") } }