Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* [FEATURE] Querier: Support for configuring query optimizers and enabling XFunctions in the Thanos engine. #6873
* [FEATURE] Query Frontend: Add support /api/v1/format_query API for formatting queries. #6893
* [FEATURE] Query Frontend: Add support for /api/v1/parse_query API (experimental) to parse a PromQL expression and return it as a JSON-formatted AST (abstract syntax tree). #6978
* [ENHANCEMENT] Upgrade the Prometheus version to 3.6.0 and add a `-validation.name-validation-scheme` flag to support UTF-8. #7040
* [ENHANCEMENT] Distributor: Emit an error with a 400 status code when empty labels are found before the relabelling or label dropping process. #7052
* [ENHANCEMENT] Parquet Storage: Add support for additional sort columns during Parquet file generation #7003
* [ENHANCEMENT] Modernizes the entire codebase by using go modernize tool. #7005
Expand Down
58 changes: 58 additions & 0 deletions integration/alertmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,64 @@ const simpleAlertmanagerConfig = `route:
receivers:
- name: dummy`

const utf8AlertmanagerConfig = `route:
receiver: dummy
group_by: [group.test.🙂]
receivers:
- name: dummy`

func TestAlertmanager_UTF8(t *testing.T) {
s, err := e2e.NewScenario(networkName)
require.NoError(t, err)
defer s.Close()

flags := mergeFlags(AlertmanagerFlags(), AlertmanagerS3Flags())

minio := e2edb.NewMinio(9000, alertsBucketName)
require.NoError(t, s.StartAndWaitReady(minio))

alertmanager := e2ecortex.NewAlertmanager(
"alertmanager",
flags,
"",
)

require.NoError(t, s.StartAndWaitReady(alertmanager))

c, err := e2ecortex.NewClient("", "", alertmanager.HTTPEndpoint(), "", "user-1")
require.NoError(t, err)

ctx := context.Background()

err = c.SetAlertmanagerConfig(ctx, utf8AlertmanagerConfig, map[string]string{})
require.NoError(t, err)

require.NoError(t, alertmanager.WaitSumMetricsWithOptions(e2e.Equals(1), []string{"cortex_alertmanager_config_last_reload_successful"}, e2e.WaitMissingMetrics))
require.NoError(t, alertmanager.WaitSumMetricsWithOptions(e2e.Greater(0), []string{"cortex_alertmanager_config_hash"}, e2e.WaitMissingMetrics))

silenceId, err := c.CreateSilence(ctx, types.Silence{
Matchers: amlabels.Matchers{
{Name: "silences.name.🙂", Value: "silences.value.🙂"},
Copy link
Contributor

@yeya24 yeya24 Oct 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did this test even succeed. We don't even add config to enable UTF 8 labels. Does it mean that AM support UTF 8 labels by default? but I think this is wrong and a behavior change after we remove /pkg/cortex/configinit/init.go. The UTF 8 validation should be properly controlled by the new runtime config.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Alertmanager is able to control which validation mode to use via InitFromFlags. https://github.com/prometheus/alertmanager/blob/main/matcher/compat/parse.go#L59C6-L59C19.

However, this is not per tenant and global. That's why I was asking if per tenant metric name validation scheme really works or not. Seems we need some code changes to make that configuration not global in alertmanager.

Also you may add more utf-8 configs to route's matchers? Adding it to group_by alone Idk if it is sufficient.

},
Comment: "test silences",
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Minute),
})
require.NoError(t, err)
require.NotEmpty(t, silenceId)
require.NoError(t, alertmanager.WaitSumMetrics(e2e.Equals(1), "cortex_alertmanager_silences"))

err = c.SendAlertToAlermanager(ctx, &model.Alert{
Labels: model.LabelSet{
"alert.name.🙂": "alert.value.🙂",
},
StartsAt: time.Now(),
EndsAt: time.Now().Add(time.Minute),
})
require.NoError(t, err)
require.NoError(t, alertmanager.WaitSumMetrics(e2e.Equals(1), "cortex_alertmanager_alerts_received_total"))
}

