diff --git a/.github/workflows/nix-ci.yaml b/.github/workflows/nix-ci.yaml index 6402996ba1672..b2c62d286ac74 100644 --- a/.github/workflows/nix-ci.yaml +++ b/.github/workflows/nix-ci.yaml @@ -20,23 +20,6 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - run: nix run --print-build-logs .#lint - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v30 - with: - nix_path: nixpkgs=channel:nixos-unstable - - uses: dorny/paths-filter@v3 - id: nix-changes - with: - filters: | - nix: - - 'nix/**' - - 'flake.nix' - - 'flake.lock' - - run: nix run --print-build-logs .#test - if: steps.nix-changes.outputs.nix == 'true' packages: runs-on: ubuntu-latest steps: diff --git a/flake.lock b/flake.lock index adf909e7d8975..68e20edc58acc 100644 --- a/flake.lock +++ b/flake.lock @@ -20,16 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1734424634, - "narHash": "sha256-cHar1vqHOOyC7f1+tVycPoWTfKIaqkoe1Q6TnKzuti4=", + "lastModified": 1741037377, + "narHash": "sha256-SvtvVKHaUX4Owb+PasySwZsoc5VUeTf1px34BByiOxw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "d3c42f187194c26d9f0309a8ecc469d6c878ce33", + "rev": "02032da4af073d0f6110540c8677f16d4be0117f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-unstable", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 32642f11d110d..6cc10dc1182ff 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Grafana Loki"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; @@ -33,37 +33,6 @@ '') }/bin/lint.sh"; }; - - test = { - type = "app"; - program = - let - loki = packages.loki.overrideAttrs (old: { - buildInputs = with pkgs; lib.optionals stdenv.hostPlatform.isLinux [ systemd.dev ]; - doCheck = true; - checkFlags = [ - "-covermode=atomic" - "-coverprofile=coverage.txt" - "-p=4" - ]; - subPackages = [ - "./..." # for tests - "cmd/loki" - "cmd/logcli" - "cmd/loki-canary" - "clients/cmd/promtail" - ]; - }); - in - "${ - (pkgs.writeShellScriptBin "test.sh" '' - ${loki}/bin/loki --version - ${loki}/bin/logcli --version - ${loki}/bin/loki-canary --version - ${loki}/bin/promtail --version - '') - }/bin/test.sh"; - }; }; devShell = pkgs.mkShell { diff --git a/nix/packages/loki.nix b/nix/packages/loki.nix index 28f215155b36a..a2ab761210acb 100644 --- a/nix/packages/loki.nix +++ b/nix/packages/loki.nix @@ -1,5 +1,5 @@ { pkgs, version, imageTag, lib }: -pkgs.buildGo123Module { +pkgs.buildGo124Module { inherit version; pname = "loki"; diff --git a/pkg/loki/loki_test.go b/pkg/loki/loki_test.go index 3d5f774247754..05377c9ac0c55 100644 --- a/pkg/loki/loki_test.go +++ b/pkg/loki/loki_test.go @@ -2,6 +2,7 @@ package loki import ( "bytes" + "context" "flag" "fmt" "io" @@ -177,6 +178,13 @@ func getRandomPorts(n int) []int { } func TestLoki_CustomRunOptsBehavior(t *testing.T) { + t.Parallel() + // Set an overall test timeout + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Create a channel to signal test completion + done := make(chan struct{}) ports := getRandomPorts(2) httpPort := ports[0] grpcPort := ports[1] @@ -210,27 +218,33 @@ schema_config: require.NoError(t, err) lokiHealthCheck := func() error { - // wait for Loki HTTP server to be ready. - // retries at most 10 times (1 second in total) to avoid infinite loops when no timeout is set. - for i := 0; i < 10; i++ { - // waits until request to /ready doesn't error. - resp, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:%d/ready", httpPort)) - if err != nil { - time.Sleep(time.Millisecond * 200) - continue - } - - // waits until /ready returns OK. - if resp.StatusCode != http.StatusOK { - time.Sleep(time.Millisecond * 200) - continue + // Set an overall timeout for health check attempts + timeout := time.After(5 * time.Second) + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() + + // Loop until timeout or success + for { + select { + case <-timeout: + return fmt.Errorf("timeout waiting for loki HTTP to become healthy") + case <-ticker.C: + // Try to connect to the /ready endpoint + resp, err := http.DefaultClient.Get(fmt.Sprintf("http://0.0.0.0:%d/ready", httpPort)) + if err != nil { + continue + } + + // Check if status is OK + if resp.StatusCode != http.StatusOK { + resp.Body.Close() + continue + } + + resp.Body.Close() + return nil // Loki is healthy } - - // Loki is healthy. - return nil } - - return fmt.Errorf("loki HTTP not healthy") } customHandlerInvoked := false @@ -240,24 +254,76 @@ schema_config: require.NoError(t, err) } + // Create a context for server shutdown + srvCtx, srvCancel := context.WithCancel(context.Background()) + // Run Loki querier in a different go routine and with custom /config handler. go func() { - err := loki.Run(RunOpts{CustomConfigEndpointHandlerFn: customHandler}) + defer close(done) + runOpts := RunOpts{ + CustomConfigEndpointHandlerFn: customHandler, + } + + // Create a separate goroutine to stop Loki when test is done or times out + go func() { + select { + case <-ctx.Done(): + t.Logf("Test timeout reached, stopping Loki server") + loki.SignalHandler.Stop() + case <-srvCtx.Done(): + t.Logf("Test completed, stopping Loki server") + loki.SignalHandler.Stop() + } + }() + + err := loki.Run(runOpts) require.NoError(t, err) }() - err = lokiHealthCheck() - require.NoError(t, err) + // Run the test in a goroutine to handle timeout properly + go func() { + // Using a separate function to handle any panics in the test goroutine + defer srvCancel() // Always cancel the context when this function exits - resp, err := http.DefaultClient.Get(fmt.Sprintf("http://localhost:%d/config", httpPort)) - require.NoError(t, err) + // Wait for Loki to be healthy + err = lokiHealthCheck() + if err != nil { + t.Errorf("Health check failed: %v", err) + return + } - defer resp.Body.Close() + // Make request to custom handler + resp, err := http.DefaultClient.Get(fmt.Sprintf("http://0.0.0.0:%d/config", httpPort)) + if err != nil { + t.Errorf("Failed to get config: %v", err) + return + } + defer resp.Body.Close() - bBytes, err := io.ReadAll(resp.Body) - require.NoError(t, err) - require.Equal(t, string(bBytes), "abc") - assert.True(t, customHandlerInvoked) + bBytes, err := io.ReadAll(resp.Body) + if err != nil { + t.Errorf("Failed to read response: %v", err) + return + } + + // Verify results + if string(bBytes) != "abc" { + t.Errorf("Expected response 'abc', got '%s'", string(bBytes)) + } + if !customHandlerInvoked { + t.Errorf("Custom handler was not invoked") + } + }() + + // Wait for either test completion or timeout + select { + case <-ctx.Done(): + t.Fatalf("Test timed out after 30 seconds") + case <-done: + // Test completed successfully + } + + // Clean up metrics unregisterLokiMetrics(loki) }