Browse code

c8d/builder: Store parent in c8d image label

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>

Paweł Gronowski authored on 2023/09/08 01:00:33
Showing 4 changed files
... ...
@@ -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")