From 662cc012f9f2a437ee45a8fec7b1ed7917265dff Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Tue, 14 Apr 2020 16:17:35 +0200 Subject: [PATCH 1/6] Switch to using pflag Signed-off-by: Ondrej Fabry --- agent/agent.go | 2 +- config/plugin_config.go | 2 +- examples/flags-lib/main.go | 2 +- .../kafka-lib/asyncproducer/asyncproducer.go | 2 +- examples/kafka-lib/consumer/consumer.go | 4 +- examples/kafka-lib/mux/main.go | 2 +- .../kafka-lib/syncproducer/syncproducer.go | 2 +- .../kafka-plugin/hash-partitioner/main.go | 4 +- .../kafka-plugin/manual-partitioner/main.go | 4 +- .../test-process/test-process.go | 2 +- examples/redis-lib/airport/airport.go | 4 +- examples/redis-lib/simple/simple.go | 2 +- go.mod | 2 +- go.sum | 4 +- rpc/grpc/config.go | 2 +- rpc/rest/config.go | 57 +++++++++---------- servicelabel/plugin_impl_servicelabel.go | 2 +- 17 files changed, 48 insertions(+), 51 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 5f03def18..02cfe52f9 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -24,7 +24,7 @@ import ( "sync" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/config" "go.ligato.io/cn-infra/v2/infra" diff --git a/config/plugin_config.go b/config/plugin_config.go index 6d67c91be..58ccc4b93 100644 --- a/config/plugin_config.go +++ b/config/plugin_config.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/logging/logrus" ) diff --git a/examples/flags-lib/main.go b/examples/flags-lib/main.go index f7217d9a0..9c89bbcaf 100644 --- a/examples/flags-lib/main.go +++ b/examples/flags-lib/main.go @@ -4,7 +4,7 @@ import ( "fmt" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" ) // ************************************************************************* diff --git a/examples/kafka-lib/asyncproducer/asyncproducer.go b/examples/kafka-lib/asyncproducer/asyncproducer.go index 65d211cb8..71fde9583 100644 --- a/examples/kafka-lib/asyncproducer/asyncproducer.go +++ b/examples/kafka-lib/asyncproducer/asyncproducer.go @@ -19,7 +19,7 @@ import ( "os" "strings" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/examples/kafka-lib/utils" "go.ligato.io/cn-infra/v2/logging/logrus" diff --git a/examples/kafka-lib/consumer/consumer.go b/examples/kafka-lib/consumer/consumer.go index edfb7c99d..3ffe217f0 100644 --- a/examples/kafka-lib/consumer/consumer.go +++ b/examples/kafka-lib/consumer/consumer.go @@ -21,8 +21,8 @@ import ( "time" "github.com/Shopify/sarama" - "github.com/bsm/sarama-cluster" - "github.com/namsral/flag" + cluster "github.com/bsm/sarama-cluster" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/logging/logrus" diff --git a/examples/kafka-lib/mux/main.go b/examples/kafka-lib/mux/main.go index 7b7e852dd..4a3425be0 100644 --- a/examples/kafka-lib/mux/main.go +++ b/examples/kafka-lib/mux/main.go @@ -20,7 +20,7 @@ import ( "os/signal" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/logging/logrus" diff --git a/examples/kafka-lib/syncproducer/syncproducer.go b/examples/kafka-lib/syncproducer/syncproducer.go index 92e175c25..c3f0a1ab5 100644 --- a/examples/kafka-lib/syncproducer/syncproducer.go +++ b/examples/kafka-lib/syncproducer/syncproducer.go @@ -19,7 +19,7 @@ import ( "os" "strings" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/examples/kafka-lib/utils" "go.ligato.io/cn-infra/v2/logging" diff --git a/examples/kafka-plugin/hash-partitioner/main.go b/examples/kafka-plugin/hash-partitioner/main.go index b4adb469c..c9dab0f61 100644 --- a/examples/kafka-plugin/hash-partitioner/main.go +++ b/examples/kafka-plugin/hash-partitioner/main.go @@ -7,10 +7,10 @@ import ( "strconv" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/agent" - "go.ligato.io/cn-infra/v2/examples/model" + etcdexample "go.ligato.io/cn-infra/v2/examples/model" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/messaging" "go.ligato.io/cn-infra/v2/messaging/kafka" diff --git a/examples/kafka-plugin/manual-partitioner/main.go b/examples/kafka-plugin/manual-partitioner/main.go index 9f3ac911e..46fafa301 100644 --- a/examples/kafka-plugin/manual-partitioner/main.go +++ b/examples/kafka-plugin/manual-partitioner/main.go @@ -7,10 +7,10 @@ import ( "strconv" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/agent" - "go.ligato.io/cn-infra/v2/examples/model" + etcdexample "go.ligato.io/cn-infra/v2/examples/model" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/messaging" "go.ligato.io/cn-infra/v2/messaging/kafka" diff --git a/examples/process-manager-plugin/test-process/test-process.go b/examples/process-manager-plugin/test-process/test-process.go index 49afc575a..f815c2664 100644 --- a/examples/process-manager-plugin/test-process/test-process.go +++ b/examples/process-manager-plugin/test-process/test-process.go @@ -21,7 +21,7 @@ package main import ( "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/logging/logrus" ) diff --git a/examples/redis-lib/airport/airport.go b/examples/redis-lib/airport/airport.go index e2c8889ba..36a2241f6 100644 --- a/examples/redis-lib/airport/airport.go +++ b/examples/redis-lib/airport/airport.go @@ -28,14 +28,14 @@ import ( "sync/atomic" "time" - "github.com/namsral/flag" "github.com/pkg/errors" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/datasync" "go.ligato.io/cn-infra/v2/db/keyval" "go.ligato.io/cn-infra/v2/db/keyval/kvproto" "go.ligato.io/cn-infra/v2/db/keyval/redis" - "go.ligato.io/cn-infra/v2/examples/redis-lib/airport/model" + flight "go.ligato.io/cn-infra/v2/examples/redis-lib/airport/model" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/logging/logrus" "go.ligato.io/cn-infra/v2/utils/safeclose" diff --git a/examples/redis-lib/simple/simple.go b/examples/redis-lib/simple/simple.go index 70701cd74..ffb7023b2 100644 --- a/examples/redis-lib/simple/simple.go +++ b/examples/redis-lib/simple/simple.go @@ -6,7 +6,7 @@ import ( "strconv" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/datasync" "go.ligato.io/cn-infra/v2/db/keyval" diff --git a/go.mod b/go.mod index 8d08c0fea..d2fc374af 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,6 @@ require ( github.com/mitchellh/mapstructure v1.1.2 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect - github.com/namsral/flag v1.7.4-pre github.com/onsi/gomega v1.4.3 github.com/philhofer/fwd v1.0.0 // indirect github.com/pierrec/lz4 v2.3.0+incompatible // indirect @@ -69,6 +68,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect github.com/sirupsen/logrus v1.4.2 github.com/soheilhy/cmux v0.1.4 // indirect + github.com/spf13/pflag v1.0.5 github.com/tinylib/msgp v1.0.2 // indirect github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d diff --git a/go.sum b/go.sum index bb9682fa4..ebedd8315 100644 --- a/go.sum +++ b/go.sum @@ -166,8 +166,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/namsral/flag v1.7.4-pre h1:b2ScHhoCUkbsq0d2C15Mv+VU8bl8hAXV8arnWiOHNZs= -github.com/namsral/flag v1.7.4-pre/go.mod h1:OXldTctbM6SWH1K899kPZcf65KxJiD7MsceFUpB5yDo= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= @@ -199,6 +197,8 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= diff --git a/rpc/grpc/config.go b/rpc/grpc/config.go index 888e441a7..9fa62d8d1 100644 --- a/rpc/grpc/config.go +++ b/rpc/grpc/config.go @@ -22,7 +22,7 @@ import ( "strconv" "strings" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "google.golang.org/grpc" "go.ligato.io/cn-infra/v2/config" diff --git a/rpc/rest/config.go b/rpc/rest/config.go index 012cfcd4d..3919a481c 100644 --- a/rpc/rest/config.go +++ b/rpc/rest/config.go @@ -19,7 +19,7 @@ import ( "strings" "time" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/config" "go.ligato.io/cn-infra/v2/infra" @@ -35,6 +35,13 @@ const ( DefaultEndpoint = DefaultHost + ":" + DefaultHTTPPort ) +// DefaultConfig returns new instance of config with default endpoint +func DefaultConfig() *Config { + return &Config{ + Endpoint: DefaultEndpoint, + } +} + // Config is a configuration for HTTP server // It is meant to be extended with security (TLS...) type Config struct { @@ -118,11 +125,23 @@ type Config struct { } `json:"rate-limiter"` } -// DefaultConfig returns new instance of config with default endpoint -func DefaultConfig() *Config { - return &Config{ - Endpoint: DefaultEndpoint, +// GetPort parses suffix from endpoint & returns integer after last ":" (otherwise it returns 0) +func (cfg *Config) GetPort() int { + if cfg.Endpoint != "" && cfg.Endpoint != ":" { + index := strings.LastIndex(cfg.Endpoint, ":") + if index >= 0 { + port, err := strconv.Atoi(cfg.Endpoint[index+1:]) + if err == nil { + return port + } + } } + return 0 +} + +// UseHTTPS returns true if server certificate and key is defined. +func (cfg *Config) UseHTTPS() bool { + return cfg.ServerCertfile != "" && cfg.ServerKeyfile != "" } // PluginConfig tries : @@ -136,11 +155,9 @@ func PluginConfig(pluginCfg config.PluginConfig, cfg *Config, pluginName infra.P cfg.Endpoint = DefaultHost + ":" + portFlag.Value.String() } - if pluginCfg != nil { - _, err := pluginCfg.LoadValue(cfg) - if err != nil { - return err - } + _, err := pluginCfg.LoadValue(cfg) + if err != nil { + return err } FixConfig(cfg) @@ -158,26 +175,6 @@ func FixConfig(cfg *Config) { } } -// GetPort parses suffix from endpoint & returns integer after last ":" (otherwise it returns 0) -func (cfg *Config) GetPort() int { - if cfg.Endpoint != "" && cfg.Endpoint != ":" { - index := strings.LastIndex(cfg.Endpoint, ":") - if index >= 0 { - port, err := strconv.Atoi(cfg.Endpoint[index+1:]) - if err == nil { - return port - } - } - } - - return 0 -} - -// UseHTTPS returns true if server certificate and key is defined. -func (cfg *Config) UseHTTPS() bool { - return cfg.ServerCertfile != "" && cfg.ServerKeyfile != "" -} - // DeclareHTTPPortFlag declares http port (with usage & default value) a flag for a particular plugin name func DeclareHTTPPortFlag(pluginName infra.PluginName, defaultPortOpts ...uint) { var defaultPort string diff --git a/servicelabel/plugin_impl_servicelabel.go b/servicelabel/plugin_impl_servicelabel.go index 73a14de11..ad37d282a 100644 --- a/servicelabel/plugin_impl_servicelabel.go +++ b/servicelabel/plugin_impl_servicelabel.go @@ -17,7 +17,7 @@ package servicelabel import ( "fmt" - "github.com/namsral/flag" + flag "github.com/spf13/pflag" "go.ligato.io/cn-infra/v2/infra" "go.ligato.io/cn-infra/v2/logging/logrus" From 3dce724e4ad1707dc52030a5191e2badbe9e76b5 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 17 Apr 2020 12:57:38 +0200 Subject: [PATCH 2/6] WIP: Refactor config and agent Signed-off-by: Ondrej Fabry --- agent/agent.go | 152 ++++++++++++++++--- agent/flags.go | 52 +++++++ agent/options.go | 116 +++++++++----- config/config.go | 173 +++++++++++++++++++++ config/config_test.go | 61 -------- config/configfileplugin.conf | 1 - config/default.go | 61 ++++++++ config/doc.go | 17 --- config/parser.go | 14 ++ config/plugin_config.go | 184 ++++++++++++++++++----- config/plugin_config_test.go | 134 +++++++++++++++++ config/testdata/config.yaml | 3 + config/testdata/testplugin.conf | 11 ++ examples/configs-plugin/config.yml | 8 + examples/configs-plugin/main.go | 100 ++++++------ go.mod | 28 +--- go.sum | 149 ++++++++++++++---- servicelabel/plugin_api_servicelabel.go | 9 +- servicelabel/plugin_impl_servicelabel.go | 17 ++- 19 files changed, 992 insertions(+), 298 deletions(-) create mode 100644 agent/flags.go create mode 100644 config/config.go delete mode 100644 config/config_test.go delete mode 100644 config/configfileplugin.conf create mode 100644 config/default.go delete mode 100644 config/doc.go create mode 100644 config/plugin_config_test.go create mode 100644 config/testdata/config.yaml create mode 100644 config/testdata/testplugin.conf create mode 100644 examples/configs-plugin/config.yml diff --git a/agent/agent.go b/agent/agent.go index 02cfe52f9..aeb553f98 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -17,6 +17,7 @@ package agent import ( "errors" "fmt" + "log" "os" "os/signal" "runtime" @@ -52,6 +53,8 @@ type Agent interface { // close of quit channel (can be set via options) and then stops the agent. // Returns nil if all the plugins were intialized and closed successfully. Run() error + // Init applies given options to the agent. + Init(...Option) // Start starts the agent with all the plugins, calling their Init() and optionally AfterInit(). // Returns nil if all the plugins were initialized successfully. Start() error @@ -60,7 +63,6 @@ type Agent interface { Stop() error // Options returns all agent's options configured via constructor. Options() Options - // Wait waits until agent is stopped and returns same error as Stop(). Wait() error // After returns a channel that is closed before the agents is stopped. @@ -71,25 +73,27 @@ type Agent interface { Error() error } +var ( + DefaultAgent Agent = NewAgent() +) + +func Init(opts ...Option) { DefaultAgent.Init(opts...) } +func Start() error { return DefaultAgent.Start() } +func Stop() error { return DefaultAgent.Stop() } +func Wait() error { return DefaultAgent.Wait() } +func Run() error { return DefaultAgent.Run() } + +/*func Run() error { + if err := Start(); err != nil { + return err + } + return Wait() +}*/ + // NewAgent creates a new agent using given options and registers all flags // defined for plugins via config.ForPlugin. func NewAgent(opts ...Option) Agent { - options := newOptions(opts...) - - if !flag.Parsed() { - config.DefineDirFlag() - for _, p := range options.Plugins { - name := p.String() - infraLogger.Debugf("registering flags for: %q", name) - config.DefineFlagsFor(name) - } - flag.Parse() - } - - return &agent{ - opts: options, - tracer: measure.NewTracer("agent-plugins"), - } + return newAgent(opts...) } type agent struct { @@ -106,6 +110,46 @@ type agent struct { curPlugin infra.Plugin } +func newAgent(opts ...Option) *agent { + options := NewOptions(opts...) + return &agent{ + opts: options, + tracer: measure.NewTracer("agent-plugins"), + } +} + +func (a *agent) Name() string { + return a.opts.Name +} + +func (a *agent) Init(opts ...Option) { + for _, o := range opts { + o(&a.opts) + } + /*if err := a.init(); err != nil { + agentLogger.Fatal("agent init error:", err) + }*/ +} + +func (a *agent) setup() error { + if a.opts.initialized { + return nil + } + a.opts.initialized = true + + if a.opts.Conf == nil { + a.opts.Conf = config.DefaultConf + } + + if err := a.parseFlags(); err != nil { + return fmt.Errorf("flags error: %w", err) + } + if err := a.loadConfig(); err != nil { + return fmt.Errorf("config error: %w", err) + } + return nil +} + // Options returns the Options the agent was created with func (a *agent) Options() Options { return a.opts @@ -130,11 +174,69 @@ func (a *agent) Run() error { return a.Wait() } +func (a *agent) parseFlags() error { + flagSet := flag.NewFlagSet(a.Name(), flag.ExitOnError) + flagSet.SortFlags = false + //flags.SetOutput(ioutil.Discard) + + log.Printf("adding %d flags from CommandLine", strings.Count(flag.CommandLine.FlagUsages(), "\n")) + flagSet.AddFlagSet(flag.CommandLine) + + // global flags + AddFlagsTo(flagSet) + //flags.AddGoFlagSet(goflag.CommandLine) + //flags.AddFlagSet(FlagSet) + + for _, p := range a.opts.Plugins { + name := p.String() + infraLogger.Debugf("registering flags for: %q", name) + flagSet.AddFlagSet(config.GetFlagSetFor(name)) + } + + err := flagSet.Parse(os.Args[1:]) + if errors.Is(err, flag.ErrHelp) { + os.Exit(2) + } else if err != nil { + //fmt.Fprintf(os.Stderr, "Usage of %s:\n", a.Name()) + //fmt.Fprint(os.Stderr, flags.FlagUsages()) + return err + } + + ver, err := flagSet.GetBool("version") + if ver { + fmt.Fprintf(os.Stdout, "%s %s\n", a.opts.Name, a.opts.Version) + os.Exit(0) + } else if err != nil { + log.Println(err) + } + + logLevel, err := flagSet.GetString("log-level") + if err == nil && logLevel != "" { + lvl, _ := logging.ParseLogLevel(logLevel) + agentLogger.Infoln("setting log level to:", lvl) + logging.DefaultLogger.SetLevel(lvl) + } + + return nil +} +func (a *agent) loadConfig() error { + conf := a.opts.Conf + if err := conf.Load(); err != nil { + return err + } + return nil +} + func (a *agent) starter() error { + if err := a.setup(); err != nil { + return err + } + agentLogger.WithFields(logging.Fields{ "CommitHash": CommitHash, "BuildDate": BuildDate, - }).Infof("Starting agent version: %v", BuildVersion) + "Version": BuildVersion, + }).Infof("Starting %s", a.opts.Name) // If we want to properly handle cleanup when a SIG comes in *during* // agent startup (ie, clean up after its finished) we need to register @@ -150,7 +252,7 @@ func (a *agent) starter() error { go func() { select { case s := <-sig: - agentLogger.Infof("Signal %v received during agent start, stopping", s) + agentLogger.Infof("Signal %v received during starting, stopping", s) os.Exit(1) case <-started: // agent started @@ -158,7 +260,7 @@ func (a *agent) starter() error { a.mu.Lock() curPlugin := a.curPlugin a.mu.Unlock() - agentLogger.Errorf("Agent failed to start before timeout (%v) last plugin: %s", timeout, curPlugin) + agentLogger.Errorf("Failed to start before timeout (%v) last plugin: %s", timeout, curPlugin) dumpStacktrace() os.Exit(1) } @@ -176,8 +278,8 @@ func (a *agent) starter() error { } close(started) - agentLogger.Infof("Agent started with %d plugins (took %v)", - len(a.opts.Plugins), time.Since(t).Round(time.Millisecond)) + agentLogger.Infof("Started %s with %d plugins (took %v)", + a.Name(), len(a.opts.Plugins), time.Since(t).Round(time.Millisecond)) a.stopCh = make(chan struct{}) // If we are started, we have a stopCh to signal stopping @@ -207,7 +309,7 @@ func (a *agent) starter() error { } func (a *agent) start() error { - agentLogger.Debugf("starting %d plugins", len(a.opts.Plugins)) + agentLogger.Debugf("starting init for %d plugins", len(a.opts.Plugins)) // Init plugins for _, plugin := range a.opts.Plugins { @@ -266,7 +368,7 @@ func (a *agent) start() error { } func (a *agent) stopper() error { - agentLogger.Infof("Stopping agent") + agentLogger.Infof("Stopping %s", a.Name()) stopped := make(chan struct{}) defer close(stopped) @@ -288,7 +390,7 @@ func (a *agent) stopper() error { return err } - agentLogger.Info("Agent stopped") + agentLogger.Infof("Stopped %s", a.Name()) return nil } diff --git a/agent/flags.go b/agent/flags.go new file mode 100644 index 000000000..71483abc0 --- /dev/null +++ b/agent/flags.go @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agent + +import ( + flag "github.com/spf13/pflag" + + "go.ligato.io/cn-infra/v2/config" +) + +func init() { + config.SetDefault("log-level", "info") + config.BindEnv("log-level", "LOG_LEVEL") + config.SetDefault("config", "") + config.BindEnv("config", "CONFIG_FILE") +} + +func AddFlagsTo(set *flag.FlagSet) { + set.StringP("config", "", "", "Config file location.") + set.StringP("log-level", "", "", "Set the logging level (debug|info|warn|error|fatal).") + set.BoolP("debug", "D", false, "Enable debug mode.") + set.BoolP("version", "V", false, "Print version and exit.") +} + +/* +var ( + FlagSet = flag.NewFlagSet("agent", flag.ExitOnError) + + logLevelVal string + configVal string + versionVal bool + debugVal bool +) + +func init() { + FlagSet.StringVarP(&logLevelVal, "log-level", "", "info", "Set the logging level (debug|info|warn|error|fatal).") + FlagSet.StringVarP(&configVal, "config", "", "", "Config file location.") + FlagSet.BoolVarP(&versionVal, "version", "V", false, "Print version and exit.") + FlagSet.BoolVarP(&debugVal, "debug", "D", false, "Enable debug mode.") +}*/ diff --git a/agent/options.go b/agent/options.go index 450723551..c16c61c9c 100644 --- a/agent/options.go +++ b/agent/options.go @@ -21,6 +21,7 @@ import ( "syscall" "time" + "go.ligato.io/cn-infra/v2/config" "go.ligato.io/cn-infra/v2/infra" ) @@ -36,39 +37,67 @@ var ( // Options specifies option list for the Agent type Options struct { + Name string + Version string + + Conf config.Config + //FlagSet *config.FlagSet + Flags []config.Flag + StartTimeout time.Duration StopTimeout time.Duration QuitSignals []os.Signal QuitChan chan struct{} Context context.Context - Plugins []infra.Plugin - pluginMap map[infra.Plugin]struct{} - pluginNames map[string]struct{} + Plugins []infra.Plugin + //pluginMap map[infra.Plugin]struct{} + //pluginNames map[string]struct{} + + initialized bool } -func newOptions(opts ...Option) Options { +func NewOptions(opts ...Option) Options { opt := Options{ + Name: "agent", + Version: "dev", + Conf: config.DefaultConf, StartTimeout: DefaultStartTimeout, StopTimeout: DefaultStopTimeout, QuitSignals: []os.Signal{ - os.Interrupt, + syscall.SIGINT, syscall.SIGTERM, }, - pluginMap: make(map[infra.Plugin]struct{}), - pluginNames: make(map[string]struct{}), } - for _, o := range opts { o(&opt) } - return opt } // Option is a function that operates on an Agent's Option type Option func(*Options) +// Name sets agent name. +func Name(name string) Option { + return func(o *Options) { + o.Name = name + } +} + +// Version sets agent version. +func Version(version string) Option { + return func(o *Options) { + o.Version = version + } +} + +func Flags(flags ...config.Flag) Option { + return func(o *Options) { + o.Flags = append(o.Flags, flags...) + } +} + // StartTimeout returns an Option that sets timeout for the start of Agent. func StartTimeout(timeout time.Duration) Option { return func(o *Options) { @@ -83,8 +112,8 @@ func StopTimeout(timeout time.Duration) Option { } } -// Version returns an Option that sets the version of the Agent to the entered string -func Version(buildVer, buildDate, commitHash string) Option { +// VersionInfo sets the version of the Agent to the entered string +func VersionInfo(buildVer, buildDate, commitHash string) Option { return func(o *Options) { BuildVersion = buildVer BuildDate = buildDate @@ -122,41 +151,48 @@ func Plugins(plugins ...infra.Plugin) Option { // AllPlugins creates an Option that adds all of the nested // plugins recursively to the Agent's plugin list. -func AllPlugins(plugins ...infra.Plugin) Option { - return func(o *Options) { - infraLogger.Debugf("AllPlugins with %d plugins", len(plugins)) +func AllPlugins(plugin infra.Plugin) Option { + pluginMap := make(map[infra.Plugin]struct{}) + pluginNames := make(map[string]struct{}) - for _, plugin := range plugins { - typ := reflect.TypeOf(plugin) - infraLogger.Debugf("searching for all deps in: %v (type: %v)", plugin, typ) + return func(o *Options) { + typ := reflect.TypeOf(plugin) + infraLogger.Debugf("searching for all deps in: %v (type: %v)", plugin, typ) - foundPlugins, err := findPlugins(reflect.ValueOf(plugin), o.pluginMap) - if err != nil { - panic(err) - } + foundPlugins, err := findPlugins(reflect.ValueOf(plugin), pluginMap) + if err != nil { + panic(err) + } - infraLogger.Debugf("found %d plugins in: %v (type: %v)", len(foundPlugins), plugin, typ) - for _, plug := range foundPlugins { - infraLogger.Debugf(" - plugin: %v (%v)", plug, reflect.TypeOf(plug)) + infraLogger.Debugf("found %d plugins in: %v (type: %v)", len(foundPlugins), plugin, typ) + for _, plug := range foundPlugins { + infraLogger.Debugf(" - plugin: %v (%v)", plug, reflect.TypeOf(plug)) - if _, ok := o.pluginNames[plug.String()]; ok { - infraLogger.Fatalf("plugin with name %q already registered", plug.String()) - } - o.pluginNames[plug.String()] = struct{}{} + if _, ok := pluginNames[plug.String()]; ok { + infraLogger.Fatalf("plugin with name %q already registered", plug.String()) } - o.Plugins = append(o.Plugins, foundPlugins...) - - // TODO: perhaps set plugin name to typ.String() if it's empty - /*p, ok := plugin.(core.PluginNamed) - if !ok { - p = core.NamePlugin(typ.String(), plugin) - }*/ - - if _, ok := o.pluginNames[plugin.String()]; ok { - infraLogger.Fatalf("plugin with name %q already registered, custom name should be used", plugin.String()) + pluginNames[plug.String()] = struct{}{} + } + o.Plugins = append(o.Plugins, foundPlugins...) + + // TODO: perhaps set plugin name to typ.String() if it's empty + /*p, ok := plugin.(core.PluginNamed) + if !ok { + p = core.NamePlugin(typ.String(), plugin) + }*/ + + //pluginName := plugin.String() + /*if plugin.String() == "" { + if p, ok := plugin.(interface{ SetName(string) }); ok { + infraLogger.Warnf("setting plugin name to: %q", typ.String()) + p.SetName(typ.String()) } - o.pluginNames[plugin.String()] = struct{}{} - o.Plugins = append(o.Plugins, plugin) + }*/ + + if _, ok := pluginNames[plugin.String()]; ok { + infraLogger.Fatalf("plugin with name %q already registered, custom name should be used", plugin.String()) } + pluginNames[plugin.String()] = struct{}{} + o.Plugins = append(o.Plugins, plugin) } } diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..e009490ce --- /dev/null +++ b/config/config.go @@ -0,0 +1,173 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "errors" + "io" + "os" + "strings" + "time" + + "github.com/spf13/viper" + + "go.ligato.io/cn-infra/v2/logging" + "go.ligato.io/cn-infra/v2/logging/logrus" +) + +// Config defines API for config management. +type Config interface { + Load() error + LoadFrom(io.Reader) error + MergeFrom(io.Reader) error + MergeMap(map[string]interface{}) error + WriteAs(filename string) error + + BindEnv(key string, env string) error + BindFlag(key string, flag *Flag) error + SetDefault(key string, val interface{}) + Set(key string, val interface{}) + + Sub(key string) Config + All() map[string]interface{} + Unmarshal(cfg interface{}) error + UnmarshalKey(key string, cfg interface{}) error + + Get(key string) interface{} + GetString(key string) string + GetBool(key string) bool + GetInt(key string) int + GetInt32(key string) int32 + GetInt64(key string) int64 + GetUint(key string) uint + GetUint32(key string) uint32 + GetUint64(key string) uint64 + GetFloat64(key string) float64 + GetTime(key string) time.Time + GetDuration(key string) time.Duration + GetIntSlice(key string) []int + GetStringSlice(key string) []string + GetStringMap(key string) map[string]interface{} + GetStringMapString(key string) map[string]string + GetStringMapStringSlice(key string) map[string][]string +} + +// NewConfig returns fresh new Config instance. +func NewConfig() Config { + v := viper.NewWithOptions() + return newConf(v) +} + +var logger = logrus.NewLogger("config") + +func init() { + if debug := os.Getenv("DEBUG"); strings.Contains(debug, "config") { + logger.SetLevel(logging.TraceLevel) + } +} + +type Conf struct { + *viper.Viper +} + +func newConf(v *viper.Viper) *Conf { + if v.Get(DirFlag) == nil { + v.SetDefault(DirFlag, ".") + v.BindEnv(DirFlag, "CONFIG_DIR") + } + return &Conf{Viper: v} +} + +func (c *Conf) Load() error { + configDir := c.Viper.GetString(DirFlag) + + logger.Debugf("adding config path: %q", configDir) + c.Viper.AddConfigPath(configDir) + + if err := c.Viper.ReadInConfig(); err != nil { + var notFoundErr viper.ConfigFileNotFoundError + if !errors.As(err, ¬FoundErr) { + logger.Debugf("ReadInConfig() error: %T %v", err, err) + return err + } + } + + logger.Debugf("loaded config from: %q", c.Viper.ConfigFileUsed()) + + if logger.GetLevel() >= logging.TraceLevel { + c.Viper.Debug() + } + + return nil +} + +func (c *Conf) LoadFrom(r io.Reader) error { + return c.Viper.ReadConfig(r) +} + +func (c *Conf) MergeFrom(r io.Reader) error { + return c.Viper.MergeConfig(r) +} + +func (c *Conf) MergeMap(m map[string]interface{}) error { + return c.Viper.MergeConfigMap(m) +} + +func (c *Conf) WriteAs(filename string) error { + logger.Tracef("Conf.WriteAs %q", filename) + return c.Viper.WriteConfigAs(filename) +} + +func (c *Conf) BindEnv(key string, env string) error { + logger.Tracef("bind config key %q to ENV %q", key, env) + err := c.Viper.BindEnv(key, env) + if err != nil { + logger.Debugf("ERROR binding to env: %v", err) + } + return err +} + +func (c *Conf) BindFlag(key string, flag *Flag) error { + logger.Tracef("bind config key %q to FLAG %q", key, flag.Name) + err := c.Viper.BindPFlag(key, flag) + if err != nil { + logger.Debugf("ERROR binding to flag: %v", err) + } + return err +} + +func (c *Conf) Sub(key string) Config { + logger.Tracef("Conf.Sub %q", key) + sub := c.Viper.Sub(key) + if sub == nil { + return nil + } + return newConf(sub) +} + +func (c *Conf) All() map[string]interface{} { + logger.Debugf("Conf.All: %+v", c.All()) + return c.Viper.AllSettings() +} + +func (c *Conf) Unmarshal(cfg interface{}) error { + logger.Tracef("Conf.Unmarshal") + return c.Viper.Unmarshal(cfg) +} + +func (c *Conf) UnmarshalKey(key string, cfg interface{}) error { + logger.Tracef("Conf.UnmarshalKey %q", key) + return c.Viper.UnmarshalKey(key, cfg) +} diff --git a/config/config_test.go b/config/config_test.go deleted file mode 100644 index 9a97fa4a6..000000000 --- a/config/config_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2018 Cisco and/or its affiliates. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package config_test - -import ( - "testing" - - . "github.com/onsi/gomega" - - "go.ligato.io/cn-infra/v2/config" -) - -const ( - pluginWithConfigFileName = "configfileplugin" -) - -func TestForPluginWithConfigFile(t *testing.T) { - RegisterTestingT(t) - flagDefault := pluginWithConfigFileName + ".conf" - pluginConfig := config.ForPlugin(pluginWithConfigFileName) - Expect(pluginConfig).ShouldNot(BeNil()) - - config.DefineFlagsFor(pluginWithConfigFileName) - Expect(pluginConfig.GetConfigName()).Should(BeEquivalentTo(flagDefault)) -} - -func TestForPluginWithoutConfigFile(t *testing.T) { - RegisterTestingT(t) - pluginName := "confignofileplugin" - pluginConfig := config.ForPlugin(pluginName) - Expect(pluginConfig).ShouldNot(BeNil()) - - config.DefineFlagsFor(pluginName) - configName := pluginConfig.GetConfigName() - Expect(configName).Should(BeEquivalentTo("")) -} - -func TestForPluginWithSpecifiedConfigFile(t *testing.T) { - RegisterTestingT(t) - pluginName := "confignofileplugin2" - configFileName := pluginWithConfigFileName + ".conf" - pluginConfig := config.ForPlugin(pluginName, config.WithCustomizedFlag( - config.FlagName(pluginName), configFileName, "customized config filename")) - Expect(pluginConfig).ShouldNot(BeNil()) - - config.DefineFlagsFor(pluginName) - configName := pluginConfig.GetConfigName() - Expect(configName).Should(BeEquivalentTo(configFileName)) -} diff --git a/config/configfileplugin.conf b/config/configfileplugin.conf deleted file mode 100644 index 1c6bae417..000000000 --- a/config/configfileplugin.conf +++ /dev/null @@ -1 +0,0 @@ -foo = bar \ No newline at end of file diff --git a/config/default.go b/config/default.go new file mode 100644 index 000000000..6c0627e94 --- /dev/null +++ b/config/default.go @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "io" + "time" + + "github.com/spf13/viper" +) + +var ( + DefaultConf Config = newConf(defaultViper) + + defaultViper = viper.GetViper() +) + +func init() { + viper.SupportedExts = append(viper.SupportedExts, "conf") + defaultViper.SetConfigName("config") + defaultViper.SetConfigType("yaml") + defaultViper.SetDefault(DirFlag, DirDefault) + defaultViper.BindEnv(DirFlag, "CONFIG_DIR") + defaultViper.AutomaticEnv() + //defaultViper.SetTypeByDefaultValue(true) +} + +func Load() error { return DefaultConf.Load() } +func LoadFrom(r io.Reader) error { return DefaultConf.LoadFrom(r) } +func MergeFrom(r io.Reader) error { return DefaultConf.MergeFrom(r) } +func MergeMap(m map[string]interface{}) error { return DefaultConf.MergeMap(m) } +func WriteAs(filename string) error { return DefaultConf.WriteAs(filename) } +func Unmarshal(cfg interface{}) error { return DefaultConf.Unmarshal(cfg) } +func UnmarshalKey(key string, cfg interface{}) error { return DefaultConf.UnmarshalKey(key, cfg) } +func BindEnv(key string, env string) error { return DefaultConf.BindEnv(key, env) } +func BindFlag(key string, flag *Flag) error { return DefaultConf.BindFlag(key, flag) } +func Sub(key string) Config { return DefaultConf.Sub(key) } +func All() map[string]interface{} { return DefaultConf.All() } +func Set(key string, val interface{}) { DefaultConf.Set(key, val) } +func SetDefault(key string, val interface{}) { DefaultConf.SetDefault(key, val) } +func Get(key string) interface{} { return DefaultConf.Get(key) } +func GetString(key string) string { return DefaultConf.GetString(key) } +func GetBool(key string) bool { return DefaultConf.GetBool(key) } +func GetInt(key string) int { return DefaultConf.GetInt(key) } +func GetFloat64(key string) float64 { return DefaultConf.GetFloat64(key) } +func GetDuration(key string) time.Duration { return DefaultConf.GetDuration(key) } +func GetStringSlice(key string) []string { return DefaultConf.GetStringSlice(key) } +func GetStringMap(key string) map[string]interface{} { return DefaultConf.GetStringMap(key) } +func GetStringMapString(key string) map[string]string { return DefaultConf.GetStringMapString(key) } diff --git a/config/doc.go b/config/doc.go deleted file mode 100644 index de62762d3..000000000 --- a/config/doc.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2017 Cisco and/or its affiliates. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package config contains helper functions for parsing of configuration -// files. -package config diff --git a/config/parser.go b/config/parser.go index 4a8e7fbe4..120211d1a 100644 --- a/config/parser.go +++ b/config/parser.go @@ -24,6 +24,20 @@ import ( "github.com/mitchellh/mapstructure" ) +func parseYamlFileForMerge(path string) (map[string]interface{}, error) { + b, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + var data map[string]interface{} + if err := yaml.Unmarshal(b, &data); err != nil { + return nil, err + } + + return data, nil +} + // ParseConfigFromYamlFile parses a configuration from a file in YAML // format. The file's location is specified by the parameter and the // resulting config is stored into the structure referenced by the diff --git a/config/plugin_config.go b/config/plugin_config.go index 58ccc4b93..ee8205be0 100644 --- a/config/plugin_config.go +++ b/config/plugin_config.go @@ -1,6 +1,7 @@ package config import ( + "encoding/json" "fmt" "os" "path/filepath" @@ -8,10 +9,13 @@ import ( "sync" flag "github.com/spf13/pflag" - - "go.ligato.io/cn-infra/v2/logging/logrus" + "go.ligato.io/cn-infra/v2/logging" ) +func init() { + flag.String("config-dir", ".", "Location of config directory") +} + const ( // FlagSuffix is added to plugin name while loading plugins configuration. FlagSuffix = "-config" @@ -23,6 +27,8 @@ const ( FileExtension = ".conf" ) +const pluginConfigsPrefix = "plugin-configs." + // FlagName returns config flag name for the name, usually plugin. func FlagName(name string) string { return strings.ToLower(name) + FlagSuffix @@ -35,7 +41,7 @@ func Filename(name string) string { // EnvVar returns config env variable for the name, usually plugin. func EnvVar(name string) string { - return strings.ToUpper(name) + EnvSuffix + return strings.ReplaceAll(strings.ToUpper(name), "-", "_") + EnvSuffix } const ( @@ -52,11 +58,11 @@ const ( ) // DefineDirFlag defines flag for configuration directory. -func DefineDirFlag() { +/*func DefineDirFlag() { if flag.CommandLine.Lookup(DirFlag) == nil { flag.CommandLine.String(DirFlag, DirDefault, DirUsage) } -} +}*/ // PluginConfig is API for plugins to access configuration. // @@ -72,22 +78,31 @@ type PluginConfig interface { GetConfigName() string } -// FlagSet is a type alias for flag.FlagSet. -type FlagSet = flag.FlagSet +type ( + FlagSet = flag.FlagSet + Flag = flag.Flag +) // pluginFlags is used for storing flags for Plugins before agent starts. var pluginFlags = make(map[string]*FlagSet) +func GetFlagSetFor(name string) *FlagSet { + return pluginFlags[name] +} + // DefineFlagsFor registers defined flags for plugin with given name. -func DefineFlagsFor(name string) { +/*func DefineFlagsFor(name string) { if plugSet, ok := pluginFlags[name]; ok { - plugSet.VisitAll(func(f *flag.Flag) { - flag.CommandLine.Var(f.Value, f.Name, f.Usage) - }) + flag.CommandLine.AddFlagSet(plugSet) + //plugSet.VisitAll(func(f *flag.Flag) { + // flag.CommandLine.Var(f.Value, f.Name, f.Usage) + //}) } -} +}*/ type options struct { + Conf Config + FlagName string FlagDefault string FlagUsage string @@ -121,6 +136,13 @@ func WithExtraFlags(f func(flags *FlagSet)) Option { } } +// WithConfig is an option to set custom Config in ForPlugin. +func WithConfig(conf Config) Option { + return func(o *options) { + o.Conf = conf + } +} + // ForPlugin returns API that is injectable to a particular Plugin // and is used to read it's configuration. // @@ -131,94 +153,178 @@ func ForPlugin(name string, opts ...Option) PluginConfig { opt := options{ FlagName: FlagName(name), FlagDefault: Filename(name), - FlagUsage: fmt.Sprintf("Location of the %q plugin config file; can also be set via %q env variable.", - name, EnvVar(name)), - flagSet: flag.NewFlagSet(name, flag.ExitOnError), + FlagUsage: fmt.Sprintf("Location of the %q plugin config file; can set also via %q.", name, EnvVar(name)), + flagSet: flag.NewFlagSet(name, flag.ContinueOnError), } for _, o := range opts { o(&opt) } + if opt.Conf == nil { + opt.Conf = DefaultConf + } + if opt.FlagName != "" && opt.flagSet.Lookup(opt.FlagName) == nil { opt.flagSet.String(opt.FlagName, opt.FlagDefault, opt.FlagUsage) + + f := opt.flagSet.Lookup(opt.FlagName) + f.Deprecated = "use single config file" //"Plugin flags XXX-config have been deprecated!" + //f.Hidden = true + opt.Conf.BindEnv(pluginConfigsPrefix+opt.FlagName, EnvVar(name)) + opt.Conf.BindFlag(pluginConfigsPrefix+opt.FlagName, f) } + opt.flagSet.VisitAll(func(f *flag.Flag) { + if f.Annotations == nil { + f.Annotations = make(map[string][]string) + } + f.Annotations["plugin"] = []string{name} + }) + pluginFlags[name] = opt.flagSet return &pluginConfig{ + name: name, + conf: opt.Conf, configFlag: opt.FlagName, } } // Dir returns config directory by evaluating the flag DirFlag. It interprets "." as current working directory. func Dir() (dir string, err error) { - if flg := flag.CommandLine.Lookup(DirFlag); flg != nil { + return GetString("config-dir"), nil + /*if flg := flag.CommandLine.Lookup(DirFlag); flg != nil { val := flg.Value.String() + logrus.DefaultLogger().Debugf("dir flag value: %q", val) if strings.HasPrefix(val, ".") { cwd, err := os.Getwd() if err != nil { + logrus.DefaultLogger().Errorf("getcwd: %v", err) return cwd, err } return filepath.Join(cwd, val), nil } return val, nil } - return "", nil + return "", nil*/ } type pluginConfig struct { + name string + conf Config + configFlag string access sync.Mutex configName string } // LoadValue binds the configuration to config method argument. -func (p *pluginConfig) LoadValue(config interface{}) (found bool, err error) { +func (p *pluginConfig) LoadValue(cfg interface{}) (found bool, err error) { + log := logger.WithField("name", p.name) + + conf := p.conf + + if logger.GetLevel() >= logging.TraceLevel { + conf.(*Conf).Debug() + } + + //var def map[string]interface{} + def := cfgToStringMap(cfg) + log.Tracef("setting default to: %+v", def) + conf.SetDefault(p.name, def) + cfgName := p.GetConfigName() - if cfgName == "" { + /*if cfgName == "" { + return false, nil + }*/ + if cfgName != "" { + log.Tracef("pluginConfig.cfgName: %q", cfgName) + data, err := parseYamlFileForMerge(cfgName) + if err != nil { + return false, err + } + log.Tracef("data from plugin config file: %+v", data) + err = conf.MergeMap(map[string]interface{}{ + p.name: data, + }) + if err != nil { + log.Debugf("ERROR merging from plugin config file: %v", err) + return false, err + } + log.Debugf("merged %q OK: %+v", p.name, conf.(*Conf).AllKeys()) + if logger.GetLevel() >= logging.TraceLevel { + conf.(*Conf).Debug() + } + } + + pluginCfg := conf.Get(p.name) + if pluginCfg == nil || !conf.(*Conf).InConfig(p.name) { return false, nil } + log.Tracef("pluginCfg: %+v", pluginCfg) - // TODO: switch to Viper (possible to have one huge config file) - err = ParseConfigFromYamlFile(cfgName, config) - if err != nil { - return false, err + /*if conf.GetBool(p.name + ".disabled") { + return false, nil + }*/ + + if err := conf.UnmarshalKey(p.name, cfg); err != nil { + return true, err } return true, nil } +func cfgToStringMap(cfg interface{}) map[string]interface{} { + b, err := json.Marshal(cfg) + if err != nil { + return nil + } + m := make(map[string]interface{}) + if err := json.Unmarshal(b, &m); err != nil { + return nil + } + return m +} + // GetConfigName looks up flag value and uses it to: // 1. Find config in flag value location. // 2. Alternatively, it tries to find it in config dir // (see also Dir() comments). func (p *pluginConfig) GetConfigName() string { + //return p.conf.GetString(p.configFlag) p.access.Lock() defer p.access.Unlock() if p.configName == "" { p.configName = p.getConfigName() } + logger.Debugf("GetConfigName %v: %q", p.configFlag, p.configName) return p.configName } func (p *pluginConfig) getConfigName() string { - if flg := flag.CommandLine.Lookup(p.configFlag); flg != nil { - if val := flg.Value.String(); val != "" { - // if the file exists (value from flag) - if _, err := os.Stat(val); !os.IsNotExist(err) { - return val - } - cfgDir, err := Dir() - if err != nil { - logrus.DefaultLogger().Error(err) - return "" - } - // if the file exists (flag value in config dir) - dirVal := filepath.Join(cfgDir, val) - if _, err := os.Stat(dirVal); !os.IsNotExist(err) { - return dirVal - } + //if flg := flag.CommandLine.Lookup(p.configFlag); flg != nil { + //logger.Tracef("found config flag %v: %v", flg.Name, flg.Value) + //if val := flg.Value.String(); val != "" { + if val := p.conf.GetString(pluginConfigsPrefix + p.configFlag); val != "" { + logger.Tracef("plugin config file value: %q", val) + // if the file exists (value from flag) + if _, err := os.Stat(val); !os.IsNotExist(err) { + return val + } + cfgDir, err := Dir() + if err != nil { + logger.Error(err) + return "" + } + // if the file exists (flag value in config dir) + dirVal := filepath.Join(cfgDir, val) + logger.Tracef("checking dirVal: %q", dirVal) + if _, err := os.Stat(dirVal); !os.IsNotExist(err) { + return dirVal + } else { + logger.Debugf("ERROR os.Stat %q: %v", dirVal, err) } } + //} return "" } diff --git a/config/plugin_config_test.go b/config/plugin_config_test.go new file mode 100644 index 000000000..716795950 --- /dev/null +++ b/config/plugin_config_test.go @@ -0,0 +1,134 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config_test + +import ( + "os" + "testing" + "time" + + flag "github.com/spf13/pflag" + + . "github.com/onsi/gomega" + + "go.ligato.io/cn-infra/v2/config" +) + +func TestFlagName(t *testing.T) { + RegisterTestingT(t) + + const pluginName = "testplugin" + + Expect(config.FlagName(pluginName)).To(BeEquivalentTo("testplugin-config")) +} + +type testConf struct { + Label string + Details string + Port int + Rate float64 + Debug bool + Timeout time.Duration + List []string + Map map[string]string + Section *struct { + Name string + } +} + +func TestForPluginWithConfigFile(t *testing.T) { + RegisterTestingT(t) + + const pluginName = "testplugin" + const configFileName = pluginName + ".conf" + + setConfigDir(t, "./testdata") + + pluginConfig := config.ForPlugin(pluginName) + Expect(pluginConfig).ShouldNot(BeNil()) + + Expect(flag.CommandLine.Lookup(config.FlagName(pluginName))).To(BeNil()) + //config.DefineFlagsFor(pluginName) + flag.CommandLine.AddFlagSet(config.GetFlagSetFor(pluginName)) + + Expect(pluginConfig.GetConfigName()).Should(BeEquivalentTo(configFileName)) + Expect(flag.CommandLine.Lookup(config.FlagName(pluginName))).ToNot(BeNil()) + + cfg := testConf{ + Label: "no_label", + Details: "NONE", + } + Expect(pluginConfig.LoadValue(&cfg)).To(BeTrue()) + Expect(cfg.Label).To(Equal("bar")) + Expect(cfg.Details).To(Equal("NONE")) + Expect(cfg.Port).To(Equal(5)) + Expect(cfg.Rate).To(Equal(2.1)) + Expect(cfg.Debug).To(Equal(true)) + Expect(cfg.Timeout).To(Equal(time.Second * 3)) + Expect(cfg.List).To(ConsistOf("a", "b", "c")) + Expect(cfg.Map).To(And( + HaveKeyWithValue("a", "A"), + HaveKeyWithValue("b", "B"), + )) + Expect(cfg.Section.Name).To(Equal("xyz")) +} + +func TestForPluginWithoutConfigFile(t *testing.T) { + RegisterTestingT(t) + + const pluginName = "confignofileplugin" + + setConfigDir(t, "./testdata") + + pluginConfig := config.ForPlugin(pluginName) + Expect(pluginConfig).ShouldNot(BeNil()) + + //config.DefineFlagsFor(pluginName) + flag.CommandLine.AddFlagSet(config.GetFlagSetFor(pluginName)) + Expect(pluginConfig.GetConfigName()).Should(BeEmpty()) +} + +func TestForPluginWithSpecifiedConfigFile(t *testing.T) { + RegisterTestingT(t) + + const pluginName = "myplugin" + const configFileName = "testplugin.conf" + + setConfigDir(t, "./testdata") + + pluginConfig := config.ForPlugin(pluginName, + config.WithCustomizedFlag(config.FlagName(pluginName), configFileName, "customized config filename"), + ) + Expect(pluginConfig).ShouldNot(BeNil()) + + //config.DefineFlagsFor(pluginName) + flag.CommandLine.AddFlagSet(config.GetFlagSetFor(pluginName)) + Expect(pluginConfig.GetConfigName()).Should(BeEquivalentTo(configFileName)) +} + +func setConfigDir(t *testing.T, dir string) { + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + if err := os.Chdir(dir); err != nil { + t.Fatal(err) + } + t.Cleanup(func() { + if err := os.Chdir(cwd); err != nil { + panic(err) + } + }) +} diff --git a/config/testdata/config.yaml b/config/testdata/config.yaml new file mode 100644 index 000000000..bc1dcb912 --- /dev/null +++ b/config/testdata/config.yaml @@ -0,0 +1,3 @@ +foo: bar +durstr: 5s +durnum: 3000000000 \ No newline at end of file diff --git a/config/testdata/testplugin.conf b/config/testdata/testplugin.conf new file mode 100644 index 000000000..9a2403400 --- /dev/null +++ b/config/testdata/testplugin.conf @@ -0,0 +1,11 @@ +label: bar +port: 5 +rate: 2.1 +debug: true +timeout: 3s +list: [a, b, c] +map: + a: A + b: B +section: + name: xyz \ No newline at end of file diff --git a/examples/configs-plugin/config.yml b/examples/configs-plugin/config.yml new file mode 100644 index 000000000..f61d450fe --- /dev/null +++ b/examples/configs-plugin/config.yml @@ -0,0 +1,8 @@ +version: v1 +example: + name: ConfigExample + # use default description + port: 4444 + settings: + timeout: 3s + extra: notrecognized \ No newline at end of file diff --git a/examples/configs-plugin/main.go b/examples/configs-plugin/main.go index ab8cec66c..099a57345 100644 --- a/examples/configs-plugin/main.go +++ b/examples/configs-plugin/main.go @@ -15,7 +15,7 @@ package main import ( - "log" + "encoding/json" "time" "go.ligato.io/cn-infra/v2/agent" @@ -24,39 +24,36 @@ import ( "go.ligato.io/cn-infra/v2/logging" ) -// PluginName is injected as the plugin name. -// LocalFlavor.InfraDeps() will create and initialize a new flag used to make -// the plugin config file name configurable for the user (via dedicated CLI -// option and env. variable). -// The flag name is composed of the plugin name and the suffix config.FlagSuffix. -// The default (flag value) filename for the configuration file is the plugin -// name with the extension ".conf". -const PluginName = "example" - -// ************************************************************************* -// This file contains a PluginConfig show case: -// - plugin binds it's configuration to an example specific Conf structure -// (see Init() to learn how the default configuration is set & how it can be -// overridden via flags) -// - cn-infra helps by locating and parsing the configuration file -// -// ************************************************************************/ +// ExampleCfg defines config values as struct. +type ExampleCfg struct { + Name string + Description string + Port uint + Settings *struct { + Max int + Timeout time.Duration + } +} func main() { + const PluginName = "example" + p := &ExamplePlugin{ Deps: Deps{ - PluginName: infra.PluginName(PluginName), - Log: logging.ForPlugin(PluginName), - PluginConfig: config.ForPlugin(PluginName), + PluginName: infra.PluginName(PluginName), + Log: logging.ForPlugin(PluginName), + Config: config.ForPlugin(PluginName), }, exampleFinished: make(chan struct{}), } + a := agent.NewAgent( agent.AllPlugins(p), agent.QuitOnClose(p.exampleFinished), ) + if err := a.Run(); err != nil { - log.Fatal(err) + logging.Fatalf("Run() error: %+v", err) } } @@ -64,7 +61,7 @@ func main() { type ExamplePlugin struct { Deps - Conf *Conf // it is possible to set config value programmatically (can be overridden) + Conf *ExampleCfg exampleFinished chan struct{} } @@ -72,53 +69,48 @@ type ExamplePlugin struct { // Deps defines dependencies for ExamplePlugin. type Deps struct { infra.PluginName - Log logging.PluginLogger - PluginConfig config.PluginConfig -} - -// Conf - example config binding -type Conf struct { - Field1 string - Sleep time.Duration - // even nested fields are possible -} - -func (conf *Conf) String() string { - return "{Field1:" + conf.Field1 + ", Sleep:" + conf.Sleep.String() + "}" + Log logging.PluginLogger + Config config.PluginConfig } // Init loads the configuration file assigned to ExamplePlugin (can be changed // via the example-config flag). // Loaded config is printed into the log file. -func (plugin *ExamplePlugin) Init() (err error) { - plugin.Log.Info("Loading plugin config ", plugin.PluginConfig.GetConfigName()) +func (p *ExamplePlugin) Init() (err error) { + p.Log.Debug("Loading plugin config ", p.Config.GetConfigName()) - if plugin.Conf == nil { - plugin.Conf = &Conf{Field1: "some default value"} + p.Conf = &ExampleCfg{ + Name: "defaultName", + Description: "no description", + Port: 9191, } + p.Log.Infof("DEFAULT: %+v", p.Conf) - found, err := plugin.PluginConfig.LoadValue(plugin.Conf) + found, err := p.Config.LoadValue(p.Conf) if err != nil { - plugin.Log.Error("Error loading config", err) - } else if found { - plugin.Log.Info("Loaded plugin config - found external configuration ", plugin.PluginConfig.GetConfigName()) + p.Log.Error("Error loading config", err) + } else if !found { + p.Log.Warn("Config not found, using default values") } else { - plugin.Log.Info("Loaded plugin config - default") + p.Log.Info("Config loaded, values from", p.Config.GetConfigName()) } - plugin.Log.Info("Plugin Config ", plugin.Conf) + p.Log.Infof("LOADED: %+v", p.Conf) - time.Sleep(plugin.Conf.Sleep) - close(plugin.exampleFinished) + b, err := json.MarshalIndent(p.Conf, "", " ") + if err != nil { + return err + } + p.Log.Infof("CONFIG JSON: %s", b) + + go func() { + time.Sleep(time.Second) + close(p.exampleFinished) + }() return nil } // Close closes the plugin. -func (plugin *ExamplePlugin) Close() (err error) { +func (p *ExamplePlugin) Close() (err error) { return nil } - -// Name returns name of the plugin. -func (plugin *ExamplePlugin) Name() string { - return PluginName -} diff --git a/go.mod b/go.mod index d2fc374af..4dafbb99c 100644 --- a/go.mod +++ b/go.mod @@ -15,11 +15,7 @@ require ( github.com/boltdb/bolt v1.3.2-0.20180302180052-fd01fc79c553 github.com/bshuster-repo/logrus-logstash-hook v0.4.1 github.com/bsm/sarama-cluster v2.1.15+incompatible - github.com/coreos/bbolt v1.3.1-etcd.8 // indirect github.com/coreos/etcd v3.3.13+incompatible - github.com/coreos/go-semver v0.2.0 // indirect - github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 // indirect - github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/eapache/go-resiliency v1.1.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect @@ -32,26 +28,20 @@ require ( github.com/ghodss/yaml v1.0.0 github.com/go-redis/redis v6.14.2+incompatible github.com/gocql/gocql v0.0.0-20181030013202-a84ce58083d3 - github.com/gogo/protobuf v1.2.1 // indirect github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 // indirect github.com/golang/protobuf v1.3.2 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect github.com/gomodule/redigo v2.0.0+incompatible // indirect - github.com/google/btree v1.0.0 // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/mux v1.6.2 - github.com/gorilla/websocket v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/grpc-ecosystem/grpc-gateway v1.5.1 // indirect github.com/hashicorp/consul v1.3.0 github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 // indirect github.com/hashicorp/memberlist v0.1.5 // indirect github.com/hashicorp/serf v0.8.1 // indirect github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6 - github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/json-iterator/go v1.1.6 // indirect github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect github.com/maraino/go-mock v0.0.0-20180321183845-4c74c434cd3a github.com/mattn/go-isatty v0.0.4 // indirect @@ -59,30 +49,24 @@ require ( github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/mapstructure v1.1.2 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect github.com/onsi/gomega v1.4.3 github.com/philhofer/fwd v1.0.0 // indirect github.com/pierrec/lz4 v2.3.0+incompatible // indirect github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v0.9.2 + github.com/prometheus/client_golang v0.9.3 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect github.com/sirupsen/logrus v1.4.2 - github.com/soheilhy/cmux v0.1.4 // indirect github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.6.3 github.com/tinylib/msgp v1.0.2 // indirect - github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d github.com/willfaught/gockle v0.0.0-20160623235217-4f254e1e0f0a - github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 // indirect github.com/yuin/gopher-lua v0.0.0-20181031023651-12c4817b42c5 // indirect go.etcd.io/bbolt v1.3.3 // indirect - go.uber.org/atomic v1.3.2 // indirect - go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.9.1 // indirect - golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 - golang.org/x/net v0.0.0-20190108150841-be88a9aa50a1 + golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 + golang.org/x/net v0.0.0-20190522155817-f3200d17e092 golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d // indirect - golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 + golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 google.golang.org/genproto v0.0.0-20181101192439-c830210a61df // indirect - google.golang.org/grpc v1.16.0 + google.golang.org/grpc v1.21.0 ) diff --git a/go.sum b/go.sum index ebedd8315..c6a42ff03 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,31 @@ cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14= github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.20.0 h1:wAMHhl1lGRlobeoV/xOKpbqD2OQsOvY4A/vIOGroIe8= github.com/Shopify/sarama v1.20.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Songmu/prompter v0.0.0-20150725163906-b5721e8d5566 h1:1liEfYDXrRp0vmZMEGRuGAvYBlKpT9saaCd7g63XUBw= github.com/Songmu/prompter v0.0.0-20150725163906-b5721e8d5566/go.mod h1:fNhSFBGC+sg+dZ7AqDHgq+xYiom23TeTESzUbO7PIrE= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U= github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= github.com/alicebob/miniredis v2.4.5+incompatible h1:Ml+i18wdJPeO/AqNQYpaDyStCAh7TKwLUiaUaQx0yhs= github.com/alicebob/miniredis v2.4.5+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.0 h1:B7AQgHi8QSEi4uHu7Sbsga+IJDU+CENgjxoo81vDUqU= github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= @@ -28,24 +36,26 @@ github.com/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0 github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bsm/sarama-cluster v2.1.15+incompatible h1:RkV6WiNRnqEEbp81druK8zYhmnIgdOjqSVi0+9Cnl2A= github.com/bsm/sarama-cluster v2.1.15+incompatible/go.mod h1:r7ao+4tTNXvWm+VRpRJchr2kQhqxgmAp2iEX5W96gMM= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.1-etcd.8 h1:xTcsP8rG1dLB1VRhYSyf6sFQnIU39vC7OkbtEU8bWIA= -github.com/coreos/bbolt v1.3.1-etcd.8/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142 h1:3jFq2xL4ZajGK4aZY8jz+DAF0FHjI51BXjjSwCzS1Dk= -github.com/coreos/go-systemd v0.0.0-20181031085051-9002847aa142/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea h1:n2Ltr3SrfQlf/9nOna1DoGKxLx3qTSI8Ttl6Xrqp6mw= -github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw= @@ -64,19 +74,25 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0= github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gocql/gocql v0.0.0-20181030013202-a84ce58083d3 h1:P8aneIsdmv/PIZerjvRn0tCYH5/WOv9GZ3UL8HBXlPM= github.com/gocql/gocql v0.0.0-20181030013202-a84ce58083d3/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -87,8 +103,12 @@ github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= @@ -99,8 +119,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmo github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.1 h1:3scN4iuXkNOyP98jF55Lv8a9j1o/IwvnDIZ0LHJK1nk= -github.com/grpc-ecosystem/grpc-gateway v1.5.1/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/consul v1.3.0 h1:0ihJs1J8ejURfAbwhwv+USnf4oyqfAddv/3xXXv4ltg= @@ -125,6 +145,8 @@ github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCS github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/memberlist v0.1.5 h1:AYBsgJOW9gab/toO5tEB8lWetVgDKZycqkebJ8xxpqM= github.com/hashicorp/memberlist v0.1.5/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.1 h1:mYs6SMzu72+90OcPa5wr3nfznA4Dw9UyR791ZFNOIf4= @@ -135,19 +157,25 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/maraino/go-mock v0.0.0-20180321183845-4c74c434cd3a h1:66Bs2T30mxkg+5ZQEb0z5556Ww4tIBZffUUNnFD5EUQ= github.com/maraino/go-mock v0.0.0-20180321183845-4c74c434cd3a/go.mod h1:KpdDhCgE2rvPhsnLbGZ8Uf1QORj6v92FOgFKnCz5CXM= github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs= @@ -162,10 +190,15 @@ github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdI github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= @@ -173,69 +206,109 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.3.0+incompatible h1:CZzRn4Ut9GbUkHlQ7jqBXeZQV41ZSKWFc302ZU6lUTk= github.com/pierrec/lz4 v2.3.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275 h1:PnBWHBf+6L0jOqq0gIVUe6Yk0/QMZ640k6NvkxcBf+8= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs= +github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8= github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc= -github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d h1:ggUgChAeyge4NZ4QUw6lhHsVymzwSDJOZcE0s2X8S20= github.com/unrolled/render v0.0.0-20180914162206-b9786414de4d/go.mod h1:tu82oB5W2ykJRVioYsB+IQKcft7ryBr7w12qMBUPyXg= github.com/willfaught/gockle v0.0.0-20160623235217-4f254e1e0f0a h1:8RS66PasPomNygpTsXqP8ZMCewyI//Xozi5gZMlK6oU= github.com/willfaught/gockle v0.0.0-20160623235217-4f254e1e0f0a/go.mod h1:NLcF+3nDpXVIZatjn5Z97gKzFFVU7TzgbAcs8G7/Jrs= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18 h1:MPPkRncZLN9Kh4MEFmbnK4h3BD7AUmskWv2+EeZJCCs= -github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/gopher-lua v0.0.0-20181031023651-12c4817b42c5 h1:d9vJ/8gXbVnNk8QFOxFZ7MN7TuHiuvolK1usz5KXVDo= github.com/yuin/gopher-lua v0.0.0-20181031023651-12c4817b42c5/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= -go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16 h1:y6ce7gCWtnH+m3dCjzQ1PCuwl28DDIc3VNnvY29DlIA= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108150841-be88a9aa50a1 h1:GPpXcPUwWLSpS88tNfabqPG/HJ5b2k2yTwzkSx1sf4c= -golang.org/x/net v0.0.0-20190108150841-be88a9aa50a1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -243,33 +316,49 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d h1:62ap6LNOjDU6uGmKXHJbSfciMoV+FeI1sRXx/pLDL44= golang.org/x/sys v0.0.0-20200317113312-5766fd39f98d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2 h1:+DCIGbF/swA92ohVg0//6X2IVY3KZs6p9mix0ziNYJM= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181101192439-c830210a61df h1:Ri2mROsxIxitlzRQ0pYoP8/dsqeLEolHrhh29dltSI4= google.golang.org/genproto v0.0.0-20181101192439-c830210a61df/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/servicelabel/plugin_api_servicelabel.go b/servicelabel/plugin_api_servicelabel.go index 2d8cbd237..efade91b7 100644 --- a/servicelabel/plugin_api_servicelabel.go +++ b/servicelabel/plugin_api_servicelabel.go @@ -14,13 +14,14 @@ package servicelabel +import ( + "path" +) + // default service key prefix, can be changed in the build time using ldflgs, e.g. // -ldflags '-X github.com/ligato/cn-infra/servicelabel.agentPrefix=/xyz/' var agentPrefix = "/vnf-agent/" -// MicroserviceLabelEnvVar label is inferred from the flag name. -const MicroserviceLabelEnvVar = "MICROSERVICE_LABEL" - // ReaderAPI allows to read microservice label and key prefix associated with // this Agent instance. // The key prefix is supposed to be prepended to all keys used to store/read @@ -55,5 +56,5 @@ func GetAllAgentsPrefix() string { // GetDifferentAgentPrefix returns the key prefix used by (another) Agent // instance from microservice labelled as . func GetDifferentAgentPrefix(microserviceLabel string) string { - return agentPrefix + microserviceLabel + "/" + return path.Join(agentPrefix, microserviceLabel) + "/" } diff --git a/servicelabel/plugin_impl_servicelabel.go b/servicelabel/plugin_impl_servicelabel.go index ad37d282a..a2efc7759 100644 --- a/servicelabel/plugin_impl_servicelabel.go +++ b/servicelabel/plugin_impl_servicelabel.go @@ -15,18 +15,25 @@ package servicelabel import ( - "fmt" + "path" flag "github.com/spf13/pflag" + "go.ligato.io/cn-infra/v2/config" "go.ligato.io/cn-infra/v2/infra" "go.ligato.io/cn-infra/v2/logging/logrus" ) -var microserviceLabelFlag string +// MicroserviceLabelEnvVar label is inferred from the flag name. +const MicroserviceLabelEnvVar = "MICROSERVICE_LABEL" + +const defaultServiceLabel = "vpp1" func init() { - flag.StringVar(µserviceLabelFlag, "microservice-label", "vpp1", fmt.Sprintf("microservice label; also set via '%v' env variable.", MicroserviceLabelEnvVar)) + config.SetDefault("microservice-label", defaultServiceLabel) + flag.String("microservice-label", defaultServiceLabel, "Microservice label sets service instance ID.") + config.BindEnv("microservice-label", MicroserviceLabelEnvVar) + config.BindFlag("microservice-label", flag.Lookup("microservice-label")) } // Plugin exposes the service label(i.e. the string used to identify the particular VNF) to the other plugins. @@ -40,7 +47,7 @@ type Plugin struct { // Init is called at plugin initialization. func (p *Plugin) Init() error { if p.MicroserviceLabel == "" { - p.MicroserviceLabel = microserviceLabelFlag + p.MicroserviceLabel = config.GetString("microservice-label") } logrus.DefaultLogger().Debugf("Microservice label is set to %v", p.MicroserviceLabel) return nil @@ -60,7 +67,7 @@ func (p *Plugin) GetAgentLabel() string { // GetAgentPrefix returns the string that is supposed to be used as the prefix for configuration of current // MicroserviceLabel "subtree" of the particular VPP Agent instance (e.g. in ETCD). func (p *Plugin) GetAgentPrefix() string { - return agentPrefix + p.MicroserviceLabel + "/" + return path.Join(agentPrefix, p.MicroserviceLabel) + "/" } // GetDifferentAgentPrefix returns the string that is supposed to be used as the prefix for configuration From b951cdfb3406b665b08ebfcb0029041c1a473353 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 17 Apr 2020 13:01:30 +0200 Subject: [PATCH 3/6] Refactor config and agent Signed-off-by: Ondrej Fabry --- config/default.go | 1 - 1 file changed, 1 deletion(-) diff --git a/config/default.go b/config/default.go index 6c0627e94..b6c5c7550 100644 --- a/config/default.go +++ b/config/default.go @@ -34,7 +34,6 @@ func init() { defaultViper.SetDefault(DirFlag, DirDefault) defaultViper.BindEnv(DirFlag, "CONFIG_DIR") defaultViper.AutomaticEnv() - //defaultViper.SetTypeByDefaultValue(true) } func Load() error { return DefaultConf.Load() } From bb28818a58bc7164657533050f74b2af2e3f32cb Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Fri, 17 Apr 2020 13:07:56 +0200 Subject: [PATCH 4/6] Refactor config and agent Signed-off-by: Ondrej Fabry --- agent/agent.go | 20 -------------------- agent/default.go | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 agent/default.go diff --git a/agent/agent.go b/agent/agent.go index aeb553f98..6d48b78d8 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -73,23 +73,6 @@ type Agent interface { Error() error } -var ( - DefaultAgent Agent = NewAgent() -) - -func Init(opts ...Option) { DefaultAgent.Init(opts...) } -func Start() error { return DefaultAgent.Start() } -func Stop() error { return DefaultAgent.Stop() } -func Wait() error { return DefaultAgent.Wait() } -func Run() error { return DefaultAgent.Run() } - -/*func Run() error { - if err := Start(); err != nil { - return err - } - return Wait() -}*/ - // NewAgent creates a new agent using given options and registers all flags // defined for plugins via config.ForPlugin. func NewAgent(opts ...Option) Agent { @@ -126,9 +109,6 @@ func (a *agent) Init(opts ...Option) { for _, o := range opts { o(&a.opts) } - /*if err := a.init(); err != nil { - agentLogger.Fatal("agent init error:", err) - }*/ } func (a *agent) setup() error { diff --git a/agent/default.go b/agent/default.go new file mode 100644 index 000000000..cb0500959 --- /dev/null +++ b/agent/default.go @@ -0,0 +1,25 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package agent + +var ( + DefaultAgent Agent = NewAgent() +) + +func Init(opts ...Option) { DefaultAgent.Init(opts...) } +func Start() error { return DefaultAgent.Start() } +func Stop() error { return DefaultAgent.Stop() } +func Wait() error { return DefaultAgent.Wait() } +func Run() error { return DefaultAgent.Run() } From dbf8722c1367e6f18af77567ffa760c066fe23d5 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Mon, 4 May 2020 15:22:10 +0200 Subject: [PATCH 5/6] Update config Signed-off-by: Ondrej Fabry --- config/config.go | 104 +++++++++++++++++---------- config/config_test.go | 152 ++++++++++++++++++++++++++++++++++++++++ config/default.go | 51 +++++++------- config/parser.go | 13 ++-- config/plugin_config.go | 26 ++++--- 5 files changed, 266 insertions(+), 80 deletions(-) create mode 100644 config/config_test.go diff --git a/config/config.go b/config/config.go index e009490ce..d45756b14 100644 --- a/config/config.go +++ b/config/config.go @@ -16,28 +16,32 @@ package config import ( "errors" + "fmt" "io" "os" "strings" "time" + "github.com/kr/pretty" + "github.com/mitchellh/mapstructure" "github.com/spf13/viper" + _ "github.com/spf13/viper/remote" "go.ligato.io/cn-infra/v2/logging" "go.ligato.io/cn-infra/v2/logging/logrus" ) -// Config defines API for config management. +// Config defines an API for handling configuration. type Config interface { - Load() error - LoadFrom(io.Reader) error + Read() error + ReadFrom(io.Reader) error MergeFrom(io.Reader) error MergeMap(map[string]interface{}) error WriteAs(filename string) error + SetDefault(key string, val interface{}) BindEnv(key string, env string) error BindFlag(key string, flag *Flag) error - SetDefault(key string, val interface{}) Set(key string, val interface{}) Sub(key string) Config @@ -66,31 +70,26 @@ type Config interface { // NewConfig returns fresh new Config instance. func NewConfig() Config { - v := viper.NewWithOptions() + v := viper.New() return newConf(v) } -var logger = logrus.NewLogger("config") - -func init() { - if debug := os.Getenv("DEBUG"); strings.Contains(debug, "config") { - logger.SetLevel(logging.TraceLevel) - } +// NewConfigFromFile returns new Config instance that reads from given file. +func NewConfigFromFile(file string) Config { + v := viper.New() + v.SetConfigFile(file) + return newConf(v) } -type Conf struct { +type config struct { *viper.Viper } -func newConf(v *viper.Viper) *Conf { - if v.Get(DirFlag) == nil { - v.SetDefault(DirFlag, ".") - v.BindEnv(DirFlag, "CONFIG_DIR") - } - return &Conf{Viper: v} +func newConf(v *viper.Viper) *config { + return &config{Viper: v} } -func (c *Conf) Load() error { +func (c *config) Read() error { configDir := c.Viper.GetString(DirFlag) logger.Debugf("adding config path: %q", configDir) @@ -103,34 +102,33 @@ func (c *Conf) Load() error { return err } } - logger.Debugf("loaded config from: %q", c.Viper.ConfigFileUsed()) if logger.GetLevel() >= logging.TraceLevel { - c.Viper.Debug() + c.Debug() } return nil } -func (c *Conf) LoadFrom(r io.Reader) error { +func (c *config) ReadFrom(r io.Reader) error { return c.Viper.ReadConfig(r) } -func (c *Conf) MergeFrom(r io.Reader) error { +func (c *config) MergeFrom(r io.Reader) error { return c.Viper.MergeConfig(r) } -func (c *Conf) MergeMap(m map[string]interface{}) error { +func (c *config) MergeMap(m map[string]interface{}) error { return c.Viper.MergeConfigMap(m) } -func (c *Conf) WriteAs(filename string) error { - logger.Tracef("Conf.WriteAs %q", filename) +func (c *config) WriteAs(filename string) error { + logger.Tracef("config.WriteAs %q", filename) return c.Viper.WriteConfigAs(filename) } -func (c *Conf) BindEnv(key string, env string) error { +func (c *config) BindEnv(key string, env string) error { logger.Tracef("bind config key %q to ENV %q", key, env) err := c.Viper.BindEnv(key, env) if err != nil { @@ -139,7 +137,7 @@ func (c *Conf) BindEnv(key string, env string) error { return err } -func (c *Conf) BindFlag(key string, flag *Flag) error { +func (c *config) BindFlag(key string, flag *Flag) error { logger.Tracef("bind config key %q to FLAG %q", key, flag.Name) err := c.Viper.BindPFlag(key, flag) if err != nil { @@ -148,8 +146,8 @@ func (c *Conf) BindFlag(key string, flag *Flag) error { return err } -func (c *Conf) Sub(key string) Config { - logger.Tracef("Conf.Sub %q", key) +func (c *config) Sub(key string) Config { + logger.Tracef("config.Sub %q", key) sub := c.Viper.Sub(key) if sub == nil { return nil @@ -157,17 +155,47 @@ func (c *Conf) Sub(key string) Config { return newConf(sub) } -func (c *Conf) All() map[string]interface{} { - logger.Debugf("Conf.All: %+v", c.All()) +func (c *config) All() map[string]interface{} { return c.Viper.AllSettings() } -func (c *Conf) Unmarshal(cfg interface{}) error { - logger.Tracef("Conf.Unmarshal") - return c.Viper.Unmarshal(cfg) +func (c *config) Unmarshal(cfg interface{}) error { + logger.Tracef("config.Unmarshal") + return c.Viper.Unmarshal(cfg, func(c *mapstructure.DecoderConfig) { c.TagName = "json" }) } -func (c *Conf) UnmarshalKey(key string, cfg interface{}) error { - logger.Tracef("Conf.UnmarshalKey %q", key) - return c.Viper.UnmarshalKey(key, cfg) +func (c *config) UnmarshalKey(key string, cfg interface{}) error { + logger.Tracef("config.UnmarshalKey %q", key) + return c.Viper.UnmarshalKey(key, cfg, func(c *mapstructure.DecoderConfig) { c.TagName = "json" }) +} +func (c *config) Dump() { + fmt.Println(" === CONFIG DUMP ===") + pretty.Printf(" VIPER: %# v\n", c.All()) + fmt.Println(" --- ") +} + +func (c *config) Debug() { + /* + fmt.Printf("Aliases:\n%#v\n", v.aliases) + fmt.Printf("Override:\n%#v\n", v.override) + fmt.Printf("PFlags:\n%#v\n", v.pflags) + fmt.Printf("Env:\n%#v\n", v.env) + fmt.Printf("Key/Value Store:\n%#v\n", v.kvstore) + fmt.Printf("Config:\n%#v\n", v.config) + fmt.Printf("Defaults:\n%#v\n", v.defaults) + */ + fmt.Println(" === CONFIG DUMP ===") + //pp.Printf(" VIPER: %#v\n", c.Viper) + c.Viper.Debug() + fmt.Println(" --- + + + + ---") + pretty.Printf(" VIPER: %# v\n", c.All()) + fmt.Println(" --- --- ---") +} + +var logger = logrus.NewLogger("config") + +func init() { + if debug := os.Getenv("DEBUG"); strings.Contains(debug, "config") { + logger.SetLevel(logging.TraceLevel) + } } diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 000000000..c45ae0b4c --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,152 @@ +// Copyright (c) 2020 Cisco and/or its affiliates. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config_test + +import ( + "fmt" + "reflect" + "strings" + "testing" + "time" + + "go.ligato.io/cn-infra/v2/config" +) + +type TestCfg struct { + Label string + Debug bool + Port int + Rate float64 + Timeout time.Duration + List []string + Levels map[string]string +} + +func TestGetters(t *testing.T) { + const cfgData = ` +label: MyLabel +debug: true +port: 4444 +rate: 1.2 +timeout: 3s +list: [a, b, c] +levels: + api: debug + server: fatal +` + conf := config.NewConfig() + + err := conf.ReadFrom(strings.NewReader(cfgData)) + if err != nil { + t.Fatal(err) + } + if len(conf.All()) == 0 { + t.Fatal("loaded config is empty") + } + t.Logf("All: %+v", conf.All()) + + t.Run("getters", func(t *testing.T) { + label := conf.GetString("label") + if label != "MyLabel" { + t.Errorf("label expected: %q, got: %q", "MyLabel", label) + } + debug := conf.GetBool("debug") + if debug != true { + t.Errorf("debug expected: %v, got: %v", true, debug) + } + port := conf.GetInt("port") + if port != 4444 { + t.Errorf("port expected: %v, got: %v", 4444, port) + } + rate := conf.GetFloat64("rate") + if rate != 1.2 { + t.Errorf("rate expected: %v, got: %v", 1.2, rate) + } + timeout := conf.GetDuration("timeout") + if timeout != time.Second*3 { + t.Errorf("timeout expected: %v, got: %v", time.Second*3, timeout) + } + list := conf.GetStringSlice("list") + expectList := []string{"a", "b", "c"} + if len(list) != len(expectList) { + t.Errorf("list length expected: %v, got: %v", len(expectList), list) + } + if str := fmt.Sprint(list); str != fmt.Sprint(expectList) { + t.Errorf("list expected: %v, got: %v", expectList, list) + } + levels := conf.GetStringMapString("levels") + expectLevels := map[string]string{"api": "debug", "server": "fatal"} + if len(levels) != len(expectLevels) { + t.Errorf("levels length expected: %v, got: %v", len(expectLevels), levels) + } + if str := fmt.Sprint(levels); str != fmt.Sprint(expectLevels) { + t.Errorf("levels expected: %v, got: %v", expectLevels, levels) + } + }) + + t.Run("unmarshal", func(t *testing.T) { + + var cfg TestCfg + + err = conf.Unmarshal(&cfg) + if err != nil { + t.Fatal(err) + } + + expectedCfg := TestCfg{ + Label: "MyLabel", + Debug: true, + Port: 4444, + Rate: 1.2, + Timeout: time.Second * 3, + List: []string{"a", "b", "c"}, + Levels: map[string]string{ + "api": "debug", + "server": "fatal", + }, + } + if !reflect.DeepEqual(cfg, expectedCfg) { + t.Fatalf("cfg:\nexpected:\n%+v\ngot:\n%+v", expectedCfg, cfg) + } + }) +} + +func TestUnmarshal(t *testing.T) { + const cfgData = ` +label: MyLabel +debug: true +port: 4444 +rate: 1.2 +timeout: 3s +list: [a, b, c] +levels: + api: debug + server: fatal +#service: +# endpoint: 10.10.1.2:3333 +` + conf := config.NewConfig() + + err := conf.ReadFrom(strings.NewReader(cfgData)) + if err != nil { + t.Fatal(err) + } + if len(conf.All()) == 0 { + t.Fatal("loaded config is empty") + } + + t.Logf("All: %+v", conf.All()) + +} diff --git a/config/default.go b/config/default.go index b6c5c7550..371b0592b 100644 --- a/config/default.go +++ b/config/default.go @@ -22,8 +22,9 @@ import ( ) var ( - DefaultConf Config = newConf(defaultViper) + DefaultConfig Config = defaultConf + defaultConf = newConf(defaultViper) defaultViper = viper.GetViper() ) @@ -31,30 +32,30 @@ func init() { viper.SupportedExts = append(viper.SupportedExts, "conf") defaultViper.SetConfigName("config") defaultViper.SetConfigType("yaml") - defaultViper.SetDefault(DirFlag, DirDefault) - defaultViper.BindEnv(DirFlag, "CONFIG_DIR") + defaultViper.SetDefault("config-dir", ".") + defaultViper.BindEnv("config-dir", "CONFIG_DIR") defaultViper.AutomaticEnv() } -func Load() error { return DefaultConf.Load() } -func LoadFrom(r io.Reader) error { return DefaultConf.LoadFrom(r) } -func MergeFrom(r io.Reader) error { return DefaultConf.MergeFrom(r) } -func MergeMap(m map[string]interface{}) error { return DefaultConf.MergeMap(m) } -func WriteAs(filename string) error { return DefaultConf.WriteAs(filename) } -func Unmarshal(cfg interface{}) error { return DefaultConf.Unmarshal(cfg) } -func UnmarshalKey(key string, cfg interface{}) error { return DefaultConf.UnmarshalKey(key, cfg) } -func BindEnv(key string, env string) error { return DefaultConf.BindEnv(key, env) } -func BindFlag(key string, flag *Flag) error { return DefaultConf.BindFlag(key, flag) } -func Sub(key string) Config { return DefaultConf.Sub(key) } -func All() map[string]interface{} { return DefaultConf.All() } -func Set(key string, val interface{}) { DefaultConf.Set(key, val) } -func SetDefault(key string, val interface{}) { DefaultConf.SetDefault(key, val) } -func Get(key string) interface{} { return DefaultConf.Get(key) } -func GetString(key string) string { return DefaultConf.GetString(key) } -func GetBool(key string) bool { return DefaultConf.GetBool(key) } -func GetInt(key string) int { return DefaultConf.GetInt(key) } -func GetFloat64(key string) float64 { return DefaultConf.GetFloat64(key) } -func GetDuration(key string) time.Duration { return DefaultConf.GetDuration(key) } -func GetStringSlice(key string) []string { return DefaultConf.GetStringSlice(key) } -func GetStringMap(key string) map[string]interface{} { return DefaultConf.GetStringMap(key) } -func GetStringMapString(key string) map[string]string { return DefaultConf.GetStringMapString(key) } +func Read() error { return DefaultConfig.Read() } +func ReadFrom(r io.Reader) error { return DefaultConfig.ReadFrom(r) } +func MergeFrom(r io.Reader) error { return DefaultConfig.MergeFrom(r) } +func MergeMap(m map[string]interface{}) error { return DefaultConfig.MergeMap(m) } +func WriteAs(filename string) error { return DefaultConfig.WriteAs(filename) } +func Unmarshal(cfg interface{}) error { return DefaultConfig.Unmarshal(cfg) } +func UnmarshalKey(key string, cfg interface{}) error { return DefaultConfig.UnmarshalKey(key, cfg) } +func Set(key string, val interface{}) { DefaultConfig.Set(key, val) } +func BindEnv(key string, env string) error { return DefaultConfig.BindEnv(key, env) } +func BindFlag(key string, flag *Flag) error { return DefaultConfig.BindFlag(key, flag) } +func SetDefault(key string, val interface{}) { DefaultConfig.SetDefault(key, val) } +func Sub(key string) Config { return DefaultConfig.Sub(key) } +func All() map[string]interface{} { return DefaultConfig.All() } +func Get(key string) interface{} { return DefaultConfig.Get(key) } +func GetString(key string) string { return DefaultConfig.GetString(key) } +func GetBool(key string) bool { return DefaultConfig.GetBool(key) } +func GetInt(key string) int { return DefaultConfig.GetInt(key) } +func GetFloat64(key string) float64 { return DefaultConfig.GetFloat64(key) } +func GetDuration(key string) time.Duration { return DefaultConfig.GetDuration(key) } +func GetStringSlice(key string) []string { return DefaultConfig.GetStringSlice(key) } +func GetStringMap(key string) map[string]interface{} { return DefaultConfig.GetStringMap(key) } +func GetStringMapString(key string) map[string]string { return DefaultConfig.GetStringMapString(key) } diff --git a/config/parser.go b/config/parser.go index 120211d1a..93f76bb36 100644 --- a/config/parser.go +++ b/config/parser.go @@ -24,18 +24,15 @@ import ( "github.com/mitchellh/mapstructure" ) -func parseYamlFileForMerge(path string) (map[string]interface{}, error) { +func parseYamlFileForMerge(path string, data interface{}) error { b, err := ioutil.ReadFile(path) if err != nil { - return nil, err + return err } - - var data map[string]interface{} - if err := yaml.Unmarshal(b, &data); err != nil { - return nil, err + if err := yaml.Unmarshal(b, data); err != nil { + return err } - - return data, nil + return nil } // ParseConfigFromYamlFile parses a configuration from a file in YAML diff --git a/config/plugin_config.go b/config/plugin_config.go index ee8205be0..a4d543cf2 100644 --- a/config/plugin_config.go +++ b/config/plugin_config.go @@ -115,6 +115,7 @@ type Option func(*options) // WithCustomizedFlag is an option to customize config flag for plugin in ForPlugin. // The parameters are used to replace defaults in this order: flag name, default, usage. +// DEPRECATED func WithCustomizedFlag(s ...string) Option { return func(o *options) { if len(s) > 0 { @@ -161,7 +162,7 @@ func ForPlugin(name string, opts ...Option) PluginConfig { } if opt.Conf == nil { - opt.Conf = DefaultConf + opt.Conf = DefaultConfig } if opt.FlagName != "" && opt.flagSet.Lookup(opt.FlagName) == nil { @@ -225,21 +226,28 @@ func (p *pluginConfig) LoadValue(cfg interface{}) (found bool, err error) { conf := p.conf if logger.GetLevel() >= logging.TraceLevel { - conf.(*Conf).Debug() + conf.(*config).Debug() } - //var def map[string]interface{} def := cfgToStringMap(cfg) - log.Tracef("setting default to: %+v", def) + log.Tracef("setting default %s config to: %+v", p.name, def) conf.SetDefault(p.name, def) + if logger.GetLevel() >= logging.TraceLevel { + conf.(*config).Debug() + } + cfgName := p.GetConfigName() /*if cfgName == "" { return false, nil }*/ if cfgName != "" { log.Tracef("pluginConfig.cfgName: %q", cfgName) - data, err := parseYamlFileForMerge(cfgName) + var data map[string]interface{} + if sub := p.conf.Sub(p.name); sub != nil { + data = sub.All() + } + err := parseYamlFileForMerge(cfgName, &data) if err != nil { return false, err } @@ -251,14 +259,14 @@ func (p *pluginConfig) LoadValue(cfg interface{}) (found bool, err error) { log.Debugf("ERROR merging from plugin config file: %v", err) return false, err } - log.Debugf("merged %q OK: %+v", p.name, conf.(*Conf).AllKeys()) + log.Debugf("merged %q OK", p.name /*, conf.(*config).AllKeys()*/) if logger.GetLevel() >= logging.TraceLevel { - conf.(*Conf).Debug() + conf.(*config).Debug() } } pluginCfg := conf.Get(p.name) - if pluginCfg == nil || !conf.(*Conf).InConfig(p.name) { + if pluginCfg == nil || !conf.(*config).InConfig(p.name) { return false, nil } log.Tracef("pluginCfg: %+v", pluginCfg) @@ -279,7 +287,7 @@ func cfgToStringMap(cfg interface{}) map[string]interface{} { if err != nil { return nil } - m := make(map[string]interface{}) + var m map[string]interface{} if err := json.Unmarshal(b, &m); err != nil { return nil } From 1dd52d24904788172bda7004f80aebd3371dfd4f Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Mon, 4 May 2020 15:22:27 +0200 Subject: [PATCH 6/6] Update agent Signed-off-by: Ondrej Fabry --- agent/agent.go | 54 +++++++++++++++++++++++++++--------------------- agent/options.go | 17 +++++---------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 6d48b78d8..5efc21075 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -94,11 +94,15 @@ type agent struct { } func newAgent(opts ...Option) *agent { - options := NewOptions(opts...) - return &agent{ + options := newOptions() + for _, o := range opts { + o(&options) + } + a := &agent{ opts: options, tracer: measure.NewTracer("agent-plugins"), } + return a } func (a *agent) Name() string { @@ -109,25 +113,9 @@ func (a *agent) Init(opts ...Option) { for _, o := range opts { o(&a.opts) } -} - -func (a *agent) setup() error { - if a.opts.initialized { - return nil - } - a.opts.initialized = true - - if a.opts.Conf == nil { - a.opts.Conf = config.DefaultConf - } - - if err := a.parseFlags(); err != nil { - return fmt.Errorf("flags error: %w", err) - } - if err := a.loadConfig(); err != nil { - return fmt.Errorf("config error: %w", err) + if err := a.setup(); err != nil { + agentLogger.Warn("setup failed:", err) } - return nil } // Options returns the Options the agent was created with @@ -154,12 +142,31 @@ func (a *agent) Run() error { return a.Wait() } +func (a *agent) setup() error { + if a.opts.initialized { + return nil + } + a.opts.initialized = true + + if a.opts.Config == nil { + a.opts.Config = config.DefaultConfig + } + + if err := a.parseFlags(); err != nil { + return fmt.Errorf("flags error: %w", err) + } + if err := a.loadConfig(); err != nil { + return fmt.Errorf("config error: %w", err) + } + return nil +} + func (a *agent) parseFlags() error { flagSet := flag.NewFlagSet(a.Name(), flag.ExitOnError) flagSet.SortFlags = false //flags.SetOutput(ioutil.Discard) - log.Printf("adding %d flags from CommandLine", strings.Count(flag.CommandLine.FlagUsages(), "\n")) + infraLogger.Debugf("adding %d flags from CommandLine", strings.Count(flag.CommandLine.FlagUsages(), "\n")) flagSet.AddFlagSet(flag.CommandLine) // global flags @@ -199,9 +206,10 @@ func (a *agent) parseFlags() error { return nil } + func (a *agent) loadConfig() error { - conf := a.opts.Conf - if err := conf.Load(); err != nil { + conf := a.opts.Config + if err := conf.Read(); err != nil { return err } return nil diff --git a/agent/options.go b/agent/options.go index c16c61c9c..d0d5e9d36 100644 --- a/agent/options.go +++ b/agent/options.go @@ -40,7 +40,7 @@ type Options struct { Name string Version string - Conf config.Config + Config config.Config //FlagSet *config.FlagSet Flags []config.Flag @@ -51,26 +51,19 @@ type Options struct { Context context.Context Plugins []infra.Plugin - //pluginMap map[infra.Plugin]struct{} - //pluginNames map[string]struct{} initialized bool } -func NewOptions(opts ...Option) Options { +func newOptions() Options { opt := Options{ Name: "agent", Version: "dev", - Conf: config.DefaultConf, + Config: config.DefaultConfig, StartTimeout: DefaultStartTimeout, StopTimeout: DefaultStopTimeout, - QuitSignals: []os.Signal{ - syscall.SIGINT, - syscall.SIGTERM, - }, - } - for _, o := range opts { - o(&opt) + QuitSignals: []os.Signal{syscall.SIGINT, syscall.SIGTERM}, + Context: context.Background(), } return opt }