Skip to content

Commit c80af8a

Browse files
committed
Add NameValidationScheme
Signed-off-by: SungJin1212 <tjdwls1201@gmail.com>
1 parent 988a8b1 commit c80af8a

File tree

8 files changed

+69
-8
lines changed

8 files changed

+69
-8
lines changed

docs/configuration/config-file-reference.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4344,6 +4344,11 @@ query_rejection:
43444344

43454345
# list of rule groups to disable
43464346
[disabled_rule_groups: <list of DisabledRuleGroup> | default = []]
4347+
4348+
# Name validation scheme for metric names and label names, Support values are:
4349+
# legacy, utf8.
4350+
# CLI flag: -validation.name-validation-scheme
4351+
[name_validation_scheme: <int> | default = legacy]
43474352
```
43484353
43494354
### `memberlist_config`

integration/e2e/service.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/go-kit/log"
1616
"github.com/pkg/errors"
1717
"github.com/prometheus/common/expfmt"
18+
"github.com/prometheus/common/model"
1819
"github.com/thanos-io/thanos/pkg/runutil"
1920

2021
"github.com/cortexproject/cortex/pkg/util/backoff"
@@ -623,7 +624,7 @@ func (s *HTTPService) SumMetrics(metricNames []string, opts ...MetricsOption) ([
623624
return nil, err
624625
}
625626

626-
var tp expfmt.TextParser
627+
tp := expfmt.NewTextParser(model.LegacyValidation)
627628
families, err := tp.TextToMetricFamilies(strings.NewReader(metrics))
628629
if err != nil {
629630
return nil, err
@@ -670,7 +671,7 @@ func (s *HTTPService) WaitRemovedMetric(metricName string, opts ...MetricsOption
670671
}
671672

672673
// Parse metrics.
673-
var tp expfmt.TextParser
674+
tp := expfmt.NewTextParser(model.LegacyValidation)
674675
families, err := tp.TextToMetricFamilies(strings.NewReader(metrics))
675676
if err != nil {
676677
return err

pkg/util/validation/exporter.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,13 @@ func ExtractNumericalValues(l *Limits) map[string]float64 {
6464

6565
switch field.Kind() {
6666
case reflect.Int, reflect.Int64:
67-
if field.Type().String() == "model.Duration" {
67+
switch fieldType.Type.String() {
68+
case "model.Duration":
6869
// we export the model.Duration in seconds
6970
metrics[tag] = time.Duration(field.Int()).Seconds()
70-
} else {
71+
case "model.ValidationScheme":
72+
// skip
73+
default:
7174
metrics[tag] = float64(field.Int())
7275
}
7376
case reflect.Uint, reflect.Uint64:

pkg/util/validation/limits.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ type Limits struct {
245245
AlertmanagerMaxSilencesCount int `yaml:"alertmanager_max_silences_count" json:"alertmanager_max_silences_count"`
246246
AlertmanagerMaxSilencesSizeBytes int `yaml:"alertmanager_max_silences_size_bytes" json:"alertmanager_max_silences_size_bytes"`
247247
DisabledRuleGroups DisabledRuleGroups `yaml:"disabled_rule_groups" json:"disabled_rule_groups" doc:"nocli|description=list of rule groups to disable"`
248+
249+
NameValidationScheme model.ValidationScheme `yaml:"name_validation_scheme" json:"name_validation_scheme"`
248250
}
249251

250252
// RegisterFlags adds the flags required to config this to the given FlagSet
@@ -354,6 +356,9 @@ func (l *Limits) RegisterFlags(f *flag.FlagSet) {
354356
f.IntVar(&l.AlertmanagerMaxAlertsSizeBytes, "alertmanager.max-alerts-size-bytes", 0, "Maximum total size of alerts that a single user can have, alert size is the sum of the bytes of its labels, annotations and generatorURL. Inserting more alerts will fail with a log message and metric increment. 0 = no limit.")
355357
f.IntVar(&l.AlertmanagerMaxSilencesCount, "alertmanager.max-silences-count", 0, "Maximum number of silences that a single user can have, including expired silences. 0 = no limit.")
356358
f.IntVar(&l.AlertmanagerMaxSilencesSizeBytes, "alertmanager.max-silences-size-bytes", 0, "Maximum size of individual silences that a single user can have. 0 = no limit.")
359+
360+
_ = l.NameValidationScheme.Set(model.LegacyValidation.String())
361+
f.Var(&l.NameValidationScheme, "validation.name-validation-scheme", fmt.Sprintf("Name validation scheme for metric names and label names, Support values are: %s.", strings.Join([]string{model.LegacyValidation.String(), model.UTF8Validation.String()}, ", ")))
357362
}
358363

359364
// Validate the limits config and returns an error if the validation
@@ -376,8 +381,18 @@ func (l *Limits) Validate(shardByAllLabels bool, activeSeriesMetricsEnabled bool
376381
return errMaxLocalNativeHistogramSeriesPerUserValidation
377382
}
378383

384+
var nameValidationScheme model.ValidationScheme
385+
switch l.NameValidationScheme {
386+
case model.LegacyValidation, model.UTF8Validation:
387+
nameValidationScheme = l.NameValidationScheme
388+
case model.UnsetValidation:
389+
nameValidationScheme = model.LegacyValidation
390+
default:
391+
return fmt.Errorf("unsupported name validation scheme: %s", l.NameValidationScheme)
392+
}
393+
379394
if err := l.RulerExternalLabels.Validate(func(l labels.Label) error {
380-
if !model.LabelName(l.Name).IsValid() {
395+
if !nameValidationScheme.IsValidMetricName(l.Name) {
381396
return fmt.Errorf("%w: %q", errInvalidLabelName, l.Name)
382397
}
383398
if !model.LabelValue(l.Value).IsValid() {

pkg/util/validation/limits_test.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ limits_per_label_set:
245245
err := yaml.Unmarshal([]byte(inputYAML), &limitsYAML)
246246
require.NoError(t, err)
247247
require.Len(t, limitsYAML.LimitsPerLabelSet, 1)
248-
require.Len(t, limitsYAML.LimitsPerLabelSet[0].LabelSet, 1)
248+
require.Equal(t, 1, limitsYAML.LimitsPerLabelSet[0].LabelSet.Len())
249249
require.Equal(t, limitsYAML.LimitsPerLabelSet[0].Limits.MaxSeries, 10)
250250

251251
duplicatedInputYAML := `
@@ -477,6 +477,34 @@ alertmanager_notification_rate_limit_per_integration:
477477
}
478478
}
479479

480+
func TestNameValidationSchemeOverrides(t *testing.T) {
481+
baseYaml := `
482+
name_validation_scheme: legacy
483+
`
484+
485+
overridesYaml := `
486+
user1:
487+
name_validation_scheme: legacy
488+
user2:
489+
name_validation_scheme: utf8
490+
`
491+
limitsYAML := Limits{}
492+
err := yaml.Unmarshal([]byte(baseYaml), &limitsYAML)
493+
require.NoError(t, err, "expected to be able to unmarshal from YAML")
494+
495+
SetDefaultLimitsForYAMLUnmarshalling(limitsYAML)
496+
497+
overrides := map[string]*Limits{}
498+
err = yaml.Unmarshal([]byte(overridesYaml), &overrides)
499+
require.NoError(t, err, "parsing overrides")
500+
501+
tl := newMockTenantLimits(overrides)
502+
503+
ov := NewOverrides(limitsYAML, tl)
504+
require.Equal(t, "legacy", ov.GetOverridesForUser("user1").NameValidationScheme.String())
505+
require.Equal(t, "utf8", ov.GetOverridesForUser("user2").NameValidationScheme.String())
506+
}
507+
480508
func TestAlertmanagerNotificationLimitsOverrides(t *testing.T) {
481509
baseYaml := `
482510
alertmanager_notification_rate_limit: 5

pkg/util/validation/validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ func ValidateLabels(validateMetrics *ValidateMetrics, limits *Limits, userID str
285285
return newNoMetricNameError()
286286
}
287287

288-
if !model.IsValidMetricName(model.LabelValue(unsafeMetricName)) {
288+
if !limits.NameValidationScheme.IsValidLabelName(unsafeMetricName) {
289289
validateMetrics.DiscardedSamples.WithLabelValues(invalidMetricName, userID).Inc()
290290
return newInvalidMetricNameError(unsafeMetricName)
291291
}
@@ -304,7 +304,7 @@ func ValidateLabels(validateMetrics *ValidateMetrics, limits *Limits, userID str
304304
labelsSizeBytes := 0
305305

306306
for _, l := range ls {
307-
if !skipLabelNameValidation && !model.LabelName(l.Name).IsValid() {
307+
if !skipLabelNameValidation && !limits.NameValidationScheme.IsValidLabelName(l.Name) {
308308
validateMetrics.DiscardedSamples.WithLabelValues(invalidLabel, userID).Inc()
309309
return newInvalidLabelError(ls, l.Name)
310310
} else if len(l.Name) > maxLabelNameLength {

pkg/util/validation/validate_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ func TestValidateLabels(t *testing.T) {
3333
cfg.MaxLabelNamesPerSeries = 2
3434
cfg.MaxLabelsSizeBytes = 90
3535
cfg.EnforceMetricName = true
36+
cfg.NameValidationScheme = model.LegacyValidation
3637
cfg.LimitsPerLabelSet = []LimitsPerLabelSet{
3738
{
3839
Limits: LimitsPerLabelSetEntry{MaxSeries: 0},
@@ -271,6 +272,7 @@ func TestValidateLabelOrder(t *testing.T) {
271272
cfg.MaxLabelNameLength = 10
272273
cfg.MaxLabelNamesPerSeries = 10
273274
cfg.MaxLabelValueLength = 10
275+
cfg.NameValidationScheme = model.LegacyValidation
274276
reg := prometheus.NewRegistry()
275277
validateMetrics := NewValidateMetrics(reg)
276278
userID := "testUser"
@@ -299,6 +301,7 @@ func TestValidateLabelDuplication(t *testing.T) {
299301
cfg.MaxLabelNameLength = 10
300302
cfg.MaxLabelNamesPerSeries = 10
301303
cfg.MaxLabelValueLength = 10
304+
cfg.NameValidationScheme = model.LegacyValidation
302305
reg := prometheus.NewRegistry()
303306
validateMetrics := NewValidateMetrics(reg)
304307
userID := "testUser"

schemas/cortex-config-schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5184,6 +5184,12 @@
51845184
"description": "List of metric relabel configurations. Note that in most situations, it is more effective to use metrics relabeling directly in the Prometheus server, e.g. remote_write.write_relabel_configs.",
51855185
"type": "string"
51865186
},
5187+
"name_validation_scheme": {
5188+
"default": "legacy",
5189+
"description": "Name validation scheme for metric names and label names, Support values are: legacy, utf8.",
5190+
"type": "number",
5191+
"x-cli-flag": "validation.name-validation-scheme"
5192+
},
51875193
"native_histogram_ingestion_burst_size": {
51885194
"default": 0,
51895195
"description": "Per-user allowed native histogram ingestion burst size (in number of samples)",

0 commit comments

Comments
 (0)