Browse code

c8d/builder: Store ContainerConfig

Serialize ContainerConfig to content store and store its digest in
label.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2024/01/16 21:36:25
Showing 2 changed files
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	"github.com/containerd/log"
23 23
 	"github.com/distribution/reference"
24 24
 	"github.com/docker/docker/api/types/backend"
25
+	"github.com/docker/docker/api/types/container"
25 26
 	imagetypes "github.com/docker/docker/api/types/image"
26 27
 	"github.com/docker/docker/api/types/registry"
27 28
 	"github.com/docker/docker/builder"
... ...
@@ -47,6 +48,19 @@ const (
47 47
 
48 48
 	// "1" means that the image was created directly from the "FROM scratch".
49 49
 	imageLabelClassicBuilderFromScratch = "org.mobyproject.image.fromscratch"
50
+
51
+	// digest of the ContainerConfig stored in the content store.
52
+	imageLabelClassicBuilderContainerConfig = "org.mobyproject.image.containerconfig"
53
+)
54
+
55
+const (
56
+	// gc.ref label that associates the ContainerConfig content blob with the
57
+	// corresponding Config content.
58
+	contentLabelGcRefContainerConfig = "containerd.io/gc.ref.content.moby/container.config"
59
+
60
+	// Digest of the image this ContainerConfig blobs describes.
61
+	// Only ContainerConfig content should be labelled with it.
62
+	contentLabelClassicBuilderImage = "org.mobyproject.content.image"
50 63
 )
51 64
 
52 65
 // GetImageAndReleasableLayer returns an image and releaseable layer for a
... ...
@@ -451,7 +465,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st
451 451
 		})
452 452
 	}
453 453
 
454
-	createdImageId, err := i.createImageOCI(ctx, ociImgToCreate, parentDigest, layers)
454
+	createdImageId, err := i.createImageOCI(ctx, ociImgToCreate, parentDigest, layers, imgToCreate.ContainerConfig)
455 455
 	if err != nil {
456 456
 		return nil, err
457 457
 	}
... ...
@@ -461,6 +475,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st
461 461
 
462 462
 func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec.DockerOCIImage,
463 463
 	parentDigest digest.Digest, layers []ocispec.Descriptor,
464
+	containerConfig container.Config,
464 465
 ) (dimage.ID, error) {
465 466
 	// Necessary to prevent the contents from being GC'd
466 467
 	// between writing them here and creating an image
... ...
@@ -474,7 +489,7 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
474 474
 		}
475 475
 	}()
476 476
 
477
-	manifestDesc, err := writeContentsForImage(ctx, i.snapshotter, i.client.ContentStore(), imgToCreate, layers)
477
+	manifestDesc, ccDesc, err := writeContentsForImage(ctx, i.snapshotter, i.client.ContentStore(), imgToCreate, layers, containerConfig)
478 478
 	if err != nil {
479 479
 		return "", err
480 480
 	}
... ...
@@ -484,7 +499,8 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
484 484
 		Target:    manifestDesc,
485 485
 		CreatedAt: time.Now(),
486 486
 		Labels: map[string]string{
487
-			imageLabelClassicBuilderParent: parentDigest.String(),
487
+			imageLabelClassicBuilderParent:          parentDigest.String(),
488
+			imageLabelClassicBuilderContainerConfig: ccDesc.Digest.String(),
488 489
 		},
489 490
 	}
490 491
 
... ...
@@ -511,10 +527,17 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
511 511
 }
512 512
 
513 513
 // writeContentsForImage will commit oci image config and manifest into containerd's content store.
514
-func writeContentsForImage(ctx context.Context, snName string, cs content.Store, newConfig imagespec.DockerOCIImage, layers []ocispec.Descriptor) (ocispec.Descriptor, error) {
514
+func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
515
+	newConfig imagespec.DockerOCIImage, layers []ocispec.Descriptor,
516
+	containerConfig container.Config,
517
+) (
518
+	manifestDesc ocispec.Descriptor,
519
+	containerConfigDesc ocispec.Descriptor,
520
+	_ error,
521
+) {
515 522
 	newConfigJSON, err := json.Marshal(newConfig)
516 523
 	if err != nil {
517
-		return ocispec.Descriptor{}, err
524
+		return ocispec.Descriptor{}, ocispec.Descriptor{}, err
518 525
 	}
519 526
 
520 527
 	configDesc := ocispec.Descriptor{
... ...
@@ -539,7 +562,7 @@ func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
539 539
 
540 540
 	newMfstJSON, err := json.MarshalIndent(newMfst, "", "    ")
541 541
 	if err != nil {
542
-		return ocispec.Descriptor{}, err
542
+		return ocispec.Descriptor{}, ocispec.Descriptor{}, err
543 543
 	}
544 544
 
545 545
 	newMfstDesc := ocispec.Descriptor{
... ...
@@ -558,17 +581,37 @@ func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
558 558
 
559 559
 	err = content.WriteBlob(ctx, cs, newMfstDesc.Digest.String(), bytes.NewReader(newMfstJSON), newMfstDesc, content.WithLabels(labels))
560 560
 	if err != nil {
561
-		return ocispec.Descriptor{}, err
561
+		return ocispec.Descriptor{}, ocispec.Descriptor{}, err
562 562
 	}
563 563
 
564
-	// config should reference to snapshotter
564
+	ccDesc, err := saveContainerConfig(ctx, cs, newMfstDesc.Digest, containerConfig)
565
+	if err != nil {
566
+		return ocispec.Descriptor{}, ocispec.Descriptor{}, err
567
+	}
568
+
569
+	// config should reference to snapshotter and container config
565 570
 	labelOpt := content.WithLabels(map[string]string{
566 571
 		fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snName): identity.ChainID(newConfig.RootFS.DiffIDs).String(),
572
+		contentLabelGcRefContainerConfig:                        ccDesc.Digest.String(),
567 573
 	})
568 574
 	err = content.WriteBlob(ctx, cs, configDesc.Digest.String(), bytes.NewReader(newConfigJSON), configDesc, labelOpt)
569 575
 	if err != nil {
576
+		return ocispec.Descriptor{}, ocispec.Descriptor{}, err
577
+	}
578
+
579
+	return newMfstDesc, ccDesc, nil
580
+}
581
+
582
+// saveContainerConfig serializes the given ContainerConfig into a json and
583
+// stores it in the content store and returns its descriptor.
584
+func saveContainerConfig(ctx context.Context, content content.Ingester, imgID digest.Digest, containerConfig container.Config) (ocispec.Descriptor, error) {
585
+	containerConfigDesc, err := storeJson(ctx, content,
586
+		"application/vnd.docker.container.image.v1+json", containerConfig,
587
+		map[string]string{contentLabelClassicBuilderImage: imgID.String()},
588
+	)
589
+	if err != nil {
570 590
 		return ocispec.Descriptor{}, err
571 591
 	}
572 592
 
573
-	return newMfstDesc, nil
593
+	return containerConfigDesc, nil
574 594
 }
... ...
@@ -96,7 +96,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
96 96
 		layers = append(layers, *diffLayerDesc)
97 97
 	}
98 98
 
99
-	return i.createImageOCI(ctx, imageConfig, digest.Digest(cc.ParentImageID), layers)
99
+	return i.createImageOCI(ctx, imageConfig, digest.Digest(cc.ParentImageID), layers, *cc.ContainerConfig)
100 100
 }
101 101
 
102 102
 // generateCommitImageConfig generates an OCI Image config based on the