Merge pull request #796 from prometheus/fabxc/cfg-err

config: raise error on unknown config parameters
This commit is contained in:
Fabian Reinartz 2015-06-12 14:59:13 +02:00
commit 9013319d3a
4 changed files with 158 additions and 68 deletions

View file

@ -93,10 +93,24 @@ type Config struct {
RuleFiles []string `yaml:"rule_files,omitempty"` RuleFiles []string `yaml:"rule_files,omitempty"`
ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"` ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
// original is the input from which the config was parsed. // original is the input from which the config was parsed.
original string original string
} }
func checkOverflow(m map[string]interface{}, ctx string) error {
if len(m) > 0 {
var keys []string
for k := range m {
keys = append(keys, k)
}
return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", "))
}
return nil
}
func (c Config) String() string { func (c Config) String() string {
if c.original != "" { if c.original != "" {
return c.original return c.original
@ -138,7 +152,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
} }
jobNames[scfg.JobName] = struct{}{} jobNames[scfg.JobName] = struct{}{}
} }
return nil return checkOverflow(c.XXX, "config")
} }
// GlobalConfig configures values that are used across other configuration // GlobalConfig configures values that are used across other configuration
@ -150,9 +164,11 @@ type GlobalConfig struct {
ScrapeTimeout Duration `yaml:"scrape_timeout,omitempty"` ScrapeTimeout Duration `yaml:"scrape_timeout,omitempty"`
// How frequently to evaluate rules by default. // How frequently to evaluate rules by default.
EvaluationInterval Duration `yaml:"evaluation_interval,omitempty"` EvaluationInterval Duration `yaml:"evaluation_interval,omitempty"`
// The labels to add to any timeseries that this Prometheus instance scrapes. // The labels to add to any timeseries that this Prometheus instance scrapes.
Labels clientmodel.LabelSet `yaml:"labels,omitempty"` Labels clientmodel.LabelSet `yaml:"labels,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -162,7 +178,7 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal((*plain)(c)); err != nil { if err := unmarshal((*plain)(c)); err != nil {
return err return err
} }
return nil return checkOverflow(c.XXX, "global config")
} }
// ScrapeConfig configures a scraping unit for Prometheus. // ScrapeConfig configures a scraping unit for Prometheus.
@ -178,7 +194,7 @@ type ScrapeConfig struct {
// The URL scheme with which to fetch metrics from targets. // The URL scheme with which to fetch metrics from targets.
Scheme string `yaml:"scheme,omitempty"` Scheme string `yaml:"scheme,omitempty"`
// The HTTP basic authentication credentials for the targets. // The HTTP basic authentication credentials for the targets.
BasicAuth *BasicAuth `yaml:"basic_auth"` BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
// List of labeled target groups for this job. // List of labeled target groups for this job.
TargetGroups []*TargetGroup `yaml:"target_groups,omitempty"` TargetGroups []*TargetGroup `yaml:"target_groups,omitempty"`
@ -190,6 +206,9 @@ type ScrapeConfig struct {
ConsulSDConfigs []*ConsulSDConfig `yaml:"consul_sd_configs,omitempty"` ConsulSDConfigs []*ConsulSDConfig `yaml:"consul_sd_configs,omitempty"`
// List of relabel configurations. // List of relabel configurations.
RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"` RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -203,13 +222,26 @@ func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if !patJobName.MatchString(c.JobName) { if !patJobName.MatchString(c.JobName) {
return fmt.Errorf("%q is not a valid job name", c.JobName) return fmt.Errorf("%q is not a valid job name", c.JobName)
} }
return nil return checkOverflow(c.XXX, "scrape_config")
} }
// BasicAuth contains basic HTTP authentication credentials. // BasicAuth contains basic HTTP authentication credentials.
type BasicAuth struct { type BasicAuth struct {
Username string `yaml:"username"` Username string `yaml:"username"`
Password string `yaml:"password"` Password string `yaml:"password"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error {
type plain BasicAuth
err := unmarshal((*plain)(a))
if err != nil {
return err
}
return checkOverflow(a.XXX, "basic_auth")
} }
// TargetGroup is a set of targets with a common label set. // TargetGroup is a set of targets with a common label set.
@ -231,8 +263,9 @@ func (tg TargetGroup) String() string {
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error { func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error {
g := struct { g := struct {
Targets []string `yaml:"targets"` Targets []string `yaml:"targets"`
Labels clientmodel.LabelSet `yaml:"labels"` Labels clientmodel.LabelSet `yaml:"labels"`
XXX map[string]interface{} `yaml:",inline"`
}{} }{}
if err := unmarshal(&g); err != nil { if err := unmarshal(&g); err != nil {
return err return err
@ -247,7 +280,7 @@ func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error {
}) })
} }
tg.Labels = g.Labels tg.Labels = g.Labels
return nil return checkOverflow(g.XXX, "target_group")
} }
// MarshalYAML implements the yaml.Marshaler interface. // MarshalYAML implements the yaml.Marshaler interface.
@ -291,6 +324,9 @@ func (tg *TargetGroup) UnmarshalJSON(b []byte) error {
type DNSSDConfig struct { type DNSSDConfig struct {
Names []string `yaml:"names"` Names []string `yaml:"names"`
RefreshInterval Duration `yaml:"refresh_interval,omitempty"` RefreshInterval Duration `yaml:"refresh_interval,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -304,13 +340,16 @@ func (c *DNSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if len(c.Names) == 0 { if len(c.Names) == 0 {
return fmt.Errorf("DNS-SD config must contain at least one SRV record name") return fmt.Errorf("DNS-SD config must contain at least one SRV record name")
} }
return nil return checkOverflow(c.XXX, "dns_sd_config")
} }
// FileSDConfig is the configuration for file based discovery. // FileSDConfig is the configuration for file based discovery.
type FileSDConfig struct { type FileSDConfig struct {
Names []string `yaml:"names"` Names []string `yaml:"names"`
RefreshInterval Duration `yaml:"refresh_interval,omitempty"` RefreshInterval Duration `yaml:"refresh_interval,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -329,7 +368,7 @@ func (c *FileSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
return fmt.Errorf("path name %q is not valid for file discovery", name) return fmt.Errorf("path name %q is not valid for file discovery", name)
} }
} }
return nil return checkOverflow(c.XXX, "file_sd_config")
} }
// ConsulSDConfig is the configuration for Consul service discovery. // ConsulSDConfig is the configuration for Consul service discovery.
@ -343,6 +382,9 @@ type ConsulSDConfig struct {
Password string `yaml:"password"` Password string `yaml:"password"`
// The list of services for which targets are discovered. // The list of services for which targets are discovered.
Services []string `yaml:"services"` Services []string `yaml:"services"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -359,7 +401,7 @@ func (c *ConsulSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
if len(c.Services) == 0 { if len(c.Services) == 0 {
return fmt.Errorf("Consul SD configuration requires at least one service name") return fmt.Errorf("Consul SD configuration requires at least one service name")
} }
return nil return checkOverflow(c.XXX, "consul_sd_config")
} }
// RelabelAction is the action to be performed on relabeling. // RelabelAction is the action to be performed on relabeling.
@ -403,6 +445,9 @@ type RelabelConfig struct {
Replacement string `yaml:"replacement,omitempty"` Replacement string `yaml:"replacement,omitempty"`
// Action is the action to be performed for the relabeling. // Action is the action to be performed for the relabeling.
Action RelabelAction `yaml:"action,omitempty"` Action RelabelAction `yaml:"action,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
} }
// UnmarshalYAML implements the yaml.Unmarshaler interface. // UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -415,7 +460,7 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if c.Regex == nil { if c.Regex == nil {
return fmt.Errorf("relabel configuration requires a regular expression") return fmt.Errorf("relabel configuration requires a regular expression")
} }
return nil return checkOverflow(c.XXX, "relabel_config")
} }
// Regexp encapsulates a regexp.Regexp and makes it YAML marshallable. // Regexp encapsulates a regexp.Regexp and makes it YAML marshallable.

View file

@ -115,6 +115,24 @@ var expectedConf = &Config{
}, },
}, },
}, },
{
JobName: "service-y",
ScrapeInterval: Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
MetricsPath: DefaultScrapeConfig.MetricsPath,
Scheme: DefaultScrapeConfig.Scheme,
ConsulSDConfigs: []*ConsulSDConfig{
{
Server: "localhost:1234",
Services: []string{"nginx", "cache", "mysql"},
TagSeparator: DefaultConsulSDConfig.TagSeparator,
Scheme: DefaultConsulSDConfig.Scheme,
},
},
},
}, },
original: "", original: "",
} }
@ -128,20 +146,20 @@ func TestLoadConfig(t *testing.T) {
c, err := LoadFromFile("testdata/conf.good.yml") c, err := LoadFromFile("testdata/conf.good.yml")
if err != nil { if err != nil {
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err) t.Fatalf("Error parsing %s: %s", "testdata/conf.good.yml", err)
} }
bgot, err := yaml.Marshal(c) bgot, err := yaml.Marshal(c)
if err != nil { if err != nil {
t.Errorf("%s", err) t.Fatalf("%s", err)
} }
bexp, err := yaml.Marshal(expectedConf) bexp, err := yaml.Marshal(expectedConf)
if err != nil { if err != nil {
t.Errorf("%s", err) t.Fatalf("%s", err)
} }
expectedConf.original = c.original expectedConf.original = c.original
if !reflect.DeepEqual(c, expectedConf) { if !reflect.DeepEqual(c, expectedConf) {
t.Errorf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.good.yml", bgot, bexp) t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.good.yml", bgot, bexp)
} }
} }
@ -170,6 +188,9 @@ var expectedErrors = []struct {
}, { }, {
filename: "rules.bad.yml", filename: "rules.bad.yml",
errMsg: "invalid rule file path", errMsg: "invalid rule file path",
}, {
filename: "unknown_attr.bad.yml",
errMsg: "unknown fields in scrape_config: consult_sd_configs",
}, },
} }

View file

@ -5,71 +5,75 @@ global:
# scrape_timeout is set to the global default (10s). # scrape_timeout is set to the global default (10s).
labels: labels:
monitor: codelab monitor: codelab
foo: bar foo: bar
rule_files: rule_files:
- "first.rules" - "first.rules"
- "second.rules" - "second.rules"
- "my/*.rules" - "my/*.rules"
scrape_configs: scrape_configs:
- job_name: prometheus - job_name: prometheus
# scrape_interval is defined by the configured global (15s). # scrape_interval is defined by the configured global (15s).
# scrape_timeout is defined by the global default (10s). # scrape_timeout is defined by the global default (10s).
# metrics_path defaults to '/metrics' # metrics_path defaults to '/metrics'
# scheme defaults to 'http'. # scheme defaults to 'http'.
file_sd_configs:
- names:
- foo/*.slow.json
- foo/*.slow.yml
- single/file.yml
refresh_interval: 10m
- names:
- bar/*.yaml
target_groups:
- targets: ['localhost:9090', 'localhost:9191']
labels: labels:
foo: baz my: label
your: label
file_sd_configs: relabel_configs:
- names: - source_labels: [job, __meta_dns_srv_name]
- foo/*.slow.json regex: (.*)some-[regex]$
- foo/*.slow.yml target_label: job
- single/file.yml replacement: foo-${1}
refresh_interval: 10m # action defaults to 'replace'
- names:
- bar/*.yaml
target_groups:
- targets: ['localhost:9090', 'localhost:9191']
labels:
my: label
your: label
relabel_configs:
- source_labels: [job, __meta_dns_srv_name]
regex: (.*)some-[regex]$
target_label: job
replacement: foo-${1}
# action defaults to 'replace'
- job_name: service-x - job_name: service-x
basic_auth: basic_auth:
username: admin username: admin
password: password password: password
scrape_interval: 50s scrape_interval: 50s
scrape_timeout: 5s scrape_timeout: 5s
metrics_path: /my_path metrics_path: /my_path
scheme: https scheme: https
dns_sd_configs: dns_sd_configs:
- refresh_interval: 15s - refresh_interval: 15s
names: names:
- first.dns.address.domain.com - first.dns.address.domain.com
- second.dns.address.domain.com - second.dns.address.domain.com
- names: - names:
- first.dns.address.domain.com - first.dns.address.domain.com
# refresh_interval defaults to 30s. # refresh_interval defaults to 30s.
relabel_configs: relabel_configs:
- source_labels: [job] - source_labels: [job]
regex: (.*)some-[regex]$ regex: (.*)some-[regex]$
action: drop action: drop
- job_name: service-y
consul_sd_configs:
- server: 'localhost:1234'
services: ['nginx', 'cache', 'mysql']

20
config/testdata/unknown_attr.bad.yml vendored Normal file
View file

@ -0,0 +1,20 @@
# my global config
global:
scrape_interval: 15s
evaluation_interval: 30s
# scrape_timeout is set to the global default (10s).
labels:
monitor: codelab
foo: bar
rule_files:
- "first.rules"
- "second.rules"
- "my/*.rules"
scrape_configs:
- job_name: prometheus
consult_sd_configs:
- server: 'localhost:1234'