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
This commit is contained in:
		
							parent
							
								
									bf8e0f4cae
								
							
						
					
					
						commit
						b10b186cc8
					
				
							
								
								
									
										22
									
								
								agent.go
								
								
								
								
							
							
						
						
									
										22
									
								
								agent.go
								
								
								
								
							|  | @ -7,6 +7,7 @@ import ( | ||||||
| 	"math/big" | 	"math/big" | ||||||
| 	"os" | 	"os" | ||||||
| 	"sort" | 	"sort" | ||||||
|  | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | @ -147,17 +148,13 @@ func (a *Agent) Close() error { | ||||||
| func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) { | func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) { | ||||||
| 	var names []string | 	var names []string | ||||||
| 
 | 
 | ||||||
| 	for _, name := range config.OutputsDeclared() { | 	for name, output := range config.OutputsDeclared() { | ||||||
| 		creator, ok := outputs.Outputs[name] | 		// Trim the ID off the output name for filtering
 | ||||||
| 		if !ok { | 		filtername := strings.TrimRight(name, "-0123456789") | ||||||
| 			return nil, fmt.Errorf("Undefined but requested output: %s", name) | 		if sliceContains(filtername, filters) || len(filters) == 0 { | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if sliceContains(name, filters) || len(filters) == 0 { |  | ||||||
| 			if a.Debug { | 			if a.Debug { | ||||||
| 				log.Println("Output Enabled: ", name) | 				log.Println("Output Enabled: ", name) | ||||||
| 			} | 			} | ||||||
| 			output := creator() |  | ||||||
| 
 | 
 | ||||||
| 			err := config.ApplyOutput(name, output) | 			err := config.ApplyOutput(name, output) | ||||||
| 			if err != nil { | 			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) { | func (a *Agent) LoadPlugins(filters []string, config *Config) ([]string, error) { | ||||||
| 	var names []string | 	var names []string | ||||||
| 
 | 
 | ||||||
| 	for _, name := range config.PluginsDeclared() { | 	for name, plugin := range config.PluginsDeclared() { | ||||||
| 		creator, ok := plugins.Plugins[name] |  | ||||||
| 		if !ok { |  | ||||||
| 			return nil, fmt.Errorf("Undefined but requested plugin: %s", name) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if sliceContains(name, filters) || len(filters) == 0 { | 		if sliceContains(name, filters) || len(filters) == 0 { | ||||||
| 			plugin := creator() |  | ||||||
| 
 |  | ||||||
| 			config, err := config.ApplyPlugin(name, plugin) | 			config, err := config.ApplyPlugin(name, plugin) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
|  |  | ||||||
							
								
								
									
										72
									
								
								config.go
								
								
								
								
							
							
						
						
									
										72
									
								
								config.go
								
								
								
								
							|  | @ -4,6 +4,7 @@ import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"log" | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"sort" | 	"sort" | ||||||
|  | @ -20,8 +21,8 @@ import ( | ||||||
| // will be logging to, as well as all the plugins that the user has
 | // will be logging to, as well as all the plugins that the user has
 | ||||||
| // specified
 | // specified
 | ||||||
| type Config struct { | type Config struct { | ||||||
| 	// This lives outside the agent because mergeStruct doesn't need to handle maps normally.
 | 	// This lives outside the agent because mergeStruct doesn't need to handle
 | ||||||
| 	// We just copy the elements manually in ApplyAgent.
 | 	// maps normally. We just copy the elements manually in ApplyAgent.
 | ||||||
| 	Tags map[string]string | 	Tags map[string]string | ||||||
| 
 | 
 | ||||||
| 	agent                *Agent | 	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.
 | // 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.
 | // PluginsDeclared returns the name of all plugins declared in the config.
 | ||||||
| func (c *Config) PluginsDeclared() []string { | func (c *Config) PluginsDeclared() map[string]plugins.Plugin { | ||||||
| 	var names []string | 	return c.plugins | ||||||
| 	for name := range c.plugins { |  | ||||||
| 		names = append(names, name) |  | ||||||
| 	} |  | ||||||
| 	sort.Strings(names) |  | ||||||
| 	return names |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // OutputsDeclared returns the name of all outputs declared in the config.
 | // OutputsDeclared returns the name of all outputs declared in the config.
 | ||||||
| func (c *Config) OutputsDeclared() []string { | func (c *Config) OutputsDeclared() map[string]outputs.Output { | ||||||
| 	var names []string | 	return c.outputs | ||||||
| 	for name := range c.outputs { |  | ||||||
| 		names = append(names, name) |  | ||||||
| 	} |  | ||||||
| 	sort.Strings(names) |  | ||||||
| 	return names |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ListTags returns a string of tags specified in the config,
 | // ListTags returns a string of tags specified in the config,
 | ||||||
|  | @ -283,7 +274,7 @@ func PrintSampleConfig(pluginFilters []string, outputFilters []string) { | ||||||
| 		creator := outputs.Outputs[oname] | 		creator := outputs.Outputs[oname] | ||||||
| 		output := creator() | 		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() | 		config := output.SampleConfig() | ||||||
| 		if config == "" { | 		if config == "" { | ||||||
|  | @ -394,20 +385,24 @@ func findField(fieldName string, value reflect.Value) reflect.Value { | ||||||
| 	return 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 { | func mergeStruct(base, overlay interface{}, fields []string) error { | ||||||
| 	baseValue := reflect.ValueOf(base).Elem() | 	baseValue := reflect.ValueOf(base).Elem() | ||||||
| 	overlayValue := reflect.ValueOf(overlay).Elem() | 	overlayValue := reflect.ValueOf(overlay).Elem() | ||||||
| 	if baseValue.Kind() != reflect.Struct { | 	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() { | 	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 { | 	for _, field := range fields { | ||||||
| 		overlayFieldValue := findField(field, overlayValue) | 		overlayFieldValue := findField(field, overlayValue) | ||||||
| 		if !overlayFieldValue.IsValid() { | 		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) | 		baseFieldValue := findField(field, baseValue) | ||||||
| 		if overlayFieldValue.Kind() == reflect.Slice { | 		if overlayFieldValue.Kind() == reflect.Slice { | ||||||
|  | @ -519,34 +514,49 @@ func LoadConfig(path string) (*Config, error) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for name, val := range tbl.Fields { | 	for name, val := range tbl.Fields { | ||||||
| 		subtbl, ok := val.(*ast.Table) | 		subTable, ok := val.(*ast.Table) | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			return nil, errors.New("invalid configuration") | 			return nil, errors.New("invalid configuration") | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		switch name { | 		switch name { | ||||||
| 		case "agent": | 		case "agent": | ||||||
| 			err := c.parseAgent(subtbl) | 			err := c.parseAgent(subTable) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
|  | 				log.Printf("Could not parse [agent] config\n") | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| 		case "tags": | 		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 | 				return nil, err | ||||||
| 			} | 			} | ||||||
| 		case "outputs": | 		case "outputs": | ||||||
| 			for outputName, outputVal := range subtbl.Fields { | 			for outputName, outputVal := range subTable.Fields { | ||||||
| 				outputSubtbl, ok := outputVal.(*ast.Table) | 				switch outputSubTable := outputVal.(type) { | ||||||
| 				if !ok { | 				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 | 						return nil, err | ||||||
| 					} | 					} | ||||||
| 				err = c.parseOutput(outputName, outputSubtbl) | 				case []*ast.Table: | ||||||
|  | 					for id, t := range outputSubTable { | ||||||
|  | 						err = c.parseOutput(outputName, t, id) | ||||||
| 						if err != nil { | 						if err != nil { | ||||||
|  | 							log.Printf("Could not parse config for output: %s\n", | ||||||
|  | 								outputName) | ||||||
| 							return nil, err | 							return nil, err | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				default: | 				default: | ||||||
| 			err = c.parsePlugin(name, subtbl) | 					return nil, fmt.Errorf("Unsupported config format: %s", | ||||||
|  | 						outputName) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		default: | ||||||
|  | 			err = c.parsePlugin(name, subTable) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				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.
 | // 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) | 	c.outputFieldsSet[name] = extractFieldNames(outputAst) | ||||||
| 	creator, ok := outputs.Outputs[name] | 	creator, ok := outputs.Outputs[name] | ||||||
| 	if !ok { | 	if !ok { | ||||||
|  | @ -590,7 +600,7 @@ func (c *Config) parseOutput(name string, outputAst *ast.Table) error { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	c.outputs[name] = output | 	c.outputs[fmt.Sprintf("%s-%d", name, id)] = output | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue