// Copyright 2015 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package config import ( "encoding/json" "fmt" "io/ioutil" "net/url" "path/filepath" "regexp" "strings" "time" "github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/session" "github.com/prometheus/common/model" "gopkg.in/yaml.v2" ) var ( patFileSDName = regexp.MustCompile(`^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$`) patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`) relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`) ) // Load parses the YAML input s into a Config. func Load(s string) (*Config, error) { cfg := &Config{} // If the entire config body is empty the UnmarshalYAML method is // never called. We thus have to set the DefaultConfig at the entry // point as well. *cfg = DefaultConfig err := yaml.Unmarshal([]byte(s), cfg) if err != nil { return nil, err } cfg.original = s return cfg, nil } // LoadFile parses the given YAML file into a Config. func LoadFile(filename string) (*Config, error) { content, err := ioutil.ReadFile(filename) if err != nil { return nil, err } cfg, err := Load(string(content)) if err != nil { return nil, err } resolveFilepaths(filepath.Dir(filename), cfg) return cfg, nil } // The defaults applied before parsing the respective config sections. var ( // DefaultConfig is the default top-level configuration. DefaultConfig = Config{ GlobalConfig: DefaultGlobalConfig, } // DefaultGlobalConfig is the default global configuration. DefaultGlobalConfig = GlobalConfig{ ScrapeInterval: model.Duration(1 * time.Minute), ScrapeTimeout: model.Duration(10 * time.Second), EvaluationInterval: model.Duration(1 * time.Minute), } // DefaultScrapeConfig is the default scrape configuration. DefaultScrapeConfig = ScrapeConfig{ // ScrapeTimeout and ScrapeInterval default to the // configured globals. MetricsPath: "/metrics", Scheme: "http", HonorLabels: false, } // DefaultAlertmanagerConfig is the default alertmanager configuration. DefaultAlertmanagerConfig = AlertmanagerConfig{ Scheme: "http", Timeout: 10 * time.Second, } // DefaultRelabelConfig is the default Relabel configuration. DefaultRelabelConfig = RelabelConfig{ Action: RelabelReplace, Separator: ";", Regex: MustNewRegexp("(.*)"), Replacement: "$1", } // DefaultDNSSDConfig is the default DNS SD configuration. DefaultDNSSDConfig = DNSSDConfig{ RefreshInterval: model.Duration(30 * time.Second), Type: "SRV", } // DefaultFileSDConfig is the default file SD configuration. DefaultFileSDConfig = FileSDConfig{ RefreshInterval: model.Duration(5 * time.Minute), } // DefaultConsulSDConfig is the default Consul SD configuration. DefaultConsulSDConfig = ConsulSDConfig{ TagSeparator: ",", Scheme: "http", } // DefaultServersetSDConfig is the default Serverset SD configuration. DefaultServersetSDConfig = ServersetSDConfig{ Timeout: model.Duration(10 * time.Second), } // DefaultNerveSDConfig is the default Nerve SD configuration. DefaultNerveSDConfig = NerveSDConfig{ Timeout: model.Duration(10 * time.Second), } // DefaultMarathonSDConfig is the default Marathon SD configuration. DefaultMarathonSDConfig = MarathonSDConfig{ Timeout: model.Duration(30 * time.Second), RefreshInterval: model.Duration(30 * time.Second), } // DefaultKubernetesSDConfig is the default Kubernetes SD configuration DefaultKubernetesSDConfig = KubernetesSDConfig{} // DefaultGCESDConfig is the default EC2 SD configuration. DefaultGCESDConfig = GCESDConfig{ Port: 80, TagSeparator: ",", RefreshInterval: model.Duration(60 * time.Second), } // DefaultEC2SDConfig is the default EC2 SD configuration. DefaultEC2SDConfig = EC2SDConfig{ Port: 80, RefreshInterval: model.Duration(60 * time.Second), } // DefaultOpenstackSDConfig is the default OpenStack SD configuration. DefaultOpenstackSDConfig = OpenstackSDConfig{ Port: 80, RefreshInterval: model.Duration(60 * time.Second), } // DefaultAzureSDConfig is the default Azure SD configuration. DefaultAzureSDConfig = AzureSDConfig{ Port: 80, RefreshInterval: model.Duration(5 * time.Minute), } // DefaultTritonSDConfig is the default Triton SD configuration. DefaultTritonSDConfig = TritonSDConfig{ Port: 9163, RefreshInterval: model.Duration(60 * time.Second), Version: 1, } // DefaultRemoteWriteConfig is the default remote write configuration. DefaultRemoteWriteConfig = RemoteWriteConfig{ RemoteTimeout: model.Duration(30 * time.Second), QueueConfig: DefaultQueueConfig, } // DefaultQueueConfig is the default remote queue configuration. DefaultQueueConfig = QueueConfig{ // With a maximum of 1000 shards, assuming an average of 100ms remote write // time and 100 samples per batch, we will be able to push 1M samples/s. MaxShards: 1000, MaxSamplesPerSend: 100, // By default, buffer 1000 batches, which at 100ms per batch is 1:40mins. At // 1000 shards, this will buffer 100M samples total. Capacity: 100 * 1000, BatchSendDeadline: 5 * time.Second, // Max number of times to retry a batch on recoverable errors. MaxRetries: 10, MinBackoff: 30 * time.Millisecond, MaxBackoff: 100 * time.Millisecond, } // DefaultRemoteReadConfig is the default remote read configuration. DefaultRemoteReadConfig = RemoteReadConfig{ RemoteTimeout: model.Duration(1 * time.Minute), } ) // URL is a custom URL type that allows validation at configuration load time. type URL struct { *url.URL } // UnmarshalYAML implements the yaml.Unmarshaler interface for URLs. func (u *URL) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } urlp, err := url.Parse(s) if err != nil { return err } u.URL = urlp return nil } // MarshalYAML implements the yaml.Marshaler interface for URLs. func (u URL) MarshalYAML() (interface{}, error) { if u.URL != nil { return u.String(), nil } return nil, nil } // Config is the top-level configuration for Prometheus's config files. type Config struct { GlobalConfig GlobalConfig `yaml:"global"` AlertingConfig AlertingConfig `yaml:"alerting,omitempty"` RuleFiles []string `yaml:"rule_files,omitempty"` ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"` RemoteWriteConfigs []*RemoteWriteConfig `yaml:"remote_write,omitempty"` RemoteReadConfigs []*RemoteReadConfig `yaml:"remote_read,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 } // Secret special type for storing secrets. type Secret string // UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets. func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Secret return unmarshal((*plain)(s)) } // MarshalYAML implements the yaml.Marshaler interface for Secrets. func (s Secret) MarshalYAML() (interface{}, error) { if s != "" { return "", nil } return nil, nil } // resolveFilepaths joins all relative paths in a configuration // with a given base directory. func resolveFilepaths(baseDir string, cfg *Config) { join := func(fp string) string { if len(fp) > 0 && !filepath.IsAbs(fp) { fp = filepath.Join(baseDir, fp) } return fp } for i, rf := range cfg.RuleFiles { cfg.RuleFiles[i] = join(rf) } clientPaths := func(scfg *HTTPClientConfig) { scfg.BearerTokenFile = join(scfg.BearerTokenFile) scfg.TLSConfig.CAFile = join(scfg.TLSConfig.CAFile) scfg.TLSConfig.CertFile = join(scfg.TLSConfig.CertFile) scfg.TLSConfig.KeyFile = join(scfg.TLSConfig.KeyFile) } sdPaths := func(cfg *ServiceDiscoveryConfig) { for _, kcfg := range cfg.KubernetesSDConfigs { kcfg.BearerTokenFile = join(kcfg.BearerTokenFile) kcfg.TLSConfig.CAFile = join(kcfg.TLSConfig.CAFile) kcfg.TLSConfig.CertFile = join(kcfg.TLSConfig.CertFile) kcfg.TLSConfig.KeyFile = join(kcfg.TLSConfig.KeyFile) } for _, mcfg := range cfg.MarathonSDConfigs { mcfg.BearerTokenFile = join(mcfg.BearerTokenFile) mcfg.TLSConfig.CAFile = join(mcfg.TLSConfig.CAFile) mcfg.TLSConfig.CertFile = join(mcfg.TLSConfig.CertFile) mcfg.TLSConfig.KeyFile = join(mcfg.TLSConfig.KeyFile) } for _, consulcfg := range cfg.ConsulSDConfigs { consulcfg.TLSConfig.CAFile = join(consulcfg.TLSConfig.CAFile) consulcfg.TLSConfig.CertFile = join(consulcfg.TLSConfig.CertFile) consulcfg.TLSConfig.KeyFile = join(consulcfg.TLSConfig.KeyFile) } } for _, cfg := range cfg.ScrapeConfigs { clientPaths(&cfg.HTTPClientConfig) sdPaths(&cfg.ServiceDiscoveryConfig) } for _, cfg := range cfg.AlertingConfig.AlertmanagerConfigs { clientPaths(&cfg.HTTPClientConfig) sdPaths(&cfg.ServiceDiscoveryConfig) } } 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 { b, err := yaml.Marshal(c) if err != nil { return fmt.Sprintf("", err) } return string(b) } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultConfig // We want to set c to the defaults and then overwrite it with the input. // To make unmarshal fill the plain data struct rather than calling UnmarshalYAML // again, we have to hide it using a type indirection. type plain Config if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "config"); err != nil { return err } // If a global block was open but empty the default global config is overwritten. // We have to restore it here. if c.GlobalConfig.isZero() { c.GlobalConfig = DefaultGlobalConfig } for _, rf := range c.RuleFiles { if !patRulePath.MatchString(rf) { return fmt.Errorf("invalid rule file path %q", rf) } } // Do global overrides and validate unique names. jobNames := map[string]struct{}{} for _, scfg := range c.ScrapeConfigs { // First set the correct scrape interval, then check that the timeout // (inferred or explicit) is not greater than that. if scfg.ScrapeInterval == 0 { scfg.ScrapeInterval = c.GlobalConfig.ScrapeInterval } if scfg.ScrapeTimeout > scfg.ScrapeInterval { return fmt.Errorf("scrape timeout greater than scrape interval for scrape config with job name %q", scfg.JobName) } if scfg.ScrapeTimeout == 0 { if c.GlobalConfig.ScrapeTimeout > scfg.ScrapeInterval { scfg.ScrapeTimeout = scfg.ScrapeInterval } else { scfg.ScrapeTimeout = c.GlobalConfig.ScrapeTimeout } } if _, ok := jobNames[scfg.JobName]; ok { return fmt.Errorf("found multiple scrape configs with job name %q", scfg.JobName) } jobNames[scfg.JobName] = struct{}{} } return nil } // GlobalConfig configures values that are used across other configuration // objects. type GlobalConfig struct { // How frequently to scrape targets by default. ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"` // The default timeout when scraping targets. ScrapeTimeout model.Duration `yaml:"scrape_timeout,omitempty"` // How frequently to evaluate rules by default. EvaluationInterval model.Duration `yaml:"evaluation_interval,omitempty"` // The labels to add to any timeseries that this Prometheus instance scrapes. ExternalLabels model.LabelSet `yaml:"external_labels,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { // Create a clean global config as the previous one was already populated // by the default due to the YAML parser behavior for empty blocks. gc := &GlobalConfig{} type plain GlobalConfig if err := unmarshal((*plain)(gc)); err != nil { return err } if err := checkOverflow(gc.XXX, "global config"); err != nil { return err } // First set the correct scrape interval, then check that the timeout // (inferred or explicit) is not greater than that. if gc.ScrapeInterval == 0 { gc.ScrapeInterval = DefaultGlobalConfig.ScrapeInterval } if gc.ScrapeTimeout > gc.ScrapeInterval { return fmt.Errorf("global scrape timeout greater than scrape interval") } if gc.ScrapeTimeout == 0 { if DefaultGlobalConfig.ScrapeTimeout > gc.ScrapeInterval { gc.ScrapeTimeout = gc.ScrapeInterval } else { gc.ScrapeTimeout = DefaultGlobalConfig.ScrapeTimeout } } if gc.EvaluationInterval == 0 { gc.EvaluationInterval = DefaultGlobalConfig.EvaluationInterval } *c = *gc return nil } // isZero returns true iff the global config is the zero value. func (c *GlobalConfig) isZero() bool { return c.ExternalLabels == nil && c.ScrapeInterval == 0 && c.ScrapeTimeout == 0 && c.EvaluationInterval == 0 } // TLSConfig configures the options for TLS connections. type TLSConfig struct { // The CA cert to use for the targets. CAFile string `yaml:"ca_file,omitempty"` // The client cert file for the targets. CertFile string `yaml:"cert_file,omitempty"` // The client key file for the targets. KeyFile string `yaml:"key_file,omitempty"` // Used to verify the hostname for the targets. ServerName string `yaml:"server_name,omitempty"` // Disable target certificate validation. InsecureSkipVerify bool `yaml:"insecure_skip_verify"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain TLSConfig if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "TLS config"); err != nil { return err } return nil } // ServiceDiscoveryConfig configures lists of different service discovery mechanisms. type ServiceDiscoveryConfig struct { // List of labeled target groups for this job. StaticConfigs []*TargetGroup `yaml:"static_configs,omitempty"` // List of DNS service discovery configurations. DNSSDConfigs []*DNSSDConfig `yaml:"dns_sd_configs,omitempty"` // List of file service discovery configurations. FileSDConfigs []*FileSDConfig `yaml:"file_sd_configs,omitempty"` // List of Consul service discovery configurations. ConsulSDConfigs []*ConsulSDConfig `yaml:"consul_sd_configs,omitempty"` // List of Serverset service discovery configurations. ServersetSDConfigs []*ServersetSDConfig `yaml:"serverset_sd_configs,omitempty"` // NerveSDConfigs is a list of Nerve service discovery configurations. NerveSDConfigs []*NerveSDConfig `yaml:"nerve_sd_configs,omitempty"` // MarathonSDConfigs is a list of Marathon service discovery configurations. MarathonSDConfigs []*MarathonSDConfig `yaml:"marathon_sd_configs,omitempty"` // List of Kubernetes service discovery configurations. KubernetesSDConfigs []*KubernetesSDConfig `yaml:"kubernetes_sd_configs,omitempty"` // List of GCE service discovery configurations. GCESDConfigs []*GCESDConfig `yaml:"gce_sd_configs,omitempty"` // List of EC2 service discovery configurations. EC2SDConfigs []*EC2SDConfig `yaml:"ec2_sd_configs,omitempty"` // List of OpenStack service discovery configurations. OpenstackSDConfigs []*OpenstackSDConfig `yaml:"openstack_sd_configs,omitempty"` // List of Azure service discovery configurations. AzureSDConfigs []*AzureSDConfig `yaml:"azure_sd_configs,omitempty"` // List of Triton service discovery configurations. TritonSDConfigs []*TritonSDConfig `yaml:"triton_sd_configs,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *ServiceDiscoveryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain ServiceDiscoveryConfig if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "service discovery config"); err != nil { return err } return nil } // HTTPClientConfig configures an HTTP client. type HTTPClientConfig struct { // The HTTP basic authentication credentials for the targets. BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"` // The bearer token for the targets. BearerToken Secret `yaml:"bearer_token,omitempty"` // The bearer token file for the targets. BearerTokenFile string `yaml:"bearer_token_file,omitempty"` // HTTP proxy server to use to connect to the targets. ProxyURL URL `yaml:"proxy_url,omitempty"` // TLSConfig to use to connect to the targets. TLSConfig TLSConfig `yaml:"tls_config,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } func (c *HTTPClientConfig) validate() error { if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured") } if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured") } return nil } // ScrapeConfig configures a scraping unit for Prometheus. type ScrapeConfig struct { // The job name to which the job label is set by default. JobName string `yaml:"job_name"` // Indicator whether the scraped metrics should remain unmodified. HonorLabels bool `yaml:"honor_labels,omitempty"` // A set of query parameters with which the target is scraped. Params url.Values `yaml:"params,omitempty"` // How frequently to scrape the targets of this scrape config. ScrapeInterval model.Duration `yaml:"scrape_interval,omitempty"` // The timeout for scraping targets of this config. ScrapeTimeout model.Duration `yaml:"scrape_timeout,omitempty"` // The HTTP resource path on which to fetch metrics from targets. MetricsPath string `yaml:"metrics_path,omitempty"` // The URL scheme with which to fetch metrics from targets. Scheme string `yaml:"scheme,omitempty"` // More than this many samples post metric-relabelling will cause the scrape to fail. SampleLimit uint `yaml:"sample_limit,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. ServiceDiscoveryConfig ServiceDiscoveryConfig `yaml:",inline"` HTTPClientConfig HTTPClientConfig `yaml:",inline"` // List of target relabel configurations. RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"` // List of metric relabel configurations. MetricRelabelConfigs []*RelabelConfig `yaml:"metric_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. func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultScrapeConfig type plain ScrapeConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err = checkOverflow(c.XXX, "scrape_config"); err != nil { return err } if len(c.JobName) == 0 { return fmt.Errorf("job_name is empty") } // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. // We cannot make it a pointer as the parser panics for inlined pointer structs. // Thus we just do its validation here. if err = c.HTTPClientConfig.validate(); err != nil { return err } // Check for users putting URLs in target groups. if len(c.RelabelConfigs) == 0 { for _, tg := range c.ServiceDiscoveryConfig.StaticConfigs { for _, t := range tg.Targets { if err = CheckTargetAddress(t[model.AddressLabel]); err != nil { return err } } } } return nil } // AlertingConfig configures alerting and alertmanager related configs. type AlertingConfig struct { AlertRelabelConfigs []*RelabelConfig `yaml:"alert_relabel_configs,omitempty"` AlertmanagerConfigs []*AlertmanagerConfig `yaml:"alertmanagers,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *AlertingConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { // Create a clean global config as the previous one was already populated // by the default due to the YAML parser behavior for empty blocks. *c = AlertingConfig{} type plain AlertingConfig if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "alerting config"); err != nil { return err } return nil } // AlertmanagerConfig configures how Alertmanagers can be discovered and communicated with. type AlertmanagerConfig struct { // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. ServiceDiscoveryConfig ServiceDiscoveryConfig `yaml:",inline"` HTTPClientConfig HTTPClientConfig `yaml:",inline"` // The URL scheme to use when talking to Alertmanagers. Scheme string `yaml:"scheme,omitempty"` // Path prefix to add in front of the push endpoint path. PathPrefix string `yaml:"path_prefix,omitempty"` // The timeout used when sending alerts. Timeout time.Duration `yaml:"timeout,omitempty"` // List of Alertmanager 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. func (c *AlertmanagerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultAlertmanagerConfig type plain AlertmanagerConfig if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "alertmanager config"); err != nil { return err } // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. // We cannot make it a pointer as the parser panics for inlined pointer structs. // Thus we just do its validation here. if err := c.HTTPClientConfig.validate(); err != nil { return err } // Check for users putting URLs in target groups. if len(c.RelabelConfigs) == 0 { for _, tg := range c.ServiceDiscoveryConfig.StaticConfigs { for _, t := range tg.Targets { if err := CheckTargetAddress(t[model.AddressLabel]); err != nil { return err } } } } return nil } // CheckTargetAddress checks if target address is valid. func CheckTargetAddress(address model.LabelValue) error { // For now check for a URL, we may want to expand this later. if strings.Contains(string(address), "/") { return fmt.Errorf("%q is not a valid hostname", address) } return nil } // BasicAuth contains basic HTTP authentication credentials. type BasicAuth struct { Username string `yaml:"username"` Password Secret `yaml:"password"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // ClientCert contains client cert credentials. type ClientCert struct { Cert string `yaml:"cert"` Key Secret `yaml:"key"` // 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. type TargetGroup struct { // Targets is a list of targets identified by a label set. Each target is // uniquely identifiable in the group by its address label. Targets []model.LabelSet // Labels is a set of labels that is common across all targets in the group. Labels model.LabelSet // Source is an identifier that describes a group of targets. Source string } func (tg TargetGroup) String() string { return tg.Source } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error { g := struct { Targets []string `yaml:"targets"` Labels model.LabelSet `yaml:"labels"` XXX map[string]interface{} `yaml:",inline"` }{} if err := unmarshal(&g); err != nil { return err } tg.Targets = make([]model.LabelSet, 0, len(g.Targets)) for _, t := range g.Targets { tg.Targets = append(tg.Targets, model.LabelSet{ model.AddressLabel: model.LabelValue(t), }) } tg.Labels = g.Labels return checkOverflow(g.XXX, "static_config") } // MarshalYAML implements the yaml.Marshaler interface. func (tg TargetGroup) MarshalYAML() (interface{}, error) { g := &struct { Targets []string `yaml:"targets"` Labels model.LabelSet `yaml:"labels,omitempty"` }{ Targets: make([]string, 0, len(tg.Targets)), Labels: tg.Labels, } for _, t := range tg.Targets { g.Targets = append(g.Targets, string(t[model.AddressLabel])) } return g, nil } // UnmarshalJSON implements the json.Unmarshaler interface. func (tg *TargetGroup) UnmarshalJSON(b []byte) error { g := struct { Targets []string `json:"targets"` Labels model.LabelSet `json:"labels"` }{} if err := json.Unmarshal(b, &g); err != nil { return err } tg.Targets = make([]model.LabelSet, 0, len(g.Targets)) for _, t := range g.Targets { tg.Targets = append(tg.Targets, model.LabelSet{ model.AddressLabel: model.LabelValue(t), }) } tg.Labels = g.Labels return nil } // DNSSDConfig is the configuration for DNS based service discovery. type DNSSDConfig struct { Names []string `yaml:"names"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Type string `yaml:"type"` Port int `yaml:"port"` // Ignored for SRV records // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *DNSSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultDNSSDConfig type plain DNSSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "dns_sd_config"); err != nil { return err } if len(c.Names) == 0 { return fmt.Errorf("DNS-SD config must contain at least one SRV record name") } switch strings.ToUpper(c.Type) { case "SRV": case "A", "AAAA": if c.Port == 0 { return fmt.Errorf("a port is required in DNS-SD configs for all record types except SRV") } default: return fmt.Errorf("invalid DNS-SD records type %s", c.Type) } return nil } // FileSDConfig is the configuration for file based discovery. type FileSDConfig struct { Files []string `yaml:"files"` RefreshInterval model.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. func (c *FileSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultFileSDConfig type plain FileSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "file_sd_config"); err != nil { return err } if len(c.Files) == 0 { return fmt.Errorf("file service discovery config must contain at least one path name") } for _, name := range c.Files { if !patFileSDName.MatchString(name) { return fmt.Errorf("path name %q is not valid for file discovery", name) } } return nil } // ConsulSDConfig is the configuration for Consul service discovery. type ConsulSDConfig struct { Server string `yaml:"server"` Token Secret `yaml:"token,omitempty"` Datacenter string `yaml:"datacenter,omitempty"` TagSeparator string `yaml:"tag_separator,omitempty"` Scheme string `yaml:"scheme,omitempty"` Username string `yaml:"username,omitempty"` Password Secret `yaml:"password,omitempty"` // The list of services for which targets are discovered. // Defaults to all services if empty. Services []string `yaml:"services"` TLSConfig TLSConfig `yaml:"tls_config,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *ConsulSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultConsulSDConfig type plain ConsulSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "consul_sd_config"); err != nil { return err } if strings.TrimSpace(c.Server) == "" { return fmt.Errorf("Consul SD configuration requires a server address") } return nil } // ServersetSDConfig is the configuration for Twitter serversets in Zookeeper based discovery. type ServersetSDConfig struct { Servers []string `yaml:"servers"` Paths []string `yaml:"paths"` Timeout model.Duration `yaml:"timeout,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *ServersetSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultServersetSDConfig type plain ServersetSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "serverset_sd_config"); err != nil { return err } if len(c.Servers) == 0 { return fmt.Errorf("serverset SD config must contain at least one Zookeeper server") } if len(c.Paths) == 0 { return fmt.Errorf("serverset SD config must contain at least one path") } for _, path := range c.Paths { if !strings.HasPrefix(path, "/") { return fmt.Errorf("serverset SD config paths must begin with '/': %s", path) } } return nil } // NerveSDConfig is the configuration for AirBnB's Nerve in Zookeeper based discovery. type NerveSDConfig struct { Servers []string `yaml:"servers"` Paths []string `yaml:"paths"` Timeout model.Duration `yaml:"timeout,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *NerveSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultNerveSDConfig type plain NerveSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "nerve_sd_config"); err != nil { return err } if len(c.Servers) == 0 { return fmt.Errorf("nerve SD config must contain at least one Zookeeper server") } if len(c.Paths) == 0 { return fmt.Errorf("nerve SD config must contain at least one path") } for _, path := range c.Paths { if !strings.HasPrefix(path, "/") { return fmt.Errorf("nerve SD config paths must begin with '/': %s", path) } } return nil } // MarathonSDConfig is the configuration for services running on Marathon. type MarathonSDConfig struct { Servers []string `yaml:"servers,omitempty"` Timeout model.Duration `yaml:"timeout,omitempty"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` TLSConfig TLSConfig `yaml:"tls_config,omitempty"` BearerToken Secret `yaml:"bearer_token,omitempty"` BearerTokenFile string `yaml:"bearer_token_file,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *MarathonSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultMarathonSDConfig type plain MarathonSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "marathon_sd_config"); err != nil { return err } if len(c.Servers) == 0 { return fmt.Errorf("Marathon SD config must contain at least one Marathon server") } if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured") } return nil } // KubernetesRole is role of the service in Kubernetes. type KubernetesRole string // The valid options for KubernetesRole. const ( KubernetesRoleNode = "node" KubernetesRolePod = "pod" KubernetesRoleService = "service" KubernetesRoleEndpoint = "endpoints" ) // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *KubernetesRole) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := unmarshal((*string)(c)); err != nil { return err } switch *c { case KubernetesRoleNode, KubernetesRolePod, KubernetesRoleService, KubernetesRoleEndpoint: return nil default: return fmt.Errorf("Unknown Kubernetes SD role %q", *c) } } // KubernetesSDConfig is the configuration for Kubernetes service discovery. type KubernetesSDConfig struct { APIServer URL `yaml:"api_server"` Role KubernetesRole `yaml:"role"` BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"` BearerToken Secret `yaml:"bearer_token,omitempty"` BearerTokenFile string `yaml:"bearer_token_file,omitempty"` TLSConfig TLSConfig `yaml:"tls_config,omitempty"` NamespaceDiscovery KubernetesNamespaceDiscovery `yaml:"namespaces"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *KubernetesSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = KubernetesSDConfig{} type plain KubernetesSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "kubernetes_sd_config"); err != nil { return err } if c.Role == "" { return fmt.Errorf("role missing (one of: pod, service, endpoints, node)") } if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured") } if c.BasicAuth != nil && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { return fmt.Errorf("at most one of basic_auth, bearer_token & bearer_token_file must be configured") } if c.APIServer.URL == nil && (c.BasicAuth != nil || c.BearerToken != "" || c.BearerTokenFile != "" || c.TLSConfig.CAFile != "" || c.TLSConfig.CertFile != "" || c.TLSConfig.KeyFile != "") { return fmt.Errorf("to use custom authentication please provide the 'api_server' URL explicitly") } return nil } // KubernetesNamespaceDiscovery is the configuration for discovering // Kubernetes namespaces. type KubernetesNamespaceDiscovery struct { Names []string `yaml:"names"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *KubernetesNamespaceDiscovery) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = KubernetesNamespaceDiscovery{} type plain KubernetesNamespaceDiscovery err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "namespaces"); err != nil { return err } return nil } // GCESDConfig is the configuration for GCE based service discovery. type GCESDConfig struct { // Project: The Google Cloud Project ID Project string `yaml:"project"` // Zone: The zone of the scrape targets. // If you need to configure multiple zones use multiple gce_sd_configs Zone string `yaml:"zone"` // Filter: Can be used optionally to filter the instance list by other criteria. // Syntax of this filter string is described here in the filter query parameter section: // https://cloud.google.com/compute/docs/reference/latest/instances/list Filter string `yaml:"filter,omitempty"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Port int `yaml:"port"` TagSeparator string `yaml:"tag_separator,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *GCESDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultGCESDConfig type plain GCESDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "gce_sd_config"); err != nil { return err } if c.Project == "" { return fmt.Errorf("GCE SD configuration requires a project") } if c.Zone == "" { return fmt.Errorf("GCE SD configuration requires a zone") } return nil } // EC2SDConfig is the configuration for EC2 based service discovery. type EC2SDConfig struct { Region string `yaml:"region"` AccessKey string `yaml:"access_key,omitempty"` SecretKey Secret `yaml:"secret_key,omitempty"` Profile string `yaml:"profile,omitempty"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Port int `yaml:"port"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultEC2SDConfig type plain EC2SDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if err := checkOverflow(c.XXX, "ec2_sd_config"); err != nil { return err } if c.Region == "" { sess, err := session.NewSession() if err != nil { return err } metadata := ec2metadata.New(sess) region, err := metadata.Region() if err != nil { return fmt.Errorf("EC2 SD configuration requires a region") } c.Region = region } return nil } // OpenstackSDConfig is the configuration for OpenStack based service discovery. type OpenstackSDConfig struct { IdentityEndpoint string `yaml:"identity_endpoint"` Username string `yaml:"username"` UserID string `yaml:"userid"` Password Secret `yaml:"password"` ProjectName string `yaml:"project_name"` ProjectID string `yaml:"project_id"` DomainName string `yaml:"domain_name"` DomainID string `yaml:"domain_id"` Region string `yaml:"region"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` Port int `yaml:"port"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *OpenstackSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultOpenstackSDConfig type plain OpenstackSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } return checkOverflow(c.XXX, "openstack_sd_config") } // AzureSDConfig is the configuration for Azure based service discovery. type AzureSDConfig struct { Port int `yaml:"port"` SubscriptionID string `yaml:"subscription_id"` TenantID string `yaml:"tenant_id,omitempty"` ClientID string `yaml:"client_id,omitempty"` ClientSecret Secret `yaml:"client_secret,omitempty"` RefreshInterval model.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. func (c *AzureSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultAzureSDConfig type plain AzureSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } return checkOverflow(c.XXX, "azure_sd_config") } // TritonSDConfig is the configuration for Triton based service discovery. type TritonSDConfig struct { Account string `yaml:"account"` DNSSuffix string `yaml:"dns_suffix"` Endpoint string `yaml:"endpoint"` Port int `yaml:"port"` RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"` TLSConfig TLSConfig `yaml:"tls_config,omitempty"` Version int `yaml:"version"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *TritonSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultTritonSDConfig type plain TritonSDConfig err := unmarshal((*plain)(c)) if err != nil { return err } if c.Account == "" { return fmt.Errorf("Triton SD configuration requires an account") } if c.DNSSuffix == "" { return fmt.Errorf("Triton SD configuration requires a dns_suffix") } if c.Endpoint == "" { return fmt.Errorf("Triton SD configuration requires an endpoint") } if c.RefreshInterval <= 0 { return fmt.Errorf("Triton SD configuration requires RefreshInterval to be a positive integer") } return checkOverflow(c.XXX, "triton_sd_config") } // RelabelAction is the action to be performed on relabeling. type RelabelAction string const ( // RelabelReplace performs a regex replacement. RelabelReplace RelabelAction = "replace" // RelabelKeep drops targets for which the input does not match the regex. RelabelKeep RelabelAction = "keep" // RelabelDrop drops targets for which the input does match the regex. RelabelDrop RelabelAction = "drop" // RelabelHashMod sets a label to the modulus of a hash of labels. RelabelHashMod RelabelAction = "hashmod" // RelabelLabelMap copies labels to other labelnames based on a regex. RelabelLabelMap RelabelAction = "labelmap" // RelabelLabelDrop drops any label matching the regex. RelabelLabelDrop RelabelAction = "labeldrop" // RelabelLabelKeep drops any label not matching the regex. RelabelLabelKeep RelabelAction = "labelkeep" ) // UnmarshalYAML implements the yaml.Unmarshaler interface. func (a *RelabelAction) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } switch act := RelabelAction(strings.ToLower(s)); act { case RelabelReplace, RelabelKeep, RelabelDrop, RelabelHashMod, RelabelLabelMap, RelabelLabelDrop, RelabelLabelKeep: *a = act return nil } return fmt.Errorf("unknown relabel action %q", s) } // RelabelConfig is the configuration for relabeling of target label sets. type RelabelConfig struct { // A list of labels from which values are taken and concatenated // with the configured separator in order. SourceLabels model.LabelNames `yaml:"source_labels,flow,omitempty"` // Separator is the string between concatenated values from the source labels. Separator string `yaml:"separator,omitempty"` // Regex against which the concatenation is matched. Regex Regexp `yaml:"regex,omitempty"` // Modulus to take of the hash of concatenated values from the source labels. Modulus uint64 `yaml:"modulus,omitempty"` // TargetLabel is the label to which the resulting string is written in a replacement. // Regexp interpolation is allowed for the replace action. TargetLabel string `yaml:"target_label,omitempty"` // Replacement is the regex replacement pattern to be used. 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. func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultRelabelConfig type plain RelabelConfig if err := unmarshal((*plain)(c)); err != nil { return err } if err := checkOverflow(c.XXX, "relabel_config"); err != nil { return err } if c.Regex.Regexp == nil { c.Regex = MustNewRegexp("") } if c.Modulus == 0 && c.Action == RelabelHashMod { return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus") } if (c.Action == RelabelReplace || c.Action == RelabelHashMod) && c.TargetLabel == "" { return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action) } if c.Action == RelabelReplace && !relabelTarget.MatchString(c.TargetLabel) { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } if c.Action == RelabelHashMod && !model.LabelName(c.TargetLabel).IsValid() { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } if c.Action == RelabelLabelDrop || c.Action == RelabelLabelKeep { if c.SourceLabels != nil || c.TargetLabel != DefaultRelabelConfig.TargetLabel || c.Modulus != DefaultRelabelConfig.Modulus || c.Separator != DefaultRelabelConfig.Separator || c.Replacement != DefaultRelabelConfig.Replacement { return fmt.Errorf("%s action requires only 'regex', and no other fields", c.Action) } } return nil } // Regexp encapsulates a regexp.Regexp and makes it YAML marshallable. type Regexp struct { *regexp.Regexp original string } // NewRegexp creates a new anchored Regexp and returns an error if the // passed-in regular expression does not compile. func NewRegexp(s string) (Regexp, error) { regex, err := regexp.Compile("^(?:" + s + ")$") return Regexp{ Regexp: regex, original: s, }, err } // MustNewRegexp works like NewRegexp, but panics if the regular expression does not compile. func MustNewRegexp(s string) Regexp { re, err := NewRegexp(s) if err != nil { panic(err) } return re } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } r, err := NewRegexp(s) if err != nil { return err } *re = r return nil } // MarshalYAML implements the yaml.Marshaler interface. func (re Regexp) MarshalYAML() (interface{}, error) { if re.original != "" { return re.original, nil } return nil, nil } // RemoteWriteConfig is the configuration for writing to remote storage. type RemoteWriteConfig struct { URL *URL `yaml:"url,omitempty"` RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"` WriteRelabelConfigs []*RelabelConfig `yaml:"write_relabel_configs,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. HTTPClientConfig HTTPClientConfig `yaml:",inline"` QueueConfig QueueConfig `yaml:"queue_config,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *RemoteWriteConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultRemoteWriteConfig type plain RemoteWriteConfig if err := unmarshal((*plain)(c)); err != nil { return err } // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. // We cannot make it a pointer as the parser panics for inlined pointer structs. // Thus we just do its validation here. if err := c.HTTPClientConfig.validate(); err != nil { return err } if err := checkOverflow(c.XXX, "remote_write"); err != nil { return err } return nil } // QueueConfig is the configuration for the queue used to write to remote // storage. type QueueConfig struct { // Number of samples to buffer per shard before we start dropping them. Capacity int `yaml:"capacity,omitempty"` // Max number of shards, i.e. amount of concurrency. MaxShards int `yaml:"max_shards,omitempty"` // Maximum number of samples per send. MaxSamplesPerSend int `yaml:"max_samples_per_send,omitempty"` // Maximum time sample will wait in buffer. BatchSendDeadline time.Duration `yaml:"batch_send_deadline,omitempty"` // Max number of times to retry a batch on recoverable errors. MaxRetries int `yaml:"max_retries,omitempty"` // On recoverable errors, backoff exponentially. MinBackoff time.Duration `yaml:"min_backoff,omitempty"` MaxBackoff time.Duration `yaml:"max_backoff,omitempty"` } // RemoteReadConfig is the configuration for reading from remote storage. type RemoteReadConfig struct { URL *URL `yaml:"url,omitempty"` RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"` // We cannot do proper Go type embedding below as the parser will then parse // values arbitrarily into the overflow maps of further-down types. HTTPClientConfig HTTPClientConfig `yaml:",inline"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *RemoteReadConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { *c = DefaultRemoteReadConfig type plain RemoteReadConfig if err := unmarshal((*plain)(c)); err != nil { return err } // The UnmarshalYAML method of HTTPClientConfig is not being called because it's not a pointer. // We cannot make it a pointer as the parser panics for inlined pointer structs. // Thus we just do its validation here. if err := c.HTTPClientConfig.validate(); err != nil { return err } if err := checkOverflow(c.XXX, "remote_read"); err != nil { return err } return nil }