2015-09-11 14:37:13 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2016-07-28 18:41:25 +00:00
|
|
|
"crypto/tls"
|
2017-06-23 13:15:07 +00:00
|
|
|
"crypto/x509"
|
2016-05-03 14:52:14 +00:00
|
|
|
"encoding/json"
|
2016-06-02 13:17:04 +00:00
|
|
|
"fmt"
|
2016-07-21 14:33:49 +00:00
|
|
|
fmtlog "log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2017-04-27 15:36:04 +00:00
|
|
|
"path/filepath"
|
2016-07-21 14:33:49 +00:00
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
2016-10-27 14:17:02 +00:00
|
|
|
"time"
|
2016-07-21 14:33:49 +00:00
|
|
|
|
2016-08-18 12:20:11 +00:00
|
|
|
"github.com/Sirupsen/logrus"
|
2016-05-03 14:52:14 +00:00
|
|
|
"github.com/containous/flaeg"
|
|
|
|
"github.com/containous/staert"
|
2016-05-25 15:06:34 +00:00
|
|
|
"github.com/containous/traefik/acme"
|
2016-09-29 13:36:52 +00:00
|
|
|
"github.com/containous/traefik/cluster"
|
2016-08-18 12:20:11 +00:00
|
|
|
"github.com/containous/traefik/log"
|
2017-04-17 10:50:02 +00:00
|
|
|
"github.com/containous/traefik/provider/kubernetes"
|
2017-05-08 01:20:38 +00:00
|
|
|
"github.com/containous/traefik/provider/rancher"
|
2016-10-27 14:17:02 +00:00
|
|
|
"github.com/containous/traefik/safe"
|
2017-04-17 20:47:53 +00:00
|
|
|
"github.com/containous/traefik/server"
|
2016-05-31 07:54:42 +00:00
|
|
|
"github.com/containous/traefik/types"
|
2016-07-21 14:33:49 +00:00
|
|
|
"github.com/containous/traefik/version"
|
2016-10-27 14:17:02 +00:00
|
|
|
"github.com/coreos/go-systemd/daemon"
|
2016-06-24 07:58:42 +00:00
|
|
|
"github.com/docker/libkv/store"
|
2016-08-18 11:03:10 +00:00
|
|
|
"github.com/satori/go.uuid"
|
2017-06-23 13:15:07 +00:00
|
|
|
"golang.org/x/net/http2"
|
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
|
2017-04-17 20:47:53 +00:00
|
|
|
traefikConfiguration := server.NewTraefikConfiguration()
|
|
|
|
traefikPointersConfiguration := server.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 {
|
|
|
|
run(traefikConfiguration)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2016-07-20 09:48:39 +00:00
|
|
|
//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")
|
|
|
|
}
|
2016-09-29 13:36:52 +00:00
|
|
|
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-07-20 09:48:39 +00:00
|
|
|
fmtlog.Printf("Storing configuration: %s\n", jsonConf)
|
2016-09-29 13:36:52 +00:00
|
|
|
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
|
|
|
|
store := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
|
|
|
object, err := store.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
|
2016-07-20 09:48:39 +00:00
|
|
|
},
|
|
|
|
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
|
2017-04-17 20:47:53 +00:00
|
|
|
f.AddParser(reflect.TypeOf(server.EntryPoints{}), &server.EntryPoints{})
|
|
|
|
f.AddParser(reflect.TypeOf(server.DefaultEntryPoints{}), &server.DefaultEntryPoints{})
|
2017-06-23 13:15:07 +00:00
|
|
|
f.AddParser(reflect.TypeOf(server.RootCAs{}), &server.RootCAs{})
|
2016-06-02 13:17:04 +00:00
|
|
|
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
2017-04-17 10:50:02 +00:00
|
|
|
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
2016-05-25 15:06:34 +00:00
|
|
|
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
|
2017-01-12 13:34:54 +00:00
|
|
|
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
2016-05-25 15:06:34 +00:00
|
|
|
|
2016-07-20 09:48:39 +00:00
|
|
|
//add commands
|
2017-04-17 20:47:53 +00:00
|
|
|
f.AddCommand(newVersionCmd())
|
|
|
|
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
|
2016-07-20 09:48:39 +00:00
|
|
|
f.AddCommand(storeconfigCmd)
|
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 {
|
2016-09-23 16:27:01 +00:00
|
|
|
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 {
|
2016-06-03 15:58:33 +00:00
|
|
|
fmtlog.Println(fmt.Errorf("Error reading TOML config file %s : %s", toml.ConfigFileUsed(), err))
|
2016-07-20 09:48:39 +00:00
|
|
|
os.Exit(-1)
|
2016-05-24 12:58:25 +00:00
|
|
|
}
|
2016-05-30 09:37:12 +00:00
|
|
|
|
|
|
|
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
|
|
|
|
2016-07-20 09:48:39 +00:00
|
|
|
kv, err = CreateKvSource(traefikConfiguration)
|
2016-06-24 07:58:42 +00:00
|
|
|
if err != nil {
|
2016-09-23 16:27:01 +00:00
|
|
|
fmtlog.Printf("Error creating kv store: %s\n", err)
|
2016-06-24 07:58:42 +00:00
|
|
|
os.Exit(-1)
|
|
|
|
}
|
|
|
|
|
2016-07-20 09:48:39 +00:00
|
|
|
// IF a KV Store is enable and no sub-command called in args
|
|
|
|
if kv != nil && usedCmd == traefikCmd {
|
2016-08-16 17:13:18 +00:00
|
|
|
if traefikConfiguration.Cluster == nil {
|
2016-08-18 11:03:10 +00:00
|
|
|
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.NewV4().String()}
|
|
|
|
}
|
|
|
|
if traefikConfiguration.Cluster.Store == nil {
|
|
|
|
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
|
2016-08-16 17:13:18 +00:00
|
|
|
}
|
2016-06-24 07:58:42 +00:00
|
|
|
s.AddSource(kv)
|
|
|
|
if _, err := s.LoadConfig(); err != nil {
|
2016-09-23 16:27:01 +00:00
|
|
|
fmtlog.Printf("Error loading configuration: %s\n", err)
|
2016-07-20 09:48:39 +00:00
|
|
|
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 {
|
2016-09-23 16:27:01 +00:00
|
|
|
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
|
|
|
|
2017-04-17 20:47:53 +00:00
|
|
|
func run(traefikConfiguration *server.TraefikConfiguration) {
|
2016-05-03 14:52:14 +00:00
|
|
|
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
|
|
|
|
|
|
|
// load global configuration
|
|
|
|
globalConfiguration := traefikConfiguration.GlobalConfiguration
|
|
|
|
|
|
|
|
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
|
2016-07-23 14:54:37 +00:00
|
|
|
if globalConfiguration.InsecureSkipVerify {
|
|
|
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
|
|
}
|
2016-05-03 14:52:14 +00:00
|
|
|
|
2017-06-23 13:15:07 +00:00
|
|
|
if len(globalConfiguration.RootCAs) > 0 {
|
|
|
|
roots := x509.NewCertPool()
|
|
|
|
for _, cert := range globalConfiguration.RootCAs {
|
|
|
|
certContent, err := cert.Read()
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Error while read RootCAs", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
roots.AppendCertsFromPEM(certContent)
|
|
|
|
}
|
|
|
|
|
|
|
|
tr := http.DefaultTransport.(*http.Transport)
|
|
|
|
tr.TLSClientConfig = &tls.Config{RootCAs: roots}
|
|
|
|
|
|
|
|
http2.ConfigureTransport(tr)
|
|
|
|
}
|
|
|
|
|
2016-06-02 09:37:51 +00:00
|
|
|
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
|
2016-05-30 09:37:12 +00:00
|
|
|
// no filename, setting to global config file
|
|
|
|
if len(traefikConfiguration.ConfigFile) != 0 {
|
2016-06-02 09:37:51 +00:00
|
|
|
globalConfiguration.File.Filename = traefikConfiguration.ConfigFile
|
2016-05-30 09:37:12 +00:00
|
|
|
} else {
|
|
|
|
log.Errorln("Error using file configuration backend, no filename defined")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-02 09:37:51 +00:00
|
|
|
if len(globalConfiguration.EntryPoints) == 0 {
|
2017-04-17 20:47:53 +00:00
|
|
|
globalConfiguration.EntryPoints = map[string]*server.EntryPoint{"http": {Address: ":80"}}
|
2016-06-02 09:37:51 +00:00
|
|
|
globalConfiguration.DefaultEntryPoints = []string{"http"}
|
2016-05-30 09:37:12 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 01:20:38 +00:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-02 13:17:04 +00:00
|
|
|
if globalConfiguration.Debug {
|
|
|
|
globalConfiguration.LogLevel = "DEBUG"
|
|
|
|
}
|
|
|
|
|
2016-06-02 09:37:51 +00:00
|
|
|
// logging
|
2016-08-18 12:20:11 +00:00
|
|
|
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
2016-06-02 09:37:51 +00:00
|
|
|
if err != nil {
|
2016-08-19 08:36:54 +00:00
|
|
|
log.Error("Error getting level", err)
|
2016-06-02 09:37:51 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2016-05-03 14:52:14 +00:00
|
|
|
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
|
|
defer func() {
|
|
|
|
if err := fi.Close(); err != nil {
|
2016-07-20 09:48:39 +00:00
|
|
|
log.Error("Error closing file", 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.SetOutput(fi)
|
2016-08-18 12:20:11 +00:00
|
|
|
log.SetFormatter(&logrus.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
|
2016-05-03 14:52:14 +00:00
|
|
|
}
|
|
|
|
} else {
|
2016-08-18 12:20:11 +00:00
|
|
|
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
2016-05-03 14:52:14 +00:00
|
|
|
}
|
|
|
|
jsonConf, _ := json.Marshal(globalConfiguration)
|
2016-07-21 14:33:49 +00:00
|
|
|
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
2016-10-27 14:17:02 +00:00
|
|
|
|
|
|
|
if globalConfiguration.CheckNewVersion {
|
|
|
|
ticker := time.NewTicker(24 * time.Hour)
|
|
|
|
safe.Go(func() {
|
|
|
|
version.CheckNewVersion()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
version.CheckNewVersion()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-05-30 15:57:57 +00:00
|
|
|
if len(traefikConfiguration.ConfigFile) != 0 {
|
|
|
|
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
|
|
|
|
}
|
2016-05-03 14:52:14 +00:00
|
|
|
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
2017-04-17 20:47:53 +00:00
|
|
|
svr := server.NewServer(globalConfiguration)
|
|
|
|
svr.Start()
|
|
|
|
defer svr.Close()
|
2017-05-02 11:29:56 +00:00
|
|
|
sent, err := daemon.SdNotify(false, "READY=1")
|
2016-10-25 15:59:39 +00:00
|
|
|
if !sent && err != nil {
|
|
|
|
log.Error("Fail to notify", err)
|
|
|
|
}
|
2017-05-02 11:29:56 +00:00
|
|
|
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
|
2017-05-02 11:29:56 +00:00
|
|
|
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 {
|
2017-05-02 11:29:56 +00:00
|
|
|
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
|
|
|
log.Error("Fail to tick watchdog")
|
|
|
|
}
|
2016-11-08 11:25:56 +00:00
|
|
|
}
|
2017-05-02 11:29:56 +00:00
|
|
|
})
|
2016-11-08 11:25:56 +00:00
|
|
|
}
|
2017-04-17 20:47:53 +00:00
|
|
|
svr.Wait()
|
2016-05-03 14:52:14 +00:00
|
|
|
log.Info("Shutting down")
|
|
|
|
}
|
2016-07-13 15:18:55 +00:00
|
|
|
|
|
|
|
// CreateKvSource creates KvSource
|
2016-08-16 17:13:18 +00:00
|
|
|
// TLS support is enable for Consul and Etcd backends
|
2017-04-17 20:47:53 +00:00
|
|
|
func CreateKvSource(traefikConfiguration *server.TraefikConfiguration) (*staert.KvSource, error) {
|
2016-07-13 15:18:55 +00:00
|
|
|
var kv *staert.KvSource
|
|
|
|
var store store.Store
|
|
|
|
var err error
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case traefikConfiguration.Consul != nil:
|
|
|
|
store, err = traefikConfiguration.Consul.CreateStore()
|
|
|
|
kv = &staert.KvSource{
|
|
|
|
Store: store,
|
|
|
|
Prefix: traefikConfiguration.Consul.Prefix,
|
|
|
|
}
|
|
|
|
case traefikConfiguration.Etcd != nil:
|
|
|
|
store, err = traefikConfiguration.Etcd.CreateStore()
|
|
|
|
kv = &staert.KvSource{
|
|
|
|
Store: store,
|
|
|
|
Prefix: traefikConfiguration.Etcd.Prefix,
|
|
|
|
}
|
|
|
|
case traefikConfiguration.Zookeeper != nil:
|
|
|
|
store, err = traefikConfiguration.Zookeeper.CreateStore()
|
|
|
|
kv = &staert.KvSource{
|
|
|
|
Store: store,
|
|
|
|
Prefix: traefikConfiguration.Zookeeper.Prefix,
|
|
|
|
}
|
|
|
|
case traefikConfiguration.Boltdb != nil:
|
|
|
|
store, err = traefikConfiguration.Boltdb.CreateStore()
|
|
|
|
kv = &staert.KvSource{
|
|
|
|
Store: store,
|
|
|
|
Prefix: traefikConfiguration.Boltdb.Prefix,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return kv, err
|
|
|
|
}
|