Skip to content

Align container list format with Docker specifications - #41375

Open
ggarzia-MSFT wants to merge 8 commits into
masterfrom
user/ggarzia/container-list-output-parity
Open

Align container list format with Docker specifications#41375
ggarzia-MSFT wants to merge 8 commits into
masterfrom
user/ggarzia/container-list-output-parity

Conversation

@ggarzia-MSFT

@ggarzia-MSFT ggarzia-MSFT commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary of the Pull Request

Brings wslc container list to output parity with docker container list, for both the table and --format json. This was the largest remaining gap found in a systematic wslc-vs-docker output comparison across 81 command pairs.

Three changes:

  • Table — column order now matches docker (CONTAINER ID / IMAGE / COMMAND / CREATED / STATUS / PORTS / NAMES), adding the missing COMMAND column and moving NAMES last. STATUS now reports the runtime's own description (Up 5 minutes, Exited (0) 2 hours ago) instead of a locally derived string.
  • --format json — emits docker's 16-key shape, with every value a pre-rendered string apart from the nested Platform object. This matches the convention already used by wslc image list and wslc network list.
  • --quiet — truncates to docker's 12-character short ID, with --no-trunc for the full ID.

The service now supplies the additional data docker reports (Command, Status, Labels, Networks, Mounts, LocalVolumes), so WSLCContainerEntry gains those fields.

PR Checklist

  • Closes: Link to issue #xxx
  • Communication: I've discussed this with core contributors already. If work hasn't been agreed, this work might be rejected
  • Tests: Added/updated if needed and all pass
  • Localization: All end user facing strings can be localized
  • Dev docs: Added/updated if needed
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

JSON output is a breaking change

container list --format json previously emitted a raw-data view of the internal model:

{"Id":"<64 chars>","Name":"web","Image":"alpine:3.20","State":1,"StateChangedAt":1755543210,"CreatedAt":1755543200,"Ports":[...]}

It now emits docker's shape:

{"Command":"\"sleep 3600\"","CreatedAt":"2026-08-18 12:06:50 -0700 PDT","HealthStatus":"none","ID":"0dba0f244a97","Image":"alpine:3.20","Labels":"...","LocalVolumes":"0","Mounts":"","Names":"web","Networks":"bridge","Platform":{"architecture":"amd64","os":"linux"},"Ports":"","RunningFor":"28 seconds ago","Size":"0B","State":"running","Status":"Up 27 seconds"}

Notably Id → ID, Name → Names, integer timestamps → formatted strings, the Ports array → a rendered string, and IDs truncated unless --no-trunc . This mirrors the rename already made deliberately for network list, and is what makes docker-oriented tooling and Go-template-style consumers work unchanged.

Design

 ToContainerOutput() builds a single  ContainerOutputInformation  consumed by both the table and the JSON renderer, so the two cannot drift. This is the same pattern as  ToImageOutput() /  ImageOutputInformation .

 ContainerOutputInformation  is kept separate from  ContainerInformation  on purpose: the former mirrors docker's all-string output shape, the latter mirrors the service's native types.

Container state names exist in two forms —  ContainerStateName() returns invariant English because it feeds the machine-readable JSON, while  LocalizedContainerStateName() is the display form used by the table.

Validation Steps Performed

• Added WSLCE2E_Container_List_TableFormat_MatchesDockerColumnOrder , asserting the header row matches docker's column order.
• Added WSLCE2E_Container_List_JsonFormat_MatchesDockerShape , asserting the exact 16-key set, that every value is a string apart from Platform , and that IDs are 12 characters by default.
• Rewrote the --quiet E2E test to cover both truncated and --no-trunc  output.
• Added 10 unit tests for  FormatCommand  (quoting, escaping, 20-character shortening, code-point counting for multi-byte input) and  FormatStatus  (runtime description preferred, fallback when empty).
• Migrated the ~12 existing E2E call sites and the shared  ListAllContainers() helper to the new JSON shape.
• Expected output was validated against real docker container list output captured from a parity harness run covering 81 wslc/docker command pairs.
•  clang-format 19.1.5 clean across all files changed on the branch.

ggarzia-MSFT and others added 3 commits August 18, 2026 14:22
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 18, 2026 22:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates wslc container list to better match docker container list output, including table column order, --format json shape, and --quiet truncation behavior. It also extends the service/container models to provide additional Docker-parity fields needed by both the table and JSON views.

Changes:

  • Reworks container list rendering to use a shared ContainerOutputInformation (table + NDJSON) with Docker-aligned fields, and updates --quiet/--no-trunc behavior.
  • Extends the service and schema models to carry Docker-reported container metadata (e.g., Command, Status, Labels, Networks, Mounts, LocalVolumes).
  • Updates and adds unit/E2E tests to validate the new table layout, JSON shape, and truncation rules.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/windows/wslc/WSLCCLIContainerCommandUnitTests.cpp Adds unit tests for FormatCommand and FormatStatus.
