From 66a774ebef72c41050c58d8d31eb3d4c793caa12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Giedrius=20Statkevi=C4=8Dius?= Date: Tue, 13 Feb 2024 16:56:41 +0200 Subject: [PATCH 1/2] receive: add glob support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for matching tenants with globs. Signed-off-by: Giedrius Statkevičius --- pkg/receive/config.go | 1 + pkg/receive/hashring.go | 28 +++++++++++++++++++------ test/e2e/receive_test.go | 44 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) diff --git a/pkg/receive/config.go b/pkg/receive/config.go index e3af1cfa98..2bdaaabdc6 100644 --- a/pkg/receive/config.go +++ b/pkg/receive/config.go @@ -65,6 +65,7 @@ func (e *Endpoint) UnmarshalJSON(data []byte) error { type HashringConfig struct { Hashring string `json:"hashring,omitempty"` Tenants []string `json:"tenants,omitempty"` + Glob bool `json:"glob,omitempty"` Endpoints []Endpoint `json:"endpoints"` Algorithm HashringAlgorithm `json:"algorithm,omitempty"` ExternalLabels map[string]string `json:"external_labels,omitempty"` diff --git a/pkg/receive/hashring.go b/pkg/receive/hashring.go index 0d7c2dc10c..c479592c62 100644 --- a/pkg/receive/hashring.go +++ b/pkg/receive/hashring.go @@ -6,6 +6,7 @@ package receive import ( "fmt" "math" + "path/filepath" "sort" "strconv" "sync" @@ -249,7 +250,7 @@ func (c ketamaHashring) GetN(tenant string, ts *prompb.TimeSeries, n uint64) (st type multiHashring struct { cache map[string]Hashring hashrings []Hashring - tenantSets []map[string]struct{} + tenantSets []map[string]bool // We need a mutex to guard concurrent access // to the cache map, as this is both written to @@ -280,8 +281,23 @@ func (m *multiHashring) GetN(tenant string, ts *prompb.TimeSeries, n uint64) (st // considered a default hashring and matches everything. if t == nil { found = true - } else if _, ok := t[tenant]; ok { - found = true + } else { + // Fast path for the common case of direct match. + if glob, ok := t[tenant]; ok && !glob { + found = true + } else { + for tenantPattern, glob := range t { + if !glob { + continue + } + matches, err := filepath.Match(tenantPattern, tenant) + if err != nil { + return "", fmt.Errorf("error matching tenant pattern %s (tenant %s): %w", tenantPattern, tenant, err) + } + found = matches + } + } + } if found { m.mu.Lock() @@ -320,12 +336,12 @@ func NewMultiHashring(algorithm HashringAlgorithm, replicationFactor uint64, cfg } m.nodes = append(m.nodes, hashring.Nodes()...) m.hashrings = append(m.hashrings, hashring) - var t map[string]struct{} + var t map[string]bool if len(h.Tenants) != 0 { - t = make(map[string]struct{}) + t = make(map[string]bool) } for _, tenant := range h.Tenants { - t[tenant] = struct{}{} + t[tenant] = h.Glob } m.tenantSets = append(m.tenantSets, t) } diff --git a/test/e2e/receive_test.go b/test/e2e/receive_test.go index 1d21607909..7e268d14fc 100644 --- a/test/e2e/receive_test.go +++ b/test/e2e/receive_test.go @@ -1014,3 +1014,47 @@ func TestReceiveExtractsTenant(t *testing.T) { testutil.Ok(t, i.WaitSumMetricsWithOptions(e2emon.Equals(0), []string{"prometheus_tsdb_blocks_loaded"}, e2emon.WithLabelMatchers(matchers.MustNewMatcher(matchers.MatchEqual, "tenant", "tenant-1")), e2emon.WaitMissingMetrics())) } + +func TestReceiveGlob(t *testing.T) { + e, err := e2e.NewDockerEnvironment("receive-glob") + testutil.Ok(t, err) + t.Cleanup(e2ethanos.CleanScenario(t, e)) + + i := e2ethanos.NewReceiveBuilder(e, "ingestor").WithIngestionEnabled().Init() + testutil.Ok(t, e2e.StartAndWaitReady(i)) + + h := receive.HashringConfig{ + Glob: true, + Tenants: []string{ + "tenant-*", + }, + Endpoints: []receive.Endpoint{ + {Address: i.InternalEndpoint("grpc")}, + }, + } + + r := e2ethanos.NewReceiveBuilder(e, "router").WithRouting(1, h).Init() + testutil.Ok(t, e2e.StartAndWaitReady(r)) + + q := e2ethanos.NewQuerierBuilder(e, "1", i.InternalEndpoint("grpc")).Init() + testutil.Ok(t, e2e.StartAndWaitReady(q)) + + require.NoError(t, runutil.RetryWithLog(logkit.NewLogfmtLogger(os.Stdout), 1*time.Second, make(<-chan struct{}), func() error { + return storeWriteRequest(context.Background(), "http://"+r.Endpoint("remote-write")+"/api/v1/receive", &prompb.WriteRequest{ + Timeseries: []prompb.TimeSeries{ + { + Labels: []prompb.Label{ + {Name: "thanos_tenant_id", Value: "tenant-1"}, + {Name: "aa", Value: "bb"}, + }, + Samples: []prompb.Sample{ + {Value: 1, Timestamp: time.Now().UnixMilli()}, + }, + }, + }, + }) + })) + + testutil.Ok(t, i.WaitSumMetricsWithOptions(e2emon.Equals(0), []string{"prometheus_tsdb_blocks_loaded"}, e2emon.WithLabelMatchers(matchers.MustNewMatcher(matchers.MatchEqual, "tenant", "tenant-1")), e2emon.WaitMissingMetrics())) + +} From a87dd660be66203a738c051c1403fc012230ac54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Giedrius=20Statkevi=C4=8Dius?= Date: Mon, 12 Feb 2024 12:31:07 +0200 Subject: [PATCH 2/2] docs: fix link (#7129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The link has moved to another since Cisco bought Banzai Cloud. Signed-off-by: Giedrius Statkevičius --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index da6b414472..4ba84b8ad5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -137,7 +137,7 @@ See up to date [jsonnet mixins](https://github.com/thanos-io/thanos/tree/main/mi * [Adopting Thanos at LastPass](https://krisztianfekete.org/adopting-thanos-at-lastpass/) * 2020: - * [Banzai Cloud user story](https://banzaicloud.com/blog/multi-cluster-monitoring/) + * [Banzai Cloud user story](https://outshift.cisco.com/blog/multi-cluster-monitoring) * [Monitoring the Beat microservices: A tale of evolution](https://build.thebeat.co/monitoring-the-beat-microservices-a-tale-of-evolution-4e246882606e) * 2019: