created docker-prometheus compose file and edited the Prometheus yml file

This commit is contained in:
Brian Christner
2015-08-18 14:36:46 +02:00
parent 4b9b5b70e3
commit bf34f627cf
664 changed files with 150581 additions and 0 deletions

760
prom/config/config.go Normal file
View File

@ -0,0 +1,760 @@
package config
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"path/filepath"
"regexp"
"strings"
"time"
"gopkg.in/yaml.v2"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/util/strutil"
)
var (
patJobName = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_-]*$`)
patFileSDName = regexp.MustCompile(`^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$`)
patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`)
patAuthLine = regexp.MustCompile(`((?:username|password|bearer_token):\s+)(".+"|'.+'|[^\s]+)`)
)
// 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 (
// The default top-level configuration.
DefaultConfig = Config{
GlobalConfig: DefaultGlobalConfig,
}
// The default global configuration.
DefaultGlobalConfig = GlobalConfig{
ScrapeInterval: Duration(1 * time.Minute),
ScrapeTimeout: Duration(10 * time.Second),
EvaluationInterval: Duration(1 * time.Minute),
}
// The default scrape configuration.
DefaultScrapeConfig = ScrapeConfig{
// ScrapeTimeout and ScrapeInterval default to the
// configured globals.
MetricsPath: "/metrics",
Scheme: "http",
HonorLabels: false,
}
// The default Relabel configuration.
DefaultRelabelConfig = RelabelConfig{
Action: RelabelReplace,
Separator: ";",
}
// The default DNS SD configuration.
DefaultDNSSDConfig = DNSSDConfig{
RefreshInterval: Duration(30 * time.Second),
Type: "SRV",
}
// The default file SD configuration.
DefaultFileSDConfig = FileSDConfig{
RefreshInterval: Duration(5 * time.Minute),
}
// The default Consul SD configuration.
DefaultConsulSDConfig = ConsulSDConfig{
TagSeparator: ",",
Scheme: "http",
}
// The default Serverset SD configuration.
DefaultServersetSDConfig = ServersetSDConfig{
Timeout: Duration(10 * time.Second),
}
// DefaultMarathonSDConfig is the default Marathon SD configuration.
DefaultMarathonSDConfig = MarathonSDConfig{
RefreshInterval: Duration(30 * time.Second),
}
// The default Kubernetes SD configuration
DefaultKubernetesSDConfig = KubernetesSDConfig{
KubeletPort: 10255,
RequestTimeout: Duration(10 * time.Second),
RetryInterval: Duration(1 * time.Second),
}
)
// This custom URL type allows validating 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"`
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
}
// 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)
}
for _, scfg := range cfg.ScrapeConfigs {
scfg.BearerTokenFile = join(scfg.BearerTokenFile)
if scfg.ClientCert != nil {
scfg.ClientCert.Cert = join(scfg.ClientCert.Cert)
scfg.ClientCert.Key = join(scfg.ClientCert.Key)
}
}
}
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 {
var s string
if c.original != "" {
s = c.original
} else {
b, err := yaml.Marshal(c)
if err != nil {
return fmt.Sprintf("<error creating config string: %s>", err)
}
s = string(b)
}
return patAuthLine.ReplaceAllString(s, "${1}<hidden>")
}
// 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 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 {
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 checkOverflow(c.XXX, "config")
}
// GlobalConfig configures values that are used across other configuration
// objects.
type GlobalConfig struct {
// How frequently to scrape targets by default.
ScrapeInterval Duration `yaml:"scrape_interval,omitempty"`
// The default timeout when scraping targets.
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.
func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultGlobalConfig
type plain GlobalConfig
if err := unmarshal((*plain)(c)); err != nil {
return err
}
return checkOverflow(c.XXX, "global config")
}
// isZero returns true iff the global config is the zero value.
func (c *GlobalConfig) isZero() bool {
return c.Labels == nil &&
c.ScrapeInterval == 0 &&
c.ScrapeTimeout == 0 &&
c.EvaluationInterval == 0
}
// 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 Duration `yaml:"scrape_interval,omitempty"`
// The timeout for scraping targets of this config.
ScrapeTimeout 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"`
// The HTTP basic authentication credentials for the targets.
BasicAuth *BasicAuth `yaml:"basic_auth,omitempty"`
// The bearer token for the targets.
BearerToken string `yaml:"bearer_token,omitempty"`
// The bearer token file for the targets.
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
// The ca cert to use for the targets.
CACert string `yaml:"ca_cert,omitempty"`
// The client cert authentication credentials for the targets.
ClientCert *ClientCert `yaml:"client_cert,omitempty"`
// HTTP proxy server to use to connect to the targets.
ProxyURL URL `yaml:"proxy_url,omitempty"`
// List of labeled target groups for this job.
TargetGroups []*TargetGroup `yaml:"target_groups,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"`
// 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 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 !patJobName.MatchString(c.JobName) {
return fmt.Errorf("%q is not a valid job name", c.JobName)
}
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 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"`
}
// ClientCert contains client cert credentials.
type ClientCert struct {
Cert string `yaml:"cert"`
Key string `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 []clientmodel.LabelSet
// Labels is a set of labels that is common across all targets in the group.
Labels clientmodel.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 clientmodel.LabelSet `yaml:"labels"`
XXX map[string]interface{} `yaml:",inline"`
}{}
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 checkOverflow(g.XXX, "target_group")
}
// MarshalYAML implements the yaml.Marshaler interface.
func (tg TargetGroup) MarshalYAML() (interface{}, error) {
g := &struct {
Targets []string `yaml:"targets"`
Labels clientmodel.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[clientmodel.AddressLabel]))
}
return g, nil
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (tg *TargetGroup) UnmarshalJSON(b []byte) error {
g := struct {
Targets []string `json:"targets"`
Labels clientmodel.LabelSet `json:"labels"`
}{}
if err := json.Unmarshal(b, &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
}
// DNSSDConfig is the configuration for DNS based service discovery.
type DNSSDConfig struct {
Names []string `yaml:"names"`
RefreshInterval 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 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 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.
func (c *FileSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultFileSDConfig
type plain FileSDConfig
err := unmarshal((*plain)(c))
if err != nil {
return err
}
if len(c.Names) == 0 {
return fmt.Errorf("file service discovery config must contain at least one path name")
}
for _, name := range c.Names {
if !patFileSDName.MatchString(name) {
return fmt.Errorf("path name %q is not valid for file discovery", name)
}
}
return checkOverflow(c.XXX, "file_sd_config")
}
// ConsulSDConfig is the configuration for Consul service discovery.
type ConsulSDConfig struct {
Server string `yaml:"server"`
Token string `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 string `yaml:"password,omitempty"`
// The list of services for which targets are discovered.
// Defaults to all services if empty.
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.
func (c *ConsulSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultConsulSDConfig
type plain ConsulSDConfig
err := unmarshal((*plain)(c))
if err != nil {
return err
}
if strings.TrimSpace(c.Server) == "" {
return fmt.Errorf("Consul SD configuration requires a server address")
}
return checkOverflow(c.XXX, "consul_sd_config")
}
// ServersetSDConfig is the configuration for Twitter serversets in Zookeeper based discovery.
type ServersetSDConfig struct {
Servers []string `yaml:"servers"`
Paths []string `yaml:"paths"`
Timeout 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 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 checkOverflow(c.XXX, "serverset_sd_config")
}
// MarathonSDConfig is the configuration for services running on Marathon.
type MarathonSDConfig struct {
Servers []string `yaml:"servers,omitempty"`
RefreshInterval Duration `yaml:"refresh_interval,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`
}
// KubernetesSDConfig is the configuration for Kubernetes service discovery.
type KubernetesSDConfig struct {
Server string `yaml:"server"`
KubeletPort int `yaml:"kubelet_port,omitempty"`
InCluster bool `yaml:"in_cluster,omitempty"`
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
Insecure bool `yaml:"insecure,omitempty"`
CertFile string `yaml:"cert_file,omitempty"`
KeyFile string `yaml:"key_file,omitempty"`
CAFile string `yaml:"ca_file,omitempty"`
RetryInterval Duration `yaml:"retry_interval,omitempty"`
RequestTimeout Duration `yaml:"request_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 *MarathonSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultMarathonSDConfig
type plain MarathonSDConfig
if len(c.Servers) == 0 {
return fmt.Errorf("Marathon SD config must contain at least one Marathon server")
}
return checkOverflow(c.XXX, "marathon_sd_config")
}
func (c *KubernetesSDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultKubernetesSDConfig
type plain KubernetesSDConfig
err := unmarshal((*plain)(c))
if err != nil {
return err
}
if strings.TrimSpace(c.Server) == "" {
return fmt.Errorf("Kubernetes SD configuration requires a server address")
}
// Make sure server ends in trailing slash - simpler URL building later
if !strings.HasSuffix(c.Server, "/") {
c.Server += "/"
}
return checkOverflow(c.XXX, "kubernetes_sd_config")
}
// 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 RelabelAction = "keep"
// Drops targets for which the input does match the regex.
RelabelDrop RelabelAction = "drop"
// Sets a label to the modulus of a hash of labels.
RelabelHashMod RelabelAction = "hashmod"
// Copy labels to other labelnames based on a regex.
RelabelLabelMap RelabelAction = "labelmap"
)
// 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:
*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 clientmodel.LabelNames `yaml:"source_labels,flow"`
// 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"`
// 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,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 c.Regex == nil && c.Action != RelabelHashMod {
return fmt.Errorf("relabel configuration requires a regular expression")
}
if c.Modulus == 0 && c.Action == RelabelHashMod {
return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus")
}
return checkOverflow(c.XXX, "relabel_config")
}
// Regexp encapsulates a regexp.Regexp and makes it YAML marshallable.
type Regexp struct {
regexp.Regexp
}
// 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
}
regex, err := regexp.Compile(s)
if err != nil {
return err
}
re.Regexp = *regex
return nil
}
// MarshalYAML implements the yaml.Marshaler interface.
func (re *Regexp) MarshalYAML() (interface{}, error) {
if re != nil {
return re.String(), nil
}
return nil, 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.Unmarshaler interface.
func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
dur, err := strutil.StringToDuration(s)
if err != nil {
return err
}
*d = Duration(dur)
return nil
}
// MarshalYAML implements the yaml.Marshaler interface.
func (d Duration) MarshalYAML() (interface{}, error) {
return strutil.DurationToString(time.Duration(d)), nil
}

