Skip to content

Commit 7f8b2a0

Browse files
authored
model.ValidationScheme: Support encoding as YAML (#799)
* model.ValidationScheme: Support encoding as YAML --------- Signed-off-by: Arve Knudsen <[email protected]>
1 parent 0a409d6 commit 7f8b2a0

File tree

2 files changed

+170
-2
lines changed

2 files changed

+170
-2
lines changed

model/metric.go

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
dto "github.com/prometheus/client_model/go"
2626
"google.golang.org/protobuf/proto"
27+
"gopkg.in/yaml.v2"
2728
)
2829

2930
var (
@@ -62,16 +63,70 @@ var (
6263
type ValidationScheme int
6364

6465
const (
66+
// UnsetValidation represents an undefined ValidationScheme.
67+
// Should not be used in practice.
68+
UnsetValidation ValidationScheme = iota
69+
6570
// LegacyValidation is a setting that requires that all metric and label names
6671
// conform to the original Prometheus character requirements described by
6772
// MetricNameRE and LabelNameRE.
68-
LegacyValidation ValidationScheme = iota
73+
LegacyValidation
6974

7075
// UTF8Validation only requires that metric and label names be valid UTF-8
7176
// strings.
7277
UTF8Validation
7378
)
7479

80+
var (
81+
_ yaml.Marshaler = UnsetValidation
82+
_ fmt.Stringer = UnsetValidation
83+
)
84+
85+
// String returns the string representation of s.
86+
func (s ValidationScheme) String() string {
87+
switch s {
88+
case UnsetValidation:
89+
return "unset"
90+
case LegacyValidation:
91+
return "legacy"
92+
case UTF8Validation:
93+
return "utf8"
94+
default:
95+
panic(fmt.Errorf("unhandled ValidationScheme: %d", s))
96+
}
97+
}
98+
99+
// MarshalYAML implements the yaml.Marshaler interface.
100+
func (s ValidationScheme) MarshalYAML() (any, error) {
101+
switch s {
102+
case UnsetValidation:
103+
return "", nil
104+
case LegacyValidation, UTF8Validation:
105+
return s.String(), nil
106+
default:
107+
panic(fmt.Errorf("unhandled ValidationScheme: %d", s))
108+
}
109+
}
110+
111+
// UnmarshalYAML implements the yaml.Unmarshaler interface.
112+
func (s *ValidationScheme) UnmarshalYAML(unmarshal func(any) error) error {
113+
var scheme string
114+
if err := unmarshal(&scheme); err != nil {
115+
return err
116+
}
117+
switch scheme {
118+
case "":
119+
// Don't change the value.
120+
case "legacy":
121+
*s = LegacyValidation
122+
case "utf8":
123+
*s = UTF8Validation
124+
default:
125+
return fmt.Errorf("unrecognized ValidationScheme: %q", scheme)
126+
}
127+
return nil
128+
}
129+
75130
type EscapingScheme int
76131

77132
const (
@@ -185,7 +240,7 @@ func IsValidMetricName(n LabelValue) bool {
185240
}
186241
return utf8.ValidString(string(n))
187242
default:
188-
panic(fmt.Sprintf("Invalid name validation scheme requested: %d", NameValidationScheme))
243+
panic(fmt.Sprintf("Invalid name validation scheme requested: %s", NameValidationScheme.String()))
189244
}
190245
}
191246

model/metric_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@
1414
package model
1515

1616
import (
17+
"errors"
18+
"strings"
1719
"testing"
1820

1921
"github.com/google/go-cmp/cmp"
2022
"github.com/google/go-cmp/cmp/cmpopts"
2123
dto "github.com/prometheus/client_model/go"
24+
"github.com/stretchr/testify/require"
2225
"google.golang.org/protobuf/proto"
26+
"gopkg.in/yaml.v2"
2327
)
2428

2529
func testMetric(t testing.TB) {
@@ -89,6 +93,115 @@ func BenchmarkMetric(b *testing.B) {
8993
}
9094
}
9195

96+
func TestValidationScheme(t *testing.T) {
97+
var scheme ValidationScheme
98+
require.Equal(t, UnsetValidation, scheme)
99+
}
100+
101+
func TestValidationScheme_String(t *testing.T) {
102+
for _, tc := range []struct {
103+
name string
104+
scheme ValidationScheme
105+
want string
106+
}{
107+
{
108+
name: "Unset",
109+
scheme: UnsetValidation,
110+
want: "unset",
111+
},
112+
{
113+
name: "Legacy",
114+
scheme: LegacyValidation,
115+
want: "legacy",
116+
},
117+
{
118+
name: "UTF8",
119+
scheme: UTF8Validation,
120+
want: "utf8",
121+
},
122+
} {
123+
t.Run(tc.name, func(t *testing.T) {
124+
require.Equal(t, tc.want, tc.scheme.String())
125+
})
126+
}
127+
}
128+
129+
func TestValidationScheme_MarshalYAML(t *testing.T) {
130+
for _, tc := range []struct {
131+
name string
132+
scheme ValidationScheme
133+
want string
134+
}{
135+
{
136+
name: "Unset",
137+
scheme: UnsetValidation,
138+
want: `""`,
139+
},
140+
{
141+
name: "Legacy",
142+
scheme: LegacyValidation,
143+
want: "legacy",
144+
},
145+
{
146+
name: "UTF8",
147+
scheme: UTF8Validation,
148+
want: "utf8",
149+
},
150+
} {
151+
t.Run(tc.name, func(t *testing.T) {
152+
marshaled, err := yaml.Marshal(tc.scheme)
153+
require.NoError(t, err)
154+
require.Equal(t, tc.want, strings.TrimSpace(string(marshaled)))
155+
})
156+
}
157+
}
158+
159+
func TestValidationScheme_UnmarshalYAML(t *testing.T) {
160+
for _, tc := range []struct {
161+
name string
162+
input string
163+
want ValidationScheme
164+
wantError error
165+
}{
166+
{
167+
name: "Unset empty input",
168+
input: "",
169+
want: UnsetValidation,
170+
},
171+
{
172+
name: "Unset quoted input",
173+
input: `""`,
174+
want: UnsetValidation,
175+
},
176+
{
177+
name: "Legacy",
178+
input: "legacy",
179+
want: LegacyValidation,
180+
},
181+
{
182+
name: "UTF8",
183+
input: "utf8",
184+
want: UTF8Validation,
185+
},
186+
{
187+
name: "Invalid",
188+
input: "invalid",
189+
wantError: errors.New(`unrecognized ValidationScheme: "invalid"`),
190+
},
191+
} {
192+
t.Run(tc.name, func(t *testing.T) {
193+
scheme := UnsetValidation
194+
err := yaml.Unmarshal([]byte(tc.input), &scheme)
195+
if tc.wantError == nil {
196+
require.NoError(t, err)
197+
require.Equal(t, tc.want, scheme)
198+
} else {
199+
require.EqualError(t, err, tc.wantError.Error())
200+
}
201+
})
202+
}
203+
}
204+
92205
func TestMetricNameIsLegacyValid(t *testing.T) {
93206
scenarios := []struct {
94207
mn LabelValue

0 commit comments

Comments
 (0)