mirror of https://github.com/grafana/loki
Loki: refactor validation and improve error messages (#2021)
* copies some of the validation logic from cortex and modifies it to appropriate for Loki, this gives us a single metric we can use for detetecting validation errors (previously we had to check cortex_ and loki_ metrics) as well as lets us use error messages more appropriate for logs than metrics. Signed-off-by: Ed Welch <edward.welch@grafana.com> * fixing tests and lint Signed-off-by: Ed Welch <edward.welch@grafana.com> * updating cause to string type Signed-off-by: Ed Welch <edward.welch@grafana.com> * extracting a function for rate limited Signed-off-by: Ed Welch <edward.welch@grafana.com> * extracting functions for the rest of the error strings Signed-off-by: Ed Welch <edward.welch@grafana.com>pull/2022/head
parent
0d9cf4946a
commit
3dfe89369f
@ -0,0 +1,154 @@ |
||||
package distributor |
||||
|
||||
import ( |
||||
"github.com/cortexproject/cortex/pkg/util/flagext" |
||||
"github.com/grafana/loki/pkg/logproto" |
||||
"github.com/grafana/loki/pkg/util/validation" |
||||
"github.com/stretchr/testify/assert" |
||||
"github.com/weaveworks/common/httpgrpc" |
||||
"net/http" |
||||
"testing" |
||||
"time" |
||||
) |
||||
|
||||
var testStreamLabels = "FIXME" |
||||
var testTime = time.Now() |
||||
|
||||
func TestValidator_ValidateEntry(t *testing.T) { |
||||
tests := []struct { |
||||
name string |
||||
userID string |
||||
overrides validation.TenantLimits |
||||
entry logproto.Entry |
||||
expected error |
||||
}{ |
||||
{ |
||||
"test valid", |
||||
"test", |
||||
nil, |
||||
logproto.Entry{Timestamp: testTime, Line: "test"}, |
||||
nil, |
||||
}, |
||||
{ |
||||
"test too old", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{ |
||||
RejectOldSamples: true, |
||||
RejectOldSamplesMaxAge: 1 * time.Hour, |
||||
} |
||||
}, |
||||
logproto.Entry{Timestamp: testTime.Add(-time.Hour * 5), Line: "test"}, |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.GreaterThanMaxSampleAgeErrorMsg(testStreamLabels, testTime.Add(-time.Hour*5))), |
||||
}, |
||||
{ |
||||
"test too new", |
||||
"test", |
||||
nil, |
||||
logproto.Entry{Timestamp: testTime.Add(time.Hour * 5), Line: "test"}, |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.TooFarInFutureErrorMsg(testStreamLabels, testTime.Add(time.Hour*5))), |
||||
}, |
||||
{ |
||||
"line too long", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{ |
||||
MaxLineSize: 10, |
||||
} |
||||
}, |
||||
logproto.Entry{Timestamp: testTime, Line: "12345678901"}, |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.LineTooLongErrorMsg(10, 11, testStreamLabels)), |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
l := &validation.Limits{} |
||||
flagext.DefaultValues(l) |
||||
o, err := validation.NewOverrides(*l, tt.overrides) |
||||
assert.NoError(t, err) |
||||
v, err := NewValidator(o) |
||||
assert.NoError(t, err) |
||||
|
||||
err = v.ValidateEntry(tt.userID, testStreamLabels, tt.entry) |
||||
assert.Equal(t, tt.expected, err) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
func TestValidator_ValidateLabels(t *testing.T) { |
||||
tests := []struct { |
||||
name string |
||||
userID string |
||||
overrides validation.TenantLimits |
||||
labels string |
||||
expected error |
||||
}{ |
||||
{ |
||||
"test valid", |
||||
"test", |
||||
nil, |
||||
"{foo=\"bar\"}", |
||||
nil, |
||||
}, |
||||
{ |
||||
"test too many labels", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{MaxLabelNamesPerSeries: 2} |
||||
}, |
||||
"{foo=\"bar\",food=\"bars\",fed=\"bears\"}", |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.MaxLabelNamesPerSeriesErrorMsg("{fed=\"bears\", foo=\"bar\", food=\"bars\"}", 3, 2)), |
||||
}, |
||||
{ |
||||
"label name too long", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{ |
||||
MaxLabelNamesPerSeries: 2, |
||||
MaxLabelNameLength: 5, |
||||
} |
||||
}, |
||||
"{fooooo=\"bar\"}", |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.LabelNameTooLongErrorMsg("{fooooo=\"bar\"}", "fooooo")), |
||||
}, |
||||
{ |
||||
"label value too long", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{ |
||||
MaxLabelNamesPerSeries: 2, |
||||
MaxLabelNameLength: 5, |
||||
MaxLabelValueLength: 5, |
||||
} |
||||
}, |
||||
"{foo=\"barrrrrr\"}", |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.LabelValueTooLongErrorMsg("{foo=\"barrrrrr\"}", "barrrrrr")), |
||||
}, |
||||
{ |
||||
"duplicate label", |
||||
"test", |
||||
func(userID string) *validation.Limits { |
||||
return &validation.Limits{ |
||||
MaxLabelNamesPerSeries: 2, |
||||
MaxLabelNameLength: 5, |
||||
MaxLabelValueLength: 5, |
||||
} |
||||
}, |
||||
"{foo=\"bar\", foo=\"barf\"}", |
||||
httpgrpc.Errorf(http.StatusBadRequest, validation.DuplicateLabelNamesErrorMsg("{foo=\"bar\", foo=\"barf\"}", "foo")), |
||||
}, |
||||
} |
||||
for _, tt := range tests { |
||||
t.Run(tt.name, func(t *testing.T) { |
||||
l := &validation.Limits{} |
||||
flagext.DefaultValues(l) |
||||
o, err := validation.NewOverrides(*l, tt.overrides) |
||||
assert.NoError(t, err) |
||||
v, err := NewValidator(o) |
||||
assert.NoError(t, err) |
||||
|
||||
err = v.ValidateLabels(tt.userID, &logproto.Stream{Labels: tt.labels}) |
||||
assert.Equal(t, tt.expected, err) |
||||
}) |
||||
} |
||||
} |
Loading…
Reference in new issue