Images built by classic builder will have an additional label (in the
containerd image object, not image config) pointing to a parent of that
image.
This allows to differentiate intermediate images (dangling
images created as a result of a each Dockerfile instruction) from the
final images.
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
| ... | ... |
@@ -36,6 +36,8 @@ import ( |
| 36 | 36 |
ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
| 37 | 37 |
) |
| 38 | 38 |
|
| 39 |
+const imageLabelClassicBuilderParent = "org.mobyproject.image.parent" |
|
| 40 |
+ |
|
| 39 | 41 |
// GetImageAndReleasableLayer returns an image and releaseable layer for a |
| 40 | 42 |
// reference or ID. Every call to GetImageAndReleasableLayer MUST call |
| 41 | 43 |
// releasableLayer.Release() to prevent leaking of layers. |
| ... | ... |
@@ -392,6 +394,8 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st |
| 392 | 392 |
ociImgToCreate := dockerImageToDockerOCIImage(*imgToCreate) |
| 393 | 393 |
|
| 394 | 394 |
var layers []ocispec.Descriptor |
| 395 |
+ |
|
| 396 |
+ var parentDigest digest.Digest |
|
| 395 | 397 |
// if the image has a parent, we need to start with the parents layers descriptors |
| 396 | 398 |
if parent != "" {
|
| 397 | 399 |
parentDesc, err := i.resolveDescriptor(ctx, parent) |
| ... | ... |
@@ -404,6 +408,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st |
| 404 | 404 |
} |
| 405 | 405 |
|
| 406 | 406 |
layers = parentImageManifest.Layers |
| 407 |
+ parentDigest = parentDesc.Digest |
|
| 407 | 408 |
} |
| 408 | 409 |
|
| 409 | 410 |
// get the info for the new layers |
| ... | ... |
@@ -443,6 +448,9 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st |
| 443 | 443 |
Name: danglingImageName(commitManifestDesc.Digest), |
| 444 | 444 |
Target: commitManifestDesc, |
| 445 | 445 |
CreatedAt: time.Now(), |
| 446 |
+ Labels: map[string]string{
|
|
| 447 |
+ imageLabelClassicBuilderParent: parentDigest.String(), |
|
| 448 |
+ }, |
|
| 446 | 449 |
} |
| 447 | 450 |
|
| 448 | 451 |
createdImage, err := i.client.ImageService().Update(ctx, img) |
| ... | ... |
@@ -106,6 +106,9 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig) |
| 106 | 106 |
Name: danglingImageName(commitManifestDesc.Digest), |
| 107 | 107 |
Target: commitManifestDesc, |
| 108 | 108 |
CreatedAt: time.Now(), |
| 109 |
+ Labels: map[string]string{
|
|
| 110 |
+ imageLabelClassicBuilderParent: cc.ParentImageID, |
|
| 111 |
+ }, |
|
| 109 | 112 |
} |
| 110 | 113 |
|
| 111 | 114 |
if _, err := i.client.ImageService().Update(ctx, img); err != nil {
|
| ... | ... |
@@ -98,8 +98,34 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) |
| 98 | 98 |
contentStore := i.client.ContentStore() |
| 99 | 99 |
uniqueImages := map[digest.Digest]images.Image{}
|
| 100 | 100 |
tagsByDigest := map[digest.Digest][]string{}
|
| 101 |
+ intermediateImages := map[digest.Digest]struct{}{}
|
|
| 102 |
+ |
|
| 103 |
+ hideIntermediate := !opts.All |
|
| 104 |
+ if hideIntermediate {
|
|
| 105 |
+ for _, img := range imgs {
|
|
| 106 |
+ parent, ok := img.Labels[imageLabelClassicBuilderParent] |
|
| 107 |
+ if ok && parent != "" {
|
|
| 108 |
+ dgst, err := digest.Parse(parent) |
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ log.G(ctx).WithFields(log.Fields{
|
|
| 111 |
+ "error": err, |
|
| 112 |
+ "value": parent, |
|
| 113 |
+ }).Warnf("invalid %s label value", imageLabelClassicBuilderParent)
|
|
| 114 |
+ } |
|
| 115 |
+ intermediateImages[dgst] = struct{}{}
|
|
| 116 |
+ } |
|
| 117 |
+ } |
|
| 118 |
+ } |
|
| 101 | 119 |
|
| 102 | 120 |
for _, img := range imgs {
|
| 121 |
+ isDangling := isDanglingImage(img) |
|
| 122 |
+ |
|
| 123 |
+ if hideIntermediate && isDangling {
|
|
| 124 |
+ if _, ok := intermediateImages[img.Target.Digest]; ok {
|
|
| 125 |
+ continue |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 103 | 129 |
if !filter(img) {
|
| 104 | 130 |
continue |
| 105 | 131 |
} |
| ... | ... |
@@ -107,7 +133,7 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) |
| 107 | 107 |
dgst := img.Target.Digest |
| 108 | 108 |
uniqueImages[dgst] = img |
| 109 | 109 |
|
| 110 |
- if isDanglingImage(img) {
|
|
| 110 |
+ if isDangling {
|
|
| 111 | 111 |
continue |
| 112 | 112 |
} |
| 113 | 113 |
|
| ... | ... |
@@ -73,6 +73,17 @@ func (i *ImageService) TagImage(ctx context.Context, imageID image.ID, newTag re |
| 73 | 73 |
return nil |
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 |
+ builderLabel, ok := sourceDanglingImg.Labels[imageLabelClassicBuilderParent] |
|
| 77 |
+ if ok {
|
|
| 78 |
+ newImg.Labels = map[string]string{
|
|
| 79 |
+ imageLabelClassicBuilderParent: builderLabel, |
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 82 |
+ if _, err := is.Update(context.Background(), newImg, "labels"); err != nil {
|
|
| 83 |
+ logger.WithError(err).Warnf("failed to set %s label on the newly tagged image", imageLabelClassicBuilderParent)
|
|
| 84 |
+ } |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 76 | 87 |
// Delete the source dangling image, as it's no longer dangling. |
| 77 | 88 |
if err := is.Delete(context.Background(), sourceDanglingImg.Name); err != nil {
|
| 78 | 89 |
logger.WithError(err).Warn("unexpected error when deleting dangling image")
|