Browse code

client: refactor `ContainerTop` to wrap options and results

Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>

Austin Vazquez authored on 2025/10/29 08:26:08
Showing 6 changed files
... ...
@@ -73,7 +73,7 @@ type ContainerAPIClient interface {
73 73
 	ContainerStats(ctx context.Context, container string, options ContainerStatsOptions) (ContainerStatsResult, error)
74 74
 	ContainerStart(ctx context.Context, container string, options ContainerStartOptions) (ContainerStartResult, error)
75 75
 	ContainerStop(ctx context.Context, container string, options ContainerStopOptions) (ContainerStopResult, error)
76
-	ContainerTop(ctx context.Context, container string, arguments []string) (container.TopResponse, error)
76
+	ContainerTop(ctx context.Context, container string, options ContainerTopOptions) (ContainerTopResult, error)
77 77
 	ContainerUnpause(ctx context.Context, container string, options ContainerUnPauseOptions) (ContainerUnPauseResult, error)
78 78
 	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.UpdateResponse, error)
79 79
 	ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error)
... ...
@@ -9,25 +9,36 @@ import (
9 9
 	"github.com/moby/moby/api/types/container"
10 10
 )
11 11
 
12
+// ContainerTopOptions defines options for container top operations.
13
+type ContainerTopOptions struct {
14
+	Arguments []string
15
+}
16
+
17
+// ContainerTopResult represents the result of a ContainerTop operation.
18
+type ContainerTopResult struct {
19
+	Processes [][]string
20
+	Titles    []string
21
+}
22
+
12 23
 // ContainerTop shows process information from within a container.
13
-func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.TopResponse, error) {
24
+func (cli *Client) ContainerTop(ctx context.Context, containerID string, options ContainerTopOptions) (ContainerTopResult, error) {
14 25
 	containerID, err := trimID("container", containerID)
15 26
 	if err != nil {
16
-		return container.TopResponse{}, err
27
+		return ContainerTopResult{}, err
17 28
 	}
18 29
 
19 30
 	query := url.Values{}
20
-	if len(arguments) > 0 {
21
-		query.Set("ps_args", strings.Join(arguments, " "))
31
+	if len(options.Arguments) > 0 {
32
+		query.Set("ps_args", strings.Join(options.Arguments, " "))
22 33
 	}
23 34
 
24 35
 	resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
25 36
 	defer ensureReaderClosed(resp)
26 37
 	if err != nil {
27
-		return container.TopResponse{}, err
38
+		return ContainerTopResult{}, err
28 39
 	}
29 40
 
30 41
 	var response container.TopResponse
31 42
 	err = json.NewDecoder(resp.Body).Decode(&response)
32
-	return response, err
43
+	return ContainerTopResult{Processes: response.Processes, Titles: response.Titles}, err
33 44
 }
... ...
@@ -15,14 +15,14 @@ import (
15 15
 func TestContainerTopError(t *testing.T) {
16 16
 	client, err := NewClientWithOpts(WithMockClient(errorMock(http.StatusInternalServerError, "Server error")))
17 17
 	assert.NilError(t, err)
18
-	_, err = client.ContainerTop(context.Background(), "nothing", []string{})
18
+	_, err = client.ContainerTop(context.Background(), "nothing", ContainerTopOptions{})
19 19
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInternal))
20 20
 
21
-	_, err = client.ContainerTop(context.Background(), "", []string{})
21
+	_, err = client.ContainerTop(context.Background(), "", ContainerTopOptions{})
22 22
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
23 23
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
24 24
 
25
-	_, err = client.ContainerTop(context.Background(), "    ", []string{})
25
+	_, err = client.ContainerTop(context.Background(), "    ", ContainerTopOptions{})
26 26
 	assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument))
27 27
 	assert.Check(t, is.ErrorContains(err, "value is empty"))
28 28
 }
... ...
@@ -54,7 +54,9 @@ func TestContainerTop(t *testing.T) {
54 54
 	}))
55 55
 	assert.NilError(t, err)
56 56
 
57
-	processList, err := client.ContainerTop(context.Background(), "container_id", []string{"arg1", "arg2"})
57
+	processList, err := client.ContainerTop(context.Background(), "container_id", ContainerTopOptions{
58
+		Arguments: []string{"arg1", "arg2"},
59
+	})
58 60
 	assert.NilError(t, err)
59 61
 	assert.Check(t, is.DeepEqual(expectedProcesses, processList.Processes))
