From 4da3b4bf2df95f77ca0e1cb9069fff8df7c3d7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Mon, 28 Apr 2025 16:28:07 +0200 Subject: [PATCH] run/pull: Warn/reject AI model images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add checks in both containerd-based and distribution-based image pull code paths to detect and AI model images early in the pull process. These are not yet supported directly by the Engine and need to be handled by the `docker model` CLI plugin. For distribution-based pull, reject the AI models pulls. For containerd image service only emit a warning. Signed-off-by: Paweł Gronowski --- daemon/containerd/image_pull.go | 21 ++++++++++++++++++--- daemon/containerd/image_snapshot.go | 10 ++++++++++ distribution/errors.go | 8 ++++++++ distribution/pull_v2.go | 3 +++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/daemon/containerd/image_pull.go b/daemon/containerd/image_pull.go index 9f76564b2d..aa520d01f8 100644 --- a/daemon/containerd/image_pull.go +++ b/daemon/containerd/image_pull.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "strings" + "sync/atomic" "time" containerd "github.com/containerd/containerd/v2/client" @@ -150,17 +151,27 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor } }() - var sentPullingFrom bool + var sentPullingFrom, sentModelNotSupported atomic.Bool ah := c8dimages.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { if desc.MediaType == c8dimages.MediaTypeDockerSchema1Manifest { return nil, distribution.DeprecatedSchema1ImageError(ref) } + + ociAiArtifactManifest := c8dimages.IsManifestType(desc.MediaType) && isModelMediaType(desc.ArtifactType) + aiMediaType := isModelMediaType(desc.MediaType) + + if ociAiArtifactManifest || aiMediaType { + if !sentModelNotSupported.Load() { + sentModelNotSupported.Store(true) + progress.Message(out, "", `WARNING: AI models are not supported by the Engine yet, did you mean to use "docker model pull/run" instead?`) + } + } if c8dimages.IsLayerType(desc.MediaType) { id := stringid.TruncateID(desc.Digest.String()) progress.Update(out, id, "Pulling fs layer") } if c8dimages.IsManifestType(desc.MediaType) { - if !sentPullingFrom { + if !sentPullingFrom.Load() { var tagOrDigest string if tagged, ok := ref.(reference.Tagged); ok { tagOrDigest = tagged.Tag() @@ -168,7 +179,7 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor tagOrDigest = ref.String() } progress.Message(out, tagOrDigest, "Pulling from "+reference.Path(ref)) - sentPullingFrom = true + sentPullingFrom.Store(true) } available, _, _, missing, err := c8dimages.Check(ctx, i.content, desc, p) @@ -254,3 +265,7 @@ func writeStatus(out progress.Output, requestedTag string, newerDownloaded bool) progress.Message(out, "", "Status: Image is up to date for "+requestedTag) } } + +func isModelMediaType(mediaType string) bool { + return strings.HasPrefix(strings.ToLower(mediaType), "application/vnd.docker.ai.") +} diff --git a/daemon/containerd/image_snapshot.go b/daemon/containerd/image_snapshot.go index 9924be68a5..0084e2141c 100644 --- a/daemon/containerd/image_snapshot.go +++ b/daemon/containerd/image_snapshot.go @@ -3,6 +3,7 @@ package containerd import ( "context" "fmt" + "strings" c8dimages "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/core/leases" @@ -104,6 +105,15 @@ func (i *ImageService) getImageSnapshot(ctx context.Context, descriptor *ocispec return "", err } + cfgDesc, err := platformImg.Config(ctx) + if err != nil { + return "", err + } + + if strings.HasPrefix(strings.ToLower(cfgDesc.MediaType), "application/vnd.docker.ai.") { + return "", errors.New("Running AI models directly by the Engine is not supported yet, please use 'docker model run' instead") + } + unpacked, err := platformImg.IsUnpacked(ctx, i.snapshotter) if err != nil { return "", err diff --git a/distribution/errors.go b/distribution/errors.go index 8463e9ec92..d443c7e421 100644 --- a/distribution/errors.go +++ b/distribution/errors.go @@ -203,6 +203,14 @@ func retryOnError(err error) error { return err } +type AIModelNotSupportedError struct{} + +func (e AIModelNotSupportedError) Error() string { + return `AI models are not yet supported by the Engine, please use "docker model pull/run" instead` +} + +func (e AIModelNotSupportedError) InvalidParameter() {} + type invalidManifestClassError struct { mediaType string class string diff --git a/distribution/pull_v2.go b/distribution/pull_v2.go index 37ec595a44..22c0d08677 100644 --- a/distribution/pull_v2.go +++ b/distribution/pull_v2.go @@ -498,6 +498,9 @@ func (p *puller) validateMediaType(mediaType string) error { func checkSupportedMediaType(mediaType string) error { lowerMt := strings.ToLower(mediaType) + if strings.HasPrefix(lowerMt, "application/vnd.docker.ai.") { + return AIModelNotSupportedError{} + } for _, mt := range supportedMediaTypes { // The should either be an exact match, or have a valid prefix // we append a "." when matching prefixes to exclude "false positives";