func TestAlertmanager(t *testing.T) {
s, err := e2e.NewScenario(networkName)
require.NoError(t, err)
Expand Down
54 changes: 54 additions & 0 deletions integration/ruler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,60 @@ func TestRulerSharding(t *testing.T) {
assert.ElementsMatch(t, expectedNames, actualNames)
}

func TestRulerAPIUTF8(t *testing.T) {
s, err := e2e.NewScenario(networkName)
require.NoError(t, err)
defer s.Close()

// Start dependencies.
consul := e2edb.NewConsul()
minio := e2edb.NewMinio(9000, bucketName, rulestoreBucketName)
require.NoError(t, s.StartAndWaitReady(consul, minio))

rulerFlags := mergeFlags(
BlocksStorageFlags(),
RulerFlags(),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for this test case.


// Start ruler.
ruler := e2ecortex.NewRuler("ruler", consul.NetworkHTTPEndpoint(), rulerFlags, "")
require.NoError(t, s.StartAndWaitReady(ruler))

c, err := e2ecortex.NewClient("", "", "", ruler.HTTPEndpoint(), "user-1")
require.NoError(t, err)

groupLabels := map[string]string{
"group.label.🙂": "val.🙂",
}
ruleLabels := map[string]string{
"rule.label.🙂": "val.🙂",
}

interval, _ := model.ParseDuration("1s")

ruleGroup := rulefmt.RuleGroup{
Name: "utf8Rule",
Interval: interval,
Rules: []rulefmt.Rule{{
Alert: "alert.rule",
Expr: "up",
Labels: ruleLabels,
}, {
Record: "record.rule",
Expr: "up",
Labels: ruleLabels,
}},
Labels: groupLabels,
}

// Set rule group
err = c.SetRuleGroup(ruleGroup, "namespace")
require.NoError(t, err)

require.NoError(t, ruler.WaitSumMetrics(e2e.Equals(1), "cortex_ruler_managers_total"))
require.NoError(t, ruler.WaitSumMetrics(e2e.Equals(1), "cortex_ruler_rule_groups_in_store"))
}

func TestRulerAPISharding(t *testing.T) {
testRulerAPIWithSharding(t, false)
}
Expand Down
36 changes: 33 additions & 3 deletions integration/utf8_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"testing"
"time"

"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/prompb"
"github.com/stretchr/testify/require"

Expand Down Expand Up @@ -105,16 +106,16 @@ overrides:
require.NoError(t, s.StartAndWaitReady(cortex))

// user-1 uses legacy validation
user1Client, err := e2ecortex.NewClient(cortex.HTTPEndpoint(), "", "", "", "user-1")
user1Client, err := e2ecortex.NewClient(cortex.HTTPEndpoint(), cortex.HTTPEndpoint(), "", "", "user-1")
require.NoError(t, err)

// user-2 uses utf8 validation
user2Client, err := e2ecortex.NewClient(cortex.HTTPEndpoint(), "", "", "", "user-2")
user2Client, err := e2ecortex.NewClient(cortex.HTTPEndpoint(), cortex.HTTPEndpoint(), "", "", "user-2")
require.NoError(t, err)

now := time.Now()

utf8Series, _ := generateSeries("series_1", now, prompb.Label{Name: "test.utf8.metric", Value: "😄"})
utf8Series, _ := generateSeries("series.1", now, prompb.Label{Name: "test.utf8.metric", Value: "😄"})
legacySeries, _ := generateSeries("series_2", now, prompb.Label{Name: "job", Value: "test"})

res, err := user1Client.Push(legacySeries)
Expand All @@ -134,4 +135,33 @@ overrides:
res, err = user2Client.Push(utf8Series)
require.NoError(t, err)
require.Equal(t, 200, res.StatusCode)

// utf8 querying
// c.f. https://prometheus.io/docs/guides/utf8/#querying
query := `{"series.1", "test.utf8.metric"="😄"}`
queryResult, err := user2Client.Query(query, now)
require.NoError(t, err)
require.Equal(t, 200, res.StatusCode)
require.Equal(t, model.ValVector, queryResult.Type())
vec := queryResult.(model.Vector)
require.Equal(t, 1, len(vec))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to cover label names and label values API as well. But we can do it in next PR

Copy link
Member Author

@SungJin1212 SungJin1212 Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we remove https://github.com/cortexproject/cortex/blob/master/pkg/cortex/configinit/init.go as we start accepting UTF-8?

I've noticed the labelValues api with UTF-8 label name fails since we currently set the legacy scheme as default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should. I think we might be missing some test cases so we didn't find this in previous PR. Any components that do label/metric validation needs to accept the validation scheme otherwise you modify the global validation scheme variable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it and added label names/values e2e test case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we are missing more test cases like alertmanager and even ruler configurations with UTF 8 labels.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added commits, testing the UTF-8 labels for Ruler and Alertmanager in the e2e test


// label names
start := now
end := now.Add(time.Minute * 5)
labelNames, err := user2Client.LabelNames(start, end)
require.NoError(t, err)
require.Equal(t, []string{"__name__", "job", "test.utf8.metric"}, labelNames)

// series
series, err := user2Client.Series([]string{`{"test.utf8.metric"="😄"}`}, start, end)
require.NoError(t, err)
require.Equal(t, 1, len(series))
require.Equal(t, `{__name__="series.1", test.utf8.metric="😄"}`, series[0].String())

// label values
labelValues, err := user2Client.LabelValues("test.utf8.metric", start, end, nil)
require.NoError(t, err)
require.Equal(t, 1, len(labelValues))
require.Equal(t, model.LabelValue("😄"), labelValues[0])
}
8 changes: 0 additions & 8 deletions pkg/cortex/configinit/init.go

This file was deleted.

1 change: 0 additions & 1 deletion pkg/cortex/cortex.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
"github.com/cortexproject/cortex/pkg/configs"
configAPI "github.com/cortexproject/cortex/pkg/configs/api"
"github.com/cortexproject/cortex/pkg/configs/db"
_ "github.com/cortexproject/cortex/pkg/cortex/configinit"
"github.com/cortexproject/cortex/pkg/cortex/storage"
"github.com/cortexproject/cortex/pkg/cortexpb"
"github.com/cortexproject/cortex/pkg/distributor"
Expand Down
1 change: 0 additions & 1 deletion pkg/distributor/distributor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import (
"google.golang.org/grpc/status"

promchunk "github.com/cortexproject/cortex/pkg/chunk/encoding"
_ "github.com/cortexproject/cortex/pkg/cortex/configinit"
"github.com/cortexproject/cortex/pkg/cortexpb"
"github.com/cortexproject/cortex/pkg/ha"
"github.com/cortexproject/cortex/pkg/ingester"
Expand Down
1 change: 0 additions & 1 deletion pkg/util/validation/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/weaveworks/common/httpgrpc"

_ "github.com/cortexproject/cortex/pkg/cortex/configinit"
"github.com/cortexproject/cortex/pkg/cortexpb"
util_log "github.com/cortexproject/cortex/pkg/util/log"
)
Expand Down