60 62
 	assert.Check(t, is.DeepEqual(expectedTitles, processList.Titles))
... ...
@@ -393,7 +393,7 @@ func (s *DockerAPISuite) TestContainerAPITop(c *testing.T) {
393 393
 	defer apiClient.Close()
394 394
 
395 395
 	// sort by comm[andline] to make sure order stays the same in case of PID rollover
396
-	top, err := apiClient.ContainerTop(testutil.GetContext(c), id, []string{"aux", "--sort=comm"})
396
+	top, err := apiClient.ContainerTop(testutil.GetContext(c), id, client.ContainerTopOptions{Arguments: []string{"aux", "--sort=comm"}})
397 397
 	assert.NilError(c, err)
398 398
 	assert.Equal(c, len(top.Titles), 11, fmt.Sprintf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
399 399
 
... ...
@@ -414,7 +414,7 @@ func (s *DockerAPISuite) TestContainerAPITopWindows(c *testing.T) {
414 414
 	assert.NilError(c, err)
415 415
 	defer apiClient.Close()
416 416
 
417
-	top, err := apiClient.ContainerTop(testutil.GetContext(c), id, nil)
417
+	top, err := apiClient.ContainerTop(testutil.GetContext(c), id, client.ContainerTopOptions{})
418 418
 	assert.NilError(c, err)
419 419
 	assert.Equal(c, len(top.Titles), 4, "expected 4 titles, found %d: %v", len(top.Titles), top.Titles)
420 420
 
... ...
@@ -73,7 +73,7 @@ type ContainerAPIClient interface {
73 73
 	ContainerStats(ctx context.Context, container string, options ContainerStatsOptions) (ContainerStatsResult, error)
74 74
 	ContainerStart(ctx context.Context, container string, options ContainerStartOptions) (ContainerStartResult, error)
75 75
 	ContainerStop(ctx context.Context, container string, options ContainerStopOptions) (ContainerStopResult, error)
76
-	ContainerTop(ctx context.Context, container string, arguments []string) (container.TopResponse, error)
76
+	ContainerTop(ctx context.Context, container string, options ContainerTopOptions) (ContainerTopResult, error)
77 77
 	ContainerUnpause(ctx context.Context, container string, options ContainerUnPauseOptions) (ContainerUnPauseResult, error)
78 78
 	ContainerUpdate(ctx context.Context, container string, updateConfig container.UpdateConfig) (container.UpdateResponse, error)
79 79
 	ContainerWait(ctx context.Context, container string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error)
... ...
@@ -9,25 +9,36 @@ import (
9 9
 	"github.com/moby/moby/api/types/container"
10 10
 )
11 11
 
12
+// ContainerTopOptions defines options for container top operations.
13
+type ContainerTopOptions struct {
14
+	Arguments []string
15
+}
16
+
17
+// ContainerTopResult represents the result of a ContainerTop operation.
18
+type ContainerTopResult struct {
19
+	Processes [][]string
20
+	Titles    []string
21
+}
22
+
12 23
 // ContainerTop shows process information from within a container.
13
-func (cli *Client) ContainerTop(ctx context.Context, containerID string, arguments []string) (container.TopResponse, error) {
24
+func (cli *Client) ContainerTop(ctx context.Context, containerID string, options ContainerTopOptions) (ContainerTopResult, error) {
14 25
 	containerID, err := trimID("container", containerID)
15 26
 	if err != nil {
16
-		return container.TopResponse{}, err
27
+		return ContainerTopResult{}, err
17 28
 	}
18 29
 
19 30
 	query := url.Values{}
20
-	if len(arguments) > 0 {
21
-		query.Set("ps_args", strings.Join(arguments, " "))
31
+	if len(options.Arguments) > 0 {
32
+		query.Set("ps_args", strings.Join(options.Arguments, " "))
22 33
 	}
23 34
 
24 35
 	resp, err := cli.get(ctx, "/containers/"+containerID+"/top", query, nil)
25 36
 	defer ensureReaderClosed(resp)
26 37
 	if err != nil {
27
-		return container.TopResponse{}, err
38
+		return ContainerTopResult{}, err
28 39
 	}
29 40
 
30 41
 	var response container.TopResponse
31 42
 	err = json.NewDecoder(resp.Body).Decode(&response)
32
-	return response, err
43
+	return ContainerTopResult{Processes: response.Processes, Titles: response.Titles}, err
33 44
 }