Merge branch 'master' of https://github.com/prometheus/prometheus into update_k8s

Signed-off-by: tariqibrahim <tariq.ibrahim@microsoft.com>
pull/4927/head
tariqibrahim 2018-12-20 11:35:34 -08:00
commit 1e4e4c46ba
49 changed files with 838 additions and 720 deletions

View File

@ -11,7 +11,6 @@ executors:
jobs:
test:
executor: golang
resource_class: large
steps:
- checkout

View File

@ -1,6 +1,5 @@
## 2.6.0-rc.0 / 2018-12-05
## 2.6.0 / 2018-12-17
* [CHANGE] Include default flags to the container's entrypoint. #4796
* [CHANGE] Promtool: Remove the `update` command. #3839
* [FEATURE] Add JSON log format via the `--log.format` flag. #4876
* [FEATURE] API: Add /api/v1/labels endpoint to get all label names. #4835
@ -8,8 +7,8 @@
* [ENHANCEMENT] Add `prometheus_tsdb_lowest_timestamp_seconds`, `prometheus_tsdb_head_min_time_seconds` and `prometheus_tsdb_head_max_time_seconds` metrics. #4888
* [ENHANCEMENT] Add `rule_group_last_evaluation_timestamp_seconds` metric. #4852
* [ENHANCEMENT] Add `prometheus_template_text_expansion_failures_total` and `prometheus_template_text_expansions_total` metrics. #4747
* [ENHANCEMENT] Remove default flags from the container's entrypoint. #4976
* [ENHANCEMENT] Set consistent User-Agent header in outgoing requests. #4891
* [ENHANCEMENT] Azure SD: Add the machine's power state to the discovery metadata. #4908
* [ENHANCEMENT] Azure SD: Error out at load time when authentication parameters are missing. #4907
* [ENHANCEMENT] EC2 SD: Add the machine's private DNS name to the discovery metadata. #4693
* [ENHANCEMENT] EC2 SD: Add the operating system's platform to the discovery metadata. #4663
@ -42,6 +41,7 @@
* [BUGFIX] Scrape: Scrape targets at fixed intervals even after Prometheus restarts. #4926
* [BUGFIX] TSDB: Support restored snapshots including the head properly. #4953
* [BUGFIX] TSDB: Repair WAL when the last record in a segment is torn. #4953
* [BUGFIX] TSDB: Fix unclosed file readers on Windows systems. #4997
* [BUGFIX] Web: Avoid proxy to connect to the local gRPC server. #4572
## 2.5.0 / 2018-11-06

View File

@ -7,16 +7,13 @@ COPY documentation/examples/prometheus.yml /etc/prometheus/prometheus.yml
COPY console_libraries/ /usr/share/prometheus/console_libraries/
COPY consoles/ /usr/share/prometheus/consoles/
RUN ln -s /usr/share/prometheus/console_libraries /usr/share/prometheus/consoles/ /etc/prometheus/
RUN mkdir -p /prometheus && \
chown -R nobody:nogroup etc/prometheus /prometheus
RUN ln -s /usr/share/prometheus/console_libraries /usr/share/prometheus/consoles/ /etc/prometheus/ && \
mkdir -p /prometheus && \
chown -R nobody:nogroup etc/prometheus /prometheus && \
ln -s /prometheus /etc/prometheus/data
USER nobody
EXPOSE 9090
VOLUME [ "/prometheus" ]
WORKDIR /prometheus
ENTRYPOINT [ "/bin/prometheus", \
"--storage.tsdb.path=/prometheus", \
"--web.console.libraries=/etc/prometheus/console_libraries", \
"--web.console.templates=/etc/prometheus/consoles", \
"--config.file=/etc/prometheus/prometheus.yml" ]
WORKDIR /etc/prometheus
ENTRYPOINT [ "/bin/prometheus" ]

View File

@ -29,6 +29,8 @@ GO ?= go
GOFMT ?= $(GO)fmt
FIRST_GOPATH := $(firstword $(subst :, ,$(shell $(GO) env GOPATH)))
GOOPTS ?=
GOHOSTOS ?= $(shell $(GO) env GOHOSTOS)
GOHOSTARCH ?= $(shell $(GO) env GOHOSTARCH)
GO_VERSION ?= $(shell $(GO) version)
GO_VERSION_NUMBER ?= $(word 3, $(GO_VERSION))
@ -62,8 +64,12 @@ PROMU := $(FIRST_GOPATH)/bin/promu
STATICCHECK := $(FIRST_GOPATH)/bin/staticcheck
pkgs = ./...
GO_VERSION ?= $(shell $(GO) version)
GO_BUILD_PLATFORM ?= $(subst /,-,$(lastword $(GO_VERSION)))
ifeq (arm, $(GOHOSTARCH))
GOHOSTARM ?= $(shell GOARM= $(GO) env GOARM)
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)v$(GOHOSTARM)
else
GO_BUILD_PLATFORM ?= $(GOHOSTOS)-$(GOHOSTARCH)
endif
PROMU_VERSION ?= 0.2.0
PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_VERSION)/promu-$(PROMU_VERSION).$(GO_BUILD_PLATFORM).tar.gz
@ -73,6 +79,13 @@ BIN_DIR ?= $(shell pwd)
DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
DOCKER_REPO ?= prom
ifeq ($(GOHOSTARCH),amd64)
ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux freebsd darwin windows))
# Only supported on amd64
test-flags := -race
endif
endif
.PHONY: all
all: precheck style staticcheck unused build test
@ -110,7 +123,7 @@ common-test-short:
.PHONY: common-test
common-test:
@echo ">> running all tests"
GO111MODULE=$(GO111MODULE) $(GO) test -race $(GOOPTS) $(pkgs)
GO111MODULE=$(GO111MODULE) $(GO) test $(test-flags) $(GOOPTS) $(pkgs)
.PHONY: common-format
common-format:

View File

