From 51a0845adba702ac02437405988b24b3b2c9fb45 Mon Sep 17 00:00:00 2001 From: James O'Doherty Date: Wed, 3 Jun 2026 23:45:45 -0400 Subject: fix: resolve resource leaks and improve namespace lifecycle management - Fix DNS resolver leaks by creating temporary resolv.conf files within the profile's runtime directory and ensuring robust cleanup. - Fix isolation block directory leaks by explicitly removing the block directory during namespace unpinning. - Improve namespace lifecycle management: - Register processes before joining an active namespace to prevent race conditions in reference counting. - Update `IsLastProcess` and corresponding tests to reflect the unregister-then-check cleanup flow. - Improve test reliability and correctness: - Convert `TestAppRun_ProfileDirInjection` to use separate binary execution, preventing process replacement and ensuring `t.TempDir()` cleanup. - Replace hardcoded test configuration paths with `t.TempDir()` in `mount_leak_test.go`. - Implement `SetEnvOverrides` helper for cleaner environment variable management in E2E tests. - Improve E2E lifecycle tests with better environment handling and output redirection. --- internal/namespace/lifecycle_test.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'internal/namespace/lifecycle_test.go') diff --git a/internal/namespace/lifecycle_test.go b/internal/namespace/lifecycle_test.go index 1fb0a13..9962e14 100644 --- a/internal/namespace/lifecycle_test.go +++ b/internal/namespace/lifecycle_test.go @@ -87,17 +87,40 @@ func TestLifecycleReferenceCounting(t *testing.T) { if err := RegisterProcess(pm, profile); err != nil { t.Fatal(err) } + + // Simulate the application flow: Unregister before checking if we are the last one + if err := UnregisterProcess(pm, profile); err != nil { + t.Fatal(err) + } + isLast, err = IsLastProcess(pm, profile) if err != nil || !isLast { - t.Errorf("Expected IsLastProcess to be true for single process, got %v, err: %v", isLast, err) + t.Errorf("Expected IsLastProcess to be true after unregistering the only process, got %v, err: %v", isLast, err) } + // Add a "stale" process to ensure it's pruned and doesn't count as active if err := os.WriteFile(filepath.Join(pidsDir, "1234567"), []byte(""), 0644); err != nil { t.Fatal(err) } + + // Register a real process so that pruning has something to do + if err := RegisterProcess(pm, profile); err != nil { + t.Fatal(err) + } + + // Prune the stale one + if err := PruneStalePids(pm, profile); err != nil { + t.Fatal(err) + } + + // Unregister the real one + if err := UnregisterProcess(pm, profile); err != nil { + t.Fatal(err) + } + isLast, err = IsLastProcess(pm, profile) if err != nil || !isLast { - t.Errorf("Expected IsLastProcess to be true because 1234567 is dead, got %v, err: %v", isLast, err) + t.Errorf("Expected IsLastProcess to be true after pruning stale and unregistering current, got %v, err: %v", isLast, err) } }) } -- cgit v1.2.3