Browse code

client: refactor `ContainerList` to wrap result

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

Austin Vazquez authored on 2025/10/29 08:37:40
Showing 17 changed files
... ...
@@ -62,7 +62,7 @@ type ContainerAPIClient interface {
62 62
 	ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
63 63
 	ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
64 64
 	ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
65
-	ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error)
65
+	ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error)
66 66
 	ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
67 67
 	ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
68 68
 	ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)
... ...
@@ -20,8 +20,12 @@ type ContainerListOptions struct {
20 20
 	Filters Filters
21 21
 }
22 22
 
23
+type ContainerListResult struct {
24
+	Items []container.Summary
25
+}
26
+
23 27
 // ContainerList returns the list of containers in the docker host.
24
-func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error) {
28
+func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error) {
25 29
 	query := url.Values{}
26 30
 
27 31
 	if options.All {
... ...
@@ -49,10 +53,10 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio
49 49
 	resp, err := cli.get(ctx, "/containers/json", query, nil)
50 50
 	defer ensureReaderClosed(resp)
51 51
 	if err != nil {
52
-		return nil, err
52
+		return ContainerListResult{}, err
53 53
 	}
54 54
 
55 55
 	var containers []container.Summary
56 56
 	err = json.NewDecoder(resp.Body).Decode(&containers)
57
-	return containers, err
57
+	return ContainerListResult{Items: containers}, err
58 58
 }
... ...
@@ -65,7 +65,7 @@ func TestContainerList(t *testing.T) {
65 65
 	)
66 66
 	assert.NilError(t, err)
67 67
 
68
-	containers, err := client.ContainerList(context.Background(), ContainerListOptions{
68
+	list, err := client.ContainerList(context.Background(), ContainerListOptions{
69 69
 		Size:  true,
70 70
 		All:   true,
71 71
 		Since: "container",
... ...
@@ -75,5 +75,5 @@ func TestContainerList(t *testing.T) {
75 75
 			Add("before", "container"),
76 76
 	})
77 77
 	assert.NilError(t, err)
78
-	assert.Check(t, is.Len(containers, 2))
78
+	assert.Check(t, is.Len(list.Items, 2))
79 79
 }
... ...
@@ -88,14 +88,14 @@ func (d *Daemon) CheckActiveContainerCount(ctx context.Context) func(t *testing.
88 88
 		apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithHost(d.Sock()))
89 89
 		assert.NilError(t, err)
90 90
 
91
-		ctrs, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
91
+		list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
92 92
 		_ = apiClient.Close()
93 93
 		assert.NilError(t, err)
94 94
 		var out strings.Builder
95
-		for _, ctr := range ctrs {
95
+		for _, ctr := range list.Items {
96 96
 			out.WriteString(stringid.TruncateID(ctr.ID) + "\n")
97 97
 		}
98
-		return len(ctrs), out.String()
98
+		return len(list.Items), out.String()
99 99
 	}
100 100
 }
101 101
 
... ...
@@ -42,12 +42,12 @@ func (s *DockerAPISuite) TestContainerAPIGetAll(c *testing.T) {
42 42
 	defer apiClient.Close()
43 43
 
44 44
 	ctx := testutil.GetContext(c)
45
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
45
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
46 46
 		All: true,
47 47
 	})
48 48
 	assert.NilError(c, err)
49
-	assert.Equal(c, len(containers), startCount+1)
50
-	actual := containers[0].Names[0]
49
+	assert.Equal(c, len(list.Items), startCount+1)
50
+	actual := list.Items[0].Names[0]
51 51
 	assert.Equal(c, actual, "/"+name)
52 52
 }
53 53
 
... ...
@@ -64,10 +64,10 @@ func (s *DockerAPISuite) TestContainerAPIGetJSONNoFieldsOmitted(c *testing.T) {
64 64
 		All: true,
65 65
 	}
66 66
 	ctx := testutil.GetContext(c)
67
-	containers, err := apiClient.ContainerList(ctx, options)
67
+	list, err := apiClient.ContainerList(ctx, options)
68 68
 	assert.NilError(c, err)
69
-	assert.Equal(c, len(containers), startCount+1)
70
-	actual := fmt.Sprintf("%+v", containers[0])
69
+	assert.Equal(c, len(list.Items), startCount+1)
70
+	actual := fmt.Sprintf("%+v", list.Items[0])
71 71
 
72 72
 	// empty Labels field triggered this bug, make sense to check for everything
73 73
 	// cause even Ports for instance can trigger this bug
... ...
@@ -116,7 +116,7 @@ func TestBuildWithRemoveAndForceRemove(t *testing.T) {
116 116
 			assert.NilError(t, err)
117 117
 			remainingContainers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{Filters: filter, All: true})
118 118
 			assert.NilError(t, err)
