Align container list format with Docker specifications - #41375
Align container list format with Docker specifications#41375ggarzia-MSFT wants to merge 8 commits into
Conversation
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>
There was a problem hiding this comment.
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-truncbehavior. - 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.
…iner json macro Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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];
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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());
Pooja Trivedi (ptrivedi)
left a comment
There was a problem hiding this comment.
thank you for the changes, a few minor comments
…strings, health status Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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
freeStringsassumes everyWSLCContainerEntryhas pointer fields initialized tonullptr(soCoTaskMemFree(nullptr)is safe), butwil::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)
{
Summary of the Pull Request
Brings
wslc container listto output parity withdocker 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:
CONTAINER ID / IMAGE / COMMAND / CREATED / STATUS / PORTS / NAMES), adding the missingCOMMANDcolumn and movingNAMESlast.STATUSnow 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 nestedPlatformobject. This matches the convention already used bywslc image listandwslc network list.--quiet— truncates to docker's 12-character short ID, with--no-truncfor the full ID.The service now supplies the additional data docker reports (
Command,Status,Labels,Networks,Mounts,LocalVolumes), soWSLCContainerEntrygains those fields.PR Checklist
Detailed Description of the Pull Request / Additional comments
JSON output is a breaking change
container list --format jsonpreviously 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.