mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-11 13:57:36 -08:00
Merge pull request #6409 from gotjosh/tests-for-target-metadata
Add tests for /api/v1/target/metadata
This commit is contained in:
commit
0ae4899c47
|
@ -209,7 +209,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app Appendable, jitterSeed uint64,
|
||||||
sp.newLoop = func(opts scrapeLoopOptions) loop {
|
sp.newLoop = func(opts scrapeLoopOptions) loop {
|
||||||
// Update the targets retrieval function for metadata to a new scrape cache.
|
// Update the targets retrieval function for metadata to a new scrape cache.
|
||||||
cache := newScrapeCache()
|
cache := newScrapeCache()
|
||||||
opts.target.setMetadataStore(cache)
|
opts.target.SetMetadataStore(cache)
|
||||||
|
|
||||||
return newScrapeLoop(
|
return newScrapeLoop(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -794,7 +794,7 @@ func (c *scrapeCache) setUnit(metric, unit []byte) {
|
||||||
c.metaMtx.Unlock()
|
c.metaMtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *scrapeCache) getMetadata(metric string) (MetricMetadata, bool) {
|
func (c *scrapeCache) GetMetadata(metric string) (MetricMetadata, bool) {
|
||||||
c.metaMtx.Lock()
|
c.metaMtx.Lock()
|
||||||
defer c.metaMtx.Unlock()
|
defer c.metaMtx.Unlock()
|
||||||
|
|
||||||
|
@ -810,7 +810,7 @@ func (c *scrapeCache) getMetadata(metric string) (MetricMetadata, bool) {
|
||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *scrapeCache) listMetadata() []MetricMetadata {
|
func (c *scrapeCache) ListMetadata() []MetricMetadata {
|
||||||
c.metaMtx.Lock()
|
c.metaMtx.Lock()
|
||||||
defer c.metaMtx.Unlock()
|
defer c.metaMtx.Unlock()
|
||||||
|
|
||||||
|
|
|
@ -615,19 +615,19 @@ test_metric 1
|
||||||
testutil.Ok(t, err)
|
testutil.Ok(t, err)
|
||||||
testutil.Equals(t, 1, total)
|
testutil.Equals(t, 1, total)
|
||||||
|
|
||||||
md, ok := cache.getMetadata("test_metric")
|
md, ok := cache.GetMetadata("test_metric")
|
||||||
testutil.Assert(t, ok, "expected metadata to be present")
|
testutil.Assert(t, ok, "expected metadata to be present")
|
||||||
testutil.Assert(t, textparse.MetricTypeCounter == md.Type, "unexpected metric type")
|
testutil.Assert(t, textparse.MetricTypeCounter == md.Type, "unexpected metric type")
|
||||||
testutil.Equals(t, "some help text", md.Help)
|
testutil.Equals(t, "some help text", md.Help)
|
||||||
testutil.Equals(t, "metric", md.Unit)
|
testutil.Equals(t, "metric", md.Unit)
|
||||||
|
|
||||||
md, ok = cache.getMetadata("test_metric_no_help")
|
md, ok = cache.GetMetadata("test_metric_no_help")
|
||||||
testutil.Assert(t, ok, "expected metadata to be present")
|
testutil.Assert(t, ok, "expected metadata to be present")
|
||||||
testutil.Assert(t, textparse.MetricTypeGauge == md.Type, "unexpected metric type")
|
testutil.Assert(t, textparse.MetricTypeGauge == md.Type, "unexpected metric type")
|
||||||
testutil.Equals(t, "", md.Help)
|
testutil.Equals(t, "", md.Help)
|
||||||
testutil.Equals(t, "", md.Unit)
|
testutil.Equals(t, "", md.Unit)
|
||||||
|
|
||||||
md, ok = cache.getMetadata("test_metric_no_type")
|
md, ok = cache.GetMetadata("test_metric_no_type")
|
||||||
testutil.Assert(t, ok, "expected metadata to be present")
|
testutil.Assert(t, ok, "expected metadata to be present")
|
||||||
testutil.Assert(t, textparse.MetricTypeUnknown == md.Type, "unexpected metric type")
|
testutil.Assert(t, textparse.MetricTypeUnknown == md.Type, "unexpected metric type")
|
||||||
testutil.Equals(t, "other help text", md.Help)
|
testutil.Equals(t, "other help text", md.Help)
|
||||||
|
|
|
@ -58,7 +58,7 @@ type Target struct {
|
||||||
lastScrape time.Time
|
lastScrape time.Time
|
||||||
lastScrapeDuration time.Duration
|
lastScrapeDuration time.Duration
|
||||||
health TargetHealth
|
health TargetHealth
|
||||||
metadata metricMetadataStore
|
metadata MetricMetadataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTarget creates a reasonably configured target for querying.
|
// NewTarget creates a reasonably configured target for querying.
|
||||||
|
@ -75,9 +75,9 @@ func (t *Target) String() string {
|
||||||
return t.URL().String()
|
return t.URL().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type metricMetadataStore interface {
|
type MetricMetadataStore interface {
|
||||||
listMetadata() []MetricMetadata
|
ListMetadata() []MetricMetadata
|
||||||
getMetadata(metric string) (MetricMetadata, bool)
|
GetMetadata(metric string) (MetricMetadata, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MetricMetadata is a piece of metadata for a metric.
|
// MetricMetadata is a piece of metadata for a metric.
|
||||||
|
@ -95,7 +95,7 @@ func (t *Target) MetadataList() []MetricMetadata {
|
||||||
if t.metadata == nil {
|
if t.metadata == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return t.metadata.listMetadata()
|
return t.metadata.ListMetadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata returns type and help metadata for the given metric.
|
// Metadata returns type and help metadata for the given metric.
|
||||||
|
@ -106,10 +106,10 @@ func (t *Target) Metadata(metric string) (MetricMetadata, bool) {
|
||||||
if t.metadata == nil {
|
if t.metadata == nil {
|
||||||
return MetricMetadata{}, false
|
return MetricMetadata{}, false
|
||||||
}
|
}
|
||||||
return t.metadata.getMetadata(metric)
|
return t.metadata.GetMetadata(metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Target) setMetadataStore(s metricMetadataStore) {
|
func (t *Target) SetMetadataStore(s MetricMetadataStore) {
|
||||||
t.mtx.Lock()
|
t.mtx.Lock()
|
||||||
defer t.mtx.Unlock()
|
defer t.mtx.Unlock()
|
||||||
t.metadata = s
|
t.metadata = s
|
||||||
|
|
|
@ -43,6 +43,7 @@ import (
|
||||||
"github.com/prometheus/prometheus/config"
|
"github.com/prometheus/prometheus/config"
|
||||||
"github.com/prometheus/prometheus/pkg/gate"
|
"github.com/prometheus/prometheus/pkg/gate"
|
||||||
"github.com/prometheus/prometheus/pkg/labels"
|
"github.com/prometheus/prometheus/pkg/labels"
|
||||||
|
"github.com/prometheus/prometheus/pkg/textparse"
|
||||||
"github.com/prometheus/prometheus/pkg/timestamp"
|
"github.com/prometheus/prometheus/pkg/timestamp"
|
||||||
"github.com/prometheus/prometheus/prompb"
|
"github.com/prometheus/prometheus/prompb"
|
||||||
"github.com/prometheus/prometheus/promql"
|
"github.com/prometheus/prometheus/promql"
|
||||||
|
@ -55,55 +56,98 @@ import (
|
||||||
"github.com/prometheus/prometheus/util/testutil"
|
"github.com/prometheus/prometheus/util/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type testTargetRetriever struct{}
|
// testMetaStore satisfies the scrape.MetricMetadataStore interface.
|
||||||
|
// It is used to inject specific metadata as part of a test case.
|
||||||
|
type testMetaStore struct {
|
||||||
|
Metadata []scrape.MetricMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *testMetaStore) ListMetadata() []scrape.MetricMetadata {
|
||||||
|
return s.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *testMetaStore) GetMetadata(metric string) (scrape.MetricMetadata, bool) {
|
||||||
|
for _, m := range s.Metadata {
|
||||||
|
if metric == m.Metric {
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scrape.MetricMetadata{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// testTargetRetriever represents a list of targets to scrape.
|
||||||
|
// It is used to represent targets as part of test cases.
|
||||||
|
type testTargetRetriever struct {
|
||||||
|
activeTargets map[string][]*scrape.Target
|
||||||
|
droppedTargets map[string][]*scrape.Target
|
||||||
|
}
|
||||||
|
|
||||||
|
type testTargetParams struct {
|
||||||
|
Identifier string
|
||||||
|
Labels []labels.Label
|
||||||
|
DiscoveredLabels []labels.Label
|
||||||
|
Params url.Values
|
||||||
|
Reports []*testReport
|
||||||
|
Active bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type testReport struct {
|
||||||
|
Start time.Time
|
||||||
|
Duration time.Duration
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestTargetRetriever(targetsInfo []*testTargetParams) *testTargetRetriever {
|
||||||
|
var activeTargets map[string][]*scrape.Target
|
||||||
|
var droppedTargets map[string][]*scrape.Target
|
||||||
|
activeTargets = make(map[string][]*scrape.Target)
|
||||||
|
droppedTargets = make(map[string][]*scrape.Target)
|
||||||
|
|
||||||
|
for _, t := range targetsInfo {
|
||||||
|
nt := scrape.NewTarget(t.Labels, t.DiscoveredLabels, t.Params)
|
||||||
|
|
||||||
|
for _, r := range t.Reports {
|
||||||
|
nt.Report(r.Start, r.Duration, r.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.Active {
|
||||||
|
activeTargets[t.Identifier] = []*scrape.Target{nt}
|
||||||
|
} else {
|
||||||
|
droppedTargets[t.Identifier] = []*scrape.Target{nt}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &testTargetRetriever{
|
||||||
|
activeTargets: activeTargets,
|
||||||
|
droppedTargets: droppedTargets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
scrapeStart = time.Now().Add(-11 * time.Second)
|
scrapeStart = time.Now().Add(-11 * time.Second)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t testTargetRetriever) TargetsActive() map[string][]*scrape.Target {
|
func (t testTargetRetriever) TargetsActive() map[string][]*scrape.Target {
|
||||||
testTarget := scrape.NewTarget(
|
return t.activeTargets
|
||||||
labels.FromMap(map[string]string{
|
|
||||||
model.SchemeLabel: "http",
|
|
||||||
model.AddressLabel: "example.com:8080",
|
|
||||||
model.MetricsPathLabel: "/metrics",
|
|
||||||
model.JobLabel: "test",
|
|
||||||
}),
|
|
||||||
nil,
|
|
||||||
url.Values{},
|
|
||||||
)
|
|
||||||
testTarget.Report(scrapeStart, 70*time.Millisecond, nil)
|
|
||||||
blackboxTarget := scrape.NewTarget(
|
|
||||||
labels.FromMap(map[string]string{
|
|
||||||
model.SchemeLabel: "http",
|
|
||||||
model.AddressLabel: "localhost:9115",
|
|
||||||
model.MetricsPathLabel: "/probe",
|
|
||||||
model.JobLabel: "blackbox",
|
|
||||||
}),
|
|
||||||
nil,
|
|
||||||
url.Values{"target": []string{"example.com"}},
|
|
||||||
)
|
|
||||||
blackboxTarget.Report(scrapeStart, 100*time.Millisecond, errors.New("failed"))
|
|
||||||
return map[string][]*scrape.Target{
|
|
||||||
"test": {testTarget},
|
|
||||||
"blackbox": {blackboxTarget},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testTargetRetriever) TargetsDropped() map[string][]*scrape.Target {
|
func (t testTargetRetriever) TargetsDropped() map[string][]*scrape.Target {
|
||||||
return map[string][]*scrape.Target{
|
return t.droppedTargets
|
||||||
"blackbox": {
|
}
|
||||||
scrape.NewTarget(
|
|
||||||
nil,
|
func (t testTargetRetriever) setMetadataStoreForTargets(identifier string, metadata scrape.MetricMetadataStore) error {
|
||||||
labels.FromMap(map[string]string{
|
targets, ok := t.activeTargets[identifier]
|
||||||
model.AddressLabel: "http://dropped.example.com:9115",
|
|
||||||
model.MetricsPathLabel: "/probe",
|
if !ok {
|
||||||
model.SchemeLabel: "http",
|
return errors.New("targets not found")
|
||||||
model.JobLabel: "blackbox",
|
|
||||||
}),
|
|
||||||
url.Values{},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, at := range targets {
|
||||||
|
at.SetMetadataStore(metadata)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type testAlertmanagerRetriever struct{}
|
type testAlertmanagerRetriever struct{}
|
||||||
|
@ -243,10 +287,12 @@ func TestEndpoints(t *testing.T) {
|
||||||
|
|
||||||
algr.RuleGroups()
|
algr.RuleGroups()
|
||||||
|
|
||||||
|
testTargetRetriever := setupTestTargetRetriever(t)
|
||||||
|
|
||||||
api := &API{
|
api := &API{
|
||||||
Queryable: suite.Storage(),
|
Queryable: suite.Storage(),
|
||||||
QueryEngine: suite.QueryEngine(),
|
QueryEngine: suite.QueryEngine(),
|
||||||
targetRetriever: testTargetRetriever{},
|
targetRetriever: testTargetRetriever,
|
||||||
alertmanagerRetriever: testAlertmanagerRetriever{},
|
alertmanagerRetriever: testAlertmanagerRetriever{},
|
||||||
flagsMap: sampleFlagMap,
|
flagsMap: sampleFlagMap,
|
||||||
now: func() time.Time { return now },
|
now: func() time.Time { return now },
|
||||||
|
@ -305,10 +351,12 @@ func TestEndpoints(t *testing.T) {
|
||||||
|
|
||||||
algr.RuleGroups()
|
algr.RuleGroups()
|
||||||
|
|
||||||
|
testTargetRetriever := setupTestTargetRetriever(t)
|
||||||
|
|
||||||
api := &API{
|
api := &API{
|
||||||
Queryable: remote,
|
Queryable: remote,
|
||||||
QueryEngine: suite.QueryEngine(),
|
QueryEngine: suite.QueryEngine(),
|
||||||
targetRetriever: testTargetRetriever{},
|
targetRetriever: testTargetRetriever,
|
||||||
alertmanagerRetriever: testAlertmanagerRetriever{},
|
alertmanagerRetriever: testAlertmanagerRetriever{},
|
||||||
flagsMap: sampleFlagMap,
|
flagsMap: sampleFlagMap,
|
||||||
now: func() time.Time { return now },
|
now: func() time.Time { return now },
|
||||||
|
@ -357,6 +405,76 @@ func TestLabelNames(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupTestTargetRetriever(t *testing.T) *testTargetRetriever {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
targets := []*testTargetParams{
|
||||||
|
{
|
||||||
|
Identifier: "test",
|
||||||
|
Labels: labels.FromMap(map[string]string{
|
||||||
|
model.SchemeLabel: "http",
|
||||||
|
model.AddressLabel: "example.com:8080",
|
||||||
|
model.MetricsPathLabel: "/metrics",
|
||||||
|
model.JobLabel: "test",
|
||||||
|
}),
|
||||||
|
DiscoveredLabels: nil,
|
||||||
|
Params: url.Values{},
|
||||||
|
Reports: []*testReport{{scrapeStart, 70 * time.Millisecond, nil}},
|
||||||
|
Active: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Identifier: "blackbox",
|
||||||
|
Labels: labels.FromMap(map[string]string{
|
||||||
|
model.SchemeLabel: "http",
|
||||||
|
model.AddressLabel: "localhost:9115",
|
||||||
|
model.MetricsPathLabel: "/probe",
|
||||||
|
model.JobLabel: "blackbox",
|
||||||
|
}),
|
||||||
|
DiscoveredLabels: nil,
|
||||||
|
Params: url.Values{"target": []string{"example.com"}},
|
||||||
|
Reports: []*testReport{{scrapeStart, 100 * time.Millisecond, errors.New("failed")}},
|
||||||
|
Active: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Identifier: "blackbox",
|
||||||
|
Labels: nil,
|
||||||
|
DiscoveredLabels: labels.FromMap(map[string]string{
|
||||||
|
model.SchemeLabel: "http",
|
||||||
|
model.AddressLabel: "http://dropped.example.com:9115",
|
||||||
|
model.MetricsPathLabel: "/probe",
|
||||||
|
model.JobLabel: "blackbox",
|
||||||
|
}),
|
||||||
|
Params: url.Values{},
|
||||||
|
Active: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
targetRetriever := newTestTargetRetriever(targets)
|
||||||
|
|
||||||
|
targetRetriever.setMetadataStoreForTargets("test", &testMetaStore{
|
||||||
|
Metadata: []scrape.MetricMetadata{
|
||||||
|
{
|
||||||
|
Metric: "go_threads",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Help: "Number of OS threads created.",
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
targetRetriever.setMetadataStoreForTargets("blackbox", &testMetaStore{
|
||||||
|
Metadata: []scrape.MetricMetadata{
|
||||||
|
{
|
||||||
|
Metric: "prometheus_tsdb_storage_blocks_bytes",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Help: "The number of bytes that are currently used for local storage by all blocks.",
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return targetRetriever
|
||||||
|
}
|
||||||
|
|
||||||
func setupRemote(s storage.Storage) *httptest.Server {
|
func setupRemote(s storage.Storage) *httptest.Server {
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
req, err := remote.DecodeReadRequest(r)
|
req, err := remote.DecodeReadRequest(r)
|
||||||
|
@ -833,6 +951,73 @@ func testEndpoints(t *testing.T, api *API, testLabelAPI bool) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// With a matching metric.
|
||||||
|
{
|
||||||
|
endpoint: api.targetMetadata,
|
||||||
|
query: url.Values{
|
||||||
|
"metric": []string{"go_threads"},
|
||||||
|
},
|
||||||
|
response: []metricMetadata{
|
||||||
|
{
|
||||||
|
Target: labels.FromMap(map[string]string{
|
||||||
|
"job": "test",
|
||||||
|
}),
|
||||||
|
Help: "Number of OS threads created.",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// With a matching target.
|
||||||
|
{
|
||||||
|
endpoint: api.targetMetadata,
|
||||||
|
query: url.Values{
|
||||||
|
"match_target": []string{"{job=\"blackbox\"}"},
|
||||||
|
},
|
||||||
|
response: []metricMetadata{
|
||||||
|
{
|
||||||
|
Target: labels.FromMap(map[string]string{
|
||||||
|
"job": "blackbox",
|
||||||
|
}),
|
||||||
|
Metric: "prometheus_tsdb_storage_blocks_bytes",
|
||||||
|
Help: "The number of bytes that are currently used for local storage by all blocks.",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Without a target or metric.
|
||||||
|
{
|
||||||
|
endpoint: api.targetMetadata,
|
||||||
|
response: []metricMetadata{
|
||||||
|
{
|
||||||
|
Target: labels.FromMap(map[string]string{
|
||||||
|
"job": "test",
|
||||||
|
}),
|
||||||
|
Metric: "go_threads",
|
||||||
|
Help: "Number of OS threads created.",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Target: labels.FromMap(map[string]string{
|
||||||
|
"job": "blackbox",
|
||||||
|
}),
|
||||||
|
Metric: "prometheus_tsdb_storage_blocks_bytes",
|
||||||
|
Help: "The number of bytes that are currently used for local storage by all blocks.",
|
||||||
|
Type: textparse.MetricTypeGauge,
|
||||||
|
Unit: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Without a matching metric.
|
||||||
|
{
|
||||||
|
endpoint: api.targetMetadata,
|
||||||
|
query: url.Values{
|
||||||
|
"match_target": []string{"{job=\"non-existentblackbox\"}"},
|
||||||
|
},
|
||||||
|
errType: errorNotFound,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
endpoint: api.alertmanagers,
|
endpoint: api.alertmanagers,
|
||||||
response: &AlertmanagerDiscovery{
|
response: &AlertmanagerDiscovery{
|
||||||
|
|
Loading…
Reference in a new issue