323
prom/config/config_test.go Normal file
View File

@ -0,0 +1,323 @@
package config
import (
"encoding/json"
"io/ioutil"
"reflect"
"regexp"
"strings"
"testing"
"time"
"gopkg.in/yaml.v2"
clientmodel "github.com/prometheus/client_golang/model"
)
var expectedConf = &Config{
GlobalConfig: GlobalConfig{
ScrapeInterval: Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
EvaluationInterval: Duration(30 * time.Second),
Labels: clientmodel.LabelSet{
"monitor": "codelab",
"foo": "bar",
},
},
RuleFiles: []string{
"testdata/first.rules",
"/absolute/second.rules",
"testdata/my/*.rules",
},
ScrapeConfigs: []*ScrapeConfig{
{
JobName: "prometheus",
HonorLabels: true,
ScrapeInterval: Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
MetricsPath: DefaultScrapeConfig.MetricsPath,
Scheme: DefaultScrapeConfig.Scheme,
BearerTokenFile: "testdata/valid_token_file",
TargetGroups: []*TargetGroup{
{
Targets: []clientmodel.LabelSet{
{clientmodel.AddressLabel: "localhost:9090"},
{clientmodel.AddressLabel: "localhost:9191"},
},
Labels: clientmodel.LabelSet{
"my": "label",
"your": "label",
},
},
},
FileSDConfigs: []*FileSDConfig{
{
Names: []string{"foo/*.slow.json", "foo/*.slow.yml", "single/file.yml"},
RefreshInterval: Duration(10 * time.Minute),
},
{
Names: []string{"bar/*.yaml"},
RefreshInterval: Duration(5 * time.Minute),
},
},
RelabelConfigs: []*RelabelConfig{
{
SourceLabels: clientmodel.LabelNames{"job", "__meta_dns_srv_name"},
TargetLabel: "job",
Separator: ";",
Regex: &Regexp{*regexp.MustCompile("(.*)some-[regex]$")},
Replacement: "foo-${1}",
Action: RelabelReplace,
},
},
},
{
JobName: "service-x",
ScrapeInterval: Duration(50 * time.Second),
ScrapeTimeout: Duration(5 * time.Second),
BasicAuth: &BasicAuth{
Username: "admin_name",
Password: "admin_password",
},
MetricsPath: "/my_path",
Scheme: "https",
DNSSDConfigs: []*DNSSDConfig{
{
Names: []string{
"first.dns.address.domain.com",
"second.dns.address.domain.com",
},
RefreshInterval: Duration(15 * time.Second),
Type: "SRV",
},
{
Names: []string{
"first.dns.address.domain.com",
},
RefreshInterval: Duration(30 * time.Second),
Type: "SRV",
},
},
RelabelConfigs: []*RelabelConfig{
{
SourceLabels: clientmodel.LabelNames{"job"},
Regex: &Regexp{*regexp.MustCompile("(.*)some-[regex]$")},
Separator: ";",
Action: RelabelDrop,
},
{
SourceLabels: clientmodel.LabelNames{"__address__"},
TargetLabel: "__tmp_hash",
Modulus: 8,
Separator: ";",
Action: RelabelHashMod,
},
{
SourceLabels: clientmodel.LabelNames{"__tmp_hash"},
Regex: &Regexp{*regexp.MustCompile("^1$")},
Separator: ";",
Action: RelabelKeep,
},
},
MetricRelabelConfigs: []*RelabelConfig{
{
SourceLabels: clientmodel.LabelNames{"__name__"},
Regex: &Regexp{*regexp.MustCompile("expensive_metric.*$")},
Separator: ";",
Action: RelabelDrop,
},
},
},
{
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,
},
},
},
{
JobName: "service-z",
ScrapeInterval: Duration(15 * time.Second),
ScrapeTimeout: Duration(10 * time.Second),
MetricsPath: "/metrics",
Scheme: "http",
ClientCert: &ClientCert{
Cert: "testdata/valid_cert_file",
Key: "testdata/valid_key_file",
},
BearerToken: "avalidtoken",
},
{
JobName: "service-kubernetes",
ScrapeInterval: Duration(15 * time.Second),
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
MetricsPath: DefaultScrapeConfig.MetricsPath,
Scheme: DefaultScrapeConfig.Scheme,
KubernetesSDConfigs: []*KubernetesSDConfig{
{
Server: "https://localhost:1234/",
Username: "myusername",
Password: "mypassword",
KubeletPort: 10255,
RequestTimeout: Duration(10 * time.Second),
RetryInterval: Duration(1 * time.Second),
},
},
},
},
original: "",
}
func TestLoadConfig(t *testing.T) {
// Parse a valid file that sets a global scrape timeout. This tests whether parsing
// an overwritten default field in the global config permanently changes the default.
if _, err := LoadFile("testdata/global_timeout.good.yml"); err != nil {
t.Errorf("Error parsing %s: %s", "testdata/conf.good.yml", err)
}
c, err := LoadFile("testdata/conf.good.yml")
if err != nil {
t.Fatalf("Error parsing %s: %s", "testdata/conf.good.yml", err)
}
bgot, err := yaml.Marshal(c)
if err != nil {
t.Fatalf("%s", err)
}
bexp, err := yaml.Marshal(expectedConf)
if err != nil {
t.Fatalf("%s", err)
}
expectedConf.original = c.original
if !reflect.DeepEqual(c, expectedConf) {
t.Fatalf("%s: unexpected config result: \n\n%s\n expected\n\n%s", "testdata/conf.good.yml", bgot, bexp)
}
// String method must not reveal authentication credentials.
s := c.String()
if strings.Contains(s, "admin_name") || strings.Contains(s, "admin_password") {
t.Fatalf("config's String method reveals authentication credentials.")
}
}
var expectedErrors = []struct {
filename string
errMsg string
}{
{
filename: "jobname.bad.yml",
errMsg: `"prom^etheus" is not a valid job name`,
}, {
filename: "jobname_dup.bad.yml",
errMsg: `found multiple scrape configs with job name "prometheus"`,
}, {
filename: "labelname.bad.yml",
errMsg: `"not$allowed" is not a valid label name`,
}, {
filename: "labelname2.bad.yml",
errMsg: `"not:allowed" is not a valid label name`,
}, {
filename: "regex.bad.yml",
errMsg: "error parsing regexp",
}, {
filename: "regex_missing.bad.yml",
errMsg: "relabel configuration requires a regular expression",
}, {
filename: "modulus_missing.bad.yml",
errMsg: "relabel configuration for hashmod requires non-zero modulus",
}, {
filename: "rules.bad.yml",
errMsg: "invalid rule file path",
}, {
filename: "unknown_attr.bad.yml",
errMsg: "unknown fields in scrape_config: consult_sd_configs",
}, {
filename: "bearertoken.bad.yml",
errMsg: "at most one of bearer_token & bearer_token_file must be configured",
}, {
filename: "bearertoken_basicauth.bad.yml",
errMsg: "at most one of basic_auth, bearer_token & bearer_token_file must be configured",
},
}
func TestBadConfigs(t *testing.T) {
for _, ee := range expectedErrors {
_, err := LoadFile("testdata/" + ee.filename)
if err == nil {
t.Errorf("Expected error parsing %s but got none", ee.filename)
continue
}
if !strings.Contains(err.Error(), ee.errMsg) {
t.Errorf("Expected error for %s to contain %q but got: %s", ee.filename, ee.errMsg, err)
}
}
}
func TestBadTargetGroup(t *testing.T) {
content, err := ioutil.ReadFile("testdata/tgroup.bad.json")
if err != nil {
t.Fatal(err)
}
var tg TargetGroup
err = json.Unmarshal(content, &tg)
if err == nil {
t.Errorf("Expected unmarshal error but got none.")
}
}
func TestEmptyConfig(t *testing.T) {
c, err := Load("")
if err != nil {
t.Fatalf("Unexpected error parsing empty config file: %s", err)
}
exp := DefaultConfig
if !reflect.DeepEqual(*c, exp) {
t.Fatalf("want %v, got %v", exp, c)
}
}
func TestEmptyGlobalBlock(t *testing.T) {
c, err := Load("global:\n")
if err != nil {
t.Fatalf("Unexpected error parsing empty config file: %s", err)
}
exp := DefaultConfig
exp.original = "global:\n"
if !reflect.DeepEqual(*c, exp) {
t.Fatalf("want %v, got %v", exp, c)
}
}

