mirror of https://github.com/grafana/loki
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
5.5 KiB
238 lines
5.5 KiB
|
3 years ago
|
package validation_test
|
||
|
4 years ago
|
|
||
|
|
import (
|
||
|
3 years ago
|
"context"
|
||
|
4 years ago
|
"testing"
|
||
|
|
|
||
|
4 years ago
|
"github.com/grafana/loki/operator/apis/loki/v1beta1"
|
||
|
3 years ago
|
"github.com/grafana/loki/operator/internal/validation"
|
||
|
4 years ago
|
|
||
|
3 years ago
|
"github.com/stretchr/testify/require"
|
||
|
4 years ago
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||
|
|
)
|
||
|
|
|
||
|
|
var rtt = []struct {
|
||
|
|
desc string
|
||
|
|
spec v1beta1.RecordingRuleSpec
|
||
|
|
err *apierrors.StatusError
|
||
|
|
}{
|
||
|
|
{
|
||
|
|
desc: "valid spec",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
Rules: []*v1beta1.RecordingRuleGroupSpec{
|
||
|
|
{
|
||
|
|
Record: "valid:record:name",
|
||
|
|
Expr: `sum(rate({app="foo", env="production"} |= "error" [5m])) by (job)`,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Record: "valid:second:name",
|
||
|
|
Expr: `sum(rate({app="foo", env="stage"} |= "error" [5m])) by (job)`,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "second",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
Rules: []*v1beta1.RecordingRuleGroupSpec{
|
||
|
|
{
|
||
|
|
Record: "nginx:requests:rate1m",
|
||
|
|
Expr: `sum(rate({container="nginx"}[1m]))`,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Record: "banana:requests:rate5m",
|
||
|
|
Expr: `sum(rate({container="banana"}[1m]))`,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
{
|
||
|
|
desc: "not unique group names",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
err: apierrors.NewInvalid(
|
||
|
|
schema.GroupKind{Group: "loki.grafana.com", Kind: "RecordingRule"},
|
||
|
|
"testing-rule",
|
||
|
|
field.ErrorList{
|
||
|
|
field.Invalid(
|
||
|
|
field.NewPath("Spec").Child("Groups").Index(1).Child("Name"),
|
||
|
|
"first",
|
||
|
|
v1beta1.ErrGroupNamesNotUnique.Error(),
|
||
|
|
),
|
||
|
|
},
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
desc: "parse eval interval err",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1mo"),
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
err: apierrors.NewInvalid(
|
||
|
|
schema.GroupKind{Group: "loki.grafana.com", Kind: "RecordingRule"},
|
||
|
|
"testing-rule",
|
||
|
|
field.ErrorList{
|
||
|
|
field.Invalid(
|
||
|
|
field.NewPath("Spec").Child("Groups").Index(0).Child("Interval"),
|
||
|
|
"1mo",
|
||
|
|
v1beta1.ErrParseEvaluationInterval.Error(),
|
||
|
|
),
|
||
|
|
},
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
desc: "invalid record metric name",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
Rules: []*v1beta1.RecordingRuleGroupSpec{
|
||
|
|
{
|
||
|
|
Record: "invalid&metric:name",
|
||
|
|
Expr: `sum(rate({label="value"}[1m]))`,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
err: apierrors.NewInvalid(
|
||
|
|
schema.GroupKind{Group: "loki.grafana.com", Kind: "RecordingRule"},
|
||
|
|
"testing-rule",
|
||
|
|
field.ErrorList{
|
||
|
|
field.Invalid(
|
||
|
|
field.NewPath("Spec").Child("Groups").Index(0).Child("Rules").Index(0).Child("Record"),
|
||
|
|
"invalid&metric:name",
|
||
|
|
v1beta1.ErrInvalidRecordMetricName.Error(),
|
||
|
|
),
|
||
|
|
},
|
||
|
|
),
|
||
|
|
},
|
||
|
|
{
|
||
|
|
desc: "parse LogQL expression err",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
Rules: []*v1beta1.RecordingRuleGroupSpec{
|
||
|
|
{
|
||
|
|
Expr: "this is not a valid expression",
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
err: apierrors.NewInvalid(
|
||
|
|
schema.GroupKind{Group: "loki.grafana.com", Kind: "RecordingRule"},
|
||
|
|
"testing-rule",
|
||
|
|
field.ErrorList{
|
||
|
|
field.Invalid(
|
||
|
|
field.NewPath("Spec").Child("Groups").Index(0).Child("Rules").Index(0).Child("Expr"),
|
||
|
|
"this is not a valid expression",
|
||
|
|
v1beta1.ErrParseLogQLExpression.Error(),
|
||
|
|
),
|
||
|
|
},
|
||
|
|
),
|
||
|
|
},
|
||
|
3 years ago
|
{
|
||
|
|
desc: "LogQL not sample-expression",
|
||
|
|
spec: v1beta1.RecordingRuleSpec{
|
||
|
|
Groups: []*v1beta1.RecordingRuleGroup{
|
||
|
|
{
|
||
|
|
Name: "first",
|
||
|
|
Interval: v1beta1.PrometheusDuration("1m"),
|
||
|
|
Rules: []*v1beta1.RecordingRuleGroupSpec{
|
||
|
|
{
|
||
|
|
Expr: `{message=~".+"}`,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
},
|
||
|
|
err: apierrors.NewInvalid(
|
||
|
|
schema.GroupKind{Group: "loki.grafana.com", Kind: "RecordingRule"},
|
||
|
|
"testing-rule",
|
||
|
|
field.ErrorList{
|
||
|
|
field.Invalid(
|
||
|
|
field.NewPath("Spec").Child("Groups").Index(0).Child("Rules").Index(0).Child("Expr"),
|
||
|
|
`{message=~".+"}`,
|
||
|
|
v1beta1.ErrParseLogQLNotSample.Error(),
|
||
|
|
),
|
||
|
|
},
|
||
|
|
),
|
||
|
|
},
|
||
|
4 years ago
|
}
|
||
|
|
|
||
|
|
func TestRecordingRuleValidationWebhook_ValidateCreate(t *testing.T) {
|
||
|
|
for _, tc := range rtt {
|
||
|
|
tc := tc
|
||
|
|
t.Run(tc.desc, func(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
3 years ago
|
|
||
|
|
ctx := context.Background()
|
||
|
|
l := &v1beta1.RecordingRule{
|
||
|
4 years ago
|
ObjectMeta: metav1.ObjectMeta{
|
||
|
|
Name: "testing-rule",
|
||
|
|
},
|
||
|
|
Spec: tc.spec,
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
v := &validation.RecordingRuleValidator{}
|
||
|
|
err := v.ValidateCreate(ctx, l)
|
||
|
4 years ago
|
if err != nil {
|
||
|
|
require.Equal(t, tc.err, err)
|
||
|
|
} else {
|
||
|
|
require.NoError(t, err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestRecordingRuleValidationWebhook_ValidateUpdate(t *testing.T) {
|
||
|
|
for _, tc := range rtt {
|
||
|
|
tc := tc
|
||
|
|
t.Run(tc.desc, func(t *testing.T) {
|
||
|
|
t.Parallel()
|
||
|
3 years ago
|
|
||
|
|
ctx := context.Background()
|
||
|
|
l := &v1beta1.RecordingRule{
|
||
|
4 years ago
|
ObjectMeta: metav1.ObjectMeta{
|
||
|
|
Name: "testing-rule",
|
||
|
|
},
|
||
|
|
Spec: tc.spec,
|
||
|
|
}
|
||
|
|
|
||
|
3 years ago
|
v := &validation.RecordingRuleValidator{}
|
||
|
|
err := v.ValidateUpdate(ctx, &v1beta1.RecordingRule{}, l)
|
||
|
4 years ago
|
if err != nil {
|
||
|
|
require.Equal(t, tc.err, err)
|
||
|
|
} else {
|
||
|
|
require.NoError(t, err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|