diff --git a/config/config.go b/config/config.go index d50ac70dc..cbbb7e2e6 100644 --- a/config/config.go +++ b/config/config.go @@ -6,7 +6,7 @@ type Config struct { Proxy Proxy Registry Registry Listen []Listen - Metrics []Metrics + Metrics Metrics UI UI Runtime Runtime } @@ -35,10 +35,13 @@ type Proxy struct { DialTimeout time.Duration ResponseHeaderTimeout time.Duration KeepAliveTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration LocalIP string ClientIPHeader string TLSHeader string TLSHeaderValue string + ListenerAddr string } type Runtime struct { @@ -47,10 +50,10 @@ type Runtime struct { } type Metrics struct { - Target string - Prefix string - Interval time.Duration - Addr string + Target string + Prefix string + Interval time.Duration + GraphiteAddr string } type Registry struct { @@ -70,6 +73,7 @@ type File struct { type Consul struct { Addr string + Scheme string Token string KVPath string TagPrefix string diff --git a/config/default.go b/config/default.go index 0193cacb4..c16dcb185 100644 --- a/config/default.go +++ b/config/default.go @@ -7,16 +7,18 @@ import ( var Default = &Config{ Proxy: Proxy{ - MaxConn: 10000, - Strategy: "rnd", - Matcher: "prefix", - DialTimeout: 30 * time.Second, - LocalIP: LocalIPString(), + MaxConn: 10000, + Strategy: "rnd", + Matcher: "prefix", + DialTimeout: 30 * time.Second, + LocalIP: LocalIPString(), + ListenerAddr: ":9999", }, Registry: Registry{ Backend: "consul", Consul: Consul{ Addr: "localhost:8500", + Scheme: "http", KVPath: "/fabio/config", TagPrefix: "urlprefix-", ServiceAddr: ":9998", @@ -25,11 +27,6 @@ var Default = &Config{ CheckTimeout: 3 * time.Second, }, }, - Listen: []Listen{ - { - Addr: ":9999", - }, - }, Runtime: Runtime{ GOGC: 800, GOMAXPROCS: runtime.NumCPU(), @@ -38,12 +35,8 @@ var Default = &Config{ Addr: ":9998", Color: "light-green", }, - Metrics: []Metrics{ - { - Target: "", - Prefix: "default", - Addr: "", - Interval: 30 * time.Second, - }, + Metrics: Metrics{ + Prefix: "default", + Interval: 30 * time.Second, }, } diff --git a/config/flagset.go b/config/flagset.go new file mode 100644 index 000000000..c2ec9fd90 --- /dev/null +++ b/config/flagset.go @@ -0,0 +1,79 @@ +package config + +import ( + "flag" + "strings" + + "github.com/magiconair/properties" +) + +type FlagSet struct { + flag.FlagSet + set map[string]bool +} + +func NewFlagSet(name string, errorHandling flag.ErrorHandling) *FlagSet { + fs := &FlagSet{set: make(map[string]bool)} + fs.Init(name, errorHandling) + return fs +} + +// IsSet returns true if a variable was set via any mechanism. +func (f *FlagSet) IsSet(name string) bool { + return f.set[name] +} + +// ParseFlags parses command line arguments and provides fallback +// values from environment variables and config file values. +// Environment variables are case-insensitive and can have either +// of the provided prefixes. +func (f *FlagSet) ParseFlags(args, environ, prefixes []string, p *properties.Properties) error { + if err := f.Parse(args); err != nil { + return err + } + + if len(prefixes) == 0 { + prefixes = []string{""} + } + + // parse environment in case-insensitive way + env := map[string]string{} + for _, e := range environ { + p := strings.Split(e, "=") + env[strings.ToUpper(p[0])] = p[1] + } + + // determine all values that were set via cmdline + f.Visit(func(fl *flag.Flag) { + f.set[fl.Name] = true + }) + + // lookup the rest via environ and properties + f.VisitAll(func(fl *flag.Flag) { + // skip if already set + if f.set[fl.Name] { + return + } + + // check environment variables + for _, pfx := range prefixes { + name := strings.ToUpper(pfx + strings.Replace(fl.Name, ".", "_", -1)) + if val, ok := env[name]; ok { + f.set[fl.Name] = true + f.Set(fl.Name, val) + return + } + } + + // check properties + if p == nil { + return + } + if val, ok := p.Get(fl.Name); ok { + f.set[fl.Name] = true + f.Set(fl.Name, val) + return + } + }) + return nil +} diff --git a/config/flagset_test.go b/config/flagset_test.go new file mode 100644 index 000000000..0c752089f --- /dev/null +++ b/config/flagset_test.go @@ -0,0 +1,62 @@ +package config + +import ( + "flag" + "testing" + + "github.com/magiconair/properties" +) + +func TestParseFlags(t *testing.T) { + props := func(s string) *properties.Properties { + return properties.MustLoadString(s) + } + + tests := []struct { + desc string + args []string + env []string + prefix []string + props string + v string + }{ + { + desc: "cmdline should win", + args: []string{"-v", "cmdline"}, + env: []string{"v=env"}, + props: "v=props", + v: "cmdline", + }, + { + desc: "env should win", + env: []string{"v=env"}, + props: "v=props", + v: "env", + }, + { + desc: "env with prefix should win", + env: []string{"v=env", "p_v=prefix"}, + prefix: []string{"p_"}, + props: "v=props", + v: "prefix", + }, + { + desc: "props should win", + props: "v=props", + v: "props", + }, + } + + for i, tt := range tests { + var v string + f := NewFlagSet("test", flag.ExitOnError) + f.StringVar(&v, "v", "default", "") + err := f.ParseFlags(tt.args, tt.env, tt.prefix, props(tt.props)) + if err != nil { + t.Errorf("%d -%s: got %v want nil", i, tt.desc, err) + } + if got, want := v, tt.v; got != want { + t.Errorf("%d - %s: got %q want %q", i, tt.desc, got, want) + } + } +} diff --git a/config/load.go b/config/load.go index df20cba8e..878047c88 100644 --- a/config/load.go +++ b/config/load.go @@ -1,88 +1,123 @@ package config import ( + "errors" + "flag" "fmt" "log" "os" "runtime" - "strconv" "strings" "time" "github.com/magiconair/properties" ) -func Load(filename string) (*Config, error) { - if filename == "" { - return fromProperties(properties.NewProperties()) +func Load() (cfg *Config, err error) { + var path string + for i, arg := range os.Args { + if arg == "-v" { + return nil, nil + } + if arg == "-cfg" { + if i == len(os.Args)-1 { + return nil, errors.New("missing path or url to config file") + } + path = os.Args[i+1] + break + } } - - p, err := properties.LoadFile(filename, properties.UTF8) + p, err := loadProperties(path) if err != nil { return nil, err } - return fromProperties(p) + return load(p) } -func fromProperties(p *properties.Properties) (cfg *Config, err error) { +func loadProperties(path string) (p *properties.Properties, err error) { + if path == "" { + return properties.NewProperties(), nil + } + if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { + return properties.LoadURL(path) + } + return properties.LoadFile(path, properties.UTF8) +} + +func load(p *properties.Properties) (cfg *Config, err error) { cfg = &Config{} - deprecate := func(key, msg string) { - _, exists := p.Get(key) - if exists { - log.Print("[WARN] config: ", msg) - } + f := NewFlagSet(os.Args[0], flag.ExitOnError) + + // dummy values which were parsed earlier + f.String("cfg", "", "Path or URL to config file") + f.Bool("v", false, "Show version") + + // config values + f.IntVar(&cfg.Proxy.MaxConn, "proxy.maxconn", Default.Proxy.MaxConn, "maximum number of cached connections") + f.StringVar(&cfg.Proxy.Strategy, "proxy.strategy", Default.Proxy.Strategy, "load balancing strategy") + f.StringVar(&cfg.Proxy.Matcher, "proxy.matcher", Default.Proxy.Matcher, "path matching algorithm") + f.DurationVar(&cfg.Proxy.ShutdownWait, "proxy.shutdownwait", Default.Proxy.ShutdownWait, "time for graceful shutdown") + f.DurationVar(&cfg.Proxy.DialTimeout, "proxy.dialtimeout", Default.Proxy.DialTimeout, "connection timeout for backend connections") + f.DurationVar(&cfg.Proxy.ResponseHeaderTimeout, "proxy.responseheadertimeout", Default.Proxy.ResponseHeaderTimeout, "response header timeout") + f.DurationVar(&cfg.Proxy.KeepAliveTimeout, "proxy.keepalivetimeout", Default.Proxy.KeepAliveTimeout, "keep-alive timeout") + f.StringVar(&cfg.Proxy.LocalIP, "proxy.localip", Default.Proxy.LocalIP, "fabio address in Forward headers") + f.StringVar(&cfg.Proxy.ClientIPHeader, "proxy.header.clientip", Default.Proxy.ClientIPHeader, "header for the request ip") + f.StringVar(&cfg.Proxy.TLSHeader, "proxy.header.tls", Default.Proxy.TLSHeader, "header for TLS connections") + f.StringVar(&cfg.Proxy.TLSHeaderValue, "proxy.header.tls.value", Default.Proxy.TLSHeaderValue, "value for TLS connection header") + f.StringVar(&cfg.Proxy.ListenerAddr, "proxy.addr", Default.Proxy.ListenerAddr, "listener config") + f.DurationVar(&cfg.Proxy.ReadTimeout, "proxy.readtimeout", Default.Proxy.ReadTimeout, "read timeout for incoming requests") + f.DurationVar(&cfg.Proxy.WriteTimeout, "proxy.writetimeout", Default.Proxy.WriteTimeout, "write timeout for outgoing responses") + f.StringVar(&cfg.Metrics.Target, "metrics.target", Default.Metrics.Target, "metrics backend") + f.StringVar(&cfg.Metrics.Prefix, "metrics.prefix", Default.Metrics.Prefix, "prefix for reported metrics") + f.DurationVar(&cfg.Metrics.Interval, "metrics.interval", Default.Metrics.Interval, "metrics reporting interval") + f.StringVar(&cfg.Metrics.GraphiteAddr, "metrics.graphite.addr", Default.Metrics.GraphiteAddr, "graphite server address") + f.StringVar(&cfg.Registry.Backend, "registry.backend", Default.Registry.Backend, "registry backend") + f.StringVar(&cfg.Registry.File.Path, "registry.file.path", Default.Registry.File.Path, "path to file based routing table") + f.StringVar(&cfg.Registry.Static.Routes, "registry.static.routes", Default.Registry.Static.Routes, "static routes") + f.StringVar(&cfg.Registry.Consul.Addr, "registry.consul.addr", Default.Registry.Consul.Addr, "address of the consul agent") + f.StringVar(&cfg.Registry.Consul.Token, "registry.consul.token", Default.Registry.Consul.Token, "token for consul agent") + f.StringVar(&cfg.Registry.Consul.KVPath, "registry.consul.kvpath", Default.Registry.Consul.KVPath, "consul KV path for manual overrides") + f.StringVar(&cfg.Registry.Consul.TagPrefix, "registry.consul.tagprefix", Default.Registry.Consul.TagPrefix, "prefix for consul tags") + f.StringVar(&cfg.Registry.Consul.ServiceAddr, "registry.consul.register.addr", Default.Registry.Consul.ServiceAddr, "service registration address") + f.StringVar(&cfg.Registry.Consul.ServiceName, "registry.consul.register.name", Default.Registry.Consul.ServiceName, "service registration name") + f.Var((*tags)(&cfg.Registry.Consul.ServiceTags), "registry.consul.register.tags", "service registration tags") + f.DurationVar(&cfg.Registry.Consul.CheckInterval, "registry.consul.register.checkInterval", Default.Registry.Consul.CheckInterval, "service check interval") + f.DurationVar(&cfg.Registry.Consul.CheckTimeout, "registry.consul.register.checkTimeout", Default.Registry.Consul.CheckTimeout, "service check timeout") + f.IntVar(&cfg.Runtime.GOGC, "runtime.gogc", Default.Runtime.GOGC, "sets runtime.GOGC") + f.IntVar(&cfg.Runtime.GOMAXPROCS, "runtime.gomaxprocs", Default.Runtime.GOMAXPROCS, "sets runtime.GOMAXPROCS") + f.StringVar(&cfg.UI.Addr, "ui.addr", Default.UI.Addr, "address the UI/API is listening on") + f.StringVar(&cfg.UI.Color, "ui.color", Default.UI.Color, "background color of the UI") + f.StringVar(&cfg.UI.Title, "ui.title", Default.UI.Title, "optional title for the UI") + + var proxyTimeout time.Duration + f.DurationVar(&proxyTimeout, "proxy.timeout", time.Duration(0), "deprecated") + + // parse configuration + prefixes := []string{"FABIO_", ""} + if err := f.ParseFlags(os.Args[1:], os.Environ(), prefixes, p); err != nil { + return nil, err } - cfg.Proxy = Proxy{ - MaxConn: intVal(p, Default.Proxy.MaxConn, "proxy.maxconn"), - Strategy: stringVal(p, Default.Proxy.Strategy, "proxy.strategy"), - Matcher: stringVal(p, Default.Proxy.Matcher, "proxy.matcher"), - ShutdownWait: durationVal(p, Default.Proxy.ShutdownWait, "proxy.shutdownwait"), - DialTimeout: durationVal(p, Default.Proxy.DialTimeout, "proxy.dialtimeout"), - ResponseHeaderTimeout: durationVal(p, Default.Proxy.ResponseHeaderTimeout, "proxy.timeout"), - KeepAliveTimeout: durationVal(p, Default.Proxy.KeepAliveTimeout, "proxy.timeout"), - LocalIP: stringVal(p, Default.Proxy.LocalIP, "proxy.localip"), - ClientIPHeader: stringVal(p, Default.Proxy.ClientIPHeader, "proxy.header.clientip"), - TLSHeader: stringVal(p, Default.Proxy.TLSHeader, "proxy.header.tls"), - TLSHeaderValue: stringVal(p, Default.Proxy.TLSHeaderValue, "proxy.header.tls.value"), + // post configuration + if cfg.Runtime.GOMAXPROCS == -1 { + cfg.Runtime.GOMAXPROCS = runtime.NumCPU() } - readTimeout := durationVal(p, time.Duration(0), "proxy.readtimeout") - writeTimeout := durationVal(p, time.Duration(0), "proxy.writetimeout") + cfg.Registry.Consul.Scheme, cfg.Registry.Consul.Addr = parseScheme(cfg.Registry.Consul.Addr) - cfg.Listen, err = parseListen(stringVal(p, Default.Listen[0].Addr, "proxy.addr"), readTimeout, writeTimeout) + cfg.Listen, err = parseListen(cfg.Proxy.ListenerAddr, cfg.Proxy.ReadTimeout, cfg.Proxy.WriteTimeout) if err != nil { return nil, err } - cfg.Metrics = parseMetrics( - stringVal(p, Default.Metrics[0].Target, "metrics.target"), - stringVal(p, Default.Metrics[0].Prefix, "metrics.prefix"), - stringVal(p, Default.Metrics[0].Addr, "metrics.graphite.addr"), - durationVal(p, Default.Metrics[0].Interval, "metrics.interval"), - ) - - cfg.Registry = Registry{ - Backend: stringVal(p, Default.Registry.Backend, "registry.backend"), - File: File{ - Path: stringVal(p, Default.Registry.File.Path, "registry.file.path"), - }, - Static: Static{ - Routes: stringVal(p, Default.Registry.Static.Routes, "registry.static.routes"), - }, - Consul: Consul{ - Addr: stringVal(p, Default.Registry.Consul.Addr, "registry.consul.addr", "consul.addr"), - Token: stringVal(p, Default.Registry.Consul.Token, "registry.consul.token", "consul.token"), - KVPath: stringVal(p, Default.Registry.Consul.KVPath, "registry.consul.kvpath", "consul.kvpath"), - TagPrefix: stringVal(p, Default.Registry.Consul.TagPrefix, "registry.consul.tagprefix", "consul.tagprefix"), - ServiceAddr: stringVal(p, Default.Registry.Consul.ServiceAddr, "registry.consul.register.addr"), - ServiceName: stringVal(p, Default.Registry.Consul.ServiceName, "registry.consul.register.name", "consul.register.name"), - ServiceTags: stringAVal(p, Default.Registry.Consul.ServiceTags, "registry.consul.register.tags"), - CheckInterval: durationVal(p, Default.Registry.Consul.CheckInterval, "registry.consul.register.checkInterval", "consul.register.checkInterval"), - CheckTimeout: durationVal(p, Default.Registry.Consul.CheckTimeout, "registry.consul.register.checkTimeout", "consul.register.checkTimeout"), - }, + // handle deprecations + deprecate := func(name, msg string) { + if f.IsSet(name) { + log.Print("[WARN] ", msg) + } } + deprecate("proxy.timeout", "proxy.timeout has been replaced by proxy.responseheadertimeout and proxy.keepalivetimeout") deprecate("consul.addr", "consul.addr has been replaced by registry.consul.addr") deprecate("consul.token", "consul.token has been replaced by registry.consul.token") deprecate("consul.kvpath", "consul.kvpath has been replaced by registry.consul.kvpath") @@ -92,103 +127,27 @@ func fromProperties(p *properties.Properties) (cfg *Config, err error) { deprecate("consul.register.checkTimeout", "consul.register.checkTimeout has been replaced by registry.consul.register.checkTimeout") deprecate("consul.url", "consul.url is obsolete. Please remove it.") - proxyRoutes := stringVal(p, "", "proxy.routes") - if strings.HasPrefix(proxyRoutes, "@") { - cfg.Registry.Backend = "file" - cfg.Registry.File.Path = proxyRoutes[1:] - deprecate("proxy.routes", "Please use registry.backend=file and registry.file.path= instead of proxy.routes=@") - } else if proxyRoutes != "" { - cfg.Registry.Backend = "static" - cfg.Registry.Static.Routes = proxyRoutes - deprecate("proxy.routes", "Please use registry.backend=static and registry.static.routes= instead of proxy.routes=") - } - - cfg.Runtime = Runtime{ - GOGC: intVal(p, Default.Runtime.GOGC, "runtime.gogc"), - GOMAXPROCS: intVal(p, Default.Runtime.GOMAXPROCS, "runtime.gomaxprocs"), - } - if cfg.Runtime.GOMAXPROCS == -1 { - cfg.Runtime.GOMAXPROCS = runtime.NumCPU() - } - - cfg.UI = UI{ - Addr: stringVal(p, Default.UI.Addr, "ui.addr"), - Color: stringVal(p, Default.UI.Color, "ui.color"), - Title: stringVal(p, Default.UI.Title, "ui.title"), - } - return cfg, nil -} - -// stringVal returns the first non-empty value found or the default value. -// Keys are checked in order and environment variables take precedence over -// properties values. Environment varaible names are derived from property -// names by replacing the dots with underscores. -func stringVal(p *properties.Properties, def string, keys ...string) string { - for _, key := range keys { - if v := os.Getenv(strings.Replace(key, ".", "_", -1)); v != "" { - return v - } - if p == nil { - continue + if proxyTimeout > 0 { + if cfg.Proxy.ResponseHeaderTimeout == 0 { + cfg.Proxy.ResponseHeaderTimeout = proxyTimeout } - if v, ok := p.Get(key); ok { - return v + if cfg.Proxy.KeepAliveTimeout == 0 { + cfg.Proxy.KeepAliveTimeout = proxyTimeout } } - return def -} - -func stringAVal(p *properties.Properties, def []string, keys ...string) []string { - v := stringVal(p, "", keys...) - if v == "" { - return def - } - return splitSkipEmpty(v, ",") -} -func splitSkipEmpty(s, sep string) (vals []string) { - for _, v := range strings.Split(s, sep) { - v = strings.TrimSpace(v) - if v == "" { - continue - } - vals = append(vals, v) - } - return vals -} - -func intVal(p *properties.Properties, def int, keys ...string) int { - v := stringVal(p, "", keys...) - if v == "" { - return def - } - n, err := strconv.Atoi(v) - if err != nil { - log.Printf("[WARN] Invalid value %s for %v", v, keys) - return def - } - return n + return cfg, nil } -func durationVal(p *properties.Properties, def time.Duration, keys ...string) time.Duration { - v := stringVal(p, "", keys...) - if v == "" { - return def - } - d, err := time.ParseDuration(v) - if err != nil { - log.Printf("[WARN] Invalid duration %s for %v", v, keys) - return def +func parseScheme(s string) (scheme, addr string) { + s = strings.ToLower(s) + if strings.HasPrefix(s, "https://") { + return "https", s[len("https://"):] } - return d -} - -func parseMetrics(target, prefix, graphiteAddr string, interval time.Duration) []Metrics { - m := Metrics{Target: target, Prefix: prefix, Interval: interval} - if target == "graphite" { - m.Addr = graphiteAddr + if strings.HasPrefix(s, "http://") { + return "http", s[len("http://"):] } - return []Metrics{m} + return "http", s } func parseListen(addrs string, readTimeout, writeTimeout time.Duration) ([]Listen, error) { @@ -219,3 +178,27 @@ func parseListen(addrs string, readTimeout, writeTimeout time.Duration) ([]Liste } return listen, nil } + +type tags []string + +func (t *tags) String() string { + return strings.Join(*t, ",") +} + +func (t *tags) Set(value string) error { + for _, v := range splitSkipEmpty(value, ",") { + *t = append(*t, v) + } + return nil +} + +func splitSkipEmpty(s, sep string) (vals []string) { + for _, v := range strings.Split(s, sep) { + v = strings.TrimSpace(v) + if v == "" { + continue + } + vals = append(vals, v) + } + return vals +} diff --git a/config/load_test.go b/config/load_test.go index c68173554..55717cc92 100644 --- a/config/load_test.go +++ b/config/load_test.go @@ -1,7 +1,6 @@ package config import ( - "os" "reflect" "testing" "time" @@ -17,7 +16,8 @@ proxy.localip = 4.4.4.4 proxy.strategy = rr proxy.matcher = prefix proxy.shutdownwait = 500ms -proxy.timeout = 3s +proxy.responseheadertimeout = 3s +proxy.keepalivetimeout = 4s proxy.dialtimeout = 60s proxy.readtimeout = 5s proxy.writetimeout = 10s @@ -28,7 +28,7 @@ proxy.header.tls.value = tls-true registry.backend = something registry.file.path = /foo/bar registry.static.routes = route add svc / http://127.0.0.1:6666/ -registry.consul.addr = 1.2.3.4:5678 +registry.consul.addr = https://1.2.3.4:5678 registry.consul.token = consul-token registry.consul.kvpath = /some/path registry.consul.tagprefix = p- @@ -55,11 +55,14 @@ ui.title = fabfab Matcher: "prefix", ShutdownWait: 500 * time.Millisecond, DialTimeout: 60 * time.Second, - KeepAliveTimeout: 3 * time.Second, ResponseHeaderTimeout: 3 * time.Second, + KeepAliveTimeout: 4 * time.Second, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, ClientIPHeader: "clientip", TLSHeader: "tls", TLSHeaderValue: "tls-true", + ListenerAddr: ":1234", }, Registry: Registry{ Backend: "something", @@ -71,6 +74,7 @@ ui.title = fabfab }, Consul: Consul{ Addr: "1.2.3.4:5678", + Scheme: "https", Token: "consul-token", KVPath: "/some/path", TagPrefix: "p-", @@ -88,13 +92,11 @@ ui.title = fabfab WriteTimeout: 10 * time.Second, }, }, - Metrics: []Metrics{ - { - Target: "graphite", - Prefix: "someprefix", - Interval: 5 * time.Second, - Addr: "5.6.7.8:9999", - }, + Metrics: Metrics{ + Target: "graphite", + Prefix: "someprefix", + Interval: 5 * time.Second, + GraphiteAddr: "5.6.7.8:9999", }, Runtime: Runtime{ GOGC: 666, @@ -112,7 +114,7 @@ ui.title = fabfab t.Fatalf("got %v want nil", err) } - cfg, err := fromProperties(p) + cfg, err := load(p) if err != nil { t.Fatalf("got %v want nil", err) } @@ -121,63 +123,24 @@ ui.title = fabfab verify.Values(t, "cfg", got, want) } -func TestStringVal(t *testing.T) { - props := func(s string) *properties.Properties { - p, err := properties.Load([]byte(s), properties.UTF8) - if err != nil { - t.Fatal(err) - } - return p - } - +func TestParseScheme(t *testing.T) { tests := []struct { - env map[string]string - props *properties.Properties - keys []string - val string - def string + in string + scheme, addr string }{ - { - env: nil, - props: nil, - keys: []string{"key"}, val: "default", def: "default", - }, - { - env: map[string]string{"key": "env"}, - props: nil, - keys: []string{"key"}, val: "env", - }, - { - env: nil, - props: props("key=props"), - keys: []string{"key"}, val: "props", - }, - { - env: map[string]string{"key": "env"}, - props: props("key=props"), - keys: []string{"key"}, val: "env", - }, - { - env: map[string]string{"key": "env"}, - props: props("other=props"), - keys: []string{"other"}, val: "props", - }, - { - env: map[string]string{"key": "env"}, - props: props("other=props"), - keys: []string{"key", "other"}, val: "env", - }, + {"foo:bar", "http", "foo:bar"}, + {"http://foo:bar", "http", "foo:bar"}, + {"https://foo:bar", "https", "foo:bar"}, + {"HTTPS://FOO:bar", "https", "foo:bar"}, } for i, tt := range tests { - for k, v := range tt.env { - os.Setenv(k, v) - } - if got, want := stringVal(tt.props, tt.def, tt.keys...), tt.val; got != want { - t.Errorf("%d: got %s want %s", i, got, want) + scheme, addr := parseScheme(tt.in) + if got, want := scheme, tt.scheme; got != want { + t.Errorf("%d: got %v want %v", i, got, want) } - for k := range tt.env { - os.Unsetenv(k) + if got, want := addr, tt.addr; got != want { + t.Errorf("%d: got %v want %v", i, got, want) } } } diff --git a/fabio.properties b/fabio.properties index 6626ea6d1..2e27dbf20 100644 --- a/fabio.properties +++ b/fabio.properties @@ -81,17 +81,26 @@ # proxy.shutdownwait = 0s -# proxy.timeout configures the response header and keep-alive timeout. +# proxy.responseheadertimeout configures the response header timeout. # -# This configures the ResponseHeaderTimeout of the http.Transport -# and the KeepAliveTimeout of the network dialer. +# This configures the ResponseHeaderTimeout of the http.Transport. # # The default is # -# proxy.timeout = 0s +# proxy.responseheadertimeout = 0s -# proxy.dialtimeout configures the connection timeout. +# proxy.keepalivetimeout configures the keep-alive timeout. +# +# This configures the KeepAliveTimeout of the network dialer. +# +# The default is +# +# proxy.keepalivetimeout = 0s + + +# proxy.dialtimeout configures the connection timeout for +# outgoing connections. # # This configures the DialTimeout of the network dialer. # @@ -100,7 +109,8 @@ # proxy.dialtimeout = 30s -# proxy.maxconn configures the maximum number of cached connections. +# proxy.maxconn configures the maximum number of cached +# incoming and outgoing connections. # # This configures the MaxConnsPerHost of the http.Transport. # diff --git a/main.go b/main.go index 6ef0de15a..3f6548df3 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,6 @@ package main import ( "encoding/json" - "flag" - "fmt" "log" "net" "net/http" @@ -33,18 +31,7 @@ import ( var version = "1.1.3rc2" func main() { - var filename string - var v bool - flag.StringVar(&filename, "cfg", "", "path to config file") - flag.BoolVar(&v, "v", false, "show version") - flag.Parse() - - if v { - fmt.Println(version) - return - } - - cfg, err := config.Load(filename) + cfg, err := config.Load() if err != nil { log.Fatalf("[FATAL] %s. %s", version, err) } diff --git a/metrics/metrics.go b/metrics/metrics.go index 5a2a34dee..6b1a938c8 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -23,16 +23,7 @@ var pfx string // with globally registered timers. var ServiceRegistry = gometrics.NewRegistry() -func Init(cfgs []config.Metrics) error { - for _, cfg := range cfgs { - if err := initMetrics(cfg); err != nil { - return err - } - } - return nil -} - -func initMetrics(cfg config.Metrics) error { +func Init(cfg config.Metrics) error { pfx = cfg.Prefix if pfx == "default" { pfx = defaultPrefix() @@ -43,12 +34,12 @@ func initMetrics(cfg config.Metrics) error { log.Printf("[INFO] Sending metrics to stdout") return initStdout(cfg.Interval) case "graphite": - if cfg.Addr == "" { + if cfg.GraphiteAddr == "" { return errors.New("metrics: graphite addr missing") } - log.Printf("[INFO] Sending metrics to Graphite on %s as %q", cfg.Addr, pfx) - return initGraphite(cfg.Addr, cfg.Interval) + log.Printf("[INFO] Sending metrics to Graphite on %s as %q", cfg.GraphiteAddr, pfx) + return initGraphite(cfg.GraphiteAddr, cfg.Interval) case "": log.Printf("[INFO] Metrics disabled") default: diff --git a/registry/consul/backend.go b/registry/consul/backend.go index 50e8039cf..a142e3e71 100644 --- a/registry/consul/backend.go +++ b/registry/consul/backend.go @@ -21,7 +21,7 @@ type be struct { func NewBackend(cfg *config.Consul) (registry.Backend, error) { // create a reusable client - c, err := api.NewClient(&api.Config{Address: cfg.Addr, Scheme: "http", Token: cfg.Token}) + c, err := api.NewClient(&api.Config{Address: cfg.Addr, Scheme: cfg.Scheme, Token: cfg.Token}) if err != nil { return nil, err }