Show dangling images in `docker image ls` output.
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"github.com/docker/docker/api/types/filters" |
| 10 | 10 |
"github.com/opencontainers/go-digest" |
| 11 | 11 |
"github.com/opencontainers/image-spec/identity" |
| 12 |
+ "github.com/sirupsen/logrus" |
|
| 12 | 13 |
) |
| 13 | 14 |
|
| 14 | 15 |
var acceptedImageFilterTags = map[string]bool{
|
| ... | ... |
@@ -64,6 +65,7 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) |
| 64 | 64 |
root = make([]*[]digest.Digest, len(imgs)) |
| 65 | 65 |
layers = make(map[digest.Digest]int) |
| 66 | 66 |
} |
| 67 |
+ |
|
| 67 | 68 |
for n, img := range imgs {
|
| 68 | 69 |
if !filter(img) {
|
| 69 | 70 |
continue |
| ... | ... |
@@ -91,12 +93,43 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) |
| 91 | 91 |
return nil, err |
| 92 | 92 |
} |
| 93 | 93 |
|
| 94 |
+ var repoTags, repoDigests []string |
|
| 95 |
+ rawImg := img.Metadata() |
|
| 96 |
+ target := rawImg.Target.Digest |
|
| 97 |
+ |
|
| 98 |
+ logger := logrus.WithFields(logrus.Fields{
|
|
| 99 |
+ "name": img.Name(), |
|
| 100 |
+ "digest": target, |
|
| 101 |
+ }) |
|
| 102 |
+ |
|
| 103 |
+ ref, err := reference.ParseNamed(rawImg.Name) |
|
| 104 |
+ if err != nil {
|
|
| 105 |
+ // If the image has unexpected name format (not a Named reference or a dangling image) |
|
| 106 |
+ // add the offending name to RepoTags but also log an error to make it clear to the |
|
| 107 |
+ // administrator that this is unexpected. |
|
| 108 |
+ // TODO: Reconsider when containerd is more strict on image names, see: |
|
| 109 |
+ // https://github.com/containerd/containerd/issues/7986 |
|
| 110 |
+ if !isDanglingImage(rawImg) {
|
|
| 111 |
+ logger.WithError(err).Error("failed to parse image name as reference")
|
|
| 112 |
+ repoTags = append(repoTags, img.Name()) |
|
| 113 |
+ } |
|
| 114 |
+ } else {
|
|
| 115 |
+ repoTags = append(repoTags, reference.TagNameOnly(ref).String()) |
|
| 116 |
+ |
|
| 117 |
+ digested, err := reference.WithDigest(reference.TrimNamed(ref), target) |
|
| 118 |
+ if err != nil {
|
|
| 119 |
+ logger.WithError(err).Error("failed to create digested reference")
|
|
| 120 |
+ } else {
|
|
| 121 |
+ repoDigests = append(repoDigests, digested.String()) |
|
| 122 |
+ } |
|
| 123 |
+ } |
|
| 124 |
+ |
|
| 94 | 125 |
summaries = append(summaries, &types.ImageSummary{
|
| 95 | 126 |
ParentID: "", |
| 96 |
- ID: img.Target().Digest.String(), |
|
| 97 |
- Created: img.Metadata().CreatedAt.Unix(), |
|
| 98 |
- RepoDigests: []string{img.Name() + "@" + img.Target().Digest.String()}, // "hello-world@sha256:bfea6278a0a267fad2634554f4f0c6f31981eea41c553fdf5a83e95a41d40c38"},
|
|
| 99 |
- RepoTags: []string{img.Name()},
|
|
| 127 |
+ ID: target.String(), |
|
| 128 |
+ Created: rawImg.CreatedAt.Unix(), |
|
| 129 |
+ RepoDigests: repoDigests, |
|
| 130 |
+ RepoTags: repoTags, |
|
| 100 | 131 |
Size: size, |
| 101 | 132 |
VirtualSize: virtualSize, |
| 102 | 133 |
// -1 indicates that the value has not been set (avoids ambiguity |
| ... | ... |
@@ -59,3 +59,7 @@ func (i *ImageService) softImageDelete(ctx context.Context, img containerdimages |
| 59 | 59 |
func danglingImageName(digest digest.Digest) string {
|
| 60 | 60 |
return "moby-dangling@" + digest.String() |
| 61 | 61 |
} |
| 62 |
+ |
|
| 63 |
+func isDanglingImage(image containerdimages.Image) bool {
|
|
| 64 |
+ return image.Name == danglingImageName(image.Target.Digest) |
|
| 65 |
+} |