@@ -2,6 +2,7 @@ package loki
2
2
3
3
import (
4
4
"bytes"
5
+ "context"
5
6
"flag"
6
7
"fmt"
7
8
"io"
@@ -177,6 +178,13 @@ func getRandomPorts(n int) []int {
177
178
}
178
179
179
180
func TestLoki_CustomRunOptsBehavior (t * testing.T ) {
181
+ t .Parallel ()
182
+ // Set an overall test timeout
183
+ ctx , cancel := context .WithTimeout (context .Background (), 30 * time .Second )
184
+ defer cancel ()
185
+
186
+ // Create a channel to signal test completion
187
+ done := make (chan struct {})
180
188
ports := getRandomPorts (2 )
181
189
httpPort := ports [0 ]
182
190
grpcPort := ports [1 ]
@@ -210,27 +218,33 @@ schema_config:
210
218
require .NoError (t , err )
211
219
212
220
lokiHealthCheck := func () error {
213
- // wait for Loki HTTP server to be ready.
214
- // retries at most 10 times (1 second in total) to avoid infinite loops when no timeout is set.
215
- for i := 0 ; i < 10 ; i ++ {
216
- // waits until request to /ready doesn't error.
217
- resp , err := http .DefaultClient .Get (fmt .Sprintf ("http://localhost:%d/ready" , httpPort ))
218
- if err != nil {
219
- time .Sleep (time .Millisecond * 200 )
220
- continue
221
- }
222
-
223
- // waits until /ready returns OK.
224
- if resp .StatusCode != http .StatusOK {
225
- time .Sleep (time .Millisecond * 200 )
226
- continue
221
+ // Set an overall timeout for health check attempts
222
+ timeout := time .After (5 * time .Second )
223
+ ticker := time .NewTicker (200 * time .Millisecond )
224
+ defer ticker .Stop ()
225
+
226
+ // Loop until timeout or success
227
+ for {
228
+ select {
229
+ case <- timeout :
230
+ return fmt .Errorf ("timeout waiting for loki HTTP to become healthy" )
231
+ case <- ticker .C :
232
+ // Try to connect to the /ready endpoint
233
+ resp , err := http .DefaultClient .Get (fmt .Sprintf ("http://0.0.0.0:%d/ready" , httpPort ))
234
+ if err != nil {
235
+ continue
236
+ }
237
+
238
+ // Check if status is OK
239
+ if resp .StatusCode != http .StatusOK {
240
+ resp .Body .Close ()
241
+ continue
242
+ }
243
+
244
+ resp .Body .Close ()
245
+ return nil // Loki is healthy
227
246
}
228
-
229
- // Loki is healthy.
230
- return nil
231
247
}
232
-
233
- return fmt .Errorf ("loki HTTP not healthy" )
234
248
}
235
249
236
250
customHandlerInvoked := false
@@ -240,24 +254,76 @@ schema_config:
240
254
require .NoError (t , err )
241
255
}
242
256
257
+ // Create a context for server shutdown
258
+ srvCtx , srvCancel := context .WithCancel (context .Background ())
259
+
243
260
// Run Loki querier in a different go routine and with custom /config handler.
244
261
go func () {
245
- err := loki .Run (RunOpts {CustomConfigEndpointHandlerFn : customHandler })
262
+ defer close (done )
263
+ runOpts := RunOpts {
264
+ CustomConfigEndpointHandlerFn : customHandler ,
265
+ }
266
+
267
+ // Create a separate goroutine to stop Loki when test is done or times out
268
+ go func () {
269
+ select {
270
+ case <- ctx .Done ():
271
+ t .Logf ("Test timeout reached, stopping Loki server" )
272
+ loki .SignalHandler .Stop ()
273
+ case <- srvCtx .Done ():
274
+ t .Logf ("Test completed, stopping Loki server" )
275
+ loki .SignalHandler .Stop ()
276
+ }
277
+ }()
278
+
279
+ err := loki .Run (runOpts )
246
280
require .NoError (t , err )
247
281
}()
248
282
249
- err = lokiHealthCheck ()
250
- require .NoError (t , err )
283
+ // Run the test in a goroutine to handle timeout properly
284
+ go func () {
285
+ // Using a separate function to handle any panics in the test goroutine
286
+ defer srvCancel () // Always cancel the context when this function exits
251
287
252
- resp , err := http .DefaultClient .Get (fmt .Sprintf ("http://localhost:%d/config" , httpPort ))
253
- require .NoError (t , err )
288
+ // Wait for Loki to be healthy
289
+ err = lokiHealthCheck ()
290
+ if err != nil {
291
+ t .Errorf ("Health check failed: %v" , err )
292
+ return
293
+ }
254
294
255
- defer resp .Body .Close ()
295
+ // Make request to custom handler
296
+ resp , err := http .DefaultClient .Get (fmt .Sprintf ("http://0.0.0.0:%d/config" , httpPort ))
297
+ if err != nil {
298
+ t .Errorf ("Failed to get config: %v" , err )
299
+ return
300
+ }
301
+ defer resp .Body .Close ()
256
302
257
- bBytes , err := io .ReadAll (resp .Body )
258
- require .NoError (t , err )
259
- require .Equal (t , string (bBytes ), "abc" )
260
- assert .True (t , customHandlerInvoked )
303
+ bBytes , err := io .ReadAll (resp .Body )
304
+ if err != nil {
305
+ t .Errorf ("Failed to read response: %v" , err )
306
+ return
307
+ }
308
+
309
+ // Verify results
310
+ if string (bBytes ) != "abc" {
311
+ t .Errorf ("Expected response 'abc', got '%s'" , string (bBytes ))
312
+ }
313
+ if ! customHandlerInvoked {
314
+ t .Errorf ("Custom handler was not invoked" )
315
+ }
316
+ }()
317
+
318
+ // Wait for either test completion or timeout
319
+ select {
320
+ case <- ctx .Done ():
321
+ t .Fatalf ("Test timed out after 30 seconds" )
322
+ case <- done :
323
+ // Test completed successfully
324
+ }
325
+
326
+ // Clean up metrics
261
327
unregisterLokiMetrics (loki )
262
328
}
263
329
0 commit comments