Skip to content

Commit eb0be55

Browse files
committed
PRW2: add label validations
Signed-off-by: SungJin1212 <tjdwls1201@gmail.com>
1 parent 5b7b4f5 commit eb0be55

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

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"
@@ -165,15 +167,26 @@ func setPRW2RespHeader(w http.ResponseWriter, samples, histograms, exemplars int
165167
w.Header().Set(rw20WrittenExemplarsHeader, strconv.FormatInt(exemplars, 10))
166168
}
167169

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

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

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

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)