relabeling: Fix labelmap action validation with legacy metric name scheme

Fixes #17370

In Prometheus v3.7.0, using labelmap actions with replacement patterns
containing regex variables (e.g., `$1`, `${1}`) would fail validation
when `metric_name_validation_scheme` was set to `legacy`, causing
Prometheus to fail at startup with:
  "$1" is invalid 'replacement' for labelmap action

This was a regression as the same configuration worked in v3.6.0.

The issue was in the validation logic: while UTF-8 validation correctly
allowed `$` characters, legacy validation incorrectly used
`IsValidLabelName` which rejects `$` characters. The fix ensures legacy
validation uses `relabelTargetLegacy` regex which explicitly supports
regex template variables.

Added test cases to verify labelmap validation works with both `$1` and
`${1}` replacement patterns under legacy validation scheme.

Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
This commit is contained in:
Julien Pivotto 2025-10-20 13:54:31 +02:00
parent 3107bdc2ea
commit c9d4689e0b
2 changed files with 23 additions and 1 deletions

View File

@ -145,7 +145,13 @@ func (c *Config) Validate(nameValidationScheme model.ValidationScheme) error {
// UTF-8 allows ${} characters, so standard validation allow $variables by default. // UTF-8 allows ${} characters, so standard validation allow $variables by default.
// TODO(bwplotka): Relabelling users cannot put $ and ${<...>} characters in metric names or values. // TODO(bwplotka): Relabelling users cannot put $ and ${<...>} characters in metric names or values.
// Design escaping mechanism to allow that, once valid use case appears. // Design escaping mechanism to allow that, once valid use case appears.
return c.NameValidationScheme.IsValidLabelName(value) switch c.NameValidationScheme {
case model.UTF8Validation:
return c.NameValidationScheme.IsValidLabelName(value)
default:
// For legacy validation, use the legacy regex that allows $variables.
return relabelTargetLegacy.MatchString(value)
}
} }
if c.Action == Replace && varInRegexTemplate(c.TargetLabel) && !isValidLabelNameWithRegexVarFn(c.TargetLabel) { if c.Action == Replace && varInRegexTemplate(c.TargetLabel) && !isValidLabelNameWithRegexVarFn(c.TargetLabel) {
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)

View File

@ -831,6 +831,22 @@ func TestRelabelValidate(t *testing.T) {
NameValidationScheme: model.UTF8Validation, NameValidationScheme: model.UTF8Validation,
}, },
}, },
{
config: Config{
Regex: MustNewRegexp("__meta_kubernetes_pod_label_(strimzi_io_.+)"),
Action: LabelMap,
Replacement: "$1",
NameValidationScheme: model.LegacyValidation,
},
},
{
config: Config{
Regex: MustNewRegexp("__meta_(.+)"),
Action: LabelMap,
Replacement: "${1}",
NameValidationScheme: model.LegacyValidation,
},
},
} }
for i, test := range tests { for i, test := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {