Skip to content

Commit e3d5aaa

Browse files
juliusmhaknuds1
authored andcommitted
fest: add option pattern to NewDesc; consolidate unit tests
Signed-off-by: Arve Knudsen <[email protected]>
1 parent 9adfe4a commit e3d5aaa

File tree

6 files changed

+124
-43
lines changed

6 files changed

+124
-43
lines changed

go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@ require (
2626
github.com/modern-go/reflect2 v1.0.2 // indirect
2727
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
2828
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
29-
golang.org/x/net v0.40.0 // indirect
29+
golang.org/x/net v0.41.0 // indirect
3030
golang.org/x/oauth2 v0.30.0 // indirect
31-
golang.org/x/text v0.25.0 // indirect
31+
golang.org/x/text v0.26.0 // indirect
3232
gopkg.in/yaml.v2 v2.4.0 // indirect
3333
)
3434

3535
exclude github.com/prometheus/client_golang v1.12.1
36-
37-
replace github.com/prometheus/common => github.com/juliusmh/common v0.65.1-0.20250701090419-a09de840631d

go.sum

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E
1313
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
1414
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
1515
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
16-
github.com/juliusmh/common v0.65.1-0.20250701090419-a09de840631d h1:CHm2BEsxiYZetQCrVaH6NQDxbW00DHTbt9jt0Fh2+78=
17-
github.com/juliusmh/common v0.65.1-0.20250701090419-a09de840631d/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
1816
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
1917
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
2018
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -37,8 +35,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
3735
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3836
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
3937
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
40-
github.com/prometheus/common v0.65.1-0.20250630090155-0c40a7a661aa h1:zdmFXaLcYT25mq6wDCYLhz2X+CvgkKaeARwmGYf5VRo=
41-
github.com/prometheus/common v0.65.1-0.20250630090155-0c40a7a661aa/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
38+
github.com/prometheus/common v0.65.1-0.20250704063051-dd764ff5ada2 h1:+PRxWMSLkDSEsjjztnLj3Dp009ayxU2LWYI8uX2caXU=
39+
github.com/prometheus/common v0.65.1-0.20250704063051-dd764ff5ada2/go.mod h1:LL3lcZII3UXGO4InbF+BTSsiAAPUBnwFVbp4gBWIMqw=
4240
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
4341
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
4442
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@@ -50,14 +48,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
5048
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
5149
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
5250
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
53-
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
54-
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
51+
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
52+
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
5553
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
5654
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
5755
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
5856
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
59-
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
60-
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
57+
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
58+
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
6159
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
6260
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
6361
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

prometheus/desc.go

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,32 @@ type Desc struct {
6666
err error
6767
}
6868

69+
type descriptorOptions struct {
70+
validationScheme model.ValidationScheme
71+
}
72+
73+
// newDescriptorOptions creates default descriptor options and applies opts.
74+
func newDescriptorOptions(opts ...DescOption) *descriptorOptions {
75+
d := &descriptorOptions{
76+
validationScheme: model.UTF8Validation,
77+
}
78+
for _, o := range opts {
79+
o(d)
80+
}
81+
return d
82+
}
83+
84+
// WithValidationScheme ensures descriptor's label and metric names adhere to scheme.
85+
// Default is UTF-8 validation.
86+
func WithValidationScheme(scheme model.ValidationScheme) DescOption {
87+
return func(o *descriptorOptions) {
88+
o.validationScheme = scheme
89+
}
90+
}
91+
92+
// DescOption are options that can be passed to NewDesc
93+
type DescOption func(*descriptorOptions)
94+
6995
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
7096
// and will be reported on registration time. variableLabels and constLabels can
7197
// be nil if no such labels should be set. fqName must not be empty.
@@ -75,8 +101,8 @@ type Desc struct {
75101
//
76102
// For constLabels, the label values are constant. Therefore, they are fully
77103
// specified in the Desc. See the Collector example for a usage pattern.
78-
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
79-
return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels)
104+
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels, opts ...DescOption) *Desc {
105+
return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels, opts...)
80106
}
81107

