Add validation for graphite config templates and tags

pull/3125/head
Jason Wilder 2015-06-23 15:23:55 -06:00
parent b55981f090
commit fed8d67946
3 changed files with 223 additions and 31 deletions

View File

@ -97,5 +97,11 @@ func (c *Config) Validate() error {
} else if c.HintedHandoff.Dir == "" {
return errors.New("HintedHandoff.Dir must be specified")
}
for _, g := range c.Graphites {
if err := g.Validate(); err != nil {
return fmt.Errorf("invalid graphite config: %v", err)
}
}
return nil
}

View File

@ -1,6 +1,7 @@
package graphite
import (
"fmt"
"strings"
"github.com/influxdb/influxdb/toml"
@ -14,15 +15,6 @@ const (
// DefaultDatabase is the default database if none is specified.
DefaultDatabase = "graphite"
// DefaultNameSeparator represents the default field separator.
DefaultNameSeparator = "."
// DefaultNameSchema represents the default schema of the name.
DefaultNameSchema = "measurement"
// By default unnamed fields from metrics will be ignored.
DefaultIgnoreUnnamed = true
// DefaultProtocol is the default IP protocol used by the Graphite input.
DefaultProtocol = "tcp"
@ -36,10 +28,6 @@ type Config struct {
Database string `toml:"database"`
Enabled bool `toml:"enabled"`
Protocol string `toml:"protocol"`
NamePosition string `toml:"name-position"`
NameSchema string `toml:"name-schema"`
NameSeparator string `toml:"name-separator"`
IgnoreUnnamed bool `toml:"ignore-unnamed"`
BatchSize int `toml:"batch-size"`
BatchTimeout toml.Duration `toml:"batch-timeout"`
ConsistencyLevel string `toml:"consistency-level"`
@ -53,9 +41,6 @@ func NewConfig() Config {
BindAddress: DefaultBindAddress,
Database: DefaultDatabase,
Protocol: DefaultProtocol,
NameSchema: DefaultNameSchema,
NameSeparator: DefaultNameSeparator,
IgnoreUnnamed: DefaultIgnoreUnnamed,
ConsistencyLevel: DefaultConsistencyLevel,
}
}
@ -73,12 +58,6 @@ func (c *Config) WithDefaults() *Config {
if d.Protocol == "" {
d.Protocol = DefaultProtocol
}
if d.NameSchema == "" {
d.NameSchema = DefaultNameSchema
}
if d.NameSeparator == "" {
d.NameSeparator = DefaultNameSeparator
}
if d.ConsistencyLevel == "" {
d.ConsistencyLevel = DefaultConsistencyLevel
}
@ -93,3 +72,116 @@ func (c *Config) DefaultTags() tsdb.Tags {
}
return tags
}
func (c *Config) Validate() error {
if err := c.validateTemplates(); err != nil {
return err
}
if err := c.validateTags(); err != nil {
return err
}
return nil
}
func (c *Config) validateTemplates() error {
for i, t := range c.Templates {
parts := strings.Fields(t)
// Ensure template string is non-empty
if len(parts) == 0 {
return fmt.Errorf("missing template at position: %d", i)
}
if len(parts) == 1 && parts[0] == "" {
return fmt.Errorf("missing template at position: %d", i)
}
if len(parts) > 3 {
return fmt.Errorf("invalid template format: '%s'", t)
}
template := t
filter := ""
tags := ""
if len(parts) >= 2 {
filter = parts[0]
template = parts[1]
}
if len(parts) == 3 {
tags = parts[2]
}
// Validate the template has one and only one measurement
if err := c.validateTemplate(template); err != nil {
return err
}
// Validate filter expression is valid
if err := c.validateFilter(filter); err != nil {
return err
}
if tags != "" {
// Validate tags
for _, tagStr := range strings.Split(tags, ",") {
if err := c.validateTag(tagStr); err != nil {
return err
}
}
}
}
return nil
}
func (c *Config) validateTags() error {
for _, t := range c.Tags {
if err := c.validateTag(t); err != nil {
return err
}
}
return nil
}
func (c *Config) validateTemplate(template string) error {
hasMeasurement := false
for _, p := range strings.Split(template, ".") {
if p == "measurement" || p == "measurement*" {
if hasMeasurement {
return fmt.Errorf("multiple measurements in template `%s`", template)
}
hasMeasurement = true
}
}
if !hasMeasurement {
return fmt.Errorf("no measurement in template `%s`", template)
}
return nil
}
func (c *Config) validateFilter(filter string) error {
for _, p := range strings.Split(filter, ".") {
if p == "" {
return fmt.Errorf("filter contains blank section: %s", filter)
}
if strings.Contains(p, "*") && p != "*" {
return fmt.Errorf("invalid filter wildcard section: %s", filter)
}
}
return nil
}
func (c *Config) validateTag(keyValue string) error {
parts := strings.Split(keyValue, "=")
if len(parts) != 2 {
return fmt.Errorf("invalid template tags: '%s'", keyValue)
}
if parts[0] == "" || parts[1] == "" {
return fmt.Errorf("invalid template tags: %s'", keyValue)
}
return nil
}

View File

@ -16,9 +16,6 @@ bind-address = ":8080"
database = "mydb"
enabled = true
protocol = "tcp"
name-schema= "measurement"
ignore-unnamed = true
name-separator = "."
batch-size=100
batch-timeout="1s"
consistency-level="one"
@ -37,12 +34,6 @@ tags=["region=us-east"]
t.Fatalf("unexpected graphite enabled: %v", c.Enabled)
} else if c.Protocol != "tcp" {
t.Fatalf("unexpected graphite protocol: %s", c.Protocol)
} else if c.NameSchema != "measurement" {
t.Fatalf("unexpected graphite name schema: %s", c.NameSchema)
} else if c.IgnoreUnnamed != true {
t.Fatalf("unexpected ignore-unnamed: %v", c.IgnoreUnnamed)
} else if c.NameSeparator != "." {
t.Fatalf("unexpected graphite name separator: %s", c.NameSeparator)
} else if c.BatchSize != 100 {
t.Fatalf("unexpected graphite batch size: %d", c.BatchSize)
} else if time.Duration(c.BatchTimeout) != time.Second {
@ -58,3 +49,106 @@ tags=["region=us-east"]
t.Fatalf("unexpected graphite templates setting: %s", c.ConsistencyLevel)
}
}
func TestConfigValidateEmptyTemplate(t *testing.T) {
c := graphite.NewConfig()
c.Templates = []string{""}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{" "}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}
func TestConfigValidateTooManyField(t *testing.T) {
c := graphite.NewConfig()
c.Templates = []string{"a measurement b c"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}
func TestConfigValidateTemplatePatterns(t *testing.T) {
c := graphite.NewConfig()
c.Templates = []string{"measurement.measurement"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{"*measurement"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{".host.region"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}
func TestConfigValidateFilter(t *testing.T) {
c := graphite.NewConfig()
c.Templates = []string{".server measurement*"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{". .server measurement*"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{"server* measurement*"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}
func TestConfigValidateTemplateTags(t *testing.T) {
c := graphite.NewConfig()
c.Templates = []string{"*.server measurement* foo"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{"*.server measurement* foo=bar="}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{"*.server measurement* foo=bar,"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Templates = []string{"*.server measurement* ="}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}
func TestConfigValidateDefaultTags(t *testing.T) {
c := graphite.NewConfig()
c.Tags = []string{"foo"}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Tags = []string{"foo=bar="}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Tags = []string{"foo=bar", ""}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
c.Tags = []string{"="}
if err := c.Validate(); err == nil {
t.Errorf("config validate expected error. got nil")
}
}