@@ -16,6 +16,7 @@ import (
16
16
"testing"
17
17
"time"
18
18
19
+ "github.com/cortexproject/cortex/pkg/cortexpbv2"
19
20
"github.com/cortexproject/promqlsmith"
20
21
"github.com/google/go-cmp/cmp"
21
22
"github.com/google/go-cmp/cmp/cmpopts"
@@ -52,6 +53,165 @@ func init() {
52
53
}
53
54
}
54
55
56
+ func TestRemoteWriteV1AndV2QueryResultFuzz (t * testing.T ) {
57
+ s , err := e2e .NewScenario (networkName )
58
+ require .NoError (t , err )
59
+ defer s .Close ()
60
+
61
+ // Start dependencies.
62
+ consul1 := e2edb .NewConsulWithName ("consul1" )
63
+ consul2 := e2edb .NewConsulWithName ("consul2" )
64
+ require .NoError (t , s .StartAndWaitReady (consul1 , consul2 ))
65
+
66
+ flags := mergeFlags (
67
+ AlertmanagerLocalFlags (),
68
+ map [string ]string {
69
+ "-store.engine" : blocksStorageEngine ,
70
+ "-blocks-storage.backend" : "filesystem" ,
71
+ "-blocks-storage.tsdb.head-compaction-interval" : "4m" ,
72
+ "-blocks-storage.tsdb.block-ranges-period" : "2h" ,
73
+ "-blocks-storage.tsdb.ship-interval" : "1h" ,
74
+ "-blocks-storage.bucket-store.sync-interval" : "15m" ,
75
+ "-blocks-storage.tsdb.retention-period" : "2h" ,
76
+ "-blocks-storage.bucket-store.index-cache.backend" : tsdb .IndexCacheBackendInMemory ,
77
+ "-querier.query-store-for-labels-enabled" : "true" ,
78
+ // Ingester.
79
+ "-ring.store" : "consul" ,
80
+ // Distributor.
81
+ "-distributor.replication-factor" : "1" ,
82
+ // Store-gateway.
83
+ "-store-gateway.sharding-enabled" : "false" ,
84
+ // alert manager
85
+ "-alertmanager.web.external-url" : "http://localhost/alertmanager" ,
86
+ },
87
+ )
88
+
89
+ // make alert manager config dir
90
+ require .NoError (t , writeFileToSharedDir (s , "alertmanager_configs" , []byte {}))
91
+
92
+ path1 := path .Join (s .SharedDir (), "cortex-1" )
93
+ path2 := path .Join (s .SharedDir (), "cortex-2" )
94
+
95
+ flags1 := mergeFlags (flags , map [string ]string {
96
+ "-blocks-storage.filesystem.dir" : path1 ,
97
+ "-consul.hostname" : consul1 .NetworkHTTPEndpoint (),
98
+ })
99
+ flags2 := mergeFlags (flags , map [string ]string {
100
+ "-blocks-storage.filesystem.dir" : path2 ,
101
+ "-consul.hostname" : consul2 .NetworkHTTPEndpoint (),
102
+ })
103
+ // Start Cortex replicas.
104
+ cortex1 := e2ecortex .NewSingleBinary ("cortex-1" , flags1 , "" )
105
+ cortex2 := e2ecortex .NewSingleBinary ("cortex-2" , flags2 , "" )
106
+ require .NoError (t , s .StartAndWaitReady (cortex1 , cortex2 ))
107
+
108
+ // Wait until Cortex replicas have updated the ring state.
109
+ require .NoError (t , cortex1 .WaitSumMetrics (e2e .Equals (float64 (512 )), "cortex_ring_tokens_total" ))
110
+ require .NoError (t , cortex2 .WaitSumMetrics (e2e .Equals (float64 (512 )), "cortex_ring_tokens_total" ))
111
+
112
+ c1 , err := e2ecortex .NewClient (cortex1 .HTTPEndpoint (), cortex1 .HTTPEndpoint (), "" , "" , "user-1" )
113
+ require .NoError (t , err )
114
+ c2 , err := e2ecortex .NewClient (cortex2 .HTTPEndpoint (), cortex2 .HTTPEndpoint (), "" , "" , "user-1" )
115
+ require .NoError (t , err )
116
+
117
+ now := time .Now ()
118
+ // Push some series to Cortex.
119
+ start := now .Add (- time .Minute * 60 )
120
+ scrapeInterval := 30 * time .Second
121
+
122
+ numSeries := 10
123
+ numSamples := 120
124
+ serieses := make ([]prompb.TimeSeries , numSeries )
125
+ seriesesV2 := make ([]cortexpbv2.TimeSeries , numSeries )
126
+ lbls := make ([]labels.Labels , numSeries )
127
+
128
+ // make v1 series
129
+ for i := 0 ; i < numSeries ; i ++ {
130
+ series := e2e .GenerateSeriesWithSamples ("test_series" , start , scrapeInterval , i * numSamples , numSamples , prompb.Label {Name : "job" , Value : "test" }, prompb.Label {Name : "series" , Value : strconv .Itoa (i )})
131
+ serieses [i ] = series
132
+
133
+ builder := labels .NewBuilder (labels .EmptyLabels ())
134
+ for _ , lbl := range series .Labels {
135
+ builder .Set (lbl .Name , lbl .Value )
136
+ }
137
+ lbls [i ] = builder .Labels ()
138
+ }
139
+ // make v2 series
140
+ for i := 0 ; i < numSeries ; i ++ {
141
+ series := e2e .GenerateSeriesWithSamplesV2 ("test_series" , start , scrapeInterval , i * numSamples , numSamples , prompb.Label {Name : "job" , Value : "test" }, prompb.Label {Name : "series" , Value : strconv .Itoa (i )})
142
+ seriesesV2 [i ] = series
143
+ }
144
+
145
+ res , err := c1 .Push (serieses )
146
+ require .NoError (t , err )
147
+ require .Equal (t , 200 , res .StatusCode )
148
+
149
+ res , err = c2 .PushV2 (seriesesV2 )
150
+ require .NoError (t , err )
151
+ require .Equal (t , 200 , res .StatusCode )
152
+
153
+ waitUntilReady (t , context .Background (), c1 , c2 , `{job="test"}` , start , now )
154
+
155
+ rnd := rand .New (rand .NewSource (now .Unix ()))
156
+ opts := []promqlsmith.Option {
157
+ promqlsmith .WithEnableOffset (true ),
158
+ promqlsmith .WithEnableAtModifier (true ),
159
+ promqlsmith .WithEnabledFunctions (enabledFunctions ),
160
+ }
161
+ ps := promqlsmith .New (rnd , lbls , opts ... )
162
+
163
+ type testCase struct {
164
+ query string
165
+ res1 , res2 model.Value
166
+ err1 , err2 error
167
+ }
168
+
169
+ queryStart := now .Add (- time .Minute * 50 )
170
+ queryEnd := now .Add (- time .Minute * 10 )
171
+ cases := make ([]* testCase , 0 , 500 )
172
+ testRun := 500
173
+ var (
174
+ expr parser.Expr
175
+ query string
176
+ )
177
+ for i := 0 ; i < testRun ; i ++ {
178
+ for {
179
+ expr = ps .WalkRangeQuery ()
180
+ query = expr .Pretty (0 )
181
+ // timestamp is a known function that break with disable chunk trimming.
182
+ if isValidQuery (expr , 5 ) && ! strings .Contains (query , "timestamp" ) {
183
+ break
184
+ }
185
+ }
186
+ res1 , err1 := c1 .QueryRange (query , queryStart , queryEnd , scrapeInterval )
187
+ res2 , err2 := c2 .QueryRange (query , queryStart , queryEnd , scrapeInterval )
188
+ cases = append (cases , & testCase {
189
+ query : query ,
190
+ res1 : res1 ,
191
+ res2 : res2 ,
192
+ err1 : err1 ,
193
+ err2 : err2 ,
194
+ })
195
+ }
196
+
197
+ failures := 0
198
+ for i , tc := range cases {
199
+ qt := "range query"
200
+ if tc .err1 != nil || tc .err2 != nil {
201
+ if ! cmp .Equal (tc .err1 , tc .err2 ) {
202
+ t .Logf ("case %d error mismatch.\n %s: %s\n err1: %v\n err2: %v\n " , i , qt , tc .query , tc .err1 , tc .err2 )
203
+ failures ++
204
+ }
205
+ } else if ! cmp .Equal (tc .res1 , tc .res2 , comparer ) {
206
+ t .Logf ("case %d results mismatch.\n %s: %s\n res1: %s\n res2: %s\n " , i , qt , tc .query , tc .res1 .String (), tc .res2 .String ())
207
+ failures ++
208
+ }
209
+ }
210
+ if failures > 0 {
211
+ require .Failf (t , "finished query fuzzing tests" , "%d test cases failed" , failures )
212
+ }
213
+ }
214
+
55
215
func TestDisableChunkTrimmingFuzz (t * testing.T ) {
56
216
s , err := e2e .NewScenario (networkName )
57
217
require .NoError (t , err )
0 commit comments