diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bde0c030d6..6742df88fff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ - [ENHANCEMENT] Go 1.17 is now used for all builds of the Agent. (@tpaschalis) +- [ENHANCEMENT] integrations-next: Add `extra_labels` to add a custom set of + labels to integration targets. (@rfratto) + - [BUGFIX] Fixed issue where Grafana Agent may panic if there is a very large WAL loading while old WALs are being deleted or the `/agent/api/v1/targets` endpoint is called. (@tpaschalis) diff --git a/docs/configuration/integrations/integrations-next/_index.md b/docs/configuration/integrations/integrations-next/_index.md index 2a724de64172..bc602dad6303 100644 --- a/docs/configuration/integrations/integrations-next/_index.md +++ b/docs/configuration/integrations/integrations-next/_index.md @@ -144,6 +144,13 @@ autoscrape: # Autoscrape interval and timeout. [scrape_interval: | default = ] [scrape_timeout: | default = ] + +# An optional extra set of labels to add to metrics from the integration target. These +# labels are only exposed via the integration service discovery HTTP API and +# added when autoscrape is used. They will not be found directly on the metrics +# page for an integration. +extra_labels: + [ : ... ] ``` The old set of common options have been removed and do not work when the revamp diff --git a/pkg/integrations/v2/common/metrics.go b/pkg/integrations/v2/common/metrics.go index 6a670f47d745..405180846bb4 100644 --- a/pkg/integrations/v2/common/metrics.go +++ b/pkg/integrations/v2/common/metrics.go @@ -2,6 +2,7 @@ package common import ( "github.com/grafana/agent/pkg/integrations/v2/autoscrape" + "github.com/prometheus/prometheus/pkg/labels" ) // MetricsConfig is a set of common options shared by metrics integrations. It @@ -14,6 +15,7 @@ import ( type MetricsConfig struct { Autoscrape autoscrape.Config `yaml:"autoscrape,omitempty"` InstanceKey *string `yaml:"instance,omitempty"` + ExtraLabels labels.Labels `yaml:"extra_labels,omitempty"` } // ApplyDefaults applies defaults to mc. diff --git a/pkg/integrations/v2/metricsutils/metricshandler_integration.go b/pkg/integrations/v2/metricsutils/metricshandler_integration.go index 5aad0504aeab..38470cfcd341 100644 --- a/pkg/integrations/v2/metricsutils/metricshandler_integration.go +++ b/pkg/integrations/v2/metricsutils/metricshandler_integration.go @@ -105,6 +105,10 @@ func (i *metricsHandlerIntegration) Targets(ep integrations.Endpoint) []*targetg Source: fmt.Sprintf("%s/%s", i.integrationName, i.instanceID), } + for _, lbl := range i.common.ExtraLabels { + group.Labels[model.LabelName(lbl.Name)] = model.LabelValue(lbl.Value) + } + for _, t := range i.targets { group.Targets = append(group.Targets, model.LabelSet{ model.AddressLabel: model.LabelValue(ep.Host), diff --git a/pkg/integrations/v2/metricsutils/metricshandler_integration_test.go b/pkg/integrations/v2/metricsutils/metricshandler_integration_test.go new file mode 100644 index 000000000000..e084df082cc7 --- /dev/null +++ b/pkg/integrations/v2/metricsutils/metricshandler_integration_test.go @@ -0,0 +1,82 @@ +package metricsutils + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + "github.com/go-kit/log" + "github.com/grafana/agent/pkg/integrations/v2" + "github.com/grafana/agent/pkg/integrations/v2/common" + "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/prometheus/prometheus/pkg/labels" + "github.com/stretchr/testify/require" +) + +func TestMetricsHandlerIntegration_Targets(t *testing.T) { + globals := integrations.Globals{ + AgentIdentifier: "testagent", + AgentBaseURL: func() *url.URL { + u, err := url.Parse("http://testagent/") + require.NoError(t, err) + return u + }(), + SubsystemOpts: integrations.DefaultSubsystemOptions, + } + + t.Run("Targets", func(t *testing.T) { + var cfg common.MetricsConfig + cfg.ApplyDefaults(globals.SubsystemOpts.Metrics.Autoscrape) + + i, err := NewMetricsHandlerIntegration(nil, fakeConfig{}, cfg, globals, http.NotFoundHandler()) + require.NoError(t, err) + + actual := i.Targets(integrations.Endpoint{Host: "test", Prefix: "/test/"}) + expect := []*targetgroup.Group{{ + Source: "fake/testagent", + Labels: model.LabelSet{ + "instance": "testagent", + "job": "integrations/fake", + "agent_hostname": "testagent", + + "__meta_agent_integration_name": "fake", + "__meta_agent_integration_instance": "testagent", + "__meta_agent_integration_autoscrape": "1", + }, + Targets: []model.LabelSet{{ + "__address__": "test", + "__metrics_path__": "/test/metrics", + }}, + }} + require.Equal(t, expect, actual) + + t.Run("Extra labels", func(t *testing.T) { + cfg := common.MetricsConfig{ + ExtraLabels: labels.FromMap(map[string]string{"foo": "bar", "fizz": "buzz"}), + } + cfg.ApplyDefaults(globals.SubsystemOpts.Metrics.Autoscrape) + + i, err := NewMetricsHandlerIntegration(nil, fakeConfig{}, cfg, globals, http.NotFoundHandler()) + require.NoError(t, err) + actual := i.Targets(integrations.Endpoint{Host: "test", Prefix: "/test/"}) + require.Len(t, actual, 1) + + for _, lbl := range cfg.ExtraLabels { + val, ok := actual[0].Labels[model.LabelName(lbl.Name)] + require.True(t, ok, "target does not have extra label %s", lbl.Name) + require.Equal(t, lbl.Value, string(val), "extra label %s does not match expectation", lbl.Name) + } + }) + }) +} + +type fakeConfig struct{} + +func (fakeConfig) Name() string { return "fake" } +func (fakeConfig) ApplyDefaults(_ integrations.Globals) error { return nil } +func (fakeConfig) Identifier(g integrations.Globals) (string, error) { return g.AgentIdentifier, nil } +func (fakeConfig) NewIntegration(_ log.Logger, _ integrations.Globals) (integrations.Integration, error) { + return nil, fmt.Errorf("not implemented") +}