mirror of
https://github.com/prometheus/prometheus.git
synced 2025-02-21 03:16:00 -08:00
Merge pull request #796 from prometheus/fabxc/cfg-err
config: raise error on unknown config parameters
This commit is contained in:
commit
9013319d3a
|
@ -93,10 +93,24 @@ type Config struct {
|
|||
RuleFiles []string `yaml:"rule_files,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 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 {
|
||||
if c.original != "" {
|
||||
return c.original
|
||||
|
@ -138,7 +152,7 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
}
|
||||
jobNames[scfg.JobName] = struct{}{}
|
||||
}
|
||||
return nil
|
||||
return checkOverflow(c.XXX, "config")
|
||||
}
|
||||
|
||||
// GlobalConfig configures values that are used across other configuration
|
||||
|
@ -150,9 +164,11 @@ type GlobalConfig struct {
|
|||
ScrapeTimeout Duration `yaml:"scrape_timeout,omitempty"`
|
||||
// How frequently to evaluate rules by default.
|
||||
EvaluationInterval Duration `yaml:"evaluation_interval,omitempty"`
|
||||
|
||||
// The labels to add to any timeseries that this Prometheus instance scrapes.
|
||||
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.
|
||||
|
@ -162,7 +178,7 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if err := unmarshal((*plain)(c)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return checkOverflow(c.XXX, "global config")
|
||||
}
|
||||
|
||||
// ScrapeConfig configures a scraping unit for Prometheus.
|
||||
|
@ -178,7 +194,7 @@ type ScrapeConfig struct {
|
|||
// The URL scheme with which to fetch metrics from targets.
|
||||
Scheme string `yaml:"scheme,omitempty"`
|
||||
// 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.
|
||||
TargetGroups []*TargetGroup `yaml:"target_groups,omitempty"`
|
||||
|
@ -190,6 +206,9 @@ type ScrapeConfig struct {
|
|||
ConsulSDConfigs []*ConsulSDConfig `yaml:"consul_sd_configs,omitempty"`
|
||||
// List of relabel configurations.
|
||||
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.
|
||||
|
@ -203,13 +222,26 @@ func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if !patJobName.MatchString(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.
|
||||
type BasicAuth struct {
|
||||
Username string `yaml:"username"`
|
||||
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.
|
||||
|
@ -231,8 +263,9 @@ func (tg TargetGroup) String() string {
|
|||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
g := struct {
|
||||
Targets []string `yaml:"targets"`
|
||||
Labels clientmodel.LabelSet `yaml:"labels"`
|
||||
Targets []string `yaml:"targets"`
|
||||
Labels clientmodel.LabelSet `yaml:"labels"`
|
||||
XXX map[string]interface{} `yaml:",inline"`
|
||||
}{}
|
||||
if err := unmarshal(&g); err != nil {
|
||||
return err
|
||||
|
@ -247,7 +280,7 @@ func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
})
|
||||
}
|
||||
tg.Labels = g.Labels
|
||||
return nil
|
||||
return checkOverflow(g.XXX, "target_group")
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
|
@ -291,6 +324,9 @@ func (tg *TargetGroup) UnmarshalJSON(b []byte) error {
|
|||
type DNSSDConfig struct {
|
||||
Names []string `yaml:"names"`
|
||||
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.
|
||||
|
@ -304,13 +340,16 @@ func (c *DNSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if len(c.Names) == 0 {
|
||||
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.
|
||||
type FileSDConfig struct {
|
||||
Names []string `yaml:"names"`
|
||||
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.
|
||||
|
@ -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 nil
|
||||
return checkOverflow(c.XXX, "file_sd_config")
|
||||
}
|
||||
|
||||
// ConsulSDConfig is the configuration for Consul service discovery.
|
||||
|
@ -343,6 +382,9 @@ type ConsulSDConfig struct {
|
|||
Password string `yaml:"password"`
|
||||
// The list of services for which targets are discovered.
|
||||
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.
|
||||
|
@ -359,7 +401,7 @@ func (c *ConsulSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error
|
|||
if len(c.Services) == 0 {
|
||||
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.
|
||||
|
@ -403,6 +445,9 @@ type RelabelConfig struct {
|
|||
Replacement string `yaml:"replacement,omitempty"`
|
||||
// Action is the action to be performed for the relabeling.
|
||||
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.
|
||||
|
@ -415,7 +460,7 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
if c.Regex == nil {
|
||||
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.
|
||||
|
|
|
@ -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: "",
|
||||
}
|
||||
|
@ -128,20 +146,20 @@ func TestLoadConfig(t *testing.T) {
|
|||
|
||||
c, err := LoadFromFile("testdata/conf.good.yml")
|
||||
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)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
bexp, err := yaml.Marshal(expectedConf)
|
||||
if err != nil {
|
||||
t.Errorf("%s", err)
|
||||
t.Fatalf("%s", err)
|
||||
}
|
||||
expectedConf.original = c.original
|
||||
|
||||
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",
|
||||
errMsg: "invalid rule file path",
|
||||
}, {
|
||||
filename: "unknown_attr.bad.yml",
|
||||
errMsg: "unknown fields in scrape_config: consult_sd_configs",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
108
config/testdata/conf.good.yml
vendored
108
config/testdata/conf.good.yml
vendored
|
@ -5,71 +5,75 @@ global:
|
|||
# scrape_timeout is set to the global default (10s).
|
||||
|
||||
labels:
|
||||
monitor: codelab
|
||||
foo: bar
|
||||
monitor: codelab
|
||||
foo: bar
|
||||
|
||||
rule_files:
|
||||
- "first.rules"
|
||||
- "second.rules"
|
||||
- "my/*.rules"
|
||||
- "first.rules"
|
||||
- "second.rules"
|
||||
- "my/*.rules"
|
||||
|
||||
scrape_configs:
|
||||
- job_name: prometheus
|
||||
- job_name: prometheus
|
||||
|
||||
# scrape_interval is defined by the configured global (15s).
|
||||
# scrape_timeout is defined by the global default (10s).
|
||||
# scrape_interval is defined by the configured global (15s).
|
||||
# scrape_timeout is defined by the global default (10s).
|
||||
|
||||
# metrics_path defaults to '/metrics'
|
||||
# scheme defaults to 'http'.
|
||||
# metrics_path defaults to '/metrics'
|
||||
# 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:
|
||||
foo: baz
|
||||
my: label
|
||||
your: label
|
||||
|
||||
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:
|
||||
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'
|
||||
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:
|
||||
username: admin
|
||||
password: password
|
||||
basic_auth:
|
||||
username: admin
|
||||
password: password
|
||||
|
||||
scrape_interval: 50s
|
||||
scrape_timeout: 5s
|
||||
scrape_interval: 50s
|
||||
scrape_timeout: 5s
|
||||
|
||||
metrics_path: /my_path
|
||||
scheme: https
|
||||
metrics_path: /my_path
|
||||
scheme: https
|
||||
|
||||
dns_sd_configs:
|
||||
- refresh_interval: 15s
|
||||
names:
|
||||
- first.dns.address.domain.com
|
||||
- second.dns.address.domain.com
|
||||
- names:
|
||||
- first.dns.address.domain.com
|
||||
# refresh_interval defaults to 30s.
|
||||
dns_sd_configs:
|
||||
- refresh_interval: 15s
|
||||
names:
|
||||
- first.dns.address.domain.com
|
||||
- second.dns.address.domain.com
|
||||
- names:
|
||||
- first.dns.address.domain.com
|
||||
# refresh_interval defaults to 30s.
|
||||
|
||||
relabel_configs:
|
||||
- source_labels: [job]
|
||||
regex: (.*)some-[regex]$
|
||||
action: drop
|
||||
relabel_configs:
|
||||
- source_labels: [job]
|
||||
regex: (.*)some-[regex]$
|
||||
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
20
config/testdata/unknown_attr.bad.yml
vendored
Normal 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'
|
Loading…
Reference in a new issue