From 8d7f34038a9d1f5593a52016631db7a880dc8f98 Mon Sep 17 00:00:00 2001 From: Immanuel Tikhonov Date: Sat, 19 Sep 2026 13:40:43 +0400 Subject: [PATCH] fix(top): handle stopped and paused containers Return an error for stopped tasks while allowing paused tasks to proceed to PID inspection. Signed-off-by: Immanuel Tikhonov --- cmd/nerdctl/container/container_top_test.go | 44 +++++++++++++++++++++ pkg/cmd/container/top_unix.go | 4 +- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/cmd/nerdctl/container/container_top_test.go b/cmd/nerdctl/container/container_top_test.go index e63d71f4150..4a859e43665 100644 --- a/cmd/nerdctl/container/container_top_test.go +++ b/cmd/nerdctl/container/container_top_test.go @@ -17,9 +17,11 @@ package container import ( + "errors" "runtime" "testing" + "github.com/containerd/nerdctl/mod/tigron/expect" "github.com/containerd/nerdctl/mod/tigron/require" "github.com/containerd/nerdctl/mod/tigron/test" @@ -69,6 +71,48 @@ func TestTop(t *testing.T) { testCase.Run(t) } +func TestTopStoppedContainer(t *testing.T) { + testCase := nerdtest.Setup() + testCase.Require = require.All(require.Linux, nerdtest.CgroupsAccessible) + + testCase.Setup = func(data test.Data, helpers test.Helpers) { + helpers.Ensure("run", "-d", "--network", "none", "--name", data.Identifier(), testutil.CommonImage, "sleep", nerdtest.Infinity) + helpers.Ensure("stop", "--time", "1", data.Identifier()) + } + + testCase.Cleanup = func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("rm", "-f", data.Identifier()) + } + + testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand { + return helpers.Command("top", data.Identifier()) + } + + testCase.Expected = test.Expects(1, []error{errors.New("is not running")}, nil) + testCase.Run(t) +} + +func TestTopPausedContainer(t *testing.T) { + testCase := nerdtest.Setup() + testCase.Require = require.All(require.Linux, nerdtest.CgroupsAccessible) + + testCase.Setup = func(data test.Data, helpers test.Helpers) { + helpers.Ensure("run", "-d", "--network", "none", "--name", data.Identifier(), testutil.CommonImage, "sleep", nerdtest.Infinity) + helpers.Ensure("pause", data.Identifier()) + } + + testCase.Cleanup = func(data test.Data, helpers test.Helpers) { + helpers.Anyhow("rm", "-f", data.Identifier()) + } + + testCase.Command = func(data test.Data, helpers test.Helpers) test.TestableCommand { + return helpers.Command("top", data.Identifier()) + } + + testCase.Expected = test.Expects(0, nil, expect.Contains("sleep")) + testCase.Run(t) +} + func TestTopHyperVContainer(t *testing.T) { testCase := nerdtest.Setup() diff --git a/pkg/cmd/container/top_unix.go b/pkg/cmd/container/top_unix.go index 92141a4c77a..4b524241dc9 100644 --- a/pkg/cmd/container/top_unix.go +++ b/pkg/cmd/container/top_unix.go @@ -74,8 +74,8 @@ func containerTop(ctx context.Context, stdio io.Writer, client *containerd.Clien return err } - if status.Status != containerd.Running { - return nil + if status.Status != containerd.Running && status.Status != containerd.Paused { + return fmt.Errorf("container %s is not running (status: %s)", id, status.Status) } //TO DO handle restarting case: wait for container to restart and then launch top command