Skip to content

Commit ad4c9f8

Browse files
committed
Update CRI runtime platform and pinned image configuration
Updates the CRI image service to own image related configuration and separate it from the runtime configuration. Signed-off-by: Derek McGowan <[email protected]>
1 parent 11f311f commit ad4c9f8

File tree

8 files changed

+254
-131
lines changed

8 files changed

+254
-131
lines changed

integration/image_pull_timeout_test.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func testCRIImagePullTimeoutBySlowCommitWriter(t *testing.T) {
8686
delayDuration := 2 * defaultImagePullProgressTimeout
8787
cli := buildLocalContainerdClient(t, tmpDir, tweakContentInitFnWithDelayer(delayDuration))
8888

89-
criService, err := initLocalCRIPlugin(cli, tmpDir, criconfig.Registry{})
89+
criService, err := initLocalCRIImageService(cli, tmpDir, criconfig.Registry{})
9090
assert.NoError(t, err)
9191

9292
ctx := namespaces.WithNamespace(logtest.WithT(context.Background(), t), k8sNamespace)
@@ -109,7 +109,7 @@ func testCRIImagePullTimeoutByHoldingContentOpenWriter(t *testing.T) {
109109

110110
cli := buildLocalContainerdClient(t, tmpDir, nil)
111111

112-
criService, err := initLocalCRIPlugin(cli, tmpDir, criconfig.Registry{})
112+
criService, err := initLocalCRIImageService(cli, tmpDir, criconfig.Registry{})
113113
assert.NoError(t, err)
114114

115115
ctx := namespaces.WithNamespace(logtest.WithT(context.Background(), t), k8sNamespace)
@@ -287,7 +287,7 @@ func testCRIImagePullTimeoutByNoDataTransferred(t *testing.T) {
287287
},
288288
},
289289
} {
290-
criService, err := initLocalCRIPlugin(cli, tmpDir, registryCfg)
290+
criService, err := initLocalCRIImageService(cli, tmpDir, registryCfg)
291291
assert.NoError(t, err)
292292

293293
dctx, _, err := cli.WithLease(ctx)
@@ -468,27 +468,27 @@ func (l *ioCopyLimiter) limitedCopy(ctx context.Context, dst io.Writer, src io.R
468468
return nil
469469
}
470470

471-
// initLocalCRIPlugin uses containerd.Client to init CRI plugin.
471+
// initLocalCRIImageService uses containerd.Client to init CRI plugin.
472472
//
473473
// NOTE: We don't need to start the CRI plugin here because we just need the
474474
// ImageService API.
475-
func initLocalCRIPlugin(client *containerd.Client, tmpDir string, registryCfg criconfig.Registry) (criserver.ImageService, error) {
475+
func initLocalCRIImageService(client *containerd.Client, tmpDir string, registryCfg criconfig.Registry) (criserver.ImageService, error) {
476476
containerdRootDir := filepath.Join(tmpDir, "root")
477-
criWorkDir := filepath.Join(tmpDir, "cri-plugin")
478-
479-
cfg := criconfig.Config{
480-
PluginConfig: criconfig.PluginConfig{
481-
ImageConfig: criconfig.ImageConfig{
482-
Snapshotter: containerd.DefaultSnapshotter,
483-
Registry: registryCfg,
484-
ImagePullProgressTimeout: defaultImagePullProgressTimeout.String(),
485-
StatsCollectPeriod: 10,
486-
},
487-
},
488-
ContainerdRootDir: containerdRootDir,
489-
RootDir: filepath.Join(criWorkDir, "root"),
490-
StateDir: filepath.Join(criWorkDir, "state"),
477+
478+
cfg := criconfig.ImageConfig{
479+
Snapshotter: containerd.DefaultSnapshotter,
480+
Registry: registryCfg,
481+
ImagePullProgressTimeout: defaultImagePullProgressTimeout.String(),
482+
StatsCollectPeriod: 10,
491483
}
492484

493-
return images.NewService(cfg.ImageConfig, map[string]string{}, map[string]images.RuntimePlatform{}, client)
485+
return images.NewService(cfg, &images.CRIImageServiceOptions{
486+
ImageFSPaths: map[string]string{
487+
containerd.DefaultSnapshotter: containerdRootDir,
488+
},
489+
RuntimePlatforms: map[string]images.ImagePlatform{},
490+
Content: client.ContentStore(),
491+
Images: client.ImageService(),
492+
Client: client,
493+
})
494494
}

pkg/cri/config/config.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ type ImageDecryption struct {
237237
KeyModel string `toml:"key_model" json:"keyModel"`
238238
}
239239

240+
// ImagePlatform represents the platform to use for an image including the
241+
// snapshotter to use. If snapshotter is not provided, the platform default
242+
// can be assumed. When platform is not provided, the default platform can
243+
// be assumed
244+
type ImagePlatform struct {
245+
Platform string
246+
Snapshotter string
247+
}
248+
240249
type ImageConfig struct {
241250
// Snapshotter is the snapshotter used by containerd.
242251
Snapshotter string `toml:"snapshotter" json:"snapshotter"`
@@ -251,6 +260,20 @@ type ImageConfig struct {
251260
// layers to the snapshotter.
252261
DiscardUnpackedLayers bool `toml:"discard_unpacked_layers" json:"discardUnpackedLayers"`
253262

263+
// PinnedImages are images which the CRI plugin uses and should not be
264+
// removed by the CRI client.
265+
// Image names should be full names including domain and tag
266+
// Examples:
267+
// docker.io/library/ubuntu:latest
268+
// images.k8s.io/core/pause:1.55
269+
PinnedImages []string
270+
271+
// RuntimePlatforms is map between the runtime and the image platform to
272+
// use for that runtime. When resolving an image for a runtime, this
273+
// mapping will be used to select the image for the platform and the
274+
// snapshotter for unpacking.
275+
RuntimePlatforms map[string]ImagePlatform
276+
254277
// Registry contains config related to the registry
255278
Registry Registry `toml:"registry" json:"registry"`
256279

pkg/cri/server/images/image_pull.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
3939
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
4040

41+
eventstypes "github.com/containerd/containerd/v2/api/events"
4142
containerd "github.com/containerd/containerd/v2/client"
4243
"github.com/containerd/containerd/v2/diff"
4344
"github.com/containerd/containerd/v2/errdefs"
@@ -319,32 +320,44 @@ func (c *CRIImageService) createImageReference(ctx context.Context, name string,
319320
// TODO(random-liu): Figure out which is the more performant sequence create then update or
320321
// update then create.
321322
// TODO: Call CRIImageService directly
322-
oldImg, err := c.client.ImageService().Create(ctx, img)
323-
if err == nil || !errdefs.IsAlreadyExists(err) {
323+
oldImg, err := c.images.Create(ctx, img)
324+
if err == nil {
325+
if c.publisher != nil {
326+
if err := c.publisher.Publish(ctx, "/images/create", &eventstypes.ImageCreate{
327+
Name: img.Name,
328+
Labels: img.Labels,
329+
}); err != nil {
330+
return err
331+
}
332+
}
333+
return nil
334+
} else if !errdefs.IsAlreadyExists(err) {
324335
return err
325336
}
326337
if oldImg.Target.Digest == img.Target.Digest && oldImg.Labels[crilabels.ImageLabelKey] == labels[crilabels.ImageLabelKey] {
327338
return nil
328339
}
329-
_, err = c.client.ImageService().Update(ctx, img, "target", "labels."+crilabels.ImageLabelKey)
340+
_, err = c.images.Update(ctx, img, "target", "labels."+crilabels.ImageLabelKey)
341+
if err == nil && c.publisher != nil {
342+
if c.publisher != nil {
343+
if err := c.publisher.Publish(ctx, "/images/update", &eventstypes.ImageUpdate{
344+
Name: img.Name,
345+
Labels: img.Labels,
346+
}); err != nil {
347+
return err
348+
}
349+
}
350+
}
330351
return err
331352
}
332353

333354
// getLabels get image labels to be added on CRI image
334355
func (c *CRIImageService) getLabels(ctx context.Context, name string) map[string]string {
335356
labels := map[string]string{crilabels.ImageLabelKey: crilabels.ImageLabelValue}
336-
// TODO: Separate config here to generalize pinned image list
337-
configSandboxImage := "" //c.config.SandboxImage
338-
// parse sandbox image
339-
sandboxNamedRef, err := distribution.ParseDockerRef(configSandboxImage)
340-
if err != nil {
341-
log.G(ctx).Errorf("failed to parse sandbox image from config %s", sandboxNamedRef)
342-
return nil
343-
}
344-
sandboxRef := sandboxNamedRef.String()
345-
// Adding pinned image label to sandbox image
346-
if sandboxRef == name {
347-
labels[crilabels.PinnedImageLabelKey] = crilabels.PinnedImageLabelValue
357+
for _, pinned := range c.config.PinnedImages {
358+
if pinned == name {
359+
labels[crilabels.PinnedImageLabelKey] = crilabels.PinnedImageLabelValue
360+
}
348361
}
349362
return labels
350363
}
@@ -767,9 +780,12 @@ func (c *CRIImageService) snapshotterFromPodSandboxConfig(ctx context.Context, i
767780
return snapshotter, nil
768781
}
769782

770-
if p, ok := c.runtimePlatforms[runtimeHandler]; ok && p.Snapshotter != snapshotter {
771-
snapshotter = p.Snapshotter
772-
log.G(ctx).Infof("experimental: PullImage %q for runtime %s, using snapshotter %s", imageRef, runtimeHandler, snapshotter)
783+
// TODO: Ensure error is returned if runtime not found?
784+
if c.runtimePlatforms != nil {
785+
if p, ok := c.runtimePlatforms[runtimeHandler]; ok && p.Snapshotter != snapshotter {
786+
snapshotter = p.Snapshotter
787+
log.G(ctx).Infof("experimental: PullImage %q for runtime %s, using snapshotter %s", imageRef, runtimeHandler, snapshotter)
788+
}
773789
}
774790

775791
return snapshotter, nil

pkg/cri/server/images/image_pull_test.go

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/containerd/containerd/v2/pkg/cri/annotations"
3030
criconfig "github.com/containerd/containerd/v2/pkg/cri/config"
3131
"github.com/containerd/containerd/v2/pkg/cri/labels"
32+
"github.com/containerd/containerd/v2/platforms"
3233
)
3334

3435
func TestParseAuth(t *testing.T) {
@@ -402,14 +403,13 @@ func TestSnapshotterFromPodSandboxConfig(t *testing.T) {
402403
expectSnapshotter: defaultSnashotter,
403404
},
404405
{
405-
desc: "should return error for runtime not found",
406+
desc: "should return default snapshotter for runtime not found",
406407
podSandboxConfig: &runtime.PodSandboxConfig{
407408
Annotations: map[string]string{
408409
annotations.RuntimeHandler: "runtime-not-exists",
409410
},
410411
},
411-
expectErr: true,
412-
expectSnapshotter: "",
412+
expectSnapshotter: defaultSnashotter,
413413
},
414414
{
415415
desc: "should return snapshotter provided in podSandboxConfig.Annotations",
@@ -426,12 +426,10 @@ func TestSnapshotterFromPodSandboxConfig(t *testing.T) {
426426
t.Run(tt.desc, func(t *testing.T) {
427427
cri, _ := newTestCRIService()
428428
cri.config.Snapshotter = defaultSnashotter
429-
/*
430-
cri.config.ContainerdConfig.Runtimes = make(map[string]criconfig.Runtime)
431-
cri.config.ContainerdConfig.Runtimes["exiting-runtime"] = criconfig.Runtime{
432-
Snapshotter: runtimeSnapshotter,
433-
}
434-
*/
429+
cri.runtimePlatforms["exiting-runtime"] = ImagePlatform{
430+
Platform: platforms.DefaultSpec(),
431+
Snapshotter: runtimeSnapshotter,
432+
}
435433
snapshotter, err := cri.snapshotterFromPodSandboxConfig(context.Background(), "test-image", tt.podSandboxConfig)
436434
assert.Equal(t, tt.expectSnapshotter, snapshotter)
437435
if tt.expectErr {
@@ -492,49 +490,51 @@ func TestImageGetLabels(t *testing.T) {
492490
criService, _ := newTestCRIService()
493491

494492
tests := []struct {
495-
name string
496-
expectedLabel map[string]string
497-
configSandboxImage string
498-
pullImageName string
493+
name string
494+
expectedLabel map[string]string
495+
pinnedImages []string
496+
pullImageName string
499497
}{
500498
{
501-
name: "pinned image labels should get added on sandbox image",
502-
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
503-
configSandboxImage: "k8s.gcr.io/pause:3.9",
504-
pullImageName: "k8s.gcr.io/pause:3.9",
499+
name: "pinned image labels should get added on sandbox image",
500+
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
501+
pinnedImages: []string{"k8s.gcr.io/pause:3.9"},
502+
pullImageName: "k8s.gcr.io/pause:3.9",
505503
},
506504
{
507-
name: "pinned image labels should get added on sandbox image without tag",
508-
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
509-
configSandboxImage: "k8s.gcr.io/pause",
510-
pullImageName: "k8s.gcr.io/pause:latest",
505+
name: "pinned image labels should get added on sandbox image without tag",
506+
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
507+
pinnedImages: []string{"k8s.gcr.io/pause", "k8s.gcr.io/pause:latest"},
508+
pullImageName: "k8s.gcr.io/pause:latest",
511509
},
512510
{
513-
name: "pinned image labels should get added on sandbox image specified with tag and digest both",
514-
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
515-
configSandboxImage: "k8s.gcr.io/pause:3.9@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
516-
pullImageName: "k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
511+
name: "pinned image labels should get added on sandbox image specified with tag and digest both",
512+
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
513+
pinnedImages: []string{
514+
"k8s.gcr.io/pause:3.9@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
515+
"k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
516+
},
517+
pullImageName: "k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
517518
},
518519

519520
{
520-
name: "pinned image labels should get added on sandbox image specified with digest",
521-
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
522-
configSandboxImage: "k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
523-
pullImageName: "k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
521+
name: "pinned image labels should get added on sandbox image specified with digest",
522+
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue, labels.PinnedImageLabelKey: labels.PinnedImageLabelValue},
523+
pinnedImages: []string{"k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"},
524+
pullImageName: "k8s.gcr.io/pause@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2",
524525
},
525526

526527
{
527-
name: "pinned image labels should not get added on other image",
528-
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue},
529-
configSandboxImage: "k8s.gcr.io/pause:3.9",
530-
pullImageName: "k8s.gcr.io/random:latest",
528+
name: "pinned image labels should not get added on other image",
529+
expectedLabel: map[string]string{labels.ImageLabelKey: labels.ImageLabelValue},
530+
pinnedImages: []string{"k8s.gcr.io/pause:3.9"},
531+
pullImageName: "k8s.gcr.io/random:latest",
531532
},
532533
}
533534

534535
for _, tt := range tests {
535536
t.Run(tt.name, func(t *testing.T) {
536-
// Change this config name
537-
//criService.config.SandboxImage = tt.configSandboxImage
537+
criService.config.PinnedImages = tt.pinnedImages
538538
labels := criService.getLabels(context.Background(), tt.pullImageName)
539539
assert.Equal(t, tt.expectedLabel, labels)
540540

pkg/cri/server/images/image_remove.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222

23+
eventstypes "github.com/containerd/containerd/v2/api/events"
2324
"github.com/containerd/containerd/v2/errdefs"
2425
"github.com/containerd/containerd/v2/images"
2526
"github.com/containerd/containerd/v2/tracing"
@@ -56,12 +57,20 @@ func (c *GRPCCRIImageService) RemoveImage(ctx context.Context, r *runtime.Remove
5657
// someone else before this point.
5758
opts = []images.DeleteOpt{images.SynchronousDelete()}
5859
}
59-
err = c.client.ImageService().Delete(ctx, ref, opts...)
60+
err = c.images.Delete(ctx, ref, opts...)
6061
if err == nil || errdefs.IsNotFound(err) {
6162
// Update image store to reflect the newest state in containerd.
6263
if err := c.imageStore.Update(ctx, ref); err != nil {
6364
return nil, fmt.Errorf("failed to update image reference %q for %q: %w", ref, image.ID, err)
6465
}
66+
67+
if c.publisher != nil {
68+
if err := c.publisher.Publish(ctx, "/images/delete", &eventstypes.ImageDelete{
69+
Name: ref,
70+
}); err != nil {
71+
return nil, err
72+
}
73+
}
6574
continue
6675
}
6776
return nil, fmt.Errorf("failed to delete image reference %q for %q: %w", ref, image.ID, err)

0 commit comments

Comments
 (0)