client: refactor ContainerList to wrap result

Signed-off-by: Austin Vazquez <austin.vazquez@docker.com>
This commit is contained in:
Austin Vazquez
2025-10-28 18:37:40 -05:00
parent 4ce86e2c9b
commit c5ddef1122
17 changed files with 68 additions and 60 deletions

View File

@@ -62,7 +62,7 @@ type ContainerAPIClient interface {
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error)
ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error)
ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)

View File

@@ -20,8 +20,12 @@ type ContainerListOptions struct {
Filters Filters
}
type ContainerListResult struct {
Items []container.Summary
}
// ContainerList returns the list of containers in the docker host.
func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error) {
func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error) {
query := url.Values{}
if options.All {
@@ -49,10 +53,10 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio
resp, err := cli.get(ctx, "/containers/json", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, err
return ContainerListResult{}, err
}
var containers []container.Summary
err = json.NewDecoder(resp.Body).Decode(&containers)
return containers, err
return ContainerListResult{Items: containers}, err
}

View File

@@ -65,7 +65,7 @@ func TestContainerList(t *testing.T) {
)
assert.NilError(t, err)
containers, err := client.ContainerList(context.Background(), ContainerListOptions{
list, err := client.ContainerList(context.Background(), ContainerListOptions{
Size: true,
All: true,
Since: "container",
@@ -75,5 +75,5 @@ func TestContainerList(t *testing.T) {
Add("before", "container"),
})
assert.NilError(t, err)
assert.Check(t, is.Len(containers, 2))
assert.Check(t, is.Len(list.Items, 2))
}

View File

@@ -88,14 +88,14 @@ func (d *Daemon) CheckActiveContainerCount(ctx context.Context) func(t *testing.
apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithHost(d.Sock()))
assert.NilError(t, err)
ctrs, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
_ = apiClient.Close()
assert.NilError(t, err)
var out strings.Builder
for _, ctr := range ctrs {
for _, ctr := range list.Items {
out.WriteString(stringid.TruncateID(ctr.ID) + "\n")
}
return len(ctrs), out.String()
return len(list.Items), out.String()
}
}

View File

@@ -42,12 +42,12 @@ func (s *DockerAPISuite) TestContainerAPIGetAll(c *testing.T) {
defer apiClient.Close()
ctx := testutil.GetContext(c)
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
})
assert.NilError(c, err)
assert.Equal(c, len(containers), startCount+1)
actual := containers[0].Names[0]
assert.Equal(c, len(list.Items), startCount+1)
actual := list.Items[0].Names[0]
assert.Equal(c, actual, "/"+name)
}
@@ -64,10 +64,10 @@ func (s *DockerAPISuite) TestContainerAPIGetJSONNoFieldsOmitted(c *testing.T) {
All: true,
}
ctx := testutil.GetContext(c)
containers, err := apiClient.ContainerList(ctx, options)
list, err := apiClient.ContainerList(ctx, options)
assert.NilError(c, err)
assert.Equal(c, len(containers), startCount+1)
actual := fmt.Sprintf("%+v", containers[0])
assert.Equal(c, len(list.Items), startCount+1)
actual := fmt.Sprintf("%+v", list.Items[0])
// empty Labels field triggered this bug, make sense to check for everything
// cause even Ports for instance can trigger this bug

View File

@@ -116,7 +116,7 @@ func TestBuildWithRemoveAndForceRemove(t *testing.T) {
assert.NilError(t, err)
remainingContainers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{Filters: filter, All: true})
assert.NilError(t, err)
assert.Equal(t, tc.numberOfIntermediateContainers, len(remainingContainers), "Expected %v remaining intermediate containers, got %v", tc.numberOfIntermediateContainers, len(remainingContainers))
assert.Equal(t, tc.numberOfIntermediateContainers, len(remainingContainers.Items), "Expected %v remaining intermediate containers, got %v", tc.numberOfIntermediateContainers, len(remainingContainers.Items))
})
}
}

View File

@@ -42,10 +42,10 @@ func TestLinksContainerNames(t *testing.T) {
container.Run(ctx, t, apiClient, container.WithName(containerA))
container.Run(ctx, t, apiClient, container.WithName(containerB), container.WithLinks(containerA+":"+containerA))
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
Filters: make(client.Filters).Add("name", containerA),
})
assert.NilError(t, err)
assert.Check(t, is.Equal(1, len(containers)))
assert.Check(t, is.DeepEqual([]string{"/" + containerA, "/" + containerB + "/" + containerA}, containers[0].Names))
assert.Check(t, is.Equal(1, len(list.Items)))
assert.Check(t, is.DeepEqual([]string{"/" + containerA, "/" + containerB + "/" + containerA}, list.Items[0].Names))
}