test/windows/wslc/e2e/WSLCE2EHelpers.h Updates helper API to return ContainerOutputInformation.
test/windows/wslc/e2e/WSLCE2EHelpers.cpp Updates helpers for new JSON shape/field names and status matching.
test/windows/wslc/e2e/WSLCE2EContainerListTests.cpp Adds/updates E2E coverage for Docker-aligned table headers, JSON shape, and ID truncation.
src/windows/wslcsession/WSLCSession.cpp Populates newly added service-side WSLCContainerEntry fields from Docker data.
src/windows/wslc/tasks/ContainerTasks.cpp Implements Docker-shape NDJSON and new table column order via ToContainerOutput().
src/windows/wslc/services/ContainerService.h Adds APIs for invariant state names and Docker-like command/status formatting.
src/windows/wslc/services/ContainerService.cpp Implements FormatCommand, FormatStatus, and splits invariant vs localized state naming.
src/windows/wslc/services/ContainerModel.h Adds ContainerOutputInformation and extends ContainerInformation to carry new fields.
src/windows/service/inc/wslc.idl Extends WSLCContainerEntry and introduces max-length constants for new fields.
src/windows/inc/docker_schema.h Extends docker schema parsing to include Command and Status.
localization/strings/en-US/Resources.resw Adds table headers (COMMAND, NAMES) and container state strings.
Suppressed comments (1)

src/windows/wslcsession/WSLCSession.cpp:2603

  • These strncpy_s calls only throw when the return value is EINVAL, but other non-zero return values would be ignored. Since truncation is expected with _TRUNCATE (STRUNCATE), it’s safer to throw on anything that is neither 0 nor STRUNCATE.
        THROW_HR_IF(E_UNEXPECTED, strncpy_s(output[index].Labels, std::size(output[index].Labels), joinedLabels.c_str(), _TRUNCATE) == EINVAL);
        THROW_HR_IF(E_UNEXPECTED, strncpy_s(output[index].Networks, std::size(output[index].Networks), joinedNetworks.c_str(), _TRUNCATE) == EINVAL);
        THROW_HR_IF(E_UNEXPECTED, strncpy_s(output[index].Mounts, std::size(output[index].Mounts), joinedMounts.c_str(), _TRUNCATE) == EINVAL);

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/windows/wslc/services/ContainerModel.h Outdated
Comment thread src/windows/wslcsession/WSLCSession.cpp Outdated
…iner json macro

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 18, 2026 23:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 19, 2026 00:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/windows/service/inc/wslc.idl:390

  • WSLCContainerEntry now includes several large fixed-size char buffers (e.g. 4K Command/Labels/Mounts). Because ListContainers returns an array of WSLCContainerEntry over COM/RPC, this significantly increases the marshaled payload and client-side allocation per entry even when the strings are short, which can slow down container list (and any callers) for large container counts.

Consider switching these fields to variable-length marshaled strings (e.g. [string] LPSTR) and allocating per-value, or gating the extended fields behind an option flag (so --quiet/minimal listings don’t pay the cost).

    char Command[WSLC_MAX_CONTAINER_COMMAND_LENGTH + 1];
    char Status[WSLC_MAX_CONTAINER_STATUS_LENGTH + 1];
    char Labels[WSLC_MAX_CONTAINER_LABELS_LENGTH + 1];
    char Networks[WSLC_MAX_CONTAINER_NETWORKS_LENGTH + 1];
    char Mounts[WSLC_MAX_CONTAINER_MOUNTS_LENGTH + 1];

@ggarzia-MSFT
ggarzia-MSFT marked this pull request as ready for review August 19, 2026 16:15
@ggarzia-MSFT
ggarzia-MSFT requested review from a team as code owners August 19, 2026 16:15
Comment thread src/windows/wslc/services/ContainerService.cpp Outdated
Comment thread src/windows/wslc/services/ContainerService.cpp Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 19, 2026 20:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (1)

test/windows/wslc/e2e/WSLCE2EHelpers.cpp:186

  • The test assumes the STATUS column always contains Docker-style runtime prefixes (e.g. "Up ", "Exited ("), but ContainerService::FormatStatus has a fallback that can emit plain state strings (e.g. "running", "exited") if the runtime doesn't supply a status description. This can make the test fail even when the container is correctly listed. Consider accepting either the Docker-style prefix or the original logical state string when validating the line.
            const std::wstring message = L"Container '" + containerNameOrId + L"' found in container list output but status '" +
                                         expectedStatus + L"' was not found in the same line";
            VERIFY_ARE_NOT_EQUAL(std::wstring::npos, line.find(expectedStatus), message.c_str());

Comment thread src/windows/wslc/services/ContainerService.cpp Outdated
Comment thread src/windows/wslc/tasks/ContainerTasks.cpp Outdated
Comment thread src/windows/wslc/tasks/ContainerTasks.cpp Outdated
Comment thread src/windows/wslc/services/ContainerService.cpp
Comment thread src/windows/wslc/services/ContainerService.cpp
Comment thread src/windows/wslc/tasks/ContainerTasks.cpp Outdated
Comment thread src/windows/wslcsession/WSLCSession.cpp Outdated
Comment thread src/windows/wslc/services/ContainerService.cpp Outdated

@ptrivedi Pooja Trivedi (ptrivedi) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the changes, a few minor comments

…strings, health status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 20, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 1 comment.

Comment thread src/windows/wslcsession/WSLCSession.cpp
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 20, 2026 22:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/windows/wslcsession/WSLCSession.cpp:2563

  • freeStrings assumes every WSLCContainerEntry has pointer fields initialized to nullptr (so CoTaskMemFree(nullptr) is safe), but wil::make_unique_cotaskmem<WSLCContainerEntry[]>(...) does not guarantee zero-initialization. If an exception is thrown before all entries are populated, FreeContainerEntryStrings(output[i]) can attempt to free uninitialized garbage pointers and crash.

Explicitly zero-initialize the allocated array before installing the scope-exit cleanup (or otherwise ensure the pointer members are set to null before any possible throw).

    auto output = wil::make_unique_cotaskmem<WSLCContainerEntry[]>(dockerContainers.size());
    auto freeStrings = wil::scope_exit([&] {
        for (size_t i = 0; i < dockerContainers.size(); ++i)
        {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants