containerd/cmd/ctr/commands/images/usage.go
Wei Fu e855a59984 cmd/ctr/commands/images: support usage subcommand
The `ctr image usage` can display the usage of snapshots with a given
image ref. It's easy for user to get chain snapshot IDs and unpack
usage. And according to the [discuss][1], this subcommand can be used to
ensure if snapshot's pagecache has been discarded in a unexpected
reboot.

How to use it:

```bash
$ bin/ctr image usage --snapshotter native docker.io/library/golang:1.19.3
ctr: image docker.io/library/golang:1.19.3 isn't unpacked in snapshotter native

$ bin/ctr image usage --snapshotter overlayfs docker.io/library/golang:1.19.3
KEY                                                                     SIZE      INODES
sha256:28114d8403bac6352c3e09cb23e37208138a0cd9d309edf3df38e57be8075a1d 16.0 KiB  4
sha256:f162c02ce6b9b594757cd76eda1c1dd119b88e69e882cb645bf7ad528b54f0d2 476.2 MiB 13660
sha256:a5b9faceaa495819b9ba7011b7276c4ffaffe6c7b9de0889e11abc1113f7b5ca 225.5 MiB 3683
sha256:412b2615d27d6b0090558d25b201b60a7dff2a40892a7e7ca868b80bf5e5de41 159.8 MiB 6196
sha256:dbce1593502d39c344ce089f98187999f294de5182a7106dcb6c9d04ce0c7265 19.4 MiB  502
sha256:8953bf5d24149e9b2236abc76bd0aa14b73828f1b63e816cb4b457249f6125bc 12.2 MiB  958
sha256:ccba29d6937047c719a6c048a7038d3907590fbb8556418d119469b2ad4f95bc 134.7 MiB 7245

$ bin/ctr image usage --snapshotter overlayfs docker.io/library/golang:1.19
ctr: failed to ensure if image docker.io/library/golang:1.19 exists: image "docker.io/library/golang:1.19": not found
```

[1]: <https://github.com/containerd/containerd/issues/5854#issuecomment-1415915765>

Signed-off-by: Wei Fu <fuweid89@gmail.com>
2023-02-14 23:24:10 +08:00

98 lines
2.6 KiB
Go

/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package images
import (
"fmt"
"os"
"text/tabwriter"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/pkg/progress"
"github.com/opencontainers/image-spec/identity"
"github.com/urfave/cli"
)
var usageCommand = cli.Command{
Name: "usage",
Usage: "Display usage of snapshots for a given image ref",
ArgsUsage: "[flags] <ref>",
Flags: commands.SnapshotterFlags,
Action: func(context *cli.Context) error {
var ref = context.Args().First()
if ref == "" {
return fmt.Errorf("please provide an image reference to mount")
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
snapshotter := context.String("snapshotter")
if snapshotter == "" {
snapshotter = containerd.DefaultSnapshotter
}
img, err := client.ImageService().Get(ctx, ref)
if err != nil {
return fmt.Errorf("failed to ensure if image %s exists: %w", ref, err)
}
i := containerd.NewImage(client, img)
if ok, err := i.IsUnpacked(ctx, snapshotter); err != nil {
return fmt.Errorf("failed to ensure if image %s has been unpacked in snapshotter %s: %w",
ref, snapshotter, err)
} else if !ok {
return fmt.Errorf("image %s isn't unpacked in snapshotter %s", ref, snapshotter)
}
diffIDs, err := i.RootFS(ctx)
if err != nil {
return err
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "KEY\tSIZE\tINODES\t")
snSrv := client.SnapshotService(snapshotter)
snID := identity.ChainID(diffIDs).String()
for snID != "" {
usage, err := snSrv.Usage(ctx, snID)
if err != nil {
return fmt.Errorf("failed to get usage for snapshot %s: %w", snID, err)
}
fmt.Fprintf(tw, "%v\t%s\t%d\t\n",
snID,
progress.Bytes(usage.Size).String(),
usage.Inodes,
)
info, err := snSrv.Stat(ctx, snID)
if err != nil {
return fmt.Errorf("failed to ensure if snapshot %s has parent or not: %w", snID, err)
}
snID = info.Parent
}
return tw.Flush()
},
}