c8d/builder: Store ContainerConfig

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

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski 2024-01-16 13:36:25 +01:00
parent a5a15c7782
commit f760cb4f97
No known key found for this signature in database
GPG key ID: B85EFCFE26DEF92A
2 changed files with 53 additions and 10 deletions

View file

@ -22,6 +22,7 @@ import (
"github.com/containerd/log"
"github.com/distribution/reference"
"github.com/docker/docker/api/types/backend"
"github.com/docker/docker/api/types/container"
imagetypes "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/builder"
@ -47,6 +48,19 @@ const (
// "1" means that the image was created directly from the "FROM scratch".
imageLabelClassicBuilderFromScratch = "org.mobyproject.image.fromscratch"
// digest of the ContainerConfig stored in the content store.
imageLabelClassicBuilderContainerConfig = "org.mobyproject.image.containerconfig"
)
const (
// gc.ref label that associates the ContainerConfig content blob with the
// corresponding Config content.
contentLabelGcRefContainerConfig = "containerd.io/gc.ref.content.moby/container.config"
// Digest of the image this ContainerConfig blobs describes.
// Only ContainerConfig content should be labelled with it.
contentLabelClassicBuilderImage = "org.mobyproject.content.image"
)
// GetImageAndReleasableLayer returns an image and releaseable layer for a
@ -451,7 +465,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st
})
}
createdImageId, err := i.createImageOCI(ctx, ociImgToCreate, parentDigest, layers)
createdImageId, err := i.createImageOCI(ctx, ociImgToCreate, parentDigest, layers, imgToCreate.ContainerConfig)
if err != nil {
return nil, err
}
@ -461,6 +475,7 @@ func (i *ImageService) CreateImage(ctx context.Context, config []byte, parent st
func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec.DockerOCIImage,
parentDigest digest.Digest, layers []ocispec.Descriptor,
containerConfig container.Config,
) (dimage.ID, error) {
// Necessary to prevent the contents from being GC'd
// between writing them here and creating an image
@ -474,7 +489,7 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
}
}()
manifestDesc, err := writeContentsForImage(ctx, i.snapshotter, i.client.ContentStore(), imgToCreate, layers)
manifestDesc, ccDesc, err := writeContentsForImage(ctx, i.snapshotter, i.client.ContentStore(), imgToCreate, layers, containerConfig)
if err != nil {
return "", err
}
@ -485,6 +500,7 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
CreatedAt: time.Now(),
Labels: map[string]string{
imageLabelClassicBuilderParent: parentDigest.String(),
imageLabelClassicBuilderContainerConfig: ccDesc.Digest.String(),
},
}
@ -511,10 +527,17 @@ func (i *ImageService) createImageOCI(ctx context.Context, imgToCreate imagespec
}
// writeContentsForImage will commit oci image config and manifest into containerd's content store.
func writeContentsForImage(ctx context.Context, snName string, cs content.Store, newConfig imagespec.DockerOCIImage, layers []ocispec.Descriptor) (ocispec.Descriptor, error) {
func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
newConfig imagespec.DockerOCIImage, layers []ocispec.Descriptor,
containerConfig container.Config,
) (
manifestDesc ocispec.Descriptor,
containerConfigDesc ocispec.Descriptor,
_ error,
) {
newConfigJSON, err := json.Marshal(newConfig)
if err != nil {
return ocispec.Descriptor{}, err
return ocispec.Descriptor{}, ocispec.Descriptor{}, err
}
configDesc := ocispec.Descriptor{
@ -539,7 +562,7 @@ func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
newMfstJSON, err := json.MarshalIndent(newMfst, "", " ")
if err != nil {
return ocispec.Descriptor{}, err
return ocispec.Descriptor{}, ocispec.Descriptor{}, err
}
newMfstDesc := ocispec.Descriptor{
@ -558,17 +581,37 @@ func writeContentsForImage(ctx context.Context, snName string, cs content.Store,
err = content.WriteBlob(ctx, cs, newMfstDesc.Digest.String(), bytes.NewReader(newMfstJSON), newMfstDesc, content.WithLabels(labels))
if err != nil {
return ocispec.Descriptor{}, err
return ocispec.Descriptor{}, ocispec.Descriptor{}, err
}
// config should reference to snapshotter
ccDesc, err := saveContainerConfig(ctx, cs, newMfstDesc.Digest, containerConfig)
if err != nil {
return ocispec.Descriptor{}, ocispec.Descriptor{}, err
}
// config should reference to snapshotter and container config
labelOpt := content.WithLabels(map[string]string{
fmt.Sprintf("containerd.io/gc.ref.snapshot.%s", snName): identity.ChainID(newConfig.RootFS.DiffIDs).String(),
contentLabelGcRefContainerConfig: ccDesc.Digest.String(),
})
err = content.WriteBlob(ctx, cs, configDesc.Digest.String(), bytes.NewReader(newConfigJSON), configDesc, labelOpt)
if err != nil {
return ocispec.Descriptor{}, ocispec.Descriptor{}, err
}
return newMfstDesc, ccDesc, nil
}
// saveContainerConfig serializes the given ContainerConfig into a json and
// stores it in the content store and returns its descriptor.
func saveContainerConfig(ctx context.Context, content content.Ingester, imgID digest.Digest, containerConfig container.Config) (ocispec.Descriptor, error) {
containerConfigDesc, err := storeJson(ctx, content,
"application/vnd.docker.container.image.v1+json", containerConfig,
map[string]string{contentLabelClassicBuilderImage: imgID.String()},
)
if err != nil {
return ocispec.Descriptor{}, err
}
return newMfstDesc, nil
return containerConfigDesc, nil
}

View file

@ -96,7 +96,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
layers = append(layers, *diffLayerDesc)
}
return i.createImageOCI(ctx, imageConfig, digest.Digest(cc.ParentImageID), layers)
return i.createImageOCI(ctx, imageConfig, digest.Digest(cc.ParentImageID), layers, *cc.ContainerConfig)
}
// generateCommitImageConfig generates an OCI Image config based on the