@ -1,12 +1,12 @@
# Releases
This page describes the release process and the currently planned schedule for upcoming releases as well as the respective release schepherds. Release shepards are chosen on a voluntary basis.
This page describes the release process and the currently planned schedule for upcoming releases as well as the respective release shepherd. Release shepherd are chosen on a voluntary basis.
## Release schedule
Release cadence of first pre-releases being cut is 6 weeks.
| release series | date of first pre-release (year-month-day) | release shepard |
| release series | date of first pre-release (year-month-day) | release shepherd |
|----------------|--------------------------------------------|---------------------------------------------|
| v2.4 | 2018-09-06 | Goutham Veeramachaneni (GitHub: @gouthamve) |
| v2.5 | 2018-10-24 | Frederic Branczyk (GitHub: @brancz) |
@ -15,12 +15,12 @@ Release cadence of first pre-releases being cut is 6 weeks.
If you are interested in volunteering please create a pull request against the [prometheus/prometheus](https://github.com/prometheus/prometheus) repository and propose yourself for the release series of your choice.
## Release shepard responsibilities
## Release shepherd responsibilities
The release shepard is responsible for the entire release series of a minor release, meaning all pre- and patch releases of a minor release. The process starts with the initial pre-release.
The release shepherd is responsible for the entire release series of a minor release, meaning all pre- and patch releases of a minor release. The process starts with the initial pre-release.
* The first pre-release is scheduled according to the above schedule.
* With the pre-release the release shepard is responsible for running and monitoring a benchmark run of the pre-release for 3 days, after which, if successful, the pre-release is promoted to a stable release.
* With the pre-release the release shepherd is responsible for running and monitoring a benchmark run of the pre-release for 3 days, after which, if successful, the pre-release is promoted to a stable release.
* Once a pre-release has been released, the `master` branch of the repository is frozen for any feature work, only critical bug fix work concerning the minor release is merged.
* Pre-releases are done from `master`, after pre-releases are promoted to the stable release a `release-major.minor` branch is created.
@ -36,7 +36,7 @@ We use [Semantic Versioning](http://semver.org/).
We maintain a separate branch for each minor release, named `release-<major>.<minor>`, e.g. `release-1.1`, `release-2.0`.
The usual flow is to merge new features and changes into the master branch and to merge bug fixes into the latest release branch. Bug fixes are then merged into master from the latest release branch. The master branch should always contain all commits from the latest release branch. Whether merging master back into a release branch makes more sense is left up to the shepard's judgement.
The usual flow is to merge new features and changes into the master branch and to merge bug fixes into the latest release branch. Bug fixes are then merged into master from the latest release branch. The master branch should always contain all commits from the latest release branch. Whether merging master back into a release branch makes more sense is left up to the shepherd's judgement.
If a bug fix got accidentally merged into master, cherry-pick commits have to be created in the latest release branch, which then have to be merged back into master. Try to avoid that situation.

View File

@ -1 +1 @@
2.6.0-rc.0
2.6.0

View File

@ -256,7 +256,7 @@ func main() {
ctxWeb, cancelWeb = context.WithCancel(context.Background())
ctxRule = context.Background()
notifier = notifier.NewManager(&cfg.notifier, log.With(logger, "component", "notifier"))
notifierManager = notifier.NewManager(&cfg.notifier, log.With(logger, "component", "notifier"))
ctxScrape, cancelScrape = context.WithCancel(context.Background())
discoveryManagerScrape = discovery.NewManager(ctxScrape, log.With(logger, "component", "discovery manager scrape"), discovery.Name("scrape"))
@ -279,7 +279,7 @@ func main() {
Appendable: fanoutStorage,
TSDB: localStorage,
QueryFunc: rules.EngineQueryFunc(queryEngine, fanoutStorage),
NotifyFunc: sendAlerts(notifier, cfg.web.ExternalURL.String()),
NotifyFunc: sendAlerts(notifierManager, cfg.web.ExternalURL.String()),
Context: ctxRule,
ExternalURL: cfg.web.ExternalURL,
Registerer: prometheus.DefaultRegisterer,
@ -296,7 +296,7 @@ func main() {
cfg.web.QueryEngine = queryEngine
cfg.web.ScrapeManager = scrapeManager
cfg.web.RuleManager = ruleManager
cfg.web.Notifier = notifier
cfg.web.Notifier = notifierManager
cfg.web.Version = &web.PrometheusVersion{
Version: version.Version,
@ -332,7 +332,7 @@ func main() {
webHandler.ApplyConfig,
// The Scrape and notifier managers need to reload before the Discovery manager as
// they need to read the most updated config when receiving the new targets list.
notifier.ApplyConfig,
notifierManager.ApplyConfig,
scrapeManager.ApplyConfig,
func(cfg *config.Config) error {
c := make(map[string]sd_config.ServiceDiscoveryConfig)
@ -611,12 +611,12 @@ func main() {
// so we wait until the config is fully loaded.
<-reloadReady.C
notifier.Run(discoveryManagerNotify.SyncCh())
notifierManager.Run(discoveryManagerNotify.SyncCh())
level.Info(logger).Log("msg", "Notifier manager stopped")
return nil
},
func(err error) {
notifier.Stop()
notifierManager.Stop()
},
)
}
@ -696,8 +696,12 @@ func computeExternalURL(u, listenAddr string) (*url.URL, error) {
return eu, nil
}
type sender interface {
Send(alerts ...*notifier.Alert)
}
// sendAlerts implements the rules.NotifyFunc for a Notifier.
func sendAlerts(n *notifier.Manager, externalURL string) rules.NotifyFunc {
func sendAlerts(s sender, externalURL string) rules.NotifyFunc {
return func(ctx context.Context, expr string, alerts ...*rules.Alert) {
var res []*notifier.Alert
@ -717,7 +721,7 @@ func sendAlerts(n *notifier.Manager, externalURL string) rules.NotifyFunc {
}
if len(alerts) > 0 {
n.Send(res...)
s.Send(res...)
}
}
}

View File

@ -14,6 +14,7 @@
package main
import (
"context"
"flag"
"fmt"
"net/http"
@ -24,6 +25,9 @@ import (
"testing"
"time"
"github.com/prometheus/prometheus/notifier"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/util/testutil"
)
@ -173,3 +177,73 @@ func TestFailedStartupExitCode(t *testing.T) {
t.Errorf("unable to retrieve the exit status for prometheus: %v", err)
}
}
type senderFunc func(alerts ...*notifier.Alert)
func (s senderFunc) Send(alerts ...*notifier.Alert) {
s(alerts...)
}
func TestSendAlerts(t *testing.T) {
testCases := []struct {
in []*rules.Alert
exp []*notifier.Alert
}{
{
in: []*rules.Alert{
&rules.Alert{
Labels: []labels.Label{{Name: "l1", Value: "v1"}},
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
ActiveAt: time.Unix(1, 0),
FiredAt: time.Unix(2, 0),
ValidUntil: time.Unix(3, 0),
},
},
exp: []*notifier.Alert{
&notifier.Alert{
Labels: []labels.Label{{Name: "l1", Value: "v1"}},
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
StartsAt: time.Unix(2, 0),
EndsAt: time.Unix(3, 0),
GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1",
},
},
},
{
in: []*rules.Alert{
&rules.Alert{
Labels: []labels.Label{{Name: "l1", Value: "v1"}},
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
ActiveAt: time.Unix(1, 0),
FiredAt: time.Unix(2, 0),
ResolvedAt: time.Unix(4, 0),
},
},
exp: []*notifier.Alert{
&notifier.Alert{
Labels: []labels.Label{{Name: "l1", Value: "v1"}},
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
StartsAt: time.Unix(2, 0),
EndsAt: time.Unix(4, 0),
GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1",
},
},
},
{
in: []*rules.Alert{},
},
}
for i, tc := range testCases {
tc := tc
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
senderFunc := senderFunc(func(alerts ...*notifier.Alert) {
if len(tc.in) == 0 {
t.Fatalf("sender called with 0 alert")
}
testutil.Equals(t, tc.exp, alerts)
})
sendAlerts(senderFunc, "http://localhost:9090")(context.TODO(), "up", tc.in...)
})
}
}

View File

@ -160,14 +160,14 @@ func (tg *testGroup) test(mint, maxt time.Time, evalInterval time.Duration, grou
// All this preparation is so that we can test alerts as we evaluate the rules.
// This avoids storing them in memory, as the number of evals might be high.
// All the `eval_time` for which we have unit tests.
var alertEvalTimes []time.Duration
// All the `eval_time` for which we have unit tests for alerts.
alertEvalTimesMap := map[time.Duration]struct{}{}
// Map of all the eval_time+alertname combination present in the unit tests.
alertsInTest := make(map[time.Duration]map[string]struct{})
// Map of all the unit tests for given eval_time.
alertTests := make(map[time.Duration][]alertTestCase)
for _, alert := range tg.AlertRuleTests {
alertEvalTimes = append(alertEvalTimes, alert.EvalTime)
alertEvalTimesMap[alert.EvalTime] = struct{}{}
if _, ok := alertsInTest[alert.EvalTime]; !ok {
alertsInTest[alert.EvalTime] = make(map[string]struct{})
@ -176,6 +176,10 @@ func (tg *testGroup) test(mint, maxt time.Time, evalInterval time.Duration, grou
alertTests[alert.EvalTime] = append(alertTests[alert.EvalTime], alert)
}
alertEvalTimes := make([]time.Duration, 0, len(alertEvalTimesMap))
for k := range alertEvalTimesMap {
alertEvalTimes = append(alertEvalTimes, k)
}
sort.Slice(alertEvalTimes, func(i, j int) bool {
return alertEvalTimes[i] < alertEvalTimes[j]
})

View File

@ -22,6 +22,8 @@ import (
"strings"
"time"
"github.com/prometheus/prometheus/pkg/relabel"
config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
sd_config "github.com/prometheus/prometheus/discovery/config"
@ -29,8 +31,7 @@ import (
)
var (
patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`)
relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`)
patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`)
)
// Load parses the YAML input s into a Config.
@ -92,14 +93,6 @@ var (
Timeout: model.Duration(10 * time.Second),
}
// DefaultRelabelConfig is the default Relabel configuration.
DefaultRelabelConfig = RelabelConfig{
Action: RelabelReplace,
Separator: ";",
Regex: MustNewRegexp("(.*)"),
Replacement: "$1",
}
// DefaultRemoteWriteConfig is the default remote write configuration.
DefaultRemoteWriteConfig = RemoteWriteConfig{
RemoteTimeout: model.Duration(30 * time.Second),
@ -350,9 +343,9 @@ type ScrapeConfig struct {
HTTPClientConfig config_util.HTTPClientConfig `yaml:",inline"`
// List of target relabel configurations.
RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"`
RelabelConfigs []*relabel.Config `yaml:"relabel_configs,omitempty"`
// List of metric relabel configurations.
MetricRelabelConfigs []*RelabelConfig `yaml:"metric_relabel_configs,omitempty"`
MetricRelabelConfigs []*relabel.Config `yaml:"metric_relabel_configs,omitempty"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -414,7 +407,7 @@ func (c *ScrapeConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
// AlertingConfig configures alerting and alertmanager related configs.
type AlertingConfig struct {
AlertRelabelConfigs []*RelabelConfig `yaml:"alert_relabel_configs,omitempty"`
AlertRelabelConfigs []*relabel.Config `yaml:"alert_relabel_configs,omitempty"`
AlertmanagerConfigs []*AlertmanagerConfig `yaml:"alertmanagers,omitempty"`
}
@ -452,7 +445,7 @@ type AlertmanagerConfig struct {
Timeout model.Duration `yaml:"timeout,omitempty"`
// List of Alertmanager relabel configurations.
RelabelConfigs []*RelabelConfig `yaml:"relabel_configs,omitempty"`
RelabelConfigs []*relabel.Config `yaml:"relabel_configs,omitempty"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -524,151 +517,11 @@ type FileSDConfig struct {
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
}
// 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"`
}
// 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.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 == RelabelLabelMap && !relabelTarget.MatchString(c.Replacement) {
return fmt.Errorf("%q is invalid 'replacement' for %s action", c.Replacement, 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 marshalable.
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 *config_util.URL `yaml:"url"`
RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"`
WriteRelabelConfigs []*RelabelConfig `yaml:"write_relabel_configs,omitempty"`
URL *config_util.URL `yaml:"url"`
RemoteTimeout model.Duration `yaml:"remote_timeout,omitempty"`
WriteRelabelConfigs []*relabel.Config `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.

View File

@ -23,6 +23,8 @@ import (
"testing"
"time"
"github.com/prometheus/prometheus/pkg/relabel"
"github.com/prometheus/prometheus/discovery/azure"
"github.com/prometheus/prometheus/discovery/consul"
"github.com/prometheus/prometheus/discovery/dns"
@ -71,13 +73,13 @@ var expectedConf = &Config{
{
URL: mustParseURL("http://remote1/push"),
RemoteTimeout: model.Duration(30 * time.Second),
WriteRelabelConfigs: []*RelabelConfig{
WriteRelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"__name__"},
Separator: ";",
Regex: MustNewRegexp("expensive.*"),
Regex: relabel.MustNewRegexp("expensive.*"),
Replacement: "$1",
Action: RelabelDrop,
Action: relabel.Drop,
},
},
QueueConfig: DefaultQueueConfig,
@ -145,33 +147,33 @@ var expectedConf = &Config{
},
},
RelabelConfigs: []*RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"job", "__meta_dns_name"},
TargetLabel: "job",
Separator: ";",
Regex: MustNewRegexp("(.*)some-[regex]"),
Regex: relabel.MustNewRegexp("(.*)some-[regex]"),
Replacement: "foo-${1}",
Action: RelabelReplace,
Action: relabel.Replace,
}, {
SourceLabels: model.LabelNames{"abc"},
TargetLabel: "cde",
Separator: ";",
Regex: DefaultRelabelConfig.Regex,
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelReplace,
Regex: relabel.DefaultRelabelConfig.Regex,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.Replace,
}, {
TargetLabel: "abc",
Separator: ";",
Regex: DefaultRelabelConfig.Regex,
Regex: relabel.DefaultRelabelConfig.Regex,
Replacement: "static",
Action: RelabelReplace,
Action: relabel.Replace,
}, {
TargetLabel: "abc",
Separator: ";",
Regex: MustNewRegexp(""),
Regex: relabel.MustNewRegexp(""),
Replacement: "static",
Action: RelabelReplace,
Action: relabel.Replace,
},
},
},
@ -212,56 +214,56 @@ var expectedConf = &Config{
},
},
RelabelConfigs: []*RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"job"},
Regex: MustNewRegexp("(.*)some-[regex]"),
Regex: relabel.MustNewRegexp("(.*)some-[regex]"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelDrop,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.Drop,
},
{
SourceLabels: model.LabelNames{"__address__"},
TargetLabel: "__tmp_hash",
Regex: DefaultRelabelConfig.Regex,
Replacement: DefaultRelabelConfig.Replacement,
Regex: relabel.DefaultRelabelConfig.Regex,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Modulus: 8,
Separator: ";",
Action: RelabelHashMod,
Action: relabel.HashMod,
},
{
SourceLabels: model.LabelNames{"__tmp_hash"},
Regex: MustNewRegexp("1"),
Regex: relabel.MustNewRegexp("1"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelKeep,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.Keep,
},
{
Regex: MustNewRegexp("1"),
Regex: relabel.MustNewRegexp("1"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelLabelMap,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.LabelMap,
},
{
Regex: MustNewRegexp("d"),
Regex: relabel.MustNewRegexp("d"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelLabelDrop,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.LabelDrop,
},
{
Regex: MustNewRegexp("k"),
Regex: relabel.MustNewRegexp("k"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelLabelKeep,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.LabelKeep,
},
},
MetricRelabelConfigs: []*RelabelConfig{
MetricRelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"__name__"},
Regex: MustNewRegexp("expensive_metric.*"),
Regex: relabel.MustNewRegexp("expensive_metric.*"),
Separator: ";",
Replacement: DefaultRelabelConfig.Replacement,
Action: RelabelDrop,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Action: relabel.Drop,
},
},
},
@ -296,14 +298,14 @@ var expectedConf = &Config{
},
},
RelabelConfigs: []*RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"__meta_sd_consul_tags"},
Regex: MustNewRegexp("label:([^=]+)=([^,]+)"),
Regex: relabel.MustNewRegexp("label:([^=]+)=([^,]+)"),
Separator: ",",
TargetLabel: "${1}",
Replacement: "${2}",
Action: RelabelReplace,
Action: relabel.Replace,
},
},
},
@ -442,13 +444,14 @@ var expectedConf = &Config{
ServiceDiscoveryConfig: sd_config.ServiceDiscoveryConfig{
AzureSDConfigs: []*azure.SDConfig{
{
Environment: "AzurePublicCloud",
SubscriptionID: "11AAAA11-A11A-111A-A111-1111A1111A11",
TenantID: "BBBB222B-B2B2-2B22-B222-2BB2222BB2B2",
ClientID: "333333CC-3C33-3333-CCC3-33C3CCCCC33C",
ClientSecret: "mysecret",
RefreshInterval: model.Duration(5 * time.Minute),
Port: 9100,
Environment: "AzurePublicCloud",
SubscriptionID: "11AAAA11-A11A-111A-A111-1111A1111A11",
TenantID: "BBBB222B-B2B2-2B22-B222-2BB2222BB2B2",
ClientID: "333333CC-3C33-3333-CCC3-33C3CCCCC33C",
ClientSecret: "mysecret",
AuthenticationMethod: "OAuth",
RefreshInterval: model.Duration(5 * time.Minute),
Port: 9100,
},
},
},
@ -767,6 +770,10 @@ var expectedErrors = []struct {
filename: "azure_tenant_id_missing.bad.yml",
errMsg: "Azure SD configuration requires a tenant_id",
},
{
filename: "azure_authentication_method.bad.yml",
errMsg: "Unknown authentication_type \"invalid\". Supported types are \"OAuth\" or \"ManagedIdentity\"",
},
{
filename: "empty_scrape_config.bad.yml",
errMsg: "empty or null scrape config section",
@ -845,33 +852,6 @@ func TestEmptyGlobalBlock(t *testing.T) {
testutil.Equals(t, exp, *c)
}
func TestTargetLabelValidity(t *testing.T) {
tests := []struct {
str string
valid bool
}{
{"-label", false},
{"label", true},
{"label${1}", true},
{"${1}label", true},
{"${1}", true},
{"${1}label", true},
{"${", false},
{"$", false},
{"${}", false},
{"foo${", false},
{"$1", true},
{"asd$2asd", true},
{"-foo${1}bar-", false},
{"_${1}_", true},
{"foo${bar}foo", true},
}
for _, test := range tests {
testutil.Assert(t, relabelTarget.Match([]byte(test.str)) == test.valid,
"Expected %q to be %v", test.str, test.valid)
}
}
func kubernetesSDHostURL() config_util.URL {
tURL, _ := url.Parse("https://localhost:1234")
return config_util.URL{URL: tURL}

View File

@ -0,0 +1,4 @@
scrape_configs:
- azure_sd_configs:
- authentication_method: invalid
subscription_id: 11AAAA11-A11A-111A-A111-1111A1111A11

View File

@ -196,6 +196,7 @@ scrape_configs:
- job_name: service-azure
azure_sd_configs:
- environment: AzurePublicCloud
authentication_method: OAuth
subscription_id: 11AAAA11-A11A-111A-A111-1111A1111A11
tenant_id: BBBB222B-B2B2-2B22-B222-2BB2222BB2B2
client_id: 333333CC-3C33-3333-CCC3-33C3CCCCC33C

View File

@ -26,13 +26,11 @@ import (
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
config_util "github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/util/strutil"
)
@ -47,7 +45,9 @@ const (
azureLabelMachinePrivateIP = azureLabel + "machine_private_ip"
azureLabelMachineTag = azureLabel + "machine_tag_"
azureLabelMachineScaleSet = azureLabel + "machine_scale_set"
azureLabelPowerState = azureLabel + "machine_power_state"
authMethodOAuth = "OAuth"
authMethodManagedIdentity = "ManagedIdentity"
)
var (
@ -64,21 +64,23 @@ var (
// DefaultSDConfig is the default Azure SD configuration.
DefaultSDConfig = SDConfig{
Port: 80,
RefreshInterval: model.Duration(5 * time.Minute),
Environment: azure.PublicCloud.Name,
Port: 80,
RefreshInterval: model.Duration(5 * time.Minute),
Environment: azure.PublicCloud.Name,
AuthenticationMethod: authMethodOAuth,
}
)
// SDConfig is the configuration for Azure based service discovery.
type SDConfig struct {
Environment string `yaml:"environment,omitempty"`
Port int `yaml:"port"`
SubscriptionID string `yaml:"subscription_id"`
TenantID string `yaml:"tenant_id,omitempty"`
ClientID string `yaml:"client_id,omitempty"`
ClientSecret config_util.Secret `yaml:"client_secret,omitempty"`
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
Environment string `yaml:"environment,omitempty"`
Port int `yaml:"port"`
SubscriptionID string `yaml:"subscription_id"`
TenantID string `yaml:"tenant_id,omitempty"`
ClientID string `yaml:"client_id,omitempty"`
ClientSecret config_util.Secret `yaml:"client_secret,omitempty"`
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
AuthenticationMethod string `yaml:"authentication_method,omitempty"`
}
func validateAuthParam(param, name string) error {
@ -96,18 +98,27 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err != nil {
return err
}
if err = validateAuthParam(c.SubscriptionID, "subscription_id"); err != nil {
return err
}
if err = validateAuthParam(c.TenantID, "tenant_id"); err != nil {
return err
if c.AuthenticationMethod == authMethodOAuth {
if err = validateAuthParam(c.TenantID, "tenant_id"); err != nil {
return err
}
if err = validateAuthParam(c.ClientID, "client_id"); err != nil {
return err
}
if err = validateAuthParam(string(c.ClientSecret), "client_secret"); err != nil {
return err
}
}
if err = validateAuthParam(c.ClientID, "client_id"); err != nil {
return err
}
if err = validateAuthParam(string(c.ClientSecret), "client_secret"); err != nil {
return err
if c.AuthenticationMethod != authMethodOAuth && c.AuthenticationMethod != authMethodManagedIdentity {
return fmt.Errorf("Unknown authentication_type %q. Supported types are %q or %q", c.AuthenticationMethod, authMethodOAuth, authMethodManagedIdentity)
}
return nil
}
@ -187,13 +198,30 @@ func createAzureClient(cfg SDConfig) (azureClient, error) {
resourceManagerEndpoint := env.ResourceManagerEndpoint
var c azureClient
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, cfg.TenantID)
if err != nil {
return azureClient{}, err
}
spt, err := adal.NewServicePrincipalToken(*oauthConfig, cfg.ClientID, string(cfg.ClientSecret), resourceManagerEndpoint)
if err != nil {
return azureClient{}, err
var spt *adal.ServicePrincipalToken
switch cfg.AuthenticationMethod {
case authMethodManagedIdentity:
msiEndpoint, err := adal.GetMSIVMEndpoint()
if err != nil {
return azureClient{}, err
}
spt, err = adal.NewServicePrincipalTokenFromMSI(msiEndpoint, resourceManagerEndpoint)
if err != nil {
return azureClient{}, err
}
case authMethodOAuth:
oauthConfig, err := adal.NewOAuthConfig(activeDirectoryEndpoint, cfg.TenantID)
if err != nil {
return azureClient{}, err
}
spt, err = adal.NewServicePrincipalToken(*oauthConfig, cfg.ClientID, string(cfg.ClientSecret), resourceManagerEndpoint)
if err != nil {
return azureClient{}, err
}
}
bearerAuthorizer := autorest.NewBearerAuthorizer(spt)
@ -229,7 +257,6 @@ type virtualMachine struct {
ScaleSet string
Tags map[string]*string
NetworkProfile compute.NetworkProfile
PowerStateCode string
}
// Create a new azureResource object from an ID string.
@ -304,21 +331,12 @@ func (d *Discovery) refresh() (tg *targetgroup.Group, err error) {
return
}
// We check if the virtual machine has been deallocated.
// If so, we skip them in service discovery.
if strings.EqualFold(vm.PowerStateCode, "PowerState/deallocated") {
level.Debug(d.logger).Log("msg", "Skipping virtual machine", "machine", vm.Name, "power_state", vm.PowerStateCode)
ch <- target{}
return
}
labels := model.LabelSet{
azureLabelMachineID: model.LabelValue(vm.ID),
azureLabelMachineName: model.LabelValue(vm.Name),
azureLabelMachineOSType: model.LabelValue(vm.OsType),
azureLabelMachineLocation: model.LabelValue(vm.Location),
azureLabelMachineResourceGroup: model.LabelValue(r.ResourceGroup),
azureLabelPowerState: model.LabelValue(vm.PowerStateCode),
}
if vm.ScaleSet != "" {
@ -346,6 +364,16 @@ func (d *Discovery) refresh() (tg *targetgroup.Group, err error) {
continue
}
// Unfortunately Azure does not return information on whether a VM is deallocated.
// This information is available via another API call however the Go SDK does not
// yet support this. On deallocated machines, this value happens to be nil so it
// is a cheap and easy way to determine if a machine is allocated or not.
if networkInterface.Properties.Primary == nil {
level.Debug(d.logger).Log("msg", "Skipping deallocated virtual machine", "machine", vm.Name)
ch <- target{}
return
}
if *networkInterface.Properties.Primary {
for _, ip := range *networkInterface.Properties.IPConfigurations {
if ip.Properties.PrivateIPAddress != nil {
@ -473,7 +501,6 @@ func mapFromVM(vm compute.VirtualMachine) virtualMachine {
ScaleSet: "",
Tags: tags,
NetworkProfile: *(vm.Properties.NetworkProfile),
PowerStateCode: getPowerStateFromVMInstanceView(vm.Properties.InstanceView),
}
}
@ -494,7 +521,6 @@ func mapFromVMScaleSetVM(vm compute.VirtualMachineScaleSetVM, scaleSetName strin
ScaleSet: scaleSetName,
Tags: tags,
NetworkProfile: *(vm.Properties.NetworkProfile),
PowerStateCode: getPowerStateFromVMInstanceView(vm.Properties.InstanceView),
}
}
@ -527,16 +553,3 @@ func (client *azureClient) getNetworkInterfaceByID(networkInterfaceID string) (n
return result, nil
}
func getPowerStateFromVMInstanceView(instanceView *compute.VirtualMachineInstanceView) (powerState string) {
if instanceView.Statuses == nil {
return
}
for _, ivs := range *instanceView.Statuses {
code := *(ivs.Code)
if strings.HasPrefix(code, "PowerState") {
powerState = code
}
}
return
}

View File

@ -26,10 +26,6 @@ func TestMapFromVMWithEmptyTags(t *testing.T) {
vmType := "type"
location := "westeurope"
networkProfile := compute.NetworkProfile{}
provisioningStatusCode := "ProvisioningState/succeeded"
provisionDisplayStatus := "Provisioning succeeded"
powerStatusCode := "PowerState/running"
powerDisplayStatus := "VM running"
properties := &compute.VirtualMachineProperties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
@ -37,20 +33,6 @@ func TestMapFromVMWithEmptyTags(t *testing.T) {
},
},
NetworkProfile: &networkProfile,
InstanceView: &compute.VirtualMachineInstanceView{
Statuses: &[]compute.InstanceViewStatus{
{
Code: &provisioningStatusCode,
Level: "Info",
DisplayStatus: &provisionDisplayStatus,
},
{
Code: &powerStatusCode,
Level: "Info",
DisplayStatus: &powerDisplayStatus,
},
},
},
}
testVM := compute.VirtualMachine{
@ -70,7 +52,6 @@ func TestMapFromVMWithEmptyTags(t *testing.T) {
OsType: "Linux",
Tags: map[string]*string{},
NetworkProfile: networkProfile,
PowerStateCode: "PowerState/running",
}
actualVM := mapFromVM(testVM)
@ -88,10 +69,6 @@ func TestMapFromVMWithTags(t *testing.T) {
tags := map[string]*string{
"prometheus": new(string),
}
provisioningStatusCode := "ProvisioningState/succeeded"
provisionDisplayStatus := "Provisioning succeeded"
powerStatusCode := "PowerState/running"
powerDisplayStatus := "VM running"
networkProfile := compute.NetworkProfile{}
properties := &compute.VirtualMachineProperties{
StorageProfile: &compute.StorageProfile{
@ -100,20 +77,6 @@ func TestMapFromVMWithTags(t *testing.T) {
},
},
NetworkProfile: &networkProfile,
InstanceView: &compute.VirtualMachineInstanceView{
Statuses: &[]compute.InstanceViewStatus{
{
Code: &provisioningStatusCode,
Level: "Info",
DisplayStatus: &provisionDisplayStatus,
},
{
Code: &powerStatusCode,
Level: "Info",
DisplayStatus: &powerDisplayStatus,
},
},
},
}
testVM := compute.VirtualMachine{
@ -133,7 +96,6 @@ func TestMapFromVMWithTags(t *testing.T) {
OsType: "Linux",
Tags: tags,
NetworkProfile: networkProfile,
PowerStateCode: "PowerState/running",
}
actualVM := mapFromVM(testVM)
@ -149,10 +111,6 @@ func TestMapFromVMScaleSetVMWithEmptyTags(t *testing.T) {
vmType := "type"
location := "westeurope"
networkProfile := compute.NetworkProfile{}
provisioningStatusCode := "ProvisioningState/succeeded"
provisionDisplayStatus := "Provisioning succeeded"
powerStatusCode := "PowerState/running"
powerDisplayStatus := "VM running"
properties := &compute.VirtualMachineScaleSetVMProperties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
@ -160,20 +118,6 @@ func TestMapFromVMScaleSetVMWithEmptyTags(t *testing.T) {
},
},
NetworkProfile: &networkProfile,
InstanceView: &compute.VirtualMachineInstanceView{
Statuses: &[]compute.InstanceViewStatus{
{
Code: &provisioningStatusCode,
Level: "Info",
DisplayStatus: &provisionDisplayStatus,
},
{
Code: &powerStatusCode,
Level: "Info",
DisplayStatus: &powerDisplayStatus,
},
},
},
}
testVM := compute.VirtualMachineScaleSetVM{
@ -195,7 +139,6 @@ func TestMapFromVMScaleSetVMWithEmptyTags(t *testing.T) {
Tags: map[string]*string{},
NetworkProfile: networkProfile,
ScaleSet: scaleSet,
PowerStateCode: "PowerState/running",
}
actualVM := mapFromVMScaleSetVM(testVM, scaleSet)
@ -214,10 +157,6 @@ func TestMapFromVMScaleSetVMWithTags(t *testing.T) {
"prometheus": new(string),
}
networkProfile := compute.NetworkProfile{}
provisioningStatusCode := "ProvisioningState/succeeded"
provisionDisplayStatus := "Provisioning succeeded"
powerStatusCode := "PowerState/running"
powerDisplayStatus := "VM running"
properties := &compute.VirtualMachineScaleSetVMProperties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
@ -225,20 +164,6 @@ func TestMapFromVMScaleSetVMWithTags(t *testing.T) {
},
},
NetworkProfile: &networkProfile,
InstanceView: &compute.VirtualMachineInstanceView{
Statuses: &[]compute.InstanceViewStatus{
{
Code: &provisioningStatusCode,
Level: "Info",
DisplayStatus: &provisionDisplayStatus,
},
{
Code: &powerStatusCode,
Level: "Info",
DisplayStatus: &powerDisplayStatus,
},
},
},
}
testVM := compute.VirtualMachineScaleSetVM{
@ -260,7 +185,6 @@ func TestMapFromVMScaleSetVMWithTags(t *testing.T) {
Tags: tags,
NetworkProfile: networkProfile,
ScaleSet: scaleSet,
PowerStateCode: "PowerState/running",
}
actualVM := mapFromVMScaleSetVM(testVM, scaleSet)
@ -269,52 +193,3 @@ func TestMapFromVMScaleSetVMWithTags(t *testing.T) {
t.Errorf("Expected %v got %v", expectedVM, actualVM)
}
}
func TestGetPowerStatusFromVM(t *testing.T) {
provisioningStatusCode := "ProvisioningState/succeeded"
provisionDisplayStatus := "Provisioning succeeded"
powerStatusCode := "PowerState/running"
powerDisplayStatus := "VM running"
properties := &compute.VirtualMachineScaleSetVMProperties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
OsType: "Linux",
},
},
InstanceView: &compute.VirtualMachineInstanceView{
Statuses: &[]compute.InstanceViewStatus{
{
Code: &provisioningStatusCode,
Level: "Info",
DisplayStatus: &provisionDisplayStatus,
},
{
Code: &powerStatusCode,
Level: "Info",
DisplayStatus: &powerDisplayStatus,
},
},
},
}
testVM := compute.VirtualMachineScaleSetVM{
Properties: properties,
}
actual := getPowerStateFromVMInstanceView(testVM.Properties.InstanceView)
expected := "PowerState/running"
if actual != expected {
t.Errorf("expected powerStatus %s, but got %s instead", expected, actual)
}
// Noq we test a virtualMachine with an empty InstanceView struct.
testVM.Properties.InstanceView = &compute.VirtualMachineInstanceView{}
actual = getPowerStateFromVMInstanceView(testVM.Properties.InstanceView)
if actual != "" {
t.Errorf("expected powerStatus %s, but got %s instead", expected, actual)
}
}

View File

@ -55,6 +55,8 @@ const (
servicePortLabel = model.MetaLabelPrefix + "consul_service_port"
// datacenterLabel is the name of the label containing the datacenter ID.
datacenterLabel = model.MetaLabelPrefix + "consul_dc"
// taggedAddressesLabel is the prefix for the labels mapping to a target's tagged addresses.
taggedAddressesLabel = model.MetaLabelPrefix + "consul_tagged_address_"
// serviceIDLabel is the name of the label containing the service ID.
serviceIDLabel = model.MetaLabelPrefix + "consul_service_id"
@ -487,7 +489,7 @@ func (srv *consulService) watch(ctx context.Context, ch chan<- []*targetgroup.Gr
var tags = srv.tagSeparator + strings.Join(node.ServiceTags, srv.tagSeparator) + srv.tagSeparator
// If the service address is not empty it should be used instead of the node address
// since the service may be registered remotely through a different node
// since the service may be registered remotely through a different node.
var addr string
if node.ServiceAddress != "" {
addr = net.JoinHostPort(node.ServiceAddress, fmt.Sprintf("%d", node.ServicePort))
@ -505,18 +507,24 @@ func (srv *consulService) watch(ctx context.Context, ch chan<- []*targetgroup.Gr
serviceIDLabel: model.LabelValue(node.ServiceID),
}
// Add all key/value pairs from the node's metadata as their own labels
// Add all key/value pairs from the node's metadata as their own labels.
for k, v := range node.NodeMeta {
name := strutil.SanitizeLabelName(k)
labels[metaDataLabel+model.LabelName(name)] = model.LabelValue(v)
}
// Add all key/value pairs from the service's metadata as their own labels
// Add all key/value pairs from the service's metadata as their own labels.
for k, v := range node.ServiceMeta {
name := strutil.SanitizeLabelName(k)
labels[serviceMetaDataLabel+model.LabelName(name)] = model.LabelValue(v)
}
// Add all key/value pairs from the service's tagged addresses as their own labels.
for k, v := range node.TaggedAddresses {
name := strutil.SanitizeLabelName(k)
labels[taggedAddressesLabel+model.LabelName(name)] = model.LabelValue(v)
}
tgroup.Targets = append(tgroup.Targets, labels)
}

View File

@ -87,6 +87,7 @@ const (
"Node": "node1",
"Address": "1.1.1.1",
"Datacenter": "test-dc",
"TaggedAddresses": {"lan":"192.168.10.10","wan":"10.0.10.10"},
"NodeMeta": {"rack_name": "2304"},
"ServiceID": "test",
"ServiceName": "test",

View File

@ -25,7 +25,6 @@ import (
"github.com/go-kit/kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
compute "google.golang.org/api/compute/v1"
@ -140,7 +139,7 @@ func NewDiscovery(conf SDConfig, logger log.Logger) (*Discovery, error) {
logger: logger,
}
var err error
gd.client, err = google.DefaultClient(oauth2.NoContext, compute.ComputeReadonlyScope)
gd.client, err = google.DefaultClient(context.Background(), compute.ComputeReadonlyScope)
if err != nil {
return nil, fmt.Errorf("error setting up communication with GCE service: %s", err)
}

View File

@ -156,7 +156,7 @@ Loop:
case <-time.After(timeout):
// Because we use queue, an object that is created then
// deleted or updated may be processed only once.
// So possibliy we may skip events, timed out here.
// So possibly we may skip events, timed out here.
t.Logf("timed out, got %d (max: %d) items, some events are skipped", len(allTgs), max)
break Loop
}

View File

@ -140,6 +140,8 @@ const (
serviceAnnotationPrefix = metaLabelPrefix + "service_annotation_"
servicePortNameLabel = metaLabelPrefix + "service_port_name"
servicePortProtocolLabel = metaLabelPrefix + "service_port_protocol"
serviceClusterIPLabel = metaLabelPrefix + "service_cluster_ip"
serviceExternalNameLabel = metaLabelPrefix + "service_external_name"
)
func serviceLabels(svc *apiv1.Service) model.LabelSet {
@ -169,11 +171,19 @@ func (s *Service) buildService(svc *apiv1.Service) *targetgroup.Group {
for _, port := range svc.Spec.Ports {
addr := net.JoinHostPort(svc.Name+"."+svc.Namespace+".svc", strconv.FormatInt(int64(port.Port), 10))
tg.Targets = append(tg.Targets, model.LabelSet{
labelSet := model.LabelSet{
model.AddressLabel: lv(addr),
servicePortNameLabel: lv(port.Name),
servicePortProtocolLabel: lv(string(port.Protocol)),
})
}
if svc.Spec.Type == apiv1.ServiceTypeExternalName {
labelSet[serviceExternalNameLabel] = lv(svc.Spec.ExternalName)
} else {
labelSet[serviceClusterIPLabel] = lv(svc.Spec.ClusterIP)
}
tg.Targets = append(tg.Targets, labelSet)
}
return tg

View File

@ -44,6 +44,8 @@ func makeMultiPortService() *v1.Service {
Port: int32(30901),
},
},
Type: v1.ServiceTypeClusterIP,
ClusterIP: "10.0.0.1",
},
}
}
@ -62,6 +64,8 @@ func makeSuffixedService(suffix string) *v1.Service {
Port: int32(30900),
},
},
Type: v1.ServiceTypeClusterIP,
ClusterIP: "10.0.0.1",
},
}
}
@ -70,6 +74,26 @@ func makeService() *v1.Service {
return makeSuffixedService("")
}
func makeExternalService() *v1.Service {
return &v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "testservice-external",
Namespace: "default",
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
Port: int32(31900),
},
},
Type: v1.ServiceTypeExternalName,
ExternalName: "FooExternalName",
},
}
}
func TestServiceDiscoveryAdd(t *testing.T) {
n, c, w := makeDiscovery(RoleService, NamespaceDiscovery{})
@ -79,15 +103,19 @@ func TestServiceDiscoveryAdd(t *testing.T) {
obj := makeService()
c.CoreV1().Services(obj.Namespace).Create(obj)
w.Services().Add(obj)
obj = makeExternalService()
c.CoreV1().Services(obj.Namespace).Create(obj)
w.Services().Add(obj)
},
expectedMaxItems: 1,
expectedMaxItems: 2,
expectedRes: map[string]*targetgroup.Group{
"svc/default/testservice": {
Targets: []model.LabelSet{
{
"__meta_kubernetes_service_port_protocol": "TCP",
"__address__": "testservice.default.svc:30900",
"__meta_kubernetes_service_port_name": "testport",
"__address__": "testservice.default.svc:30900",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
},
},
Labels: model.LabelSet{
@ -96,6 +124,21 @@ func TestServiceDiscoveryAdd(t *testing.T) {
},
Source: "svc/default/testservice",
},
"svc/default/testservice-external": {
Targets: []model.LabelSet{
{
"__meta_kubernetes_service_port_protocol": "TCP",
"__address__": "testservice-external.default.svc:31900",
"__meta_kubernetes_service_port_name": "testport",
"__meta_kubernetes_service_external_name": "FooExternalName",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_service_name": "testservice-external",
"__meta_kubernetes_namespace": "default",
},
Source: "svc/default/testservice-external",
},
},
}.Run(t)
}
@ -135,13 +178,15 @@ func TestServiceDiscoveryUpdate(t *testing.T) {
Targets: []model.LabelSet{
{
"__meta_kubernetes_service_port_protocol": "TCP",
"__address__": "testservice.default.svc:30900",
"__meta_kubernetes_service_port_name": "testport0",
"__address__": "testservice.default.svc:30900",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport0",
},
{
"__meta_kubernetes_service_port_protocol": "UDP",
"__address__": "testservice.default.svc:30901",
"__meta_kubernetes_service_port_name": "testport1",
"__address__": "testservice.default.svc:30901",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport1",
},
},
Labels: model.LabelSet{
@ -175,8 +220,9 @@ func TestServiceDiscoveryNamespaces(t *testing.T) {
Targets: []model.LabelSet{
{
"__meta_kubernetes_service_port_protocol": "TCP",
"__address__": "testservice.ns1.svc:30900",
"__meta_kubernetes_service_port_name": "testport",
"__address__": "testservice.ns1.svc:30900",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
},
},
Labels: model.LabelSet{
@ -189,8 +235,9 @@ func TestServiceDiscoveryNamespaces(t *testing.T) {
Targets: []model.LabelSet{
{
"__meta_kubernetes_service_port_protocol": "TCP",
"__address__": "testservice.ns2.svc:30900",
"__meta_kubernetes_service_port_name": "testport",
"__address__": "testservice.ns2.svc:30900",
"__meta_kubernetes_service_cluster_ip": "10.0.0.1",
"__meta_kubernetes_service_port_name": "testport",
},
},
Labels: model.LabelSet{

View File

@ -41,11 +41,12 @@ import (
)
var (
failedConfigs = prometheus.NewCounter(
failedConfigs = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "prometheus_sd_configs_failed_total",
Help: "Total number of service discovery configurations that failed to load.",
},
[]string{"name"},
)
discoveredTargets = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
@ -54,23 +55,26 @@ var (
},
[]string{"name", "config"},
)
receivedUpdates = prometheus.NewCounter(
receivedUpdates = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "prometheus_sd_received_updates_total",
Help: "Total number of update events received from the SD providers.",
},
[]string{"name"},
)
delayedUpdates = prometheus.NewCounter(
delayedUpdates = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "prometheus_sd_updates_delayed_total",
Help: "Total number of update events that couldn't be sent immediately.",
},
[]string{"name"},
)
sentUpdates = prometheus.NewCounter(
sentUpdates = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "prometheus_sd_updates_total",
Help: "Total number of update events sent to the SD consumers.",
},
[]string{"name"},
)
)
@ -226,7 +230,7 @@ func (m *Manager) updater(ctx context.Context, p *provider, updates chan []*targ
case <-ctx.Done():
return
case tgs, ok := <-updates:
receivedUpdates.Inc()
receivedUpdates.WithLabelValues(m.name).Inc()
if !ok {
level.Debug(m.logger).Log("msg", "discoverer channel closed", "provider", p.name)
return
@ -255,11 +259,11 @@ func (m *Manager) sender() {
case <-ticker.C: // Some discoverers send updates too often so we throttle these with the ticker.
select {
case <-m.triggerSend:
sentUpdates.Inc()
sentUpdates.WithLabelValues(m.name).Inc()
select {
case m.syncCh <- m.allGroups():
default:
delayedUpdates.Inc()
delayedUpdates.WithLabelValues(m.name).Inc()
level.Debug(m.logger).Log("msg", "discovery receiver's channel was full so will retry the next cycle")
select {
case m.triggerSend <- struct{}{}:
@ -328,7 +332,7 @@ func (m *Manager) registerProviders(cfg sd_config.ServiceDiscoveryConfig, setNam
d, err := newDiscoverer()
if err != nil {
level.Error(m.logger).Log("msg", "Cannot create service discovery", "err", err, "type", t)
failedConfigs.Inc()
failedConfigs.WithLabelValues(m.name).Inc()
return
}

View File

@ -263,11 +263,10 @@ The following meta labels are available on targets during relabeling:
* `__meta_azure_machine_location`: the location the machine runs in
* `__meta_azure_machine_name`: the machine name
* `__meta_azure_machine_os_type`: the machine operating system
* `__meta_azure_machine_power_state`: the current power state of the machine
* `__meta_azure_machine_private_ip`: the machine's private IP
* `__meta_azure_machine_resource_group`: the machine's resource group
* `__meta_azure_machine_scale_set`: the name of the scale set which the vm is part of (this value is only set if you are using a [scale set](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/))
* `__meta_azure_machine_tag_<tagname>`: each tag value of the machine
* `__meta_azure_machine_scale_set`: the name of the scale set which the vm is part of (this value is only set if you are using a [scale set](https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/))
See below for the configuration options for Azure discovery:
@ -275,14 +274,18 @@ See below for the configuration options for Azure discovery:
# The information to access the Azure API.
# The Azure environment.
[ environment: <string> | default = AzurePublicCloud ]
# The subscription ID.
# The authentication method, either OAuth or ManagedIdentity.
# See https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview
[ authentication_method: <string> | default = OAuth]
# The subscription ID. Always required.
subscription_id: <string>
# The tenant ID.
tenant_id: <string>
# The client ID.
client_id: <string>
# The client secret.
client_secret: <secret>
# Optional tenant ID. Only required with authentication_method OAuth.
[ tenant_id: <string> ]
# Optional client ID. Only required with authentication_method OAuth.
[ client_id: <string> ]
# Optional client secret. Only required with authentication_method OAuth.
[ client_secret: <secret> ]
# Refresh interval to re-read the instance list.
[ refresh_interval: <duration> | default = 300s ]
@ -301,6 +304,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
* `__meta_consul_address`: the address of the target
* `__meta_consul_dc`: the datacenter name for the target
* `__meta_consul_tagged_address_<key>`: each node tagged address key value of the target
* `__meta_consul_metadata_<key>`: each node metadata key value of the target
* `__meta_consul_node`: the node name defined for the target
* `__meta_consul_service_address`: the service address of the target
@ -694,9 +698,11 @@ service port.
Available meta labels:
* `__meta_kubernetes_namespace`: The namespace of the service object.
* `__meta_kubernetes_service_name`: The name of the service object.
* `__meta_kubernetes_service_label_<labelname>`: The label of the service object.
* `__meta_kubernetes_service_annotation_<annotationname>`: The annotation of the service object.
* `__meta_kubernetes_service_cluster_ip`: The cluster IP address of the service. (Does not apply to services of type ExternalName)
* `__meta_kubernetes_service_external_name`: The DNS name of the service. (Applies to services of type ExternalName)
* `__meta_kubernetes_service_label_<labelname>`: The label of the service object.
* `__meta_kubernetes_service_name`: The name of the service object.
* `__meta_kubernetes_service_port_name`: Name of the service port for the target.
* `__meta_kubernetes_service_port_number`: Number of the service port for the target.
* `__meta_kubernetes_service_port_protocol`: Protocol of the service port for the target.

View File

@ -6,10 +6,11 @@ sort_rank: 1
# Querying Prometheus
Prometheus provides a functional expression language that lets the user select
and aggregate time series data in real time. The result of an expression can
either be shown as a graph, viewed as tabular data in Prometheus's expression
browser, or consumed by external systems via the [HTTP API](api.md).
Prometheus provides a functional query language called PromQL (Prometheus Query
Language) that lets the user select and aggregate time series data in real
time. The result of an expression can either be shown as a graph, viewed as
tabular data in Prometheus's expression browser, or consumed by external
systems via the [HTTP API](api.md).
## Examples

View File

@ -13,7 +13,7 @@ yet found the exporter and is not scraping data from it.
For more details on how to use Kubernetes service discovery take a look at the
[documentation](http://prometheus.io/docs/operating/configuration/#kubernetes-sd-configurations-kubernetes_sd_config)
and at the [available examples](./documentation/examples).
and at the [available examples](./../).
After you got Kubernetes service discovery up and running you just need to advertise that RabbitMQ
is exposing metrics. To do that you need to define a service that:

2
go.mod
View File

@ -88,7 +88,7 @@ require (
github.com/prometheus/client_golang v0.9.1
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910
github.com/prometheus/common v0.0.0-20181119215939-b36ad289a3ea
github.com/prometheus/tsdb v0.3.0
github.com/prometheus/tsdb v0.3.1
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a // indirect
github.com/rlmcpherson/s3gof3r v0.5.0 // indirect
github.com/rubyist/circuitbreaker v2.2.1+incompatible // indirect

8
go.sum
View File

@ -36,7 +36,7 @@ github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJW
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/etcd v3.3.10+incompatible h1:KjVWqrZ5U0wa3CxY2AxlH6/UcB+PK2td1DcsYhA+HRs=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -107,7 +107,7 @@ github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c h1:BTAbnbegUI
github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:9HVkPxOpo+yO93Ah4yrO67d/qh0fbLLWbKqhYjyHq9A=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90 h1:VBj0QYQ0u2MCJzBfeYXGexnAl17GsH1yidnoxCqqD9E=
github.com/hashicorp/go-rootcerts v0.0.0-20160503143440-6bb64b370b90/go.mod h1:o4zcYY1e0GEZI6eSEr+43QDYmuGglw1qSO6qdHUHCgg=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 h1:7YOlAIO2YWnJZkQp7B5eFykaIY7C9JndqAFQyVV5BhM=
github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@ -209,8 +209,8 @@ github.com/prometheus/common v0.0.0-20181119215939-b36ad289a3ea h1:4RkbEb5XX0Wvu
github.com/prometheus/common v0.0.0-20181119215939-b36ad289a3ea/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/tsdb v0.3.0 h1:NQIaA1zfXQWPOWkpfaVBwURsm7nViKLtI3uwYpe8LKs=
github.com/prometheus/tsdb v0.3.0/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/prometheus/tsdb v0.3.1 h1:uGgfubT2MesNpx3T46c5R32RcUoKAPGyWX+4x1orJLE=
github.com/prometheus/tsdb v0.3.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rlmcpherson/s3gof3r v0.5.0 h1:1izOJpTiohSibfOHuNyEA/yQnAirh05enzEdmhez43k=

View File

@ -123,7 +123,7 @@ type Manager struct {
type Options struct {
QueueCapacity int
ExternalLabels model.LabelSet
RelabelConfigs []*config.RelabelConfig
RelabelConfigs []*relabel.Config
// Used for sending HTTP requests to the Alertmanager.
Do func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error)

View File

@ -25,6 +25,8 @@ import (
"testing"
"time"
"github.com/prometheus/prometheus/pkg/relabel"
yaml "gopkg.in/yaml.v2"
config_util "github.com/prometheus/common/config"
@ -236,12 +238,12 @@ func TestExternalLabels(t *testing.T) {
h := NewManager(&Options{
QueueCapacity: 3 * maxBatchSize,
ExternalLabels: model.LabelSet{"a": "b"},
RelabelConfigs: []*config.RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"alertname"},
TargetLabel: "a",
Action: "replace",
Regex: config.MustNewRegexp("externalrelabelthis"),
Regex: relabel.MustNewRegexp("externalrelabelthis"),
Replacement: "c",
},
},
@ -269,17 +271,17 @@ func TestExternalLabels(t *testing.T) {
func TestHandlerRelabel(t *testing.T) {
h := NewManager(&Options{
QueueCapacity: 3 * maxBatchSize,
RelabelConfigs: []*config.RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
SourceLabels: model.LabelNames{"alertname"},
Action: "drop",
Regex: config.MustNewRegexp("drop"),
Regex: relabel.MustNewRegexp("drop"),
},
{
SourceLabels: model.LabelNames{"alertname"},
TargetLabel: "alertname",
Action: "replace",
Regex: config.MustNewRegexp("rename"),
Regex: relabel.MustNewRegexp("rename"),
Replacement: "renamed",
},
},

View File

@ -16,19 +16,170 @@ package relabel
import (
"crypto/md5"
"fmt"
"regexp"
"strings"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/pkg/labels"
)
var (
relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`)
DefaultRelabelConfig = Config{
Action: Replace,
Separator: ";",
Regex: MustNewRegexp("(.*)"),
Replacement: "$1",
}
)
// Action is the action to be performed on relabeling.
type Action string
const (
// Replace performs a regex replacement.
Replace Action = "replace"
// Keep drops targets for which the input does not match the regex.
Keep Action = "keep"
// Drop drops targets for which the input does match the regex.
Drop Action = "drop"
// HashMod sets a label to the modulus of a hash of labels.
HashMod Action = "hashmod"
// LabelMap copies labels to other labelnames based on a regex.
LabelMap Action = "labelmap"
// LabelDrop drops any label matching the regex.
LabelDrop Action = "labeldrop"
// LabelKeep drops any label not matching the regex.
LabelKeep Action = "labelkeep"
)
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (a *Action) UnmarshalYAML(unmarshal func(interface{}) error) error {
var s string
if err := unmarshal(&s); err != nil {
return err
}
switch act := Action(strings.ToLower(s)); act {
case Replace, Keep, Drop, HashMod, LabelMap, LabelDrop, LabelKeep:
*a = act
return nil
}
return fmt.Errorf("unknown relabel action %q", s)
}
// Config is the configuration for relabeling of target label sets.
type Config 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 Action `yaml:"action,omitempty"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
*c = DefaultRelabelConfig
type plain Config
if err := unmarshal((*plain)(c)); err != nil {
return err
}
if c.Regex.Regexp == nil {
c.Regex = MustNewRegexp("")
}
if c.Modulus == 0 && c.Action == HashMod {
return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus")
}
if (c.Action == Replace || c.Action == HashMod) && c.TargetLabel == "" {
return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action)
}
if c.Action == Replace && !relabelTarget.MatchString(c.TargetLabel) {
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
}
if c.Action == LabelMap && !relabelTarget.MatchString(c.Replacement) {
return fmt.Errorf("%q is invalid 'replacement' for %s action", c.Replacement, c.Action)
}
if c.Action == HashMod && !model.LabelName(c.TargetLabel).IsValid() {
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
}
if c.Action == LabelDrop || c.Action == LabelKeep {
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 marshalable.
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
}
// Process returns a relabeled copy of the given label set. The relabel configurations
// are applied in order of input.
// If a label set is dropped, nil is returned.
// May return the input labelSet modified.
func Process(labels labels.Labels, cfgs ...*config.RelabelConfig) labels.Labels {
func Process(labels labels.Labels, cfgs ...*Config) labels.Labels {
for _, cfg := range cfgs {
labels = relabel(labels, cfg)
if labels == nil {
@ -38,7 +189,7 @@ func Process(labels labels.Labels, cfgs ...*config.RelabelConfig) labels.Labels
return labels
}
func relabel(lset labels.Labels, cfg *config.RelabelConfig) labels.Labels {
func relabel(lset labels.Labels, cfg *Config) labels.Labels {
values := make([]string, 0, len(cfg.SourceLabels))
for _, ln := range cfg.SourceLabels {
values = append(values, lset.Get(string(ln)))
@ -48,15 +199,15 @@ func relabel(lset labels.Labels, cfg *config.RelabelConfig) labels.Labels {
lb := labels.NewBuilder(lset)
switch cfg.Action {
case config.RelabelDrop:
case Drop:
if cfg.Regex.MatchString(val) {
return nil
}
case config.RelabelKeep:
case Keep:
if !cfg.Regex.MatchString(val) {
return nil
}
case config.RelabelReplace:
case Replace:
indexes := cfg.Regex.FindStringSubmatchIndex(val)
// If there is no match no replacement must take place.
if indexes == nil {
@ -73,23 +224,23 @@ func relabel(lset labels.Labels, cfg *config.RelabelConfig) labels.Labels {
break
}
lb.Set(string(target), string(res))
case config.RelabelHashMod:
case HashMod:
mod := sum64(md5.Sum([]byte(val))) % cfg.Modulus
lb.Set(cfg.TargetLabel, fmt.Sprintf("%d", mod))
case config.RelabelLabelMap:
case LabelMap:
for _, l := range lset {
if cfg.Regex.MatchString(l.Name) {
res := cfg.Regex.ReplaceAllString(l.Name, cfg.Replacement)
lb.Set(res, l.Value)
}
}
case config.RelabelLabelDrop:
case LabelDrop:
for _, l := range lset {
if cfg.Regex.MatchString(l.Name) {
lb.Del(l.Name)
}
}
case config.RelabelLabelKeep:
case LabelKeep:
for _, l := range lset {
if !cfg.Regex.MatchString(l.Name) {
lb.Del(l.Name)

View File

@ -18,7 +18,6 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/pkg/labels"
"github.com/prometheus/prometheus/util/testutil"
)
@ -26,7 +25,7 @@ import (
func TestRelabel(t *testing.T) {
tests := []struct {
input labels.Labels
relabel []*config.RelabelConfig
relabel []*Config
output labels.Labels
}{
{
@ -35,14 +34,14 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f(.*)"),
Regex: MustNewRegexp("f(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "ch${1}-ch${1}",
Action: config.RelabelReplace,
Action: Replace,
},
},
output: labels.FromMap(map[string]string{
@ -58,22 +57,22 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a", "b"},
Regex: config.MustNewRegexp("f(.*);(.*)r"),
Regex: MustNewRegexp("f(.*);(.*)r"),
TargetLabel: "a",
Separator: ";",
Replacement: "b${1}${2}m", // boobam
Action: config.RelabelReplace,
Action: Replace,
},
{
SourceLabels: model.LabelNames{"c", "a"},
Regex: config.MustNewRegexp("(b).*b(.*)ba(.*)"),
Regex: MustNewRegexp("(b).*b(.*)ba(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "$1$2$2$3",
Action: config.RelabelReplace,
Action: Replace,
},
},
output: labels.FromMap(map[string]string{
@ -87,18 +86,18 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "foo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*o.*"),
Action: config.RelabelDrop,
Regex: MustNewRegexp(".*o.*"),
Action: Drop,
}, {
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f(.*)"),
Regex: MustNewRegexp("f(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "ch$1-ch$1",
Action: config.RelabelReplace,
Action: Replace,
},
},
output: nil,
@ -108,11 +107,11 @@ func TestRelabel(t *testing.T) {
"a": "foo",
"b": "bar",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*o.*"),
Action: config.RelabelDrop,
Regex: MustNewRegexp(".*o.*"),
Action: Drop,
},
},
output: nil,
@ -121,14 +120,14 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "abc",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*(b).*"),
Regex: MustNewRegexp(".*(b).*"),
TargetLabel: "d",
Separator: ";",
Replacement: "$1",
Action: config.RelabelReplace,
Action: Replace,
},
},
output: labels.FromMap(map[string]string{
@ -140,11 +139,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "foo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("no-match"),
Action: config.RelabelDrop,
Regex: MustNewRegexp("no-match"),
Action: Drop,
},
},
output: labels.FromMap(map[string]string{
@ -155,11 +154,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "foo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f|o"),
Action: config.RelabelDrop,
Regex: MustNewRegexp("f|o"),
Action: Drop,
},
},
output: labels.FromMap(map[string]string{
@ -170,11 +169,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "foo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("no-match"),
Action: config.RelabelKeep,
Regex: MustNewRegexp("no-match"),
Action: Keep,
},
},
output: nil,
@ -183,11 +182,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "foo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f.*"),
Action: config.RelabelKeep,
Regex: MustNewRegexp("f.*"),
Action: Keep,
},
},
output: labels.FromMap(map[string]string{
@ -199,13 +198,13 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "boo",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f"),
Regex: MustNewRegexp("f"),
TargetLabel: "b",
Replacement: "bar",
Action: config.RelabelReplace,
Action: Replace,
},
},
output: labels.FromMap(map[string]string{
@ -218,12 +217,12 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"c"},
TargetLabel: "d",
Separator: ";",
Action: config.RelabelHashMod,
Action: HashMod,
Modulus: 1000,
},
},
@ -240,11 +239,11 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Regex: MustNewRegexp("(b.*)"),
Replacement: "bar_${1}",
Action: config.RelabelLabelMap,
Action: LabelMap,
},
},
output: labels.FromMap(map[string]string{
@ -262,11 +261,11 @@ func TestRelabel(t *testing.T) {
"__meta_my_baz": "bbb",
"__meta_other": "ccc",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
Regex: config.MustNewRegexp("__meta_(my.*)"),
Regex: MustNewRegexp("__meta_(my.*)"),
Replacement: "${1}",
Action: config.RelabelLabelMap,
Action: LabelMap,
},
},
output: labels.FromMap(map[string]string{
@ -282,11 +281,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "some-name-value",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: Replace,
Replacement: "${2}",
TargetLabel: "${1}",
},
@ -300,11 +299,11 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "some-name-value",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: Replace,
Replacement: "${3}",
TargetLabel: "${1}",
},
@ -317,25 +316,25 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"a": "some-name-value",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: Replace,
Replacement: "${1}",
TargetLabel: "${3}",
},
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: Replace,
Replacement: "${1}",
TargetLabel: "0${3}",
},
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: Replace,
Replacement: "${1}",
TargetLabel: "-${3}",
},
@ -348,25 +347,25 @@ func TestRelabel(t *testing.T) {
input: labels.FromMap(map[string]string{
"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
Action: Replace,
Replacement: "${1}",
TargetLabel: "__metrics_path__",
},
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
Action: Replace,
Replacement: "${1}",
TargetLabel: "job",
},
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
Action: config.RelabelReplace,
Regex: MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
Action: Replace,
Replacement: "${2}",
TargetLabel: "${1}",
},
@ -384,10 +383,10 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Action: config.RelabelLabelKeep,
Regex: MustNewRegexp("(b.*)"),
Action: LabelKeep,
},
},
output: labels.FromMap(map[string]string{
@ -401,10 +400,10 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
}),
relabel: []*config.RelabelConfig{
relabel: []*Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Action: config.RelabelLabelDrop,
Regex: MustNewRegexp("(b.*)"),
Action: LabelDrop,
},
},
output: labels.FromMap(map[string]string{
@ -418,3 +417,30 @@ func TestRelabel(t *testing.T) {
testutil.Equals(t, test.output, res)
}
}
func TestTargetLabelValidity(t *testing.T) {
tests := []struct {
str string
valid bool
}{
{"-label", false},
{"label", true},
{"label${1}", true},
{"${1}label", true},
{"${1}", true},
{"${1}label", true},
{"${", false},
{"$", false},
{"${}", false},
{"foo${", false},
{"$1", true},
{"asd$2asd", true},
{"-foo${1}bar-", false},
{"_${1}_", true},
{"foo${bar}foo", true},
}
for _, test := range tests {
testutil.Assert(t, relabelTarget.Match([]byte(test.str)) == test.valid,
"Expected %q to be %v", test.str, test.valid)
}
}

View File

@ -24,7 +24,7 @@ import (
"github.com/prometheus/prometheus/pkg/timestamp"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/template"
yaml "gopkg.in/yaml.v2"
"gopkg.in/yaml.v2"
)
// Error represents semantical errors on parsing rule groups.

View File

@ -20,14 +20,16 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
)
// Process returns a relabeled copy of the given label set. The relabel configurations
// are applied in order of input.
// If a label set is dropped, nil is returned.
// May return the input labelSet modified.
func Process(labels model.LabelSet, cfgs ...*config.RelabelConfig) model.LabelSet {
// TODO(https://github.com/prometheus/prometheus/issues/3647): Get rid of this package in favor of pkg/relabel
// once usage of `model.LabelSet` is removed.
func Process(labels model.LabelSet, cfgs ...*pkgrelabel.Config) model.LabelSet {
for _, cfg := range cfgs {
labels = relabel(labels, cfg)
if labels == nil {
@ -37,7 +39,7 @@ func Process(labels model.LabelSet, cfgs ...*config.RelabelConfig) model.LabelSe
return labels
}
func relabel(labels model.LabelSet, cfg *config.RelabelConfig) model.LabelSet {
func relabel(labels model.LabelSet, cfg *pkgrelabel.Config) model.LabelSet {
values := make([]string, 0, len(cfg.SourceLabels))
for _, ln := range cfg.SourceLabels {
values = append(values, string(labels[ln]))
@ -45,15 +47,15 @@ func relabel(labels model.LabelSet, cfg *config.RelabelConfig) model.LabelSet {
val := strings.Join(values, cfg.Separator)
switch cfg.Action {
case config.RelabelDrop:
case pkgrelabel.Drop:
if cfg.Regex.MatchString(val) {
return nil
}
case config.RelabelKeep:
case pkgrelabel.Keep:
if !cfg.Regex.MatchString(val) {
return nil
}
case config.RelabelReplace:
case pkgrelabel.Replace:
indexes := cfg.Regex.FindStringSubmatchIndex(val)
// If there is no match no replacement must take place.
if indexes == nil {
@ -70,10 +72,10 @@ func relabel(labels model.LabelSet, cfg *config.RelabelConfig) model.LabelSet {
break
}
labels[target] = model.LabelValue(res)
case config.RelabelHashMod:
case pkgrelabel.HashMod:
mod := sum64(md5.Sum([]byte(val))) % cfg.Modulus
labels[model.LabelName(cfg.TargetLabel)] = model.LabelValue(fmt.Sprintf("%d", mod))
case config.RelabelLabelMap:
case pkgrelabel.LabelMap:
out := make(model.LabelSet, len(labels))
// Take a copy to avoid infinite loops.
for ln, lv := range labels {
@ -86,13 +88,13 @@ func relabel(labels model.LabelSet, cfg *config.RelabelConfig) model.LabelSet {
}
}
labels = out
case config.RelabelLabelDrop:
case pkgrelabel.LabelDrop:
for ln := range labels {
if cfg.Regex.MatchString(string(ln)) {
delete(labels, ln)
}
}
case config.RelabelLabelKeep:
case pkgrelabel.LabelKeep:
for ln := range labels {
if !cfg.Regex.MatchString(string(ln)) {
delete(labels, ln)

View File

@ -18,14 +18,14 @@ import (
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
"github.com/prometheus/prometheus/util/testutil"
)
func TestRelabel(t *testing.T) {
tests := []struct {
input model.LabelSet
relabel []*config.RelabelConfig
relabel []*pkgrelabel.Config
output model.LabelSet
}{
{
@ -34,14 +34,14 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f(.*)"),
Regex: pkgrelabel.MustNewRegexp("f(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "ch${1}-ch${1}",
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
},
output: model.LabelSet{
@ -57,22 +57,22 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a", "b"},
Regex: config.MustNewRegexp("f(.*);(.*)r"),
Regex: pkgrelabel.MustNewRegexp("f(.*);(.*)r"),
TargetLabel: "a",
Separator: ";",
Replacement: "b${1}${2}m", // boobam
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
{
SourceLabels: model.LabelNames{"c", "a"},
Regex: config.MustNewRegexp("(b).*b(.*)ba(.*)"),
Regex: pkgrelabel.MustNewRegexp("(b).*b(.*)ba(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "$1$2$2$3",
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
},
output: model.LabelSet{
@ -86,18 +86,18 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "foo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*o.*"),
Action: config.RelabelDrop,
Regex: pkgrelabel.MustNewRegexp(".*o.*"),
Action: pkgrelabel.Drop,
}, {
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f(.*)"),
Regex: pkgrelabel.MustNewRegexp("f(.*)"),
TargetLabel: "d",
Separator: ";",
Replacement: "ch$1-ch$1",
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
},
output: nil,
@ -107,11 +107,11 @@ func TestRelabel(t *testing.T) {
"a": "foo",
"b": "bar",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*o.*"),
Action: config.RelabelDrop,
Regex: pkgrelabel.MustNewRegexp(".*o.*"),
Action: pkgrelabel.Drop,
},
},
output: nil,
@ -120,14 +120,14 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "abc",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp(".*(b).*"),
Regex: pkgrelabel.MustNewRegexp(".*(b).*"),
TargetLabel: "d",
Separator: ";",
Replacement: "$1",
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
},
output: model.LabelSet{
@ -139,11 +139,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "foo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("no-match"),
Action: config.RelabelDrop,
Regex: pkgrelabel.MustNewRegexp("no-match"),
Action: pkgrelabel.Drop,
},
},
output: model.LabelSet{
@ -154,11 +154,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "foo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f|o"),
Action: config.RelabelDrop,
Regex: pkgrelabel.MustNewRegexp("f|o"),
Action: pkgrelabel.Drop,
},
},
output: model.LabelSet{
@ -169,11 +169,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "foo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("no-match"),
Action: config.RelabelKeep,
Regex: pkgrelabel.MustNewRegexp("no-match"),
Action: pkgrelabel.Keep,
},
},
output: nil,
@ -182,11 +182,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "foo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f.*"),
Action: config.RelabelKeep,
Regex: pkgrelabel.MustNewRegexp("f.*"),
Action: pkgrelabel.Keep,
},
},
output: model.LabelSet{
@ -198,13 +198,13 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "boo",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("f"),
Regex: pkgrelabel.MustNewRegexp("f"),
TargetLabel: "b",
Replacement: "bar",
Action: config.RelabelReplace,
Action: pkgrelabel.Replace,
},
},
output: model.LabelSet{
@ -217,12 +217,12 @@ func TestRelabel(t *testing.T) {
"b": "bar",
"c": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"c"},
TargetLabel: "d",
Separator: ";",
Action: config.RelabelHashMod,
Action: pkgrelabel.HashMod,
Modulus: 1000,
},
},
@ -239,11 +239,11 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
Replacement: "bar_${1}",
Action: config.RelabelLabelMap,
Action: pkgrelabel.LabelMap,
},
},
output: model.LabelSet{
@ -261,11 +261,11 @@ func TestRelabel(t *testing.T) {
"__meta_my_baz": "bbb",
"__meta_other": "ccc",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
Regex: config.MustNewRegexp("__meta_(my.*)"),
Regex: pkgrelabel.MustNewRegexp("__meta_(my.*)"),
Replacement: "${1}",
Action: config.RelabelLabelMap,
Action: pkgrelabel.LabelMap,
},
},
output: model.LabelSet{
@ -281,11 +281,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "some-name-value",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: pkgrelabel.Replace,
Replacement: "${2}",
TargetLabel: "${1}",
},
@ -299,11 +299,11 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "some-name-value",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: pkgrelabel.Replace,
Replacement: "${3}",
TargetLabel: "${1}",
},
@ -316,25 +316,25 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"a": "some-name-value",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: pkgrelabel.Replace,
Replacement: "${1}",
TargetLabel: "${3}",
},
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: pkgrelabel.Replace,
Replacement: "${1}",
TargetLabel: "0${3}",
},
{
SourceLabels: model.LabelNames{"a"},
Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
Action: pkgrelabel.Replace,
Replacement: "${1}",
TargetLabel: "-${3}",
},
@ -347,25 +347,25 @@ func TestRelabel(t *testing.T) {
input: model.LabelSet{
"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
Action: pkgrelabel.Replace,
Replacement: "${1}",
TargetLabel: "__metrics_path__",
},
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
Action: pkgrelabel.Replace,
Replacement: "${1}",
TargetLabel: "job",
},
{
SourceLabels: model.LabelNames{"__meta_sd_tags"},
Regex: config.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
Action: config.RelabelReplace,
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
Action: pkgrelabel.Replace,
Replacement: "${2}",
TargetLabel: "${1}",
},
@ -383,10 +383,10 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Action: config.RelabelLabelKeep,
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
Action: pkgrelabel.LabelKeep,
},
},
output: model.LabelSet{
@ -400,10 +400,10 @@ func TestRelabel(t *testing.T) {
"b1": "bar",
"b2": "baz",
},
relabel: []*config.RelabelConfig{
relabel: []*pkgrelabel.Config{
{
Regex: config.MustNewRegexp("(b.*)"),
Action: config.RelabelLabelDrop,
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
Action: pkgrelabel.LabelDrop,
},
},
output: model.LabelSet{

View File

@ -495,22 +495,22 @@ func (r *AlertingRule) HTMLSnippet(pathPrefix string) html_template.HTML {
alertNameLabel: model.LabelValue(r.name),
}
labels := make(map[string]string, len(r.labels))
labelsMap := make(map[string]string, len(r.labels))
for _, l := range r.labels {
labels[l.Name] = html_template.HTMLEscapeString(l.Value)
labelsMap[l.Name] = html_template.HTMLEscapeString(l.Value)
}
annotations := make(map[string]string, len(r.annotations))
annotationsMap := make(map[string]string, len(r.annotations))
for _, l := range r.annotations {
annotations[l.Name] = html_template.HTMLEscapeString(l.Value)
annotationsMap[l.Name] = html_template.HTMLEscapeString(l.Value)
}
ar := rulefmt.Rule{
Alert: fmt.Sprintf("<a href=%q>%s</a>", pathPrefix+strutil.TableLinkForExpression(alertMetric.String()), r.name),
Expr: fmt.Sprintf("<a href=%q>%s</a>", pathPrefix+strutil.TableLinkForExpression(r.vector.String()), html_template.HTMLEscapeString(r.vector.String())),
For: model.Duration(r.holdDuration),
Labels: labels,
Annotations: annotations,
Labels: labelsMap,
Annotations: annotationsMap,
}
byt, err := yaml.Marshal(ar)

View File

@ -19,6 +19,8 @@ import (
"testing"
"time"
"github.com/prometheus/prometheus/pkg/relabel"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
"github.com/prometheus/prometheus/discovery/targetgroup"
@ -28,14 +30,6 @@ import (
yaml "gopkg.in/yaml.v2"
)
func mustNewRegexp(s string) config.Regexp {
re, err := config.NewRegexp(s)
if err != nil {
panic(err)
}
return re
}
func TestPopulateLabels(t *testing.T) {
cases := []struct {
in labels.Labels
@ -144,10 +138,10 @@ func TestPopulateLabels(t *testing.T) {
Scheme: "https",
MetricsPath: "/metrics",
JobName: "job",
RelabelConfigs: []*config.RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
Action: config.RelabelReplace,
Regex: mustNewRegexp("(.*)"),
Action: relabel.Replace,
Regex: relabel.MustNewRegexp("(.*)"),
SourceLabels: model.LabelNames{"custom"},
Replacement: "${1}",
TargetLabel: string(model.AddressLabel),
@ -176,10 +170,10 @@ func TestPopulateLabels(t *testing.T) {
Scheme: "https",
MetricsPath: "/metrics",
JobName: "job",
RelabelConfigs: []*config.RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
Action: config.RelabelReplace,
Regex: mustNewRegexp("(.*)"),
Action: relabel.Replace,
Regex: relabel.MustNewRegexp("(.*)"),
SourceLabels: model.LabelNames{"custom"},
Replacement: "${1}",
TargetLabel: string(model.AddressLabel),
@ -249,7 +243,7 @@ scrape_configs:
scrapeManager.ApplyConfig(cfg)
// As reload never happens, new loop should never be called.
newLoop := func(_ *Target, s scraper, _ int, _ bool, _ []*config.RelabelConfig) loop {
newLoop := func(_ *Target, s scraper, _ int, _ bool, _ []*relabel.Config) loop {
t.Fatal("reload happened")
return nil
}

View File

@ -129,7 +129,7 @@ type scrapePool struct {
cancel context.CancelFunc
// Constructor for new scrape loops. This is settable for testing convenience.
newLoop func(*Target, scraper, int, bool, []*config.RelabelConfig) loop
newLoop func(*Target, scraper, int, bool, []*relabel.Config) loop
}
const maxAheadTime = 10 * time.Minute
@ -159,7 +159,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app Appendable, logger log.Logger)
loops: map[uint64]loop{},
logger: logger,
}
sp.newLoop = func(t *Target, s scraper, limit int, honor bool, mrc []*config.RelabelConfig) loop {
sp.newLoop = func(t *Target, s scraper, limit int, honor bool, mrc []*relabel.Config) loop {
// Update the targets retrieval function for metadata to a new scrape cache.
cache := newScrapeCache()
t.setMetadataStore(cache)
@ -366,7 +366,7 @@ func (sp *scrapePool) sync(targets []*Target) {
wg.Wait()
}
func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*config.RelabelConfig) labels.Labels {
func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*relabel.Config) labels.Labels {
lb := labels.NewBuilder(lset)
if honor {

View File

@ -29,6 +29,8 @@ import (
"testing"
"time"
"github.com/prometheus/prometheus/pkg/relabel"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
@ -68,10 +70,10 @@ func TestDroppedTargetsList(t *testing.T) {
cfg = &config.ScrapeConfig{
JobName: "dropMe",
ScrapeInterval: model.Duration(1),
RelabelConfigs: []*config.RelabelConfig{
RelabelConfigs: []*relabel.Config{
{
Action: config.RelabelDrop,
Regex: mustNewRegexp("dropMe"),
Action: relabel.Drop,
Regex: relabel.MustNewRegexp("dropMe"),
SourceLabels: model.LabelNames{"job"},
},
},
@ -219,7 +221,7 @@ func TestScrapePoolReload(t *testing.T) {
}
// On starting to run, new loops created on reload check whether their preceding
// equivalents have been stopped.
newLoop := func(_ *Target, s scraper, _ int, _ bool, _ []*config.RelabelConfig) loop {
newLoop := func(_ *Target, s scraper, _ int, _ bool, _ []*relabel.Config) loop {
l := &testLoop{}
l.startFunc = func(interval, timeout time.Duration, errc chan<- error) {
if interval != 3*time.Second {

View File

@ -59,11 +59,7 @@ func (b *BufferedSeriesIterator) Reset(it SeriesIterator) {
// ReduceDelta lowers the buffered time delta, for the current SeriesIterator only.
func (b *BufferedSeriesIterator) ReduceDelta(delta int64) bool {
if delta > b.buf.delta {
return false
}
b.buf.delta = delta
return true
return b.buf.reduceDelta(delta)
}
// PeekBack returns the nth previous element of the iterator. If there is none buffered,
@ -222,7 +218,8 @@ func (r *sampleRing) add(t int64, v float64) {
r.l++
// Free head of the buffer of samples that just fell out of the range.
for r.buf[r.f].t < t-r.delta {
tmin := t - r.delta
for r.buf[r.f].t < tmin {
r.f++
if r.f >= l {
r.f -= l
@ -231,6 +228,31 @@ func (r *sampleRing) add(t int64, v float64) {
}
}
// reduceDelta lowers the buffered time delta, dropping any samples that are
// out of the new delta range.
func (r *sampleRing) reduceDelta(delta int64) bool {
if delta > r.delta {
return false
}
r.delta = delta
if r.l == 0 {
return true
}
// Free head of the buffer of samples that just fell out of the range.
l := len(r.buf)
tmin := r.buf[r.i].t - delta
for r.buf[r.f].t < tmin {
r.f++
if r.f >= l {
r.f -= l
}
r.l--
}
return true
}
// nthLast returns the nth most recent element added to the ring.
func (r *sampleRing) nthLast(n int) (int64, float64, bool) {
if n > r.l {

View File

@ -163,21 +163,8 @@ func TestBufferedSeriesIteratorNoBadAt(t *testing.T) {
}
func BenchmarkBufferedSeriesIterator(b *testing.B) {
var (
samples []sample
lastT int64
)
for i := 0; i < b.N; i++ {
lastT += 30
samples = append(samples, sample{
t: lastT,
v: 123, // doesn't matter
})
}
// Simulate a 5 minute rate.
it := NewBufferIterator(newListSeriesIterator(samples), 5*60)
it := NewBufferIterator(newFakeSeriesIterator(int64(b.N), 30), 5*60)
b.SetBytes(int64(b.N * 16))
b.ReportAllocs()
@ -255,3 +242,31 @@ func (it *listSeriesIterator) Seek(t int64) bool {
func (it *listSeriesIterator) Err() error {
return nil
}
type fakeSeriesIterator struct {
nsamples int64
step int64
idx int64
}
func newFakeSeriesIterator(nsamples, step int64) *fakeSeriesIterator {
return &fakeSeriesIterator{nsamples: nsamples, step: step, idx: -1}
}
func (it *fakeSeriesIterator) At() (int64, float64) {
return it.idx * it.step, 123 // value doesn't matter
}
func (it *fakeSeriesIterator) Next() bool {
it.idx++
return it.idx < it.nsamples
}
func (it *fakeSeriesIterator) Seek(t int64) bool {
it.idx = t / it.step
return it.idx < it.nsamples
}
func (it *fakeSeriesIterator) Err() error {
return nil
}

View File

@ -27,6 +27,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/config"
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
"github.com/prometheus/prometheus/prompb"
"github.com/prometheus/prometheus/relabel"
)
@ -144,7 +145,7 @@ type QueueManager struct {
flushDeadline time.Duration
cfg config.QueueConfig
externalLabels model.LabelSet
relabelConfigs []*config.RelabelConfig
relabelConfigs []*pkgrelabel.Config
client StorageClient
queueName string
logLimiter *rate.Limiter
@ -161,7 +162,7 @@ type QueueManager struct {
}
// NewQueueManager builds a new QueueManager.
func NewQueueManager(logger log.Logger, cfg config.QueueConfig, externalLabels model.LabelSet, relabelConfigs []*config.RelabelConfig, client StorageClient, flushDeadline time.Duration) *QueueManager {
func NewQueueManager(logger log.Logger, cfg config.QueueConfig, externalLabels model.LabelSet, relabelConfigs []*pkgrelabel.Config, client StorageClient, flushDeadline time.Duration) *QueueManager {
if logger == nil {
logger = log.NewNopLogger()
} else {

View File

@ -2,6 +2,10 @@
sudo: required
dist: trusty
language: go
os:
- windows
- linux
- osx
go:
- 1.10.x
@ -9,9 +13,12 @@ go:
go_import_path: github.com/prometheus/tsdb
before_install:
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then choco install make; fi
install:
- go get -v -t ./...
script:
# `staticcheck` target is omitted due to linting errors
- make check_license style unused test
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then make test; else make check_license style unused test; fi

View File

@ -1,5 +1,9 @@
## master / unreleased
## 0.3.1
- [BUGFIX] Fixed most windows test and some actual bugs for unclosed file readers.
## 0.3.0
- [CHANGE] `LastCheckpoint()` used to return just the segment name and now it returns the full relative path.

View File

@ -473,14 +473,13 @@ func (h *Head) Init(minValidTime int64) error {
if err != nil {
return errors.Wrap(err, "open WAL segments")
}
defer sr.Close()
err = h.loadWAL(wal.NewReader(sr))
sr.Close() // Close the reader so that if there was an error the repair can remove the corrupted file under Windows.
if err == nil {
return nil
}
level.Warn(h.logger).Log("msg", "encountered WAL error, attempting repair", "err", err)
if err := h.wal.Repair(err); err != nil {
return errors.Wrap(err, "repair corrupted WAL")
}
@ -572,7 +571,7 @@ func (h *Head) Truncate(mint int64) (err error) {
}
// initTime initializes a head with the first timestamp. This only needs to be called
// for a compltely fresh head with an empty WAL.
// for a completely fresh head with an empty WAL.
// Returns true if the initialization took an effect.
func (h *Head) initTime(t int64) (initialized bool) {
if !atomic.CompareAndSwapInt64(&h.minTime, math.MaxInt64, t) {

View File

@ -71,7 +71,7 @@ func repairBadIndexVersion(logger log.Logger, dir string) error {
if _, err := io.Copy(repl, broken); err != nil {
return wrapErr(err, d)
}
// Set the 5th byte to 2 to indiciate the correct file format version.
// Set the 5th byte to 2 to indicate the correct file format version.
if _, err := repl.WriteAt([]byte{2}, 4); err != nil {
return wrapErr(err, d)
}

View File

@ -322,7 +322,7 @@ func (w *SegmentWAL) putBuffer(b *encbuf) {
}
// Truncate deletes the values prior to mint and the series which the keep function
// does not indiciate to preserve.
// does not indicate to preserve.
func (w *SegmentWAL) Truncate(mint int64, keep func(uint64) bool) error {
// The last segment is always active.
if len(w.files) < 2 {

View File

@ -298,9 +298,6 @@ func (w *WAL) Repair(origErr error) error {
level.Warn(w.logger).Log("msg", "deleting all segments behind corruption", "segment", cerr.Segment)
for _, s := range segs {
if s.index <= cerr.Segment {
continue
}
if w.segment.i == s.index {
// The active segment needs to be removed,
// close it first (Windows!). Can be closed safely
@ -310,6 +307,9 @@ func (w *WAL) Repair(origErr error) error {
return errors.Wrap(err, "close active segment")
}
}
if s.index <= cerr.Segment {
continue
}
if err := os.Remove(filepath.Join(w.dir, s.name)); err != nil {
return errors.Wrapf(err, "delete segment:%v", s.index)
}

8
vendor/modules.txt vendored
View File

@ -187,7 +187,7 @@ github.com/prometheus/procfs
github.com/prometheus/procfs/nfs
github.com/prometheus/procfs/xfs
github.com/prometheus/procfs/internal/util
# github.com/prometheus/tsdb v0.3.0
# github.com/prometheus/tsdb v0.3.1
github.com/prometheus/tsdb
github.com/prometheus/tsdb/labels
github.com/prometheus/tsdb/chunkenc
@ -228,8 +228,8 @@ golang.org/x/net/internal/socket
golang.org/x/net/idna
golang.org/x/net/lex/httplex
# golang.org/x/oauth2 v0.0.0-20160608215109-65a8d08c6292
golang.org/x/oauth2
golang.org/x/oauth2/google
golang.org/x/oauth2
golang.org/x/oauth2/internal
golang.org/x/oauth2/jws
golang.org/x/oauth2/jwt
@ -251,12 +251,12 @@ google.golang.org/api/gensupport
google.golang.org/api/googleapi
google.golang.org/api/googleapi/internal/uritemplates
# google.golang.org/appengine v0.0.0-20170522224838-a2f4131514e5
google.golang.org/appengine/urlfetch
google.golang.org/appengine
google.golang.org/appengine/urlfetch
google.golang.org/appengine/internal
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/internal/app_identity
google.golang.org/appengine/internal/modules
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/internal/base
google.golang.org/appengine/internal/datastore
google.golang.org/appengine/internal/log

View File

@ -139,7 +139,7 @@ func (s *AdminDisabled) TSDBCleanTombstones(_ old_ctx.Context, _ *pb.TSDBCleanTo
return nil, status.Error(codes.Unavailable, "Admin APIs are disabled")
}
// DeleteSeries imeplements pb.AdminServer.
// DeleteSeries implements pb.AdminServer.
func (s *AdminDisabled) DeleteSeries(_ old_ctx.Context, r *pb.SeriesDeleteRequest) (*pb.SeriesDeleteResponse, error) {
return nil, status.Error(codes.Unavailable, "Admin APIs are disabled")
}