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.
218 lines
6.6 KiB
218 lines
6.6 KiB
|
4 years ago
|
package queryrangebase
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/cortexproject/cortex/pkg/util"
|
||
|
|
"github.com/stretchr/testify/assert"
|
||
|
|
"github.com/stretchr/testify/mock"
|
||
|
|
"github.com/stretchr/testify/require"
|
||
|
|
"github.com/weaveworks/common/user"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestLimitsMiddleware_MaxQueryLookback(t *testing.T) {
|
||
|
|
const (
|
||
|
|
thirtyDays = 30 * 24 * time.Hour
|
||
|
|
)
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
|
||
|
|
tests := map[string]struct {
|
||
|
|
maxQueryLookback time.Duration
|
||
|
|
reqStartTime time.Time
|
||
|
|
reqEndTime time.Time
|
||
|
|
expectedSkipped bool
|
||
|
|
expectedStartTime time.Time
|
||
|
|
expectedEndTime time.Time
|
||
|
|
}{
|
||
|
|
"should not manipulate time range if max lookback is disabled": {
|
||
|
|
maxQueryLookback: 0,
|
||
|
|
reqStartTime: time.Unix(0, 0),
|
||
|
|
reqEndTime: now,
|
||
|
|
expectedStartTime: time.Unix(0, 0),
|
||
|
|
expectedEndTime: now,
|
||
|
|
},
|
||
|
|
"should not manipulate time range for a query on short time range": {
|
||
|
|
maxQueryLookback: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
expectedStartTime: now.Add(-time.Hour),
|
||
|
|
expectedEndTime: now,
|
||
|
|
},
|
||
|
|
"should not manipulate a query on large time range close to the limit": {
|
||
|
|
maxQueryLookback: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-thirtyDays).Add(time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
expectedStartTime: now.Add(-thirtyDays).Add(time.Hour),
|
||
|
|
expectedEndTime: now,
|
||
|
|
},
|
||
|
|
"should manipulate a query on large time range over the limit": {
|
||
|
|
maxQueryLookback: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-thirtyDays).Add(-100 * time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
expectedStartTime: now.Add(-thirtyDays),
|
||
|
|
expectedEndTime: now,
|
||
|
|
},
|
||
|
|
"should skip executing a query outside the allowed time range": {
|
||
|
|
maxQueryLookback: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-thirtyDays).Add(-100 * time.Hour),
|
||
|
|
reqEndTime: now.Add(-thirtyDays).Add(-90 * time.Hour),
|
||
|
|
expectedSkipped: true,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for testName, testData := range tests {
|
||
|
|
t.Run(testName, func(t *testing.T) {
|
||
|
|
req := &PrometheusRequest{
|
||
|
|
Start: util.TimeToMillis(testData.reqStartTime),
|
||
|
|
End: util.TimeToMillis(testData.reqEndTime),
|
||
|
|
}
|
||
|
|
|
||
|
|
limits := mockLimits{maxQueryLookback: testData.maxQueryLookback}
|
||
|
|
middleware := NewLimitsMiddleware(limits)
|
||
|
|
|
||
|
|
innerRes := NewEmptyPrometheusResponse()
|
||
|
|
inner := &mockHandler{}
|
||
|
|
inner.On("Do", mock.Anything, mock.Anything).Return(innerRes, nil)
|
||
|
|
|
||
|
|
ctx := user.InjectOrgID(context.Background(), "test")
|
||
|
|
outer := middleware.Wrap(inner)
|
||
|
|
res, err := outer.Do(ctx, req)
|
||
|
|
require.NoError(t, err)
|
||
|
|
|
||
|
|
if testData.expectedSkipped {
|
||
|
|
// We expect an empty response, but not the one returned by the inner handler
|
||
|
|
// which we expect has been skipped.
|
||
|
|
assert.NotSame(t, innerRes, res)
|
||
|
|
assert.Len(t, inner.Calls, 0)
|
||
|
|
} else {
|
||
|
|
// We expect the response returned by the inner handler.
|
||
|
|
assert.Same(t, innerRes, res)
|
||
|
|
|
||
|
|
// Assert on the time range of the request passed to the inner handler (5s delta).
|
||
|
|
delta := float64(5000)
|
||
|
|
require.Len(t, inner.Calls, 1)
|
||
|
|
assert.InDelta(t, util.TimeToMillis(testData.expectedStartTime), inner.Calls[0].Arguments.Get(1).(Request).GetStart(), delta)
|
||
|
|
assert.InDelta(t, util.TimeToMillis(testData.expectedEndTime), inner.Calls[0].Arguments.Get(1).(Request).GetEnd(), delta)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestLimitsMiddleware_MaxQueryLength(t *testing.T) {
|
||
|
|
const (
|
||
|
|
thirtyDays = 30 * 24 * time.Hour
|
||
|
|
)
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
|
||
|
|
tests := map[string]struct {
|
||
|
|
maxQueryLength time.Duration
|
||
|
|
reqStartTime time.Time
|
||
|
|
reqEndTime time.Time
|
||
|
|
expectedErr string
|
||
|
|
}{
|
||
|
|
"should skip validation if max length is disabled": {
|
||
|
|
maxQueryLength: 0,
|
||
|
|
reqStartTime: time.Unix(0, 0),
|
||
|
|
reqEndTime: now,
|
||
|
|
},
|
||
|
|
"should succeed on a query on short time range, ending now": {
|
||
|
|
maxQueryLength: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
},
|
||
|
|
"should succeed on a query on short time range, ending in the past": {
|
||
|
|
maxQueryLength: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-2 * thirtyDays).Add(-time.Hour),
|
||
|
|
reqEndTime: now.Add(-2 * thirtyDays),
|
||
|
|
},
|
||
|
|
"should succeed on a query on large time range close to the limit, ending now": {
|
||
|
|
maxQueryLength: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-thirtyDays).Add(time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
},
|
||
|
|
"should fail on a query on large time range over the limit, ending now": {
|
||
|
|
maxQueryLength: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-thirtyDays).Add(-100 * time.Hour),
|
||
|
|
reqEndTime: now,
|
||
|
|
expectedErr: "the query time range exceeds the limit",
|
||
|
|
},
|
||
|
|
"should fail on a query on large time range over the limit, ending in the past": {
|
||
|
|
maxQueryLength: thirtyDays,
|
||
|
|
reqStartTime: now.Add(-4 * thirtyDays),
|
||
|
|
reqEndTime: now.Add(-2 * thirtyDays),
|
||
|
|
expectedErr: "the query time range exceeds the limit",
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
for testName, testData := range tests {
|
||
|
|
t.Run(testName, func(t *testing.T) {
|
||
|
|
req := &PrometheusRequest{
|
||
|
|
Start: util.TimeToMillis(testData.reqStartTime),
|
||
|
|
End: util.TimeToMillis(testData.reqEndTime),
|
||
|
|
}
|
||
|
|
|
||
|
|
limits := mockLimits{maxQueryLength: testData.maxQueryLength}
|
||
|
|
middleware := NewLimitsMiddleware(limits)
|
||
|
|
|
||
|
|
innerRes := NewEmptyPrometheusResponse()
|
||
|
|
inner := &mockHandler{}
|
||
|
|
inner.On("Do", mock.Anything, mock.Anything).Return(innerRes, nil)
|
||
|
|
|
||
|
|
ctx := user.InjectOrgID(context.Background(), "test")
|
||
|
|
outer := middleware.Wrap(inner)
|
||
|
|
res, err := outer.Do(ctx, req)
|
||
|
|
|
||
|
|
if testData.expectedErr != "" {
|
||
|
|
require.Error(t, err)
|
||
|
|
assert.Contains(t, err.Error(), testData.expectedErr)
|
||
|
|
assert.Nil(t, res)
|
||
|
|
assert.Len(t, inner.Calls, 0)
|
||
|
|
} else {
|
||
|
|
// We expect the response returned by the inner handler.
|
||
|
|
require.NoError(t, err)
|
||
|
|
assert.Same(t, innerRes, res)
|
||
|
|
|
||
|
|
// The time range of the request passed to the inner handler should have not been manipulated.
|
||
|
|
require.Len(t, inner.Calls, 1)
|
||
|
|
assert.Equal(t, util.TimeToMillis(testData.reqStartTime), inner.Calls[0].Arguments.Get(1).(Request).GetStart())
|
||
|
|
assert.Equal(t, util.TimeToMillis(testData.reqEndTime), inner.Calls[0].Arguments.Get(1).(Request).GetEnd())
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
type mockLimits struct {
|
||
|
|
maxQueryLookback time.Duration
|
||
|
|
maxQueryLength time.Duration
|
||
|
|
maxCacheFreshness time.Duration
|
||
|
|
}
|
||
|
|
|
||
|
|
func (m mockLimits) MaxQueryLookback(string) time.Duration {
|
||
|
|
return m.maxQueryLookback
|
||
|
|
}
|
||
|
|
|
||
|
|
func (m mockLimits) MaxQueryLength(string) time.Duration {
|
||
|
|
return m.maxQueryLength
|
||
|
|
}
|
||
|
|
|
||
|
|
func (mockLimits) MaxQueryParallelism(string) int {
|
||
|
|
return 14 // Flag default.
|
||
|
|
}
|
||
|
|
|
||
|
|
func (m mockLimits) MaxCacheFreshness(string) time.Duration {
|
||
|
|
return m.maxCacheFreshness
|
||
|
|
}
|
||
|
|
|
||
|
|
type mockHandler struct {
|
||
|
|
mock.Mock
|
||
|
|
}
|
||
|
|
|
||
|
|
func (m *mockHandler) Do(ctx context.Context, req Request) (Response, error) {
|
||
|
|
args := m.Called(ctx, req)
|
||
|
|
return args.Get(0).(Response), args.Error(1)
|
||
|
|
}
|