Skip to content

Commit 041da7a

Browse files
committed
Resolve and store image variant digest when creating container
This addresses the previous issue with the containerd store where, after a container is created, we can't deterministically resolve which image variant was used to run it (since we also don't store what platform the image was fetched for). This is required for things like `docker commit`, and computing the containers layer size later, since we need to resolve the actual image variant. Signed-off-by: Laura Brehm <[email protected]>
1 parent 7061e10 commit 041da7a

6 files changed

Lines changed: 82 additions & 29 deletions

File tree

container/container.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,22 +61,23 @@ type ExitStatus struct {
6161
type Container struct {
6262
StreamConfig *stream.Config
6363
// embed for Container to support states directly.
64-
*State `json:"State"` // Needed for Engine API version <= 1.11
65-
Root string `json:"-"` // Path to the "home" of the container, including metadata.
66-
BaseFS string `json:"-"` // Path to the graphdriver mountpoint
67-
RWLayer layer.RWLayer `json:"-"`
68-
ID string
69-
Created time.Time
70-
Managed bool
71-
Path string
72-
Args []string
73-
Config *containertypes.Config
74-
ImageID image.ID `json:"Image"`
75-
NetworkSettings *network.Settings
76-
LogPath string
77-
Name string
78-
Driver string
79-
OS string
64+
*State `json:"State"` // Needed for Engine API version <= 1.11
65+
Root string `json:"-"` // Path to the "home" of the container, including metadata.
66+
BaseFS string `json:"-"` // Path to the graphdriver mountpoint
67+
RWLayer layer.RWLayer `json:"-"`
68+
ID string
69+
Created time.Time
70+
Managed bool
71+
Path string
72+
Args []string
73+
Config *containertypes.Config
74+
ImageID image.ID `json:"Image"`
75+
ImageVariantDigest string
76+
NetworkSettings *network.Settings
77+
LogPath string
78+
Name string
79+
Driver string
80+
OS string
8081
// MountLabel contains the options for the 'mount' command
8182
MountLabel string
8283
ProcessLabel string

daemon/containerd/image.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,36 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
122122
return img, nil
123123
}
124124

125+
func (i *ImageService) GetImageVariant(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*ocispec.Descriptor, error) {
126+
cs := i.client.ContentStore()
127+
128+
desc, err := i.resolveDescriptor(ctx, refOrID)
129+
if err != nil {
130+
return nil, nil
131+
}
132+
133+
platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
134+
if options.Platform != nil {
135+
platform = cplatforms.OnlyStrict(*options.Platform)
136+
}
137+
138+
childManifests, err := containerdimages.LimitManifests(containerdimages.ChildrenHandler(cs), platform, 1)(ctx, desc)
139+
if err != nil {
140+
if cerrdefs.IsNotFound(err) {
141+
return nil, errdefs.NotFound(err)
142+
}
143+
return nil, errdefs.System(err)
144+
}
145+
146+
// len(childManifests) == 1 since we requested 1 and if none
147+
// were found LimitManifests would have thrown an error
148+
if !containerdimages.IsManifestType(childManifests[0].MediaType) {
149+
return nil, errdefs.NotFound(fmt.Errorf("manifest has incorrect mediatype: %s", childManifests[0].MediaType))
150+
}
151+
152+
return &childManifests[0], nil
153+
}
154+
125155
// size returns the total size of the image's packed resources.
126156
func (i *ImageService) size(ctx context.Context, desc ocispec.Descriptor, platform cplatforms.MatchComparer) (int64, error) {
127157
var size int64

daemon/containerd/image_commit.go

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import (
1616
cerrdefs "github.com/containerd/containerd/errdefs"
1717
"github.com/containerd/containerd/images"
1818
"github.com/containerd/containerd/leases"
19-
"github.com/containerd/containerd/platforms"
2019
"github.com/containerd/containerd/rootfs"
2120
"github.com/containerd/containerd/snapshots"
2221
"github.com/docker/docker/api/types/backend"
@@ -39,20 +38,24 @@ with adaptations to match the Moby data model and services.
3938
// CommitImage creates a new image from a commit config.
4039
func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig) (image.ID, error) {
4140
container := i.containers.Get(cc.ContainerID)
42-
43-
desc, err := i.resolveDescriptor(ctx, container.Config.Image)
44-
if err != nil {
45-
return "", err
41+
manifestDescriptor := ocispec.Descriptor{
42+
MediaType: ocispec.MediaTypeImageManifest,
43+
Digest: digest.Digest(container.ImageVariantDigest),
4644
}
4745

4846
cs := i.client.ContentStore()
4947

50-
ocimanifest, err := images.Manifest(ctx, cs, desc, platforms.DefaultStrict())
48+
imageManifestBytes, err := content.ReadBlob(ctx, cs, manifestDescriptor)
5149
if err != nil {
5250
return "", err
5351
}
5452

55-
imageConfigBytes, err := content.ReadBlob(ctx, cs, ocimanifest.Config)
53+
var manifest ocispec.Manifest
54+
if err := json.Unmarshal(imageManifestBytes, &manifest); err != nil {
55+
return "", err
56+
}
57+
58+
imageConfigBytes, err := content.ReadBlob(ctx, cs, manifest.Config)
5659
if err != nil {
5760
return "", err
5861
}
@@ -88,7 +91,7 @@ func (i *ImageService) CommitImage(ctx context.Context, cc backend.CommitConfig)
8891
return "", fmt.Errorf("failed to apply diff: %w", err)
8992
}
9093

91-
layers := append(ocimanifest.Layers, diffLayerDesc)
94+
layers := append(manifest.Layers, diffLayerDesc)
9295
commitManifestDesc, configDigest, err := writeContentsForImage(ctx, i.snapshotter, cs, imageConfig, layers)
9396
if err != nil {
9497
return "", err

daemon/create.go

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,28 @@ func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (con
115115
// Create creates a new container from the given configuration with a given name.
116116
func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *container.Container, retErr error) {
117117
var (
118-
ctr *container.Container
119-
img *image.Image
120-
imgID image.ID
121-
err error
122-
os = runtime.GOOS
118+
ctr *container.Container
119+
img *image.Image
120+
imgVariantManifest *v1.Descriptor
121+
imgID image.ID
122+
err error
123+
os = runtime.GOOS
123124
)
124125

125126
if opts.params.Config.Image != "" {
126127
img, err = daemon.imageService.GetImage(ctx, opts.params.Config.Image, imagetypes.GetImageOpts{Platform: opts.params.Platform})
127128
if err != nil {
128129
return nil, err
129130
}
131+
// when using the containerd store, we need to get the actual image variant
132+
// manifest so we can store a reference to the content addressable blob and later
133+
// we can deterministically resolve the image variant the container is running
134+
if daemon.UsesSnapshotter() {
135+
imgVariantManifest, err = daemon.imageService.GetImageVariant(ctx, opts.params.Config.Image, imagetypes.GetImageOpts{Platform: opts.params.Platform})
136+
if err != nil {
137+
return nil, err
138+
}
139+
}
130140
os = img.OperatingSystem()
131141
imgID = img.ID()
132142
} else if isWindows {
@@ -169,6 +179,10 @@ func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *contai
169179
}
170180

171181
ctr.HostConfig.StorageOpt = opts.params.HostConfig.StorageOpt
182+
if imgVariantManifest != nil {
183+
// store the image variant digest
184+
ctr.ImageVariantDigest = string(imgVariantManifest.Digest)
185+
}
172186

173187
if daemon.UsesSnapshotter() {
174188
if err := daemon.imageService.PrepareSnapshot(ctx, ctr.ID, opts.params.Config.Image, opts.params.Platform); err != nil {

daemon/image_service.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ type ImageService interface {
4646
// Containerd related methods
4747

4848
PrepareSnapshot(ctx context.Context, id string, image string, platform *v1.Platform) error
49+
GetImageVariant(ctx context.Context, refOrID string, options imagetype.GetImageOpts) (*v1.Descriptor, error)
4950

5051
// Layers
5152

daemon/images/image.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@ func (i *ImageService) GetImage(ctx context.Context, refOrID string, options ima
192192
return img, nil
193193
}
194194

195+
func (i *ImageService) GetImageVariant(ctx context.Context, refOrID string, options imagetypes.GetImageOpts) (*v1.Descriptor, error) {
196+
panic("not implemented")
197+
}
198+
195199
func (i *ImageService) getImage(ctx context.Context, refOrID string, options imagetypes.GetImageOpts) (retImg *image.Image, retErr error) {
196200
defer func() {
197201
if retErr != nil || retImg == nil || options.Platform == nil {

0 commit comments

Comments
 (0)