Skip to content

Commit 658f837

Browse files
authored
fix breaking change in prom metrics validation scheme (#2781) (#2783)
* fix breaking change in prom metrics validation scheme * add test and changelog * punctuation
1 parent 1a5d6c7 commit 658f837

File tree

4 files changed

+143
-26
lines changed

4 files changed

+143
-26
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ This document contains a historical list of changes between releases. Only
77
changes that impact end-user behavior are listed; changes to documentation or
88
internal API changes are not present.
99

10+
v1.7.0-rc.2
11+
-----------------
12+
13+
### Bugfixes
14+
15+
- Fix an issue where Prometheus metric name validation scheme was set by default to UTF-8. It is now set back to the
16+
previous "legacy" scheme. An experimental flag `--feature.prometheus.metric-validation-scheme` can be used to switch
17+
it to `utf-8` to experiment with UTF-8 support.
18+
1019
v1.7.0-rc.1
1120
-----------------
1221

docs/sources/reference/cli/run.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ The following flags are supported:
6060
* `--config.extra-args`: Extra arguments from the original format used by the converter.
6161
* `--stability.level`: The minimum permitted stability level of functionality to run. Supported values: `experimental`, `public-preview`, `generally-available` (default `"generally-available"`).
6262
* `--feature.community-components.enabled`: Enable community components (default `false`).
63+
* `--feature.prometheus.metric-validation-scheme`: Prometheus metric validation scheme to use. Supported values: `legacy`, `utf-8`. NOTE: this is an experimental flag and may be removed in future releases (default `"legacy"`).
6364

6465
## Update the configuration file
6566

internal/alloycli/cmd_run.go

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/grafana/ckit/advertise"
2121
"github.com/grafana/ckit/peer"
2222
"github.com/prometheus/client_golang/prometheus"
23+
"github.com/prometheus/common/model"
2324
"github.com/spf13/cobra"
2425
"github.com/spf13/pflag"
2526
"go.opentelemetry.io/otel"
@@ -50,6 +51,11 @@ import (
5051
_ "github.com/grafana/alloy/internal/component/all"
5152
)
5253

54+
var (
55+
prometheusLegacyMetricValidationScheme = "legacy"
56+
prometheusUTF8MetricValidationScheme = "utf-8"
57+
)
58+
5359
func runCommand() *cobra.Command {
5460
r := &alloyRun{
5561
inMemoryAddr: "alloy.internal:12345",
@@ -64,6 +70,9 @@ func runCommand() *cobra.Command {
6470
clusterMaxJoinPeers: 5,
6571
clusterRejoinInterval: 60 * time.Second,
6672
disableSupportBundle: false,
73+
// For backwards compatibility - use the LegacyValidation of Prometheus metrics name. This is a global variable
74+
// setting that has changed upstream. See https://github.com/prometheus/common/pull/724.
75+
prometheusMetricNameValidationScheme: prometheusLegacyMetricValidationScheme,
6776
}
6877

6978
cmd := &cobra.Command{
@@ -155,38 +164,40 @@ depending on the nature of the reload error.
155164
cmd.Flags().StringVar(&r.storagePath, "storage.path", r.storagePath, "Base directory where components can store data")
156165
cmd.Flags().Var(&r.minStability, "stability.level", fmt.Sprintf("Minimum stability level of features to enable. Supported values: %s", strings.Join(featuregate.AllowedValues(), ", ")))
157166
cmd.Flags().BoolVar(&r.enableCommunityComps, "feature.community-components.enabled", r.enableCommunityComps, "Enable community components.")
167+
cmd.Flags().StringVar(&r.prometheusMetricNameValidationScheme, "feature.prometheus.metric-validation-scheme", prometheusLegacyMetricValidationScheme, fmt.Sprintf("Prometheus metric validation scheme to use. Supported values: %q, %q. NOTE: this is an experimental flag and may be removed in future releases.", prometheusLegacyMetricValidationScheme, prometheusUTF8MetricValidationScheme))
158168

159169
addDeprecatedFlags(cmd)
160170
return cmd
161171
}
162172

163173
type alloyRun struct {
164-
inMemoryAddr string
165-
httpListenAddr string
166-
storagePath string
167-
minStability featuregate.Stability
168-
uiPrefix string
169-
enablePprof bool
170-
disableReporting bool
171-
clusterEnabled bool
172-
clusterNodeName string
173-
clusterAdvAddr string
174-
clusterJoinAddr string
175-
clusterDiscoverPeers string
176-
clusterAdvInterfaces []string
177-
clusterRejoinInterval time.Duration
178-
clusterMaxJoinPeers int
179-
clusterName string
180-
clusterEnableTLS bool
181-
clusterTLSCAPath string
182-
clusterTLSCertPath string
183-
clusterTLSKeyPath string
184-
clusterTLSServerName string
185-
configFormat string
186-
configBypassConversionErrors bool
187-
configExtraArgs string
188-
enableCommunityComps bool
189-
disableSupportBundle bool
174+
inMemoryAddr string
175+
httpListenAddr string
176+
storagePath string
177+
minStability featuregate.Stability
178+
uiPrefix string
179+
enablePprof bool
180+
disableReporting bool
181+
clusterEnabled bool
182+
clusterNodeName string
183+
clusterAdvAddr string
184+
clusterJoinAddr string
185+
clusterDiscoverPeers string
186+
clusterAdvInterfaces []string
187+
clusterRejoinInterval time.Duration
188+
clusterMaxJoinPeers int
189+
clusterName string
190+
clusterEnableTLS bool
191+
clusterTLSCAPath string
192+
clusterTLSCertPath string
193+
clusterTLSKeyPath string
194+
clusterTLSServerName string
195+
configFormat string
196+
configBypassConversionErrors bool
197+
configExtraArgs string
198+
enableCommunityComps bool
199+
disableSupportBundle bool
200+
prometheusMetricNameValidationScheme string
190201
}
191202

192203
func (fr *alloyRun) Run(cmd *cobra.Command, configPath string) error {
@@ -211,6 +222,10 @@ func (fr *alloyRun) Run(cmd *cobra.Command, configPath string) error {
211222
return fmt.Errorf("building tracer: %w", err)
212223
}
213224

225+
if err := fr.configurePrometheusMetricNameValidationScheme(l); err != nil {
226+
return err
227+
}
228+
214229
// Set the global tracer provider to catch global traces, but ideally things
215230
// use the tracer provider given to them so the appropriate attributes get
216231
// injected.
@@ -435,6 +450,26 @@ func (fr *alloyRun) Run(cmd *cobra.Command, configPath string) error {
435450
}
436451
}
437452

453+
func (fr *alloyRun) configurePrometheusMetricNameValidationScheme(l log.Logger) error {
454+
switch fr.prometheusMetricNameValidationScheme {
455+
case prometheusLegacyMetricValidationScheme:
456+
model.NameValidationScheme = model.LegacyValidation
457+
case prometheusUTF8MetricValidationScheme:
458+
if err := featuregate.CheckAllowed(
459+
featuregate.StabilityExperimental,
460+
fr.minStability,
461+
"Prometheus utf-8 metric name validation scheme",
462+
); err != nil {
463+
return err
464+
}
465+
level.Warn(l).Log("msg", "Using experimental UTF-8 Prometheus metric name validation scheme")
466+
model.NameValidationScheme = model.UTF8Validation
467+
default:
468+
return fmt.Errorf("invalid prometheus metric name validation scheme: %q", fr.prometheusMetricNameValidationScheme)
469+
}
470+
return nil
471+
}
472+
438473
// getEnabledComponentsFunc returns a function that gets the current enabled components
439474
func getEnabledComponentsFunc(f *alloy_runtime.Runtime) func() map[string]interface{} {
440475
return func() map[string]interface{} {

internal/alloycli/cmd_run_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package alloycli
2+
3+
import (
4+
"testing"
5+
6+
"github.com/go-kit/log"
7+
"github.com/prometheus/common/model"
8+
"github.com/stretchr/testify/require"
9+
10+
"github.com/grafana/alloy/internal/featuregate"
11+
)
12+
13+
func TestConfigurePrometheusMetricNameValidationScheme(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
run alloyRun
17+
expectedError string
18+
expectedScheme model.ValidationScheme
19+
}{
20+
{
21+
name: "legacy validation scheme",
22+
run: alloyRun{
23+
prometheusMetricNameValidationScheme: prometheusLegacyMetricValidationScheme,
24+
minStability: featuregate.StabilityGenerallyAvailable,
25+
},
26+
expectedScheme: model.LegacyValidation,
27+
},
28+
{
29+
name: "utf8 validation scheme with experimental stability",
30+
run: alloyRun{
31+
prometheusMetricNameValidationScheme: prometheusUTF8MetricValidationScheme,
32+
minStability: featuregate.StabilityExperimental,
33+
},
34+
expectedScheme: model.UTF8Validation,
35+
},
36+
{
37+
name: "utf8 validation scheme with GA stability should fail",
38+
run: alloyRun{
39+
prometheusMetricNameValidationScheme: prometheusUTF8MetricValidationScheme,
40+
minStability: featuregate.StabilityGenerallyAvailable,
41+
},
42+
expectedError: `Prometheus utf-8 metric name validation scheme is at stability level "experimental", which is below the minimum allowed stability level "generally-available"`,
43+
},
44+
{
45+
name: "invalid validation scheme",
46+
run: alloyRun{
47+
prometheusMetricNameValidationScheme: "invalid",
48+
minStability: featuregate.StabilityGenerallyAvailable,
49+
},
50+
expectedError: `invalid prometheus metric name validation scheme: "invalid"`,
51+
},
52+
}
53+
54+
for _, tc := range tests {
55+
t.Run(tc.name, func(t *testing.T) {
56+
// Reset the global validation scheme before each test
57+
defer func() {
58+
model.NameValidationScheme = model.LegacyValidation
59+
}()
60+
61+
err := tc.run.configurePrometheusMetricNameValidationScheme(log.NewNopLogger())
62+
63+
if tc.expectedError != "" {
64+
require.ErrorContains(t, err, tc.expectedError)
65+
return
66+
}
67+
68+
require.NoError(t, err)
69+
require.Equal(t, tc.expectedScheme, model.NameValidationScheme)
70+
})
71+
}
72+
}

0 commit comments

Comments
 (0)