82108
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
@@ -89,13 +115,14 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
89115
//
90116
// For constLabels, the label values are constant. Therefore, they are fully
91117
// specified in the Desc. See the Collector example for a usage pattern.
92-
func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc {
118+
func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels, opts ...DescOption) *Desc {
93119
d := &Desc{
94120
fqName: fqName,
95121
help: help,
96122
variableLabels: variableLabels.compile(),
97123
}
98-
if !model.IsValidMetricName(model.LabelValue(fqName), model.UTF8Validation) {
124+
descOpts := newDescriptorOptions(opts...)
125+
if !model.IsValidMetricName(model.LabelValue(fqName), descOpts.validationScheme) {
99126
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
100127
return d
101128
}
@@ -107,7 +134,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
107134
labelNameSet := map[string]struct{}{}
108135
// First add only the const label names and sort them...
109136
for labelName := range constLabels {
110-
if !checkLabelName(labelName, model.UTF8Validation) {
137+
if !checkLabelName(labelName, descOpts.validationScheme) {
111138
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
112139
return d
113140
}
@@ -129,7 +156,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
129156
// cannot be in a regular label name. That prevents matching the label
130157
// dimension with a different mix between preset and variable labels.
131158
for _, label := range d.variableLabels.names {
132-
if !checkLabelName(label, model.UTF8Validation) {
159+
if !checkLabelName(label, descOpts.validationScheme) {
133160
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName)
134161
return d
135162
}

prometheus/desc_test.go

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,86 @@ package prometheus
1515

1616
import (
1717
"testing"
18+
19+
"github.com/prometheus/common/model"
1820
)
1921

20-
func TestNewDescInvalidLabelValues(t *testing.T) {
21-
desc := NewDesc(
22-
"sample_label",
23-
"sample label",
24-
nil,
25-
Labels{"a": "\xFF"},
26-
)
27-
if desc.err == nil {
28-
t.Errorf("NewDesc: expected error because: %s", desc.err)
22+
func TestNewDesc(t *testing.T) {
23+
testCases := []struct {
24+
name string
25+
fqName string
26+
help string
27+
variableLabels []string
28+
labels Labels
29+
opts []DescOption
30+
wantErr string
31+
}{
32+
{
33+
name: "invalid label value",
34+
fqName: "sample_label",
35+
help: "sample label",
36+
variableLabels: nil,
37+
labels: Labels{"a": "\xff"},
38+
wantErr: `label value "\xff" is not valid UTF-8`,
39+
},
40+
{
41+
name: "nil label values",
42+
fqName: "sample_label",
43+
help: "sample label",
44+
variableLabels: nil,
45+
labels: nil,
46+
},
47+
{
48+
name: "invalid label name",
49+
fqName: "sample_label",
50+
help: "sample label",
51+
variableLabels: nil,
52+
labels: Labels{"\xff": "test"},
53+
wantErr: `"\xff" is not a valid label name for metric "sample_label"`,
54+
},
55+
{
56+
name: "invalid legacy label name",
57+
fqName: "sample_label",
58+
help: "sample label",
59+
variableLabels: nil,
60+
labels: Labels{"test😀": "test"},
61+
opts: []DescOption{WithValidationScheme(model.LegacyValidation)},
62+
wantErr: `"test😀" is not a valid label name for metric "sample_label"`,
63+
},
64+
{
65+
name: "invalid legacy metric name",
66+
fqName: "sample_label😀",
67+
help: "sample label",
68+
opts: []DescOption{WithValidationScheme(model.LegacyValidation)},
69+
wantErr: `"sample_label😀" is not a valid metric name`,
70+
},
71+
{
72+
name: "valid utf8 label name",
73+
fqName: "sample_label",
74+
help: "sample label",
75+
variableLabels: nil,
76+
labels: Labels{"test😀": "test"},
77+
},
2978
}
30-
}
31-
32-
func TestNewDescNilLabelValues(t *testing.T) {
33-
desc := NewDesc(
34-
"sample_label",
35-
"sample label",
36-
nil,
37-
nil,
38-
)
39-
if desc.err != nil {
40-
t.Errorf("NewDesc: unexpected error: %s", desc.err)
79+
for _, tc := range testCases {
80+
t.Run(tc.name, func(t *testing.T) {
81+
desc := NewDesc(
82+
tc.fqName,
83+
tc.help,
84+
tc.variableLabels,
85+
tc.labels,
86+
tc.opts...,
87+
)
88+
if desc.err != nil && tc.wantErr != desc.err.Error() {
89+
t.Fatalf("NewDesc: expected error %q but got %+v", tc.wantErr, desc.err)
90+
} else if desc.err == nil && tc.wantErr != "" {
91+
t.Fatalf("NewDesc: expected error %q but got nil", tc.wantErr)
92+
} else if desc.err != nil && tc.wantErr == "" {
93+
t.Fatalf("NewDesc: %+v", desc.err)
94+
}
95+
})
4196
}
97+
4298
}
4399

44100
func TestNewDescWithNilLabelValues_String(t *testing.T) {

prometheus/testutil/lint_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"testing"
1818

1919
"github.com/prometheus/client_golang/prometheus"
20+
"github.com/prometheus/common/model"
2021
)
2122

2223
func TestCollectAndLintGood(t *testing.T) {
@@ -33,7 +34,7 @@ func TestCollectAndLintGood(t *testing.T) {
3334
cnt.WithLabelValues("bar")
3435
cnt.WithLabelValues("baz")
3536

36-
problems, err := CollectAndLint(cnt)
37+
problems, err := CollectAndLint(cnt, model.UTF8Validation)
3738
if err != nil {
3839
t.Error("Unexpected error:", err)
3940
}
@@ -56,7 +57,7 @@ func TestCollectAndLintBad(t *testing.T) {
5657
cnt.WithLabelValues("bar")
5758
cnt.WithLabelValues("baz")
5859

59-
problems, err := CollectAndLint(cnt)
60+
problems, err := CollectAndLint(cnt, model.UTF8Validation)
6061
if err != nil {
6162
t.Error("Unexpected error:", err)
6263
}

prometheus/testutil/promlint/promlint_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"testing"
2222

2323
dto "github.com/prometheus/client_model/go"
24+
"github.com/prometheus/common/model"
2425

2526
"github.com/prometheus/client_golang/prometheus/testutil/promlint"
2627
)
@@ -424,7 +425,7 @@ x_ounces 10
424425
t.Run(tt.name, func(t *testing.T) {
425426
l := promlint.New(strings.NewReader(tt.in))
426427

427-
problems, err := l.Lint()
428+
problems, err := l.Lint(model.UTF8Validation)
428429
if err != nil {
429430
t.Fatalf("unexpected error: %v", err)
430431
}
@@ -776,7 +777,7 @@ func runTests(t *testing.T, tests []test) {
776777
t.Run(tt.name, func(t *testing.T) {
777778
l := promlint.New(strings.NewReader(tt.in))
778779

779-
problems, err := l.Lint()
780+
problems, err := l.Lint(model.UTF8Validation)
780781
if err != nil {
781782
t.Fatalf("unexpected error: %v", err)
782783
}
@@ -791,7 +792,7 @@ func runTests(t *testing.T, tests []test) {
791792

792793
func TestCustomValidations(t *testing.T) {
793794
lintAndVerify := func(l *promlint.Linter, cv test) {
794-
problems, err := l.Lint()
795+
problems, err := l.Lint(model.UTF8Validation)
795796
if err != nil {
796797
t.Fatalf("unexpected error: %v", err)
797798
}

0 commit comments

Comments
 (0)