From b10b186cc8f2a7a70a06ff045d6acfe59806eca8 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Fri, 13 Nov 2015 16:14:07 -0700 Subject: [PATCH] Allow users to specify outputs as lists This will provide the ability to specify multiple outputs for a single type of output. In essence, allowing this: [outputs] [[outputs.influxdb]] urls = ["udp://localhost:8089"] database = "udp-telegraf" [[outputs.influxdb]] urls = ["http://myhost:8086"] database = "telegraf" [[outputs.kafka]] brokers = ["192.168.99.100:9092"] topic = "telegraf" closes #335 --- agent.go | 22 +++++---------- config.go | 80 +++++++++++++++++++++++++++++++------------------------ 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/agent.go b/agent.go index 94ee88543..47c2293cc 100644 --- a/agent.go +++ b/agent.go @@ -7,6 +7,7 @@ import ( "math/big" "os" "sort" + "strings" "sync" "time" @@ -147,17 +148,13 @@ func (a *Agent) Close() error { func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) { var names []string - for _, name := range config.OutputsDeclared() { - creator, ok := outputs.Outputs[name] - if !ok { - return nil, fmt.Errorf("Undefined but requested output: %s", name) - } - - if sliceContains(name, filters) || len(filters) == 0 { + for name, output := range config.OutputsDeclared() { + // Trim the ID off the output name for filtering + filtername := strings.TrimRight(name, "-0123456789") + if sliceContains(filtername, filters) || len(filters) == 0 { if a.Debug { log.Println("Output Enabled: ", name) } - output := creator() err := config.ApplyOutput(name, output) if err != nil { @@ -178,15 +175,8 @@ func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) func (a *Agent) LoadPlugins(filters []string, config *Config) ([]string, error) { var names []string - for _, name := range config.PluginsDeclared() { - creator, ok := plugins.Plugins[name] - if !ok { - return nil, fmt.Errorf("Undefined but requested plugin: %s", name) - } - + for name, plugin := range config.PluginsDeclared() { if sliceContains(name, filters) || len(filters) == 0 { - plugin := creator() - config, err := config.ApplyPlugin(name, plugin) if err != nil { return nil, err diff --git a/config.go b/config.go index 8ca8a2c0f..1181e109b 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/ioutil" + "log" "path/filepath" "reflect" "sort" @@ -20,8 +21,8 @@ import ( // will be logging to, as well as all the plugins that the user has // specified type Config struct { - // This lives outside the agent because mergeStruct doesn't need to handle maps normally. - // We just copy the elements manually in ApplyAgent. + // This lives outside the agent because mergeStruct doesn't need to handle + // maps normally. We just copy the elements manually in ApplyAgent. Tags map[string]string agent *Agent @@ -156,23 +157,13 @@ func (c *Config) ApplyPlugin(name string, v interface{}) (*ConfiguredPlugin, err // Couldn't figure out how to get this to work with the declared function. // PluginsDeclared returns the name of all plugins declared in the config. -func (c *Config) PluginsDeclared() []string { - var names []string - for name := range c.plugins { - names = append(names, name) - } - sort.Strings(names) - return names +func (c *Config) PluginsDeclared() map[string]plugins.Plugin { + return c.plugins } // OutputsDeclared returns the name of all outputs declared in the config. -func (c *Config) OutputsDeclared() []string { - var names []string - for name := range c.outputs { - names = append(names, name) - } - sort.Strings(names) - return names +func (c *Config) OutputsDeclared() map[string]outputs.Output { + return c.outputs } // ListTags returns a string of tags specified in the config, @@ -283,7 +274,7 @@ func PrintSampleConfig(pluginFilters []string, outputFilters []string) { creator := outputs.Outputs[oname] output := creator() - fmt.Printf("\n# %s\n[outputs.%s]", output.Description(), oname) + fmt.Printf("\n# %s\n[[outputs.%s]]", output.Description(), oname) config := output.SampleConfig() if config == "" { @@ -394,20 +385,24 @@ func findField(fieldName string, value reflect.Value) reflect.Value { return reflect.Value{} } -// A very limited merge. Merges the fields named in the fields parameter, replacing most values, but appending to arrays. +// A very limited merge. Merges the fields named in the fields parameter, +// replacing most values, but appending to arrays. func mergeStruct(base, overlay interface{}, fields []string) error { baseValue := reflect.ValueOf(base).Elem() overlayValue := reflect.ValueOf(overlay).Elem() if baseValue.Kind() != reflect.Struct { - return fmt.Errorf("Tried to merge something that wasn't a struct: type %v was %v", baseValue.Type(), baseValue.Kind()) + return fmt.Errorf("Tried to merge something that wasn't a struct: type %v was %v", + baseValue.Type(), baseValue.Kind()) } if baseValue.Type() != overlayValue.Type() { - return fmt.Errorf("Tried to merge two different types: %v and %v", baseValue.Type(), overlayValue.Type()) + return fmt.Errorf("Tried to merge two different types: %v and %v", + baseValue.Type(), overlayValue.Type()) } for _, field := range fields { overlayFieldValue := findField(field, overlayValue) if !overlayFieldValue.IsValid() { - return fmt.Errorf("could not find field in %v matching %v", overlayValue.Type(), field) + return fmt.Errorf("could not find field in %v matching %v", + overlayValue.Type(), field) } baseFieldValue := findField(field, baseValue) if overlayFieldValue.Kind() == reflect.Slice { @@ -519,34 +514,49 @@ func LoadConfig(path string) (*Config, error) { } for name, val := range tbl.Fields { - subtbl, ok := val.(*ast.Table) + subTable, ok := val.(*ast.Table) if !ok { return nil, errors.New("invalid configuration") } switch name { case "agent": - err := c.parseAgent(subtbl) + err := c.parseAgent(subTable) if err != nil { + log.Printf("Could not parse [agent] config\n") return nil, err } case "tags": - if err = toml.UnmarshalTable(subtbl, c.Tags); err != nil { + if err = toml.UnmarshalTable(subTable, c.Tags); err != nil { + log.Printf("Could not parse [tags] config\n") return nil, err } case "outputs": - for outputName, outputVal := range subtbl.Fields { - outputSubtbl, ok := outputVal.(*ast.Table) - if !ok { - return nil, err - } - err = c.parseOutput(outputName, outputSubtbl) - if err != nil { - return nil, err + for outputName, outputVal := range subTable.Fields { + switch outputSubTable := outputVal.(type) { + case *ast.Table: + err = c.parseOutput(outputName, outputSubTable, 0) + if err != nil { + log.Printf("Could not parse config for output: %s\n", + outputName) + return nil, err + } + case []*ast.Table: + for id, t := range outputSubTable { + err = c.parseOutput(outputName, t, id) + if err != nil { + log.Printf("Could not parse config for output: %s\n", + outputName) + return nil, err + } + } + default: + return nil, fmt.Errorf("Unsupported config format: %s", + outputName) } } default: - err = c.parsePlugin(name, subtbl) + err = c.parsePlugin(name, subTable) if err != nil { return nil, err } @@ -579,7 +589,7 @@ func (c *Config) parseAgent(agentAst *ast.Table) error { } // Parse an output config out of the given *ast.Table. -func (c *Config) parseOutput(name string, outputAst *ast.Table) error { +func (c *Config) parseOutput(name string, outputAst *ast.Table, id int) error { c.outputFieldsSet[name] = extractFieldNames(outputAst) creator, ok := outputs.Outputs[name] if !ok { @@ -590,7 +600,7 @@ func (c *Config) parseOutput(name string, outputAst *ast.Table) error { if err != nil { return err } - c.outputs[name] = output + c.outputs[fmt.Sprintf("%s-%d", name, id)] = output return nil }