From bfbabbb89a3119e904d48028fb2534446e31a642 Mon Sep 17 00:00:00 2001 From: Simon Pasquier Date: Fri, 8 Nov 2024 10:57:04 +0100 Subject: [PATCH] Fix ExternalLabels() for Prometheus v3.0 (#7893) Prometheus v3.0.0-rc.0 introduces a new scrape protocol (`PrometheusText1.0.0`) which is present by default in the global configuration. It breaks the Thanos sidecar when it wants to retrieve the external labels. This change replaces the use of the Prometheus `GlobalConfig` struct by a minimal struct which unmarshals only the `external_labels` key. See also https://github.com/prometheus-operator/prometheus-operator/issues/7078 Signed-off-by: Simon Pasquier --- CHANGELOG.md | 1 + pkg/promclient/promclient.go | 7 ++-- pkg/promclient/promclient_test.go | 67 +++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 pkg/promclient/promclient_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 24206ec32b..36d6e89aa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re - [#7852](https://github.com/thanos-io/thanos/pull/7852) Query Frontend: pass "stats" parameter forward to queriers and fix Prometheus stats merging. - [#7832](https://github.com/thanos-io/thanos/pull/7832) Query Frontend: Fix cache keys for dynamic split intervals. - [#7885](https://github.com/thanos-io/thanos/pull/7885) Store: Return chunks to the pool after completing a Series call. +- [#7893](https://github.com/thanos-io/thanos/pull/7893) Sidecar: Fix retrieval of external labels for Prometheus v3.0.0. ### Added - [#7763](https://github.com/thanos-io/thanos/pull/7763) Ruler: use native histograms for client latency metrics. diff --git a/pkg/promclient/promclient.go b/pkg/promclient/promclient.go index 271f3f0daf..eeca44db92 100644 --- a/pkg/promclient/promclient.go +++ b/pkg/promclient/promclient.go @@ -27,7 +27,6 @@ import ( "github.com/pkg/errors" "github.com/prometheus/common/expfmt" "github.com/prometheus/common/model" - "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/timestamp" "github.com/prometheus/prometheus/promql" @@ -201,13 +200,15 @@ func (c *Client) ExternalLabels(ctx context.Context, base *url.URL) (labels.Labe return labels.EmptyLabels(), errors.Wrapf(err, "unmarshal response: %v", string(body)) } var cfg struct { - GlobalConfig config.GlobalConfig `yaml:"global"` + GlobalConfig struct { + ExternalLabels map[string]string `yaml:"external_labels"` + } `yaml:"global"` } if err := yaml.Unmarshal([]byte(d.Data.YAML), &cfg); err != nil { return labels.EmptyLabels(), errors.Wrapf(err, "parse Prometheus config: %v", d.Data.YAML) } - return cfg.GlobalConfig.ExternalLabels, nil + return labels.FromMap(cfg.GlobalConfig.ExternalLabels), nil } type Flags struct { diff --git a/pkg/promclient/promclient_test.go b/pkg/promclient/promclient_test.go new file mode 100644 index 0000000000..00173d3302 --- /dev/null +++ b/pkg/promclient/promclient_test.go @@ -0,0 +1,67 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + +// Package promclient offers helper client function for various API endpoints. + +package promclient + +import ( + "context" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/efficientgo/core/testutil" +) + +func TestExternalLabels(t *testing.T) { + for _, tc := range []struct { + name string + response string + err bool + labels map[string]string + }{ + { + name: "invalid payload", + response: `{`, + err: true, + }, + { + name: "unknown scrape protocol", + response: `{"status":"success","data":{"yaml":"global:\n scrape_interval: 1m\n scrape_timeout: 10s\n scrape_protocols:\n - OpenMetricsText1.0.0\n - OpenMetricsText0.0.1\n - PrometheusText1.0.0\n - PrometheusText0.0.4\n - UnknownScrapeProto\n evaluation_interval: 1m\n external_labels:\n az: \"1\"\n region: eu-west\nruntime:\n gogc: 75\n"}}`, + labels: map[string]string{ + "region": "eu-west", + "az": "1", + }, + }, + { + name: "no external labels", + response: `{"status":"success","data":{"yaml":"global:\n scrape_interval: 1m\n scrape_timeout: 10s\n scrape_protocols:\n - OpenMetricsText1.0.0\n - OpenMetricsText0.0.1\n - PrometheusText1.0.0\n - PrometheusText0.0.4\n - UnknownScrapeProto\n evaluation_interval: 1m\nruntime:\n gogc: 75\n"}}`, + labels: map[string]string{}, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, tc.response) + })) + defer ts.Close() + + u, err := url.Parse(ts.URL) + testutil.Ok(t, err) + + ext, err := NewDefaultClient().ExternalLabels(context.Background(), u) + if tc.err { + testutil.NotOk(t, err) + return + } + + testutil.Ok(t, err) + testutil.Equals(t, len(tc.labels), ext.Len()) + for k, v := range tc.labels { + testutil.Equals(t, v, ext.Get(k)) + } + }) + } +}