mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
config: raise error on unknown config parameters.
The YAML parser ignores additional parameters on unmarshaling. This causes frequent confusion with bad configs that pass parsing. These changes raise errors on additional parameters.
This commit is contained in:
parent
3a24a7779d
commit
116e6df096
|
@ -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.
|
||||||
|
|
|
@ -146,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,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",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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