Repository navigation
feat(image): adopt Docker v29 output for images list - #5093
Conversation
21a312d to
97dbfbd
Compare
97dbfbd to
a22cd95
Compare
Make the default `nerdctl images` output match Docker v29: a collapsed view with IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, an "In Use" (U) indicator, and <untagged> for dangling images. Multi-platform images are collapsed into a single row with aggregated disk and content size. Like Docker, the "In Use" legend is only printed when the output is a terminal, so piped and redirected output stays clean, and rows are ordered by image reference with untagged images last rather than by creation time. In-use is resolved by image target digest, the way Docker matches containers to images, so every reference to a used target is flagged, not only the one the container was created from. The new view is used only for the bare command. Passing --format, --quiet, --no-trunc, --digests or --names falls back to the legacy table (REPOSITORY, TAG, IMAGE ID, CREATED, PLATFORM, SIZE, BLOB SIZE), so existing scripts and templates keep working, including their creation-time ordering. This mirrors Docker's own shouldUseTree fallback. Since the default output now matches Docker, the images tests also run against the Docker target; only the nerdctl-specific --names subtest stays gated. Tests that assert on the default `images` output for untagged images (image prune/remove and build-without-tag) are updated to expect <untagged>. The expanded per-platform `--tree` view is left for a follow-up. Closes containerd#5027 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
a22cd95 to
4dc5675
Compare
|
Force-pushed. Both threads above are addressed ( 1. "In Use" is now resolved by image target digest instead of image name moby matches containers to images by digest ( if c.ImageManifest != nil && c.ImageManifest.Digest == target.Digest {nerdctl was keying the in-use set on 2. Row ordering in the new view now follows docker/cli docker/cli's tree view sorts by image name lexicographically and places untagged images last, while nerdctl kept its creation-time ordering. The new view is now sorted by the rendered One known divergence left, which I'd rather fix separately - let me know if you want it here
So nerdctl can over-report for partially pulled images - which is not exotic here, given the lazy-pulling snapshotters (stargz, nydus, SOCI, overlaybd) - and under-report when attestations are present. This math predates this PR (it is the same code behind today's PTAL. |
Expand the Docker v29 view added in containerd#5093 with `--tree`, matching `docker image ls --tree`: the same IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, plus one row per platform present in the content store, prefixed with the branch glyphs docker/cli uses. A platform row carries its own manifest digest as the ID, its own sizes, and the "U" flag only when a container actually runs that platform, resolved via the container platform label. The image row keeps aggregating its platforms and reuses the existing target-digest lookup, so the default view is unchanged. The label is normalized before matching: platforms.DefaultString does not normalize, so on arm64 it carries a variant while the manifest platform normalizes to a bare linux/arm64, and a raw comparison would never match. Unlike docker/cli, which computes its column widths itself and separates the images with a blank line, the rows go through a tabwriter shared with the header, where a blank line would terminate the column block and misalign every following group. The groups are therefore separated by the glyphs alone. The flag combinations docker rejects in shouldUseTree are rejected here too, with the same messages. They are validated in pkg/cmd/image, where the options are consumed, so that library callers are covered as well: the tree branch takes precedence over the formatter, so Tree together with Format used to print unaligned rows with no header. The CLI validates too, so that the error still surfaces before a containerd connection is attempted. Also read the platform of a multi-platform image from its index descriptor rather than from the image config. The config may be less specific: alpine ships linux/arm/v6 and linux/arm/v7 manifests whose configs both declare a bare "linux/arm", which normalizes to linux/arm/v7 and collapsed the two onto a single key, dropping one platform. That also left it out of the aggregated sizes of the default view. The expected per-platform content sizes are declared in testutil, and the integration test runs against docker as well, only diverging where docker does: `docker pull` has no --all-platforms, and its message for the digests conflict names its internal flag. Closes containerd#5005 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Expand the Docker v29 view added in containerd#5093 with `--tree`, matching `docker image ls --tree`: the same IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, plus one row per platform the image declares, prefixed with the branch glyphs docker/cli uses. Like docker, a platform that an index lists but that was never pulled is shown with zero sizes. The collapsed and the legacy views keep describing only what is in the store, and so does EnsureAllContent, which would otherwise reach out for platforms the user never asked for. A platform row carries its own manifest digest as the ID, its own sizes, and the "U" flag only when a container actually runs that platform, resolved via the container platform label. The image row keeps aggregating its platforms and reuses the existing target-digest lookup, so the default view is unchanged. The label is normalized before matching: platforms.DefaultString does not normalize, so on arm64 it carries a variant while the manifest platform normalizes to a bare linux/arm64, and a raw comparison would never match. Unlike docker/cli, which computes its column widths itself and separates the images with a blank line, the rows go through a tabwriter shared with the header, where a blank line would terminate the column block and misalign every following group. The groups are therefore separated by the glyphs alone. The flag combinations docker rejects in shouldUseTree are rejected here too, with the same messages. They are validated in pkg/cmd/image, where the options are consumed, so that library callers are covered as well: the tree branch takes precedence over the formatter, so Tree together with Format used to print unaligned rows with no header. The CLI validates too, so that the error still surfaces before a containerd connection is attempted. Also read the platform of a multi-platform image from its index descriptor rather than from the image config. The config may be less specific: alpine ships linux/arm/v6 and linux/arm/v7 manifests whose configs both declare a bare "linux/arm", which normalizes to linux/arm/v7 and collapsed the two onto a single key, dropping one platform. That also left it out of the aggregated sizes of the default view. The expected per-platform content sizes are declared in testutil, and the integration test runs against docker as well, only diverging where docker does: `docker pull` has no --all-platforms, and its message for the digests conflict names its internal flag. DISK USAGE is only asserted to cover CONTENT SIZE, because which platforms are unpacked depends on what the rest of the suite did with the shared image store. Closes containerd#5005 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Expand the Docker v29 view added in containerd#5093 with `--tree`, matching `docker image ls --tree`: the same IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, plus one row per platform the image declares, prefixed with the branch glyphs docker/cli uses. Like docker, a platform that an index lists but that was never pulled is shown with zero sizes. The collapsed and the legacy views keep describing only what is in the store, and so does EnsureAllContent, which would otherwise reach out for platforms the user never asked for. A platform row carries its own manifest digest as the ID, its own sizes, and the "U" flag only when a container actually runs that platform. The image row keeps aggregating its platforms and reuses the existing target-digest lookup, so the default view is unchanged. Matching a container to a platform is done on the full platform form, not on the name the row displays: an index can carry several windows/amd64 manifests that differ only by OSVersion, and keying on the displayed name would let a container on one build flag all of them. The container label is normalized first, since platforms.DefaultString does not normalize: on arm64 it carries a variant while the manifest platform normalizes to a bare linux/arm64, and a raw comparison would never match. The rows are sorted on that same full form, so the ones that render identically keep a stable order. Unlike docker/cli, which computes its column widths itself and separates the images with a blank line, the rows go through a tabwriter shared with the header, where a blank line would terminate the column block and misalign every following group. The groups are therefore separated by the glyphs alone. The flag combinations docker rejects in shouldUseTree are rejected here too, with the same messages. They are validated in pkg/cmd/image, where the options are consumed, so that library callers are covered as well: the tree branch takes precedence over the formatter, so Tree together with Format used to print unaligned rows with no header. The CLI validates too, so that the error still surfaces before a containerd connection is attempted. Also read the platform of a multi-platform image from its index descriptor rather than from the image config. The config may be less specific: alpine ships linux/arm/v6 and linux/arm/v7 manifests whose configs both declare a bare "linux/arm", which normalizes to linux/arm/v7 and collapsed the two onto a single key, dropping one platform. That also left it out of the aggregated sizes of the default view. The expected per-platform content sizes are declared in testutil, and the integration test runs against docker as well, only diverging where docker does: `docker pull` has no --all-platforms, and its message for the digests conflict names its internal flag. DISK USAGE is only asserted to cover CONTENT SIZE, because which platforms are unpacked depends on what the rest of the suite did with the shared image store. Closes containerd#5005 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Expand the Docker v29 view added in containerd#5093 with `--tree`, matching `docker image ls --tree`: the same IMAGE, ID, DISK USAGE, CONTENT SIZE and EXTRA columns, plus one row per platform the image declares, prefixed with the branch glyphs docker/cli uses. Like docker, a platform that an index lists but that was never pulled is shown with zero sizes. The collapsed and the legacy views keep describing only what is in the store, and so does EnsureAllContent, which would otherwise reach out for platforms the user never asked for. A platform row carries its own manifest digest as the ID, its own sizes, and the "U" flag only when a container actually runs that platform. The image row keeps aggregating its platforms and reuses the existing target-digest lookup, so the default view is unchanged. Matching a container to a platform is done on the full platform form, not on the name the row displays: an index can carry several windows/amd64 manifests that differ only by OSVersion, and keying on the displayed name would let a container on one build flag all of them. The container label is normalized first, since platforms.DefaultString does not normalize: on arm64 it carries a variant while the manifest platform normalizes to a bare linux/arm64, and a raw comparison would never match. The rows are sorted on that same full form, so the ones that render identically keep a stable order. Unlike docker/cli, which computes its column widths itself and separates the images with a blank line, the rows go through a tabwriter shared with the header, where a blank line would terminate the column block and misalign every following group. The groups are therefore separated by the glyphs alone. The flag combinations docker rejects in shouldUseTree are rejected here too, with the same messages. They are validated in pkg/cmd/image, where the options are consumed, so that library callers are covered as well: the tree branch takes precedence over the formatter, so Tree together with Format used to print unaligned rows with no header. The CLI validates too, so that the error still surfaces before a containerd connection is attempted. Also read the platform of a multi-platform image from its index descriptor rather than from the image config. The config may be less specific: alpine ships linux/arm/v6 and linux/arm/v7 manifests whose configs both declare a bare "linux/arm", which normalizes to linux/arm/v7 and collapsed the two onto a single key, dropping one platform. That also left it out of the aggregated sizes of the default view. The expected per-platform content sizes are declared in testutil, and the integration test runs against docker as well, only diverging where docker does: `docker pull` has no --all-platforms, and its message for the digests conflict names its internal flag. DISK USAGE is only asserted to cover CONTENT SIZE, because which platforms are unpacked depends on what the rest of the suite did with the shared image store. Closes containerd#5005 Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
Make the default
nerdctl imagesoutput match Docker v29: a collapsed view withIMAGE,ID,DISK USAGE,CONTENT SIZEandEXTRAcolumns, an "In Use" (U)indicator, and
<untagged>for dangling images. Multi-platform images are collapsedinto a single row with aggregated disk and content size.
Details that follow Docker's behavior:
DISK USAGEis the content store blobs plus the unpacked snapshots (Docker'sTotal),CONTENT SIZEis the blobs alone. Both are formatted with 3 significantdigits, like Docker.
images, so every reference to a used target is flagged, not only the one the
container was created from.
redirected output stays clean.
creation time.
IDcolumn is the OCI digest of the image target, which matches Docker's IDwith the containerd image store (it differs only from the legacy graphdriver
image ID). The help text and docs are updated accordingly.
The new view is used only for the bare command. Passing
--format,--quiet,--no-trunc,--digestsor--namesfalls back to the legacy table (REPOSITORY,TAG,IMAGE ID,CREATED,PLATFORM,SIZE,BLOB SIZE), so existing scripts andtemplates keep working, including their creation-time ordering. This mirrors Docker's
own
shouldUseTreefallback.Since the default output now matches Docker, the images tests also run against the
Docker target; only the nerdctl-specific
--namessubtest stays gated. Tests thatassert on the default
imagesoutput for untagged images (image prune/remove andbuild-without-tag) are updated to expect
<untagged>.The expanded per-platform
--treeview is left for a follow-up.Closes #5027