View File

@@ -35,12 +35,12 @@ func TestContainerList(t *testing.T) {
}
// list them and verify correctness
containerList, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
assert.NilError(t, err)
assert.Assert(t, is.Len(containerList, num))
assert.Assert(t, is.Len(list.Items, num))
for i := range num {
// container list should be ordered in descending creation order
assert.Assert(t, is.Equal(containerList[i].ID, containers[num-1-i]))
assert.Assert(t, is.Equal(list.Items[i].ID, containers[num-1-i]))
}
}
@@ -65,14 +65,14 @@ func TestContainerList_Annotations(t *testing.T) {
id := container.Create(ctx, t, apiClient, container.WithAnnotations(annotations))
defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
Filters: make(client.Filters).Add("id", id),
})
assert.NilError(t, err)
assert.Assert(t, is.Len(containers, 1))
assert.Equal(t, containers[0].ID, id)
assert.Check(t, is.DeepEqual(containers[0].HostConfig.Annotations, tc.expectedAnnotations))
assert.Assert(t, is.Len(list.Items, 1))
assert.Equal(t, list.Items[0].ID, id)
assert.Check(t, is.DeepEqual(list.Items[0].HostConfig.Annotations, tc.expectedAnnotations))
})
}
}
@@ -101,22 +101,22 @@ func TestContainerList_Filter(t *testing.T) {
t.Run("since", func(t *testing.T) {
ctx := testutil.StartSpan(ctx, t)
results, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
Filters: make(client.Filters).Add("since", top),
})
assert.NilError(t, err)
assert.Check(t, is.Contains(containerIDs(results), next))
assert.Check(t, is.Contains(containerIDs(list.Items), next))
})
t.Run("before", func(t *testing.T) {
ctx := testutil.StartSpan(ctx, t)
results, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
Filters: make(client.Filters).Add("before", top),
})
assert.NilError(t, err)
assert.Check(t, is.Contains(containerIDs(results), prev))
assert.Check(t, is.Contains(containerIDs(list.Items), prev))
})
}
@@ -132,13 +132,13 @@ func TestContainerList_ImageManifestPlatform(t *testing.T) {
id := container.Create(ctx, t, apiClient)
defer container.Remove(ctx, t, apiClient, id, client.ContainerRemoveOptions{Force: true})
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
})
assert.NilError(t, err)
assert.Assert(t, len(containers) > 0)
assert.Assert(t, len(list.Items) > 0)
ctr := containers[0]
ctr := list.Items[0]
if assert.Check(t, ctr.ImageManifestDescriptor != nil && ctr.ImageManifestDescriptor.Platform != nil) {
// Check that at least OS and Architecture have a value. Other values
// depend on the platform on which we're running the test.
@@ -149,7 +149,7 @@ func TestContainerList_ImageManifestPlatform(t *testing.T) {
func pollForHealthStatusSummary(ctx context.Context, apiClient client.APIClient, containerID string, healthStatus containertypes.HealthStatus) func(log poll.LogT) poll.Result {
return func(log poll.LogT) poll.Result {
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
Filters: make(client.Filters).Add("id", containerID),
})
@@ -158,7 +158,7 @@ func pollForHealthStatusSummary(ctx context.Context, apiClient client.APIClient,
}
total := 0
version := apiClient.ClientVersion()
for _, ctr := range containers {
for _, ctr := range list.Items {
if ctr.Health == nil && versions.LessThan(version, "1.52") {
total++
} 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,
}
}
if total == len(containers) {
if total == len(list.Items) {
return poll.Success()
}

View File

@@ -176,10 +176,10 @@ func Remove(ctx context.Context, t *testing.T, apiClient client.APIClient, conta
func RemoveAll(ctx context.Context, t *testing.T, apiClient client.APIClient) {
t.Helper()
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{All: true})
assert.NilError(t, err)
for _, c := range containers {
for _, c := range list.Items {
Remove(ctx, t, apiClient, c.ID, client.ContainerRemoveOptions{Force: true})
}
}

View File

@@ -382,12 +382,12 @@ func createServices(ctx context.Context, t *testing.T, d *daemon.Daemon, section
}
func pollService(ctx context.Context, t *testing.T, c *client.Client, host networking.Host) poll.Result {
cl, err := c.ContainerList(ctx, client.ContainerListOptions{})
list, err := c.ContainerList(ctx, client.ContainerListOptions{})
if err != nil {
return poll.Error(fmt.Errorf("failed to list containers: %w", err))
}
if len(cl) != 1 {
return poll.Continue("got %d containers, want 1", len(cl))
if len(list.Items) != 1 {
return poll.Continue("got %d containers, want 1", len(list.Items))
}
// The DOCKER-INGRESS chain seems to be created, then populated, a few
// milliseconds after the container starts. So, also wait for a conntrack

View File

@@ -89,12 +89,12 @@ func TestHostPortMappings(t *testing.T) {
poll.WaitOn(t, swarm.RunningTasksCount(ctx, apiClient, svcID, 1), swarm.ServicePoll)
ctrs, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{})
assert.NilError(t, err)
assert.Equal(t, 1, len(ctrs))
assert.Equal(t, 1, len(list.Items))
var addrs []string
for _, port := range ctrs[0].Ports {
for _, port := range list.Items[0].Ports {
addrs = append(addrs, fmt.Sprintf("%s:%d/%s", net.JoinHostPort(port.IP.String(), strconv.Itoa(int(port.PublicPort))), port.PrivatePort, port.Type))
}

View File

@@ -64,13 +64,13 @@ func testServiceCreateInit(ctx context.Context, daemonEnabled bool) func(t *test
func inspectServiceContainer(ctx context.Context, t *testing.T, apiClient client.APIClient, serviceID string) container.InspectResponse {
t.Helper()
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
Filters: make(client.Filters).Add("label", "com.docker.swarm.service.id="+serviceID),
})
assert.NilError(t, err)
assert.Check(t, is.Len(containers, 1))
assert.Check(t, is.Len(list.Items, 1))
inspect, err := apiClient.ContainerInspect(ctx, containers[0].ID, client.ContainerInspectOptions{})
inspect, err := apiClient.ContainerInspect(ctx, list.Items[0].ID, client.ContainerInspectOptions{})
assert.NilError(t, err)
return inspect.Container
}

View File

@@ -14,11 +14,11 @@ func (d *Daemon) ActiveContainers(ctx context.Context, t testing.TB) []string {
cli := d.NewClientT(t)
defer cli.Close()
containers, err := cli.ContainerList(context.Background(), client.ContainerListOptions{})
list, err := cli.ContainerList(context.Background(), client.ContainerListOptions{})
assert.NilError(t, err)
ids := make([]string, len(containers))
for i, c := range containers {
ids := make([]string, len(list.Items))
for i, c := range list.Items {
ids[i] = c.ID
}
return ids

View File

@@ -51,12 +51,12 @@ func unpauseAllContainers(ctx context.Context, t testing.TB, apiClient client.Co
func getPausedContainers(ctx context.Context, t testing.TB, apiClient client.ContainerAPIClient) []container.Summary {
t.Helper()
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
Filters: make(client.Filters).Add("status", "paused"),
All: true,
})
assert.Check(t, err, "failed to list containers")
return containers
return list.Items
}
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
func getAllContainers(ctx context.Context, t testing.TB, apiClient client.ContainerAPIClient) []container.Summary {
t.Helper()
containers, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
list, err := apiClient.ContainerList(ctx, client.ContainerListOptions{
All: true,
})
assert.Check(t, err, "failed to list containers")
return containers
return list.Items
}
func deleteAllImages(ctx context.Context, t testing.TB, apiclient client.ImageAPIClient, protectedImages map[string]struct{}) {

View File

@@ -79,13 +79,13 @@ func ProtectContainers(ctx context.Context, t testing.TB, testEnv *Execution) {
func getExistingContainers(ctx context.Context, t testing.TB, testEnv *Execution) []string {
t.Helper()
containerList, err := testEnv.APIClient().ContainerList(ctx, client.ContainerListOptions{
list, err := testEnv.APIClient().ContainerList(ctx, client.ContainerListOptions{
All: true,
})
assert.NilError(t, err, "failed to list containers")
var containers []string
for _, container := range containerList {
for _, container := range list.Items {
containers = append(containers, container.ID)
}
return containers

View File

@@ -62,7 +62,7 @@ type ContainerAPIClient interface {
ContainerExport(ctx context.Context, container string) (io.ReadCloser, error)
ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error)
ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error)
ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)

View File

@@ -20,8 +20,12 @@ type ContainerListOptions struct {
Filters Filters
}
type ContainerListResult struct {
Items []container.Summary
}
// ContainerList returns the list of containers in the docker host.
func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) ([]container.Summary, error) {
func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error) {
query := url.Values{}
if options.All {
@@ -49,10 +53,10 @@ func (cli *Client) ContainerList(ctx context.Context, options ContainerListOptio
resp, err := cli.get(ctx, "/containers/json", query, nil)
defer ensureReaderClosed(resp)
if err != nil {
return nil, err
return ContainerListResult{}, err
}
var containers []container.Summary
err = json.NewDecoder(resp.Body).Decode(&containers)
return containers, err
return ContainerListResult{Items: containers}, err
}