119
-			assert.Equal(t, tc.numberOfIntermediateContainers, len(remainingContainers), "Expected %v remaining intermediate containers, got %v", tc.numberOfIntermediateContainers, len(remainingContainers))
119
+			assert.Equal(t, tc.numberOfIntermediateContainers, len(remainingContainers.Items), "Expected %v remaining intermediate containers, got %v", tc.numberOfIntermediateContainers, len(remainingContainers.Items))
120 120
 		})
121 121
 	}
122 122
 }
... ...
@@ -42,10 +42,10 @@ func TestLinksContainerNames(t *testing.T) {
42 42
 	container.Run(ctx, t, apiClient, container.WithName(containerA))
43 43
 	container.Run(ctx, t, apiClient, container.WithName(containerB), container.WithLinks(containerA+":"+containerA))
44 44
 
45
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
45
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
46 46
 		Filters: make(client.Filters).Add("name", containerA),
47 47
 	})
48 48
 	assert.NilError(t, err)
49
-	assert.Check(t, is.Equal(1, len(containers)))
50
-	assert.Check(t, is.DeepEqual([]string{"/" + containerA, "/" + containerB + "/" + containerA}, containers[0].Names))
49
+	assert.Check(t, is.Equal(1, len(list.Items)))
50
+	assert.Check(t, is.DeepEqual([]string{"/" + containerA, "/" + containerB + "/" + containerA}, list.Items[0].Names))
51 51
 }
... ...
@@ -35,12 +35,12 @@ func TestContainerList(t *testing.T) {
35 35
 	}
36 36
 
37 37
 	// list them and verify correctness
38
-	containerList, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
38
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
39 39
 	assert.NilError(t, err)
40
-	assert.Assert(t, is.Len(containerList, num))
40
+	assert.Assert(t, is.Len(list.Items, num))
41 41
 	for i := range num {
42 42
 		// container list should be ordered in descending creation order
43
-		assert.Assert(t, is.Equal(containerList[i].ID, containers[num-1-i]))
43
+		assert.Assert(t, is.Equal(list.Items[i].ID, containers[num-1-i]))
44 44
 	}
45 45
 }
46 46
 
... ...
@@ -65,14 +65,14 @@ func TestContainerList_Annotations(t *testing.T) {
65 65
 			id := container.Create(ctx, t, apiClient, container.WithAnnotations(annotations))
66 66
 			defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
67 67
 
68
-			containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
68
+			list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
69 69
 				All:     true,
70 70
 				Filters: make(client.Filters).Add("id", id),
71 71
 			})
72 72
 			assert.NilError(t, err)
73
-			assert.Assert(t, is.Len(containers, 1))
74
-			assert.Equal(t, containers[0].ID, id)
75
-			assert.Check(t, is.DeepEqual(containers[0].HostConfig.Annotations, tc.expectedAnnotations))
73
+			assert.Assert(t, is.Len(list.Items, 1))
74
+			assert.Equal(t, list.Items[0].ID, id)
75
+			assert.Check(t, is.DeepEqual(list.Items[0].HostConfig.Annotations, tc.expectedAnnotations))
76 76
 		})
77 77
 	}
78 78
 }
... ...
@@ -101,22 +101,22 @@ func TestContainerList_Filter(t *testing.T) {
101 101
 
102 102
 	t.Run("since", func(t *testing.T) {
103 103
 		ctx := testutil.StartSpan(ctx, t)
104
-		results, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
104
+		list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
105 105
 			All:     true,
106 106
 			Filters: make(client.Filters).Add("since", top),
107 107
 		})
108 108
 		assert.NilError(t, err)
109
-		assert.Check(t, is.Contains(containerIDs(results), next))
109
+		assert.Check(t, is.Contains(containerIDs(list.Items), next))
110 110
 	})
111 111
 
112 112
 	t.Run("before", func(t *testing.T) {
113 113
 		ctx := testutil.StartSpan(ctx, t)
114
-		results, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
114
+		list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
115 115
 			All:     true,
116 116
 			Filters: make(client.Filters).Add("before", top),
117 117
 		})
118 118
 		assert.NilError(t, err)
119
-		assert.Check(t, is.Contains(containerIDs(results), prev))
119
+		assert.Check(t, is.Contains(containerIDs(list.Items), prev))
120 120
 	})
121 121
 }
122 122
 
... ...
@@ -132,13 +132,13 @@ func TestContainerList_ImageManifestPlatform(t *testing.T) {
132 132
 	id := container.Create(ctx, t, apiClient)
133 133
 	defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
134 134
 
135
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
135
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
136 136
 		All: true,
137 137
 	})
138 138
 	assert.NilError(t, err)
