|
...
|
...
|
@@ -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
|
}
|