package config import ( "fmt" "io/ioutil" "regexp" "strings" "time" "gopkg.in/yaml.v2" clientmodel "github.com/prometheus/client_golang/model" "github.com/prometheus/prometheus/utility" ) var jobNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_-]*$") // Load parses the YAML input s into a Config. func Load(s string) (*Config, error) { cfg := &Config{ original: s, } err := yaml.Unmarshal([]byte(s), cfg) if err != nil { return nil, err } return cfg, nil } // LoadFromFile parses the given YAML file into a Config. func LoadFromFile(filename string) (*Config, error) { content, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return Load(string(content)) } // The defaults applied before parsing the respective config sections. var ( // The default top-level configuration. DefaultConfig = DefaultedConfig{ GlobalConfig: &GlobalConfig{DefaultGlobalConfig}, } // The default global configuration. DefaultGlobalConfig = DefaultedGlobalConfig{ ScrapeInterval: Duration(10 * time.Second), ScrapeTimeout: Duration(10 * time.Second), EvaluationInterval: Duration(1 * time.Minute), } // Te default scrape configuration. DefaultScrapeConfig = DefaultedScrapeConfig{ // ScrapeTimeout and ScrapeInterval default to the // configured globals. MetricsPath: "/metrics", Scheme: "http", } // The default Relabel configuration. DefaultRelabelConfig = DefaultedRelabelConfig{ Action: RelabelReplace, Separator: ";", } // The default DNS SD configuration. DefaultDNSConfig = DefaultedDNSConfig{ RefreshInterval: Duration(30 * time.Second), } ) // Config is the top-level configuration for Prometheus's config files. type Config struct { // DefaultedConfig contains the actual fields of Config. DefaultedConfig `yaml:",inline"` // original is the input from which the config was parsed. original string } func (c *Config) String() string { if c.original != "" { return c.original } b, err := yaml.Marshal(c) if err != nil { return fmt.Sprintf("", err) } return string(b) } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { c.DefaultedConfig = DefaultConfig if err := unmarshal(&c.DefaultedConfig); err != nil { return err } // Do global overrides and validate unique names. jobNames := map[string]struct{}{} for _, scfg := range c.ScrapeConfigs { if scfg.ScrapeInterval == 0 { scfg.ScrapeInterval = c.GlobalConfig.ScrapeInterval } if scfg.ScrapeTimeout == 0 { 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 } // DefaultedConfig is a proxy type for Config. type DefaultedConfig struct { GlobalConfig *GlobalConfig `yaml:"global_config"` RuleFiles []string `yaml:"rule_files,omitempty"` ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"` } // GlobalConfig configures values that used across other configuration // objects. type GlobalConfig struct { // DefaultedGlobalConfig contains the actual fields for GlobalConfig. DefaultedGlobalConfig `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { c.DefaultedGlobalConfig = DefaultGlobalConfig if err := unmarshal(&c.DefaultedGlobalConfig); err != nil { return err } return nil } // DefaultedGlobalConfig is a proxy type for GlobalConfig. type DefaultedGlobalConfig struct { // How frequently to scrape targets by default. ScrapeInterval Duration `yaml:"scrape_interval"` // The default timeout when scraping targets. ScrapeTimeout Duration `yaml:"scrape_timeout"` // How frequently to evaluate rules by default. EvaluationInterval Duration `yaml:"evaluation_interval"` // The labels to add to any timeseries that this Prometheus instance scrapes. Labels clientmodel.LabelSet `yaml:"labels,omitempty"` } // ScrapeConfig configures a scraping unit for Prometheus. type ScrapeConfig struct { // DefaultedScrapeConfig contains the actual fields for ScrapeConfig. DefaultedScrapeConfig `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { c.DefaultedScrapeConfig = DefaultScrapeConfig err := unmarshal(&c.DefaultedScrapeConfig) if err != nil { return err } if !jobNameRE.MatchString(c.JobName) { return fmt.Errorf("%q is not a valid job name", c.JobName) } return nil } // DefaultedScrapeConfig is a proxy type for ScrapeConfig. type DefaultedScrapeConfig struct { // The job name to which the job label is set by default. JobName string `yaml:"job_name"` // How frequently to scrape the targets of this scrape config. ScrapeInterval Duration `yaml:"scrape_interval"` // The timeout for scraping targets of this config. ScrapeTimeout Duration `yaml:"scrape_timeout"` // The HTTP resource path on which to fetch metrics from targets. MetricsPath string `yaml:"metrics_path"` // The URL scheme with which to fetch metrics from targets. Scheme string `yaml:"scheme"` // List of labeled target groups for this job. TargetGroups []*TargetGroup `yaml:"target_groups,omitempty"` // List of DNS service discovery configurations. DNSConfigs []*DNSConfig `yaml:"dns_configs,omitempty"` // List of relabel configurations. RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"` } // A labeled group of targets to scrape for a job. 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 []clientmodel.LabelSet `yaml:"targets,omitempty" json:"targets,omitempty"` // Labels is a set of labels that is common across all targets in the group. Labels clientmodel.LabelSet `yaml:"labels,omitempty" json:"labels,omitempty"` // Source is an identifier that describes a group of targets. Source string `yaml:"-", json:"-"` } func (tg *TargetGroup) String() string { return tg.Source } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (tg *TargetGroup) UnmarshalYAML(unmarshal func(interface{}) error) error { g := struct { Targets []string `yaml:"targets"` Labels clientmodel.LabelSet `yaml:"labels"` }{} if err := unmarshal(&g); err != nil { return err } tg.Targets = make([]clientmodel.LabelSet, 0, len(g.Targets)) for _, t := range g.Targets { if strings.Contains(t, "/") { return fmt.Errorf("%q is not a valid hostname", t) } tg.Targets = append(tg.Targets, clientmodel.LabelSet{ clientmodel.AddressLabel: clientmodel.LabelValue(t), }) } tg.Labels = g.Labels return nil } // DNSConfig is the configuration for DNS based service discovery. type DNSConfig struct { // DefaultedDNSConfig contains the actual fields for DNSConfig. DefaultedDNSConfig `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (c *DNSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { c.DefaultedDNSConfig = DefaultDNSConfig err := unmarshal(&c.DefaultedDNSConfig) if err != nil { return err } if len(c.Names) == 0 { return fmt.Errorf("DNS config must contain at least one SRV server name") } return nil } // DefaultedDNSConfig is a proxy type for DNSConfig. type DefaultedDNSConfig struct { Names []string `yaml:"names"` RefreshInterval Duration `yaml:"refresh_interval"` } // RelabelAction is the action to be performed on relabeling. type RelabelAction string const ( // Performs a regex replacement. RelabelReplace RelabelAction = "replace" // Drops targets for which the input does not match the regex. RelabelKeep = "keep" // Drops targets for which the input does match the regex. RelabelDrop = "drop" ) // UnmarshalYAML implements the yaml.Unmarshaller 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: *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 { // DefaultedRelabelConfig contains the actual fields for RelabelConfig. DefaultedRelabelConfig `yaml:",inline"` } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { c.DefaultedRelabelConfig = DefaultRelabelConfig return unmarshal(&c.DefaultedRelabelConfig) } // DefaultedRelabelConfig is a proxy type for RelabelConfig. type DefaultedRelabelConfig struct { // A list of labels from which values are taken and concatenated // with the configured separator in order. SourceLabels clientmodel.LabelNames `yaml:"source_labels,flow"` // Separator is the string between concatenated values from the source labels. Separator string `yaml:"separator"` // Regex against which the concatenation is matched. Regex *Regexp `yaml:"regex"` // The label to which the resulting string is written in a replacement. TargetLabel clientmodel.LabelName `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"` } // Regexp encapsulates a regexp.Regexp and makes it YAML marshallable. type Regexp struct { regexp.Regexp } // UnmarshalYAML implements the yaml.Unmarshaller interface. func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } regex, err := regexp.Compile(s) if err != nil { return err } re.Regexp = *regex return nil } // MarshalYAML implements the yaml.Marshaller interface. func (re *Regexp) MarshalYAML() (interface{}, error) { return re.String(), nil } // Duration encapsulates a time.Duration and makes it YAML marshallable. // // TODO(fabxc): Since we have custom types for most things, including timestamps, // we might want to move this into our model as well, eventually. type Duration time.Duration // UnmarshalYAML implements the yaml.Unmarshaller interface. func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string if err := unmarshal(&s); err != nil { return err } dur, err := utility.StringToDuration(s) if err != nil { return err } *d = Duration(dur) return nil } // MarshalYAML implements the yaml.Marshaller interface. func (d Duration) MarshalYAML() (interface{}, error) { return utility.DurationToString(time.Duration(d)), nil }