139
-	assert.Assert(t, len(containers) > 0)
139
+	assert.Assert(t, len(list.Items) > 0)
140 140
 
141
-	ctr := containers[0]
141
+	ctr := list.Items[0]
142 142
 	if assert.Check(t, ctr.ImageManifestDescriptor != nil && ctr.ImageManifestDescriptor.Platform != nil) {
143 143
 		// Check that at least OS and Architecture have a value. Other values
144 144
 		// depend on the platform on which we're running the test.
... ...
@@ -149,7 +149,7 @@ func TestContainerList_ImageManifestPlatform(t *testing.T) {
149 149
 
150 150
 func pollForHealthStatusSummary(ctx context.Context, apiClient client.APIClient, containerID string, healthStatus containertypes.HealthStatus) func(log poll.LogT) poll.Result {
151 151
 	return func(log poll.LogT) poll.Result {
152
-		containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
152
+		list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
153 153
 			All:     true,
154 154
 			Filters: make(client.Filters).Add("id", containerID),
155 155
 		})
... ...
@@ -158,7 +158,7 @@ func pollForHealthStatusSummary(ctx context.Context, apiClient client.APIClient,
158 158
 		}
159 159
 		total := 0
160 160
 		version := apiClient.ClientVersion()
161
-		for _, ctr := range containers {
161
+		for _, ctr := range list.Items {
162 162
 			if ctr.Health == nil && versions.LessThan(version, "1.52") {
163 163
 				total++
164 164
 			} else if ctr.Health != nil && ctr.Health.Status == healthStatus && versions.GreaterThanOrEqualTo(version, "1.52") {
... ...
@@ -166,7 +166,7 @@ func pollForHealthStatusSummary(ctx context.Context, apiClient client.APIClient,
166 166
 			}
167 167
 		}
168 168
 
169
-		if total == len(containers) {
169
+		if total == len(list.Items) {
170 170
 			return poll.Success()
171 171
 		}
172 172
 
... ...
@@ -176,10 +176,10 @@ func Remove(ctx context.Context, t *testing.T, apiClient client.APIClient, conta
176 176
 func RemoveAll(ctx context.Context, t *testing.T, apiClient client.APIClient) {
177 177
 	t.Helper()
178 178
 
179
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
179
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
180 180
 	assert.NilError(t, err)
181 181
 
182
-	for _, c := range containers {
182
+	for _, c := range list.Items {
183 183
 		Remove(ctx, t, apiClient, c.ID, client.ContainerRemoveOptions{Force: true})
184 184
 	}
185 185
 }
... ...
@@ -382,12 +382,12 @@ func createServices(ctx context.Context, t *testing.T, d *daemon.Daemon, section
382 382
 }
383 383
 
384 384
 func pollService(ctx context.Context, t *testing.T, c *client.Client, host networking.Host) poll.Result {
385
-	cl, err := c.ContainerList(ctx, client.ContainerListOptions{})
385
+	list, err := c.ContainerList(ctx, client.ContainerListOptions{})
386 386
 	if err != nil {
387 387
 		return poll.Error(fmt.Errorf("failed to list containers: %w", err))
388 388
 	}
389
-	if len(cl) != 1 {
390
-		return poll.Continue("got %d containers, want 1", len(cl))
389
+	if len(list.Items) != 1 {
390
+		return poll.Continue("got %d containers, want 1", len(list.Items))
391 391
 	}
392 392
 	// The DOCKER-INGRESS chain seems to be created, then populated, a few
393 393
 	// milliseconds after the container starts. So, also wait for a conntrack
... ...
@@ -89,12 +89,12 @@ func TestHostPortMappings(t *testing.T) {
89 89
 
90 90
 	poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, svcID, 1), swarm.ServicePoll)
91 91
 
92
-	ctrs, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
92
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
93 93
 	assert.NilError(t, err)
94
-	assert.Equal(t, 1, len(ctrs))
94
+	assert.Equal(t, 1, len(list.Items))
95 95
 
96 96
 	var addrs []string
97
-	for _, port := range ctrs[0].Ports {
97
+	for _, port := range list.Items[0].Ports {
98 98
 		addrs = append(addrs, fmt.Sprintf("%s:%d/%s", net.JoinHostPort(port.IP.String(), strconv.Itoa(int(port.PublicPort))), port.PrivatePort, port.Type))
99 99
 	}
100 100
 
... ...
@@ -64,13 +64,13 @@ func testServiceCreateInit(ctx context.Context, daemonEnabled bool) func(t *test
64 64
 
65 65
 func inspectServiceContainer(ctx context.Context, t *testing.T, apiClient client.APIClient, serviceID string) container.InspectResponse {
66 66
 	t.Helper()
67
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
67
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
68 68
 		Filters: make(client.Filters).Add("label", "com.docker.swarm.service.id="+serviceID),
69 69
 	})
70 70
 	assert.NilError(t, err)
71
-	assert.Check(t, is.Len(containers, 1))
71
+	assert.Check(t, is.Len(list.Items, 1))
72 72
 
73
-	inspect, err := apiClient.ContainerInspect(ctx, containers[0].ID, client.ContainerInspectOptions{})
73
+	inspect, err := apiClient.ContainerInspect(ctx, list.Items[0].ID, client.ContainerInspectOptions{})
74 74
 	assert.NilError(t, err)
75 75
 	return inspect.Container
76 76
 }
... ...
@@ -14,11 +14,11 @@ func (d *Daemon) ActiveContainers(ctx context.Context, t testing.TB) []string {
14 14
 	cli := d.NewClientT(t)
15 15
 	defer cli.Close()
16 16
 
17
-	containers, err := cli.ContainerList(context.Background(), client.ContainerListOptions{})
17
+	list, err := cli.ContainerList(context.Background(), client.ContainerListOptions{})
18 18
 	assert.NilError(t, err)
19 19
 
20
-	ids := make([]string, len(containers))
21
-	for i, c := range containers {
20
+	ids := make([]string, len(list.Items))
21
+	for i, c := range list.Items {
22 22
 		ids[i] = c.ID
23 23
 	}
24 24
 	return ids
... ...
@@ -51,12 +51,12 @@ func unpauseAllContainers(ctx context.Context, t testing.TB, apiClient client.Co
51 51
 
52 52
 func getPausedContainers(ctx context.Context, t testing.TB, apiClient client.ContainerAPIClient) []container.Summary {
53 53
 	t.Helper()
54
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
54
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
55 55
 		Filters: make(client.Filters).Add("status", "paused"),
56 56
 		All:     true,
57 57
 	})
58 58
 	assert.Check(t, err, "failed to list containers")
59
-	return containers
59
+	return list.Items
60 60
 }
61 61
 
62 62
 func deleteAllContainers(ctx context.Context, t testing.TB, apiclient client.ContainerAPIClient, protectedContainers map[string]struct{}) {
... ...
@@ -85,11 +85,11 @@ func deleteAllContainers(ctx context.Context, t testing.TB, apiclient client.Con
85 85
 
86 86
 func getAllContainers(ctx context.Context, t testing.TB, apiClient client.ContainerAPIClient) []container.Summary {
87 87
 	t.Helper()
88
-	containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
88
+	list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
89 89
 		All: true,
90 90
 	})
91 91
 	assert.Check(t, err, "failed to list containers")
92
-	return containers
92
+	return list.Items
93 93
 }
94 94
 
95 95
 func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {
... ...
@@ -79,13 +79,13 @@ func ProtectContainers(ctx context.Context, t testing.TB, testEnv *Execution) {
79 79
 
80 80
 func getExistingContainers(ctx context.Context, t testing.TB, testEnv *Execution) []string {
81 81
 	t.Helper()
82
-	containerList, err := testEnv.APIClient().ContainerList(ctx, client.ContainerListOptions{
82
+	list, err := testEnv.APIClient().ContainerList(ctx, client.ContainerListOptions{
83 83
 		All: true,
84 84
 	})
85 85
 	assert.NilError(t, err, "failed to list containers")
86 86
 
87 87
 	var containers []string
88
-	for _, container := range containerList {
88
+	for _, container := range list.Items {
89 89
 		containers = append(containers, container.ID)
90 90
 	}
91 91
 	return containers
... ...
@@ -62,7 +62,7 @@ type ContainerAPIClient interface {
62 62
 	ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
63 63
 	ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
64 64
 	ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
65
-	ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error)
65
+	ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error)
66 66
 	ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
67 67
 	ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
68 68
 	ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)
... ...
@@ -20,8 +20,12 @@ type ContainerListOptions struct {
20 20
 	Filters Filters
21 21
 }
22 22
 
23
+type ContainerListResult struct {
24
+	Items []container.Summary
25
+}
26
+
23 27
 // ContainerList returns the list of containers in the docker host.
24
-func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error) {
28
+func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error) {
25 29
 	query := url.Values{}
26 30
 
27 31
 	if options.All {
... ...
@@ -49,10 +53,10 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio
49 49
 	resp, err := cli.get(ctx, "/containers/json", query, nil)
50 50
 	defer ensureReaderClosed(resp)
51 51
 	if err != nil {
52
-		return nil, err
52
+		return ContainerListResult{}, err
53 53
 	}
54 54
 
55 55
 	var containers []container.Summary
56 56
 	err = json.NewDecoder(resp.Body).Decode(&containers)
57
-	return containers, err
57
+	return ContainerListResult{Items: containers}, err
58 58
 }