Skip to content

Commit 21b09ab

Browse files
committed
PRW2: add label validations
Signed-off-by: SungJin1212 <tjdwls1201@gmail.com>
1 parent 821304f commit 21b09ab

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

pkg/util/push/push.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package push
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"net/http"
78
"strconv"
89

910
"github.com/go-kit/log/level"
1011
"github.com/prometheus/client_golang/exp/api/remote"
12+
"github.com/prometheus/common/model"
1113
"github.com/prometheus/prometheus/model/labels"
1214
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
1315
"github.com/prometheus/prometheus/util/compression"
@@ -164,15 +166,26 @@ func setPRW2RespHeader(w http.ResponseWriter, samples, histograms, exemplars int
164166
w.Header().Set(rw20WrittenExemplarsHeader, strconv.FormatInt(exemplars, 10))
165167
}
166168

167-
func convertV2RequestToV1(req *writev2.Request) (cortexpb.PreallocWriteRequest, error) {
168-
var v1Req cortexpb.PreallocWriteRequest
169+
func convertV2RequestToV1(req *writev2.Request) (v1Req cortexpb.PreallocWriteRequest, err error) {
170+
var (
171+
badLabelErrs []error
172+
)
173+
169174
v1Timeseries := make([]cortexpb.PreallocTimeseries, 0, len(req.Timeseries))
170175
var v1Metadata []*cortexpb.MetricMetadata
171176

172177
b := labels.NewScratchBuilder(0)
173178
symbols := req.Symbols
174179
for _, v2Ts := range req.Timeseries {
175180
lbs := v2Ts.ToLabels(&b, symbols)
181+
// PRW2 spec allows UTF-8.
182+
// c.f. https://prometheus.io/docs/specs/prw/remote_write_spec_2_0/#series-labels
183+
if !lbs.Has(labels.MetricName) || !lbs.IsValid(model.UTF8Validation) {
184+
badLabelErrs = append(badLabelErrs, fmt.Errorf("invalid metric name or labels, got %v", lbs.String()))
185+
continue
186+
} else if duplicateLabel, hasDuplicate := lbs.HasDuplicateLabelNames(); hasDuplicate {
187+
badLabelErrs = append(badLabelErrs, fmt.Errorf("invalid labels for series, labels %v, duplicated label %s", lbs.String(), duplicateLabel))
188+
}
176189
v1Timeseries = append(v1Timeseries, cortexpb.PreallocTimeseries{
177190
TimeSeries: &cortexpb.TimeSeries{
178191
Labels: cortexpb.FromLabelsToLabelAdapters(lbs),
@@ -191,6 +204,10 @@ func convertV2RequestToV1(req *writev2.Request) (cortexpb.PreallocWriteRequest,
191204
}
192205
}
193206

207+
if len(badLabelErrs) > 0 {
208+
return v1Req, errors.Join(badLabelErrs...)
209+
}
210+
194211
v1Req.Timeseries = v1Timeseries
195212
v1Req.Metadata = v1Metadata
196213

pkg/util/push/push_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,57 @@ func Benchmark_convertV2RequestToV1(b *testing.B) {
160160
}
161161
}
162162

163+
func Test_convertV2RequestToV1_InvalidLabels(t *testing.T) {
164+
symbols := []string{"", "__name__", "test_metric", "b", "c", "baz", "qux", "d", "e", "foo", "bar", "f", "g", "h", "i", "Test gauge for test purposes", "Maybe op/sec who knows (:", "Test counter for test purposes"}
165+
166+
tests := []struct {
167+
desc string
168+
ts []writev2.TimeSeries
169+
expectedErrMsg string
170+
}{
171+
{
172+
desc: "no metric name",
173+
ts: []writev2.TimeSeries{
174+
{
175+
LabelsRefs: []uint32{2, 3},
176+
Samples: []writev2.Sample{{Value: 2, Timestamp: 2}},
177+
},
178+
},
179+
expectedErrMsg: "invalid metric name or labels, got {test_metric=\"b\"}",
180+
},
181+
{
182+
desc: "no label",
183+
ts: []writev2.TimeSeries{
184+
{
185+
LabelsRefs: []uint32{},
186+
Samples: []writev2.Sample{{Value: 2, Timestamp: 2}},
187+
},
188+
},
189+
expectedErrMsg: "invalid metric name or labels, got {}",
190+
},
191+
{
192+
desc: "duplicate label",
193+
ts: []writev2.TimeSeries{
194+
{
195+
LabelsRefs: []uint32{1, 2, 2, 2, 2, 2},
196+
Samples: []writev2.Sample{{Value: 2, Timestamp: 2}},
197+
},
198+
},
199+
expectedErrMsg: "invalid labels for series, labels {__name__=\"test_metric\", test_metric=\"test_metric\", test_metric=\"test_metric\"}, duplicated label test_metric",
200+
},
201+
}
202+
203+
for _, test := range tests {
204+
t.Run(test.desc, func(t *testing.T) {
205+
var v2Req writev2.Request
206+
v2Req.Symbols = symbols
207+
v2Req.Timeseries = test.ts
208+
_, err := convertV2RequestToV1(&v2Req)
209+
require.EqualError(t, err, test.expectedErrMsg)
210+
})
211+
}
212+
}
213+
163214
func Test_convertV2RequestToV1(t *testing.T) {
164215
var v2Req writev2.Request
165216

0 commit comments

Comments
 (0)