images: support checking status of image content

The `Check` function returns information about an image's content components
over a content provider. From this information, one can tell which content is
required, present or missing to run an image.

The utility can be demonstrated with the `check` command:

```console
$ ctr images check
REF                               TYPE                                                      DIGEST                                                                  STATUS            SIZE
docker.io/library/alpine:latest   application/vnd.docker.distribution.manifest.list.v2+json sha256:f006ecbb824d87947d0b51ab8488634bf69fe4094959d935c0c103f4820a417d incomplete (1/2)  1.5 KiB/1.9 MiB
docker.io/library/postgres:latest application/vnd.docker.distribution.manifest.v2+json      sha256:2f8080b9910a8b4f38ff5a55a82e77cb43d88bdbb16d723c71d18493590832e9 complete (13/13)  99.3 MiB/99.3 MiB
docker.io/library/redis:alpine    application/vnd.docker.distribution.manifest.v2+json      sha256:e633cded055a94202e4ccccb8125b7f383cd6ee56527ab890db643383a2647dd incomplete (6/7)  8.1 MiB/10.0 MiB
docker.io/library/ubuntu:latest   application/vnd.docker.distribution.manifest.list.v2+json sha256:60f835698ea19e8d9d3a59e68fb96fb35bc43e745941cb2ea9eaf4ba3029ed8a unavailable (0/?) 0.0 B/?
docker.io/trollin/busybox:latest  application/vnd.docker.distribution.manifest.list.v2+json sha256:54a6424f7a2d5f4f27b3d69e5f9f2bc25fe9087f0449d3cb4215db349f77feae complete (2/2)    699.9 KiB/699.9 KiB
```

The above shows us that we have two incomplete images and one that is
unavailable. The incomplete images are those that we know the complete
size of all content but some are missing. "Unavailable" means that the
check could not get enough information about the image to get its full
size.

Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
Stephen J Day
2017-09-27 16:12:12 -07:00
parent 7c9b0eab9f
commit c555df54c0
2 changed files with 135 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ var imageCommand = cli.Command{
Usage: "manage images",
Subcommands: cli.Commands{
imagesListCommand,
imagesCheckCommand,
imageRemoveCommand,
imagesSetLabelsCommand,
imagesImportCommand,
@@ -176,6 +177,90 @@ var imagesSetLabelsCommand = cli.Command{
},
}
var imagesCheckCommand = cli.Command{
Name: "check",
Usage: "Check that an image has all content available locally.",
ArgsUsage: "[flags] <ref> [<ref>, ...]",
Description: "Check that an image has all content available locally.",
Flags: []cli.Flag{},
Action: func(clicontext *cli.Context) error {
var (
exitErr error
)
ctx, cancel := appContext(clicontext)
defer cancel()
client, err := newClient(clicontext)
if err != nil {
return err
}
imageStore := client.ImageService()
contentStore := client.ContentStore()
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "REF\tTYPE\tDIGEST\tSTATUS\tSIZE\t")
args := []string(clicontext.Args())
imageList, err := imageStore.List(ctx, args...)
if err != nil {
return errors.Wrap(err, "failed listing images")
}
for _, image := range imageList {
var (
status string = "complete"
size string
requiredSize int64
presentSize int64
)
available, required, present, missing, err := images.Check(ctx, contentStore, image.Target, platforms.Default())
if err != nil {
if exitErr == nil {
exitErr = errors.Wrapf(err, "unable to check %v", image.Name)
}
log.G(ctx).WithError(err).Errorf("unable to check %v", image.Name)
status = "error"
}
if status != "error" {
for _, d := range required {
requiredSize += d.Size
}
for _, d := range present {
presentSize += d.Size
}
if len(missing) > 0 {
status = "incomplete"
}
if available {
status += fmt.Sprintf(" (%v/%v)", len(present), len(required))
size = fmt.Sprintf("%v/%v", progress.Bytes(presentSize), progress.Bytes(requiredSize))
} else {
status = fmt.Sprintf("unavailable (%v/?)", len(present))
size = fmt.Sprintf("%v/?", progress.Bytes(presentSize))
}
} else {
size = "-"
}
fmt.Fprintf(tw, "%v\t%v\t%v\t%v\t%v\t\n",
image.Name,
image.Target.MediaType,
image.Target.Digest,
status,
size)
}
tw.Flush()
return exitErr
},
}
var imageRemoveCommand = cli.Command{
Name: "remove",
Aliases: []string{"rm"},