View File

@ -0,0 +1,6 @@
scrape_configs:
- job_name: prometheus
bearer_token: 1234
bearer_token_file: somefile

View File

@ -0,0 +1,8 @@
scrape_configs:
- job_name: prometheus
bearer_token: 1234
basic_auth:
username: user
password: password

108
prom/config/testdata/conf.good.yml vendored Normal file
View File

@ -0,0 +1,108 @@
# 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"
- "/absolute/second.rules"
- "my/*.rules"
scrape_configs:
- job_name: prometheus
honor_labels: true
# 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'.
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'
bearer_token_file: valid_token_file
- job_name: service-x
basic_auth:
username: admin_name
password: admin_password
scrape_interval: 50s
scrape_timeout: 5s
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.
relabel_configs:
- source_labels: [job]
regex: (.*)some-[regex]$
action: drop
- source_labels: [__address__]
modulus: 8
target_label: __tmp_hash
action: hashmod
- source_labels: [__tmp_hash]
regex: ^1$
action: keep
metric_relabel_configs:
- source_labels: [__name__]
regex: expensive_metric.*$
action: drop
- job_name: service-y
consul_sd_configs:
- server: 'localhost:1234'
services: ['nginx', 'cache', 'mysql']
- job_name: service-z
client_cert:
cert: valid_cert_file
key: valid_key_file
bearer_token: avalidtoken
- job_name: service-kubernetes
kubernetes_sd_configs:
- server: 'https://localhost:1234'
username: 'myusername'
password: 'mypassword'

