traefik/cmd/traefik/traefik.go

297 lines
8.8 KiB
Go
Raw Normal View History

2015-09-11 14:37:13 +00:00
package main
import (
2018-03-14 12:14:03 +00:00
"context"
2016-05-03 14:52:14 +00:00
"encoding/json"
"fmt"
stdlog "log"
2017-08-21 21:18:02 +00:00
"net/http"
"os"
2017-04-27 15:36:04 +00:00
"path/filepath"
"strings"
"time"
"github.com/containous/traefik/autogen/genstatic"
2018-03-01 07:10:04 +00:00
"github.com/containous/traefik/cmd"
"github.com/containous/traefik/cmd/healthcheck"
cmdVersion "github.com/containous/traefik/cmd/version"
"github.com/containous/traefik/pkg/cli"
2019-03-15 08:42:03 +00:00
"github.com/containous/traefik/pkg/collector"
"github.com/containous/traefik/pkg/config"
"github.com/containous/traefik/pkg/config/static"
"github.com/containous/traefik/pkg/log"
"github.com/containous/traefik/pkg/provider/aggregator"
"github.com/containous/traefik/pkg/safe"
"github.com/containous/traefik/pkg/server"
"github.com/containous/traefik/pkg/server/router"
traefiktls "github.com/containous/traefik/pkg/tls"
"github.com/containous/traefik/pkg/version"
"github.com/coreos/go-systemd/daemon"
2019-02-18 06:52:03 +00:00
assetfs "github.com/elazarl/go-bindata-assetfs"
2018-01-22 11:16:03 +00:00
"github.com/sirupsen/logrus"
2018-04-11 14:30:04 +00:00
"github.com/vulcand/oxy/roundrobin"
2015-09-11 23:55:10 +00:00
)
2015-09-11 14:37:13 +00:00
func init() {
goDebug := os.Getenv("GODEBUG")
if len(goDebug) > 0 {
goDebug += ","
}
os.Setenv("GODEBUG", goDebug+"tls13=1")
}
2015-09-11 14:37:13 +00:00
func main() {
2018-02-19 00:04:45 +00:00
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}}
2018-02-19 00:04:45 +00:00
cmdTraefik := &cli.Command{
2016-05-03 14:52:14 +00:00
Name: "traefik",
2019-04-05 10:44:03 +00:00
Description: `Traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
2016-05-03 14:52:14 +00:00
Complete documentation is available at https://traefik.io`,
Configuration: tConfig,
Resources: loaders,
Run: func(_ []string) error {
return runCmd(&tConfig.Configuration, cli.GetConfigFile(loaders))
2016-05-03 14:52:14 +00:00
},
}
err := cmdTraefik.AddCommand(healthcheck.NewCmd(&tConfig.Configuration, loaders))
2016-07-29 15:36:53 +00:00
if err != nil {
stdlog.Println(err)
2018-02-19 00:05:39 +00:00
os.Exit(1)
2016-07-29 15:36:53 +00:00
}
err = cmdTraefik.AddCommand(cmdVersion.NewCmd())
2016-06-24 07:58:42 +00:00
if err != nil {
stdlog.Println(err)
2018-02-19 00:05:39 +00:00
os.Exit(1)
2016-06-24 07:58:42 +00:00
}
err = cli.Execute(cmdTraefik)
if err != nil {
stdlog.Println(err)
2018-02-19 00:05:39 +00:00
os.Exit(1)
2015-11-23 14:41:16 +00:00
}
2016-05-03 14:52:14 +00:00
2016-01-13 21:46:44 +00:00
os.Exit(0)
2015-09-11 14:37:13 +00:00
}
2016-05-03 14:52:14 +00:00
func runCmd(staticConfiguration *static.Configuration, configFile string) error {
configureLogging(staticConfiguration)
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
2018-08-06 18:00:03 +00:00
if err := roundrobin.SetDefaultWeight(0); err != nil {
log.WithoutContext().Errorf("Could not set roundrobin default weight: %v", err)
2018-08-06 18:00:03 +00:00
}
2018-04-11 14:30:04 +00:00
staticConfiguration.SetEffectiveConfiguration(configFile)
staticConfiguration.ValidateConfiguration()
log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
2018-10-01 17:18:03 +00:00
jsonConf, err := json.Marshal(staticConfiguration)
2018-10-01 17:18:03 +00:00
if err != nil {
log.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
2018-10-01 17:18:03 +00:00
} else {
log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
2018-10-01 17:18:03 +00:00
}
if staticConfiguration.API != nil && staticConfiguration.API.Dashboard {
staticConfiguration.API.DashboardAssets = &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}
}
if staticConfiguration.Global.CheckNewVersion {
checkNewVersion()
}
stats(staticConfiguration)
2017-11-25 12:36:03 +00:00
providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers)
acmeProvider, err := staticConfiguration.InitACMEProvider()
if err != nil {
log.WithoutContext().Errorf("Unable to initialize ACME provider: %v", err)
2018-10-09 09:19:55 +00:00
} else if acmeProvider != nil {
if err := providerAggregator.AddProvider(acmeProvider); err != nil {
log.WithoutContext().Errorf("Unable to add ACME provider to the providers list: %v", err)
2018-10-09 09:19:55 +00:00
acmeProvider = nil
}
2018-03-05 19:54:04 +00:00
}
serverEntryPointsTCP := make(server.TCPEntryPoints)
for entryPointName, config := range staticConfiguration.EntryPoints {
ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
serverEntryPointsTCP[entryPointName], err = server.NewTCPEntryPoint(ctx, config)
if err != nil {
return fmt.Errorf("error while building entryPoint %s: %v", entryPointName, err)
}
serverEntryPointsTCP[entryPointName].RouteAppenderFactory = router.NewRouteAppenderFactory(*staticConfiguration, entryPointName, acmeProvider)
}
2018-07-03 10:44:04 +00:00
tlsManager := traefiktls.NewManager()
if acmeProvider != nil {
acmeProvider.SetTLSManager(tlsManager)
if acmeProvider.TLSChallenge != nil &&
acmeProvider.HTTPChallenge == nil &&
acmeProvider.DNSChallenge == nil {
tlsManager.TLSAlpnGetter = acmeProvider.GetTLSALPNCertificate
}
}
svr := server.NewServer(*staticConfiguration, providerAggregator, serverEntryPointsTCP, tlsManager)
2018-11-14 09:18:03 +00:00
2018-10-09 09:19:55 +00:00
if acmeProvider != nil && acmeProvider.OnHostRule {
2018-11-14 09:18:03 +00:00
acmeProvider.SetConfigListenerChan(make(chan config.Configuration))
2018-10-09 09:19:55 +00:00
svr.AddListener(acmeProvider.ListenConfiguration)
2018-03-05 19:54:04 +00:00
}
2018-03-14 12:14:03 +00:00
ctx := cmd.ContextWithSignal(context.Background())
if staticConfiguration.Ping != nil {
staticConfiguration.Ping.WithContext(ctx)
}
svr.Start(ctx)
defer svr.Close()
2017-11-23 15:10:04 +00:00
sent, err := daemon.SdNotify(false, "READY=1")
if !sent && err != nil {
log.WithoutContext().Errorf("Failed to notify: %v", err)
}
2017-11-23 15:10:04 +00:00
t, err := daemon.SdWatchdogEnabled(false)
if err != nil {
log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
} else if t != 0 {
// Send a ping each half time given
2019-02-05 16:10:03 +00:00
t /= 2
log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
safe.Go(func() {
tick := time.Tick(t)
for range tick {
resp, errHealthCheck := healthcheck.Do(*staticConfiguration)
if resp != nil {
resp.Body.Close()
}
if staticConfiguration.Ping == nil || errHealthCheck == nil {
2017-11-23 15:10:04 +00:00
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.WithoutContext().Error("Fail to tick watchdog")
2017-11-23 15:10:04 +00:00
}
} else {
log.WithoutContext().Error(errHealthCheck)
}
}
})
}
2017-11-23 15:10:04 +00:00
svr.Wait()
log.WithoutContext().Info("Shutting down")
logrus.Exit(0)
return nil
}
func configureLogging(staticConfiguration *static.Configuration) {
// configure default log flags
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
// configure log level
// an explicitly defined log level always has precedence. if none is
// given and debug mode is disabled, the default is ERROR, and DEBUG
// otherwise.
levelStr := "error"
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
levelStr = strings.ToLower(staticConfiguration.Log.Level)
}
level, err := logrus.ParseLevel(levelStr)
if err != nil {
log.WithoutContext().Errorf("Error getting level: %v", err)
}
log.SetLevel(level)
2018-07-31 17:28:03 +00:00
var logFile string
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
logFile = staticConfiguration.Log.FilePath
}
// configure log format
var formatter logrus.Formatter
if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
formatter = &logrus.JSONFormatter{}
} else {
2018-02-19 00:04:45 +00:00
disableColors := len(logFile) > 0
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
}
log.SetFormatter(formatter)
if len(logFile) > 0 {
dir := filepath.Dir(logFile)
2017-04-27 15:36:04 +00:00
2018-02-19 00:04:45 +00:00
if err := os.MkdirAll(dir, 0755); err != nil {
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
2017-04-27 15:36:04 +00:00
}
err = log.OpenFile(logFile)
logrus.RegisterExitHandler(func() {
if err := log.CloseFile(); err != nil {
log.WithoutContext().Errorf("Error while closing log: %v", err)
2016-05-03 14:52:14 +00:00
}
})
2016-05-03 14:52:14 +00:00
if err != nil {
log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
2016-05-03 14:52:14 +00:00
}
}
}
2017-10-02 08:32:02 +00:00
func checkNewVersion() {
2017-11-25 12:36:03 +00:00
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
version.CheckNewVersion()
}
})
}
func stats(staticConfiguration *static.Configuration) {
2019-03-14 18:32:03 +00:00
if staticConfiguration.Global.SendAnonymousUsage == nil {
log.WithoutContext().Error(`
2019-05-21 15:12:09 +00:00
You haven't specified the sendAnonymousUsage option, it will be enabled by default.
2019-03-14 18:32:03 +00:00
`)
sendAnonymousUsage := true
staticConfiguration.Global.SendAnonymousUsage = &sendAnonymousUsage
}
if *staticConfiguration.Global.SendAnonymousUsage {
log.WithoutContext().Info(`
2017-11-25 12:36:03 +00:00
Stats collection is enabled.
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
Help us improve Traefik by leaving this feature on :)
More details on: https://docs.traefik.io/basics/#collected-data
2017-11-25 12:36:03 +00:00
`)
collect(staticConfiguration)
2017-11-25 12:36:03 +00:00
} else {
log.WithoutContext().Info(`
2017-11-25 12:36:03 +00:00
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://docs.traefik.io/basics/#collected-data
2017-11-25 12:36:03 +00:00
`)
}
}
func collect(staticConfiguration *static.Configuration) {
2017-11-25 12:36:03 +00:00
ticker := time.Tick(24 * time.Hour)
2017-10-02 08:32:02 +00:00
safe.Go(func() {
2017-11-25 12:36:03 +00:00
for time.Sleep(10 * time.Minute); ; <-ticker {
if err := collector.Collect(staticConfiguration); err != nil {
log.WithoutContext().Debug(err)
2017-10-02 08:32:02 +00:00
}
}
})
}