traefik/cmd/traefik/traefik.go

395 lines
12 KiB
Go
Raw Normal View History

2015-09-11 14:37:13 +00:00
package main
import (
2017-08-21 21:18:02 +00:00
"crypto/tls"
2016-05-03 14:52:14 +00:00
"encoding/json"
"fmt"
fmtlog "log"
2017-08-21 21:18:02 +00:00
"net/http"
"os"
2017-04-27 15:36:04 +00:00
"path/filepath"
"reflect"
"runtime"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/cenk/backoff"
2016-05-03 14:52:14 +00:00
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
2017-08-22 09:46:03 +00:00
"github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/server"
2017-10-02 08:32:02 +00:00
"github.com/containous/traefik/server/uuid"
"github.com/containous/traefik/types"
"github.com/containous/traefik/version"
"github.com/coreos/go-systemd/daemon"
2016-06-24 07:58:42 +00:00
"github.com/docker/libkv/store"
2015-09-11 23:55:10 +00:00
)
2015-09-11 14:37:13 +00:00
func main() {
2015-09-25 22:20:45 +00:00
runtime.GOMAXPROCS(runtime.NumCPU())
2016-05-03 14:52:14 +00:00
//traefik config inits
traefikConfiguration := NewTraefikConfiguration()
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
2016-05-03 14:52:14 +00:00
//traefik Command init
traefikCmd := &flaeg.Command{
Name: "traefik",
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
Complete documentation is available at https://traefik.io`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
globalConfiguration := traefikConfiguration.GlobalConfiguration
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
// no filename, setting to global config file
if len(traefikConfiguration.ConfigFile) != 0 {
globalConfiguration.File.Filename = traefikConfiguration.ConfigFile
} else {
log.Errorln("Error using file configuration backend, no filename defined")
}
}
if len(traefikConfiguration.ConfigFile) != 0 {
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
}
run(&globalConfiguration)
2016-05-03 14:52:14 +00:00
return nil
},
}
//storeconfig Command init
var kv *staert.KvSource
var err error
storeConfigCmd := &flaeg.Command{
Name: "storeconfig",
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
if kv == nil {
return fmt.Errorf("Error using command storeconfig, no Key-value store defined")
}
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
fmtlog.Printf("Storing configuration: %s\n", jsonConf)
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
// convert ACME json file to KV store
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
object, err := localStore.Load()
if err != nil {
return err
}
meta := cluster.NewMetadata(object)
err = meta.Marshall()
if err != nil {
return err
}
source := staert.KvSource{
Store: kv,
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
}
err = source.StoreConfig(meta)
if err != nil {
return err
}
}
return nil
},
Metadata: map[string]string{
"parseAllSources": "true",
},
}
healthCheckCmd := &flaeg.Command{
2017-08-21 21:18:02 +00:00
Name: "healthcheck",
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
if traefikConfiguration.Web == nil {
fmt.Println("Please enable the web provider to use healtcheck.")
os.Exit(1)
}
client := &http.Client{Timeout: 5 * time.Second}
protocol := "http"
if len(traefikConfiguration.Web.CertFile) > 0 {
protocol = "https"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client.Transport = tr
}
resp, err := client.Head(protocol + "://" + traefikConfiguration.Web.Address + "/ping")
if err != nil {
fmt.Printf("Error calling healthcheck: %s\n", err)
os.Exit(1)
}
if resp.StatusCode != http.StatusOK {
fmt.Printf("Bad healthcheck status: %s\n", resp.Status)
os.Exit(1)
}
fmt.Printf("OK: %s\n", resp.Request.URL)
os.Exit(0)
return nil
},
Metadata: map[string]string{
"parseAllSources": "true",
},
}
2016-05-03 14:52:14 +00:00
//init flaeg source
f := flaeg.New(traefikCmd, os.Args[1:])
//add custom parsers
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
f.AddParser(reflect.TypeOf(configuration.RootCAs{}), &configuration.RootCAs{})
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
2017-08-22 09:46:03 +00:00
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
2017-01-12 13:34:54 +00:00
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
//add commands
f.AddCommand(newVersionCmd())
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
f.AddCommand(storeConfigCmd)
f.AddCommand(healthCheckCmd)
2016-07-29 15:36:53 +00:00
usedCmd, err := f.GetCommand()
if err != nil {
fmtlog.Println(err)
os.Exit(-1)
}
if _, err := f.Parse(usedCmd); err != nil {
fmtlog.Printf("Error parsing command: %s\n", err)
2016-05-24 12:58:25 +00:00
os.Exit(-1)
}
//staert init
s := staert.NewStaert(traefikCmd)
//init toml source
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
2016-05-03 14:52:14 +00:00
//add sources to staert
s.AddSource(toml)
s.AddSource(f)
2016-05-27 08:04:56 +00:00
if _, err := s.LoadConfig(); err != nil {
2017-10-02 08:32:02 +00:00
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
os.Exit(-1)
2016-05-24 12:58:25 +00:00
}
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
kv, err = CreateKvSource(traefikConfiguration)
2016-06-24 07:58:42 +00:00
if err != nil {
fmtlog.Printf("Error creating kv store: %s\n", err)
2016-06-24 07:58:42 +00:00
os.Exit(-1)
}
// IF a KV Store is enable and no sub-command called in args
if kv != nil && usedCmd == traefikCmd {
if traefikConfiguration.Cluster == nil {
2017-10-02 08:32:02 +00:00
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
}
if traefikConfiguration.Cluster.Store == nil {
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
}
2016-06-24 07:58:42 +00:00
s.AddSource(kv)
operation := func() error {
_, err := s.LoadConfig()
return err
}
notify := func(err error, time time.Duration) {
log.Errorf("Load config error: %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
if err != nil {
fmtlog.Printf("Error loading configuration: %s\n", err)
os.Exit(-1)
2016-06-24 07:58:42 +00:00
}
}
2016-05-03 14:52:14 +00:00
if err := s.Run(); err != nil {
fmtlog.Printf("Error running traefik: %s\n", err)
2016-01-13 21:46:44 +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 run(globalConfiguration *configuration.GlobalConfiguration) {
2016-05-03 14:52:14 +00:00
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
if len(globalConfiguration.EntryPoints) == 0 {
2017-10-16 10:46:03 +00:00
globalConfiguration.EntryPoints = map[string]*configuration.EntryPoint{"http": {
Address: ":80",
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
}}
globalConfiguration.DefaultEntryPoints = []string{"http"}
}
if globalConfiguration.Rancher != nil {
// Ensure backwards compatibility for now
if len(globalConfiguration.Rancher.AccessKey) > 0 ||
len(globalConfiguration.Rancher.Endpoint) > 0 ||
len(globalConfiguration.Rancher.SecretKey) > 0 {
if globalConfiguration.Rancher.API == nil {
globalConfiguration.Rancher.API = &rancher.APIConfiguration{
AccessKey: globalConfiguration.Rancher.AccessKey,
SecretKey: globalConfiguration.Rancher.SecretKey,
Endpoint: globalConfiguration.Rancher.Endpoint,
}
}
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
}
if globalConfiguration.Rancher.Metadata != nil && len(globalConfiguration.Rancher.Metadata.Prefix) == 0 {
globalConfiguration.Rancher.Metadata.Prefix = "latest"
}
}
if globalConfiguration.Debug {
globalConfiguration.LogLevel = "DEBUG"
}
2017-10-16 10:46:03 +00:00
// ForwardedHeaders must be remove in the next breaking version
for entryPointName := range globalConfiguration.EntryPoints {
entryPoint := globalConfiguration.EntryPoints[entryPointName]
if entryPoint.ForwardedHeaders == nil {
entryPoint.ForwardedHeaders = &configuration.ForwardedHeaders{Insecure: true}
}
}
// logging
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
2016-08-19 08:36:54 +00:00
log.Error("Error getting level", err)
}
log.SetLevel(level)
2016-05-03 14:52:14 +00:00
if len(globalConfiguration.TraefikLogsFile) > 0 {
2017-04-27 15:36:04 +00:00
dir := filepath.Dir(globalConfiguration.TraefikLogsFile)
err := os.MkdirAll(dir, 0755)
if err != nil {
log.Errorf("Failed to create log path %s: %s", dir, err)
}
err = log.OpenFile(globalConfiguration.TraefikLogsFile)
2016-05-03 14:52:14 +00:00
defer func() {
if err := log.CloseFile(); err != nil {
log.Error("Error closing log", err)
2016-05-03 14:52:14 +00:00
}
}()
if err != nil {
2016-08-19 08:36:54 +00:00
log.Error("Error opening file", err)
2016-05-03 14:52:14 +00:00
} else {
log.SetFormatter(&logrus.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
2016-05-03 14:52:14 +00:00
}
} else {
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true, DisableSorting: true})
2016-05-03 14:52:14 +00:00
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
if globalConfiguration.CheckNewVersion {
2017-10-02 08:32:02 +00:00
checkNewVersion()
}
2016-05-03 14:52:14 +00:00
log.Debugf("Global configuration loaded %s", string(jsonConf))
svr := server.NewServer(*globalConfiguration)
svr.Start()
defer svr.Close()
sent, err := daemon.SdNotify(false, "READY=1")
if !sent && err != nil {
log.Error("Fail to notify", err)
}
t, err := daemon.SdWatchdogEnabled(false)
2016-11-08 11:25:56 +00:00
if err != nil {
log.Error("Problem with watchdog", err)
} else if t != 0 {
// Send a ping each half time given
t = t / 2
log.Info("Watchdog activated with timer each ", t)
safe.Go(func() {
tick := time.Tick(t)
2016-11-08 11:25:56 +00:00
for range tick {
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.Error("Fail to tick watchdog")
}
2016-11-08 11:25:56 +00:00
}
})
2016-11-08 11:25:56 +00:00
}
svr.Wait()
2016-05-03 14:52:14 +00:00
log.Info("Shutting down")
}
// CreateKvSource creates KvSource
// TLS support is enable for Consul and Etcd backends
func CreateKvSource(traefikConfiguration *TraefikConfiguration) (*staert.KvSource, error) {
var kv *staert.KvSource
var kvStore store.Store
var err error
switch {
case traefikConfiguration.Consul != nil:
kvStore, err = traefikConfiguration.Consul.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Consul.Prefix,
}
case traefikConfiguration.Etcd != nil:
kvStore, err = traefikConfiguration.Etcd.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Etcd.Prefix,
}
case traefikConfiguration.Zookeeper != nil:
kvStore, err = traefikConfiguration.Zookeeper.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Zookeeper.Prefix,
}
case traefikConfiguration.Boltdb != nil:
kvStore, err = traefikConfiguration.Boltdb.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Boltdb.Prefix,
}
}
return kv, err
}
2017-10-02 08:32:02 +00:00
func checkNewVersion() {
ticker := time.NewTicker(24 * time.Hour)
safe.Go(func() {
2017-10-05 06:42:02 +00:00
time.Sleep(10 * time.Minute)
2017-10-02 08:32:02 +00:00
version.CheckNewVersion()
for {
select {
case <-ticker.C:
version.CheckNewVersion()
}
}
})
}