View File

@ -0,0 +1,2 @@
global:
scrape_timeout: 1h

2
prom/config/testdata/jobname.bad.yml vendored Normal file
View File

@ -0,0 +1,2 @@
scrape_configs:
- job_name: prom^etheus

View File

@ -0,0 +1,5 @@
# Two scrape configs with the same job names are not allowed.
scrape_configs:
- job_name: prometheus
- job_name: service-x
- job_name: prometheus

View File

@ -0,0 +1,3 @@
global:
labels:
not$allowed: value

View File

@ -0,0 +1,3 @@
global:
labels:
'not:allowed': value

View File

@ -0,0 +1,5 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- regex: abcdef
action: hashmod

4
prom/config/testdata/regex.bad.yml vendored Normal file
View File

@ -0,0 +1,4 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- regex: abc(def

View File

@ -0,0 +1,4 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- source_labels: ['blub']

3
prom/config/testdata/rules.bad.yml vendored Normal file
View File

@ -0,0 +1,3 @@
rule_files:
- 'my_rule' # fine
- 'my/*/rule' # bad

7
prom/config/testdata/tgroup.bad.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"targets": ["1.2.3.4:9100"],
"labels": {
"some_valid_label": "foo",
"oops:this-label-is-invalid": "bar"
}
}

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'