Browse code

run/pull: Warn/reject AI model images

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 <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2025/04/28 23:28:07
Showing 4 changed files
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"fmt"
6 6
 	"io"
7 7
 	"strings"
8
+	"sync/atomic"
8 9
 	"time"
9 10
 
10 11
 	containerd "github.com/containerd/containerd/v2/client"
... ...
@@ -150,17 +151,27 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
150 150
 		}
151 151
 	}()
152 152
 
153
-	var sentPullingFrom bool
153
+	var sentPullingFrom, sentModelNotSupported atomic.Bool
154 154
 	ah := c8dimages.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
155 155
 		if desc.MediaType == c8dimages.MediaTypeDockerSchema1Manifest {
156 156
 			return nil, distribution.DeprecatedSchema1ImageError(ref)
157 157
 		}
158
+
159
+		ociAiArtifactManifest := c8dimages.IsManifestType(desc.MediaType) && isModelMediaType(desc.ArtifactType)
160
+		aiMediaType := isModelMediaType(desc.MediaType)
161
+
162
+		if ociAiArtifactManifest || aiMediaType {
163
+			if !sentModelNotSupported.Load() {
164
+				sentModelNotSupported.Store(true)
165
+				progress.Message(out, "", `WARNING: AI models are not supported by the Engine yet, did you mean to use "docker model pull/run" instead?`)
166
+			}
167
+		}
158 168
 		if c8dimages.IsLayerType(desc.MediaType) {
159 169
 			id := stringid.TruncateID(desc.Digest.String())
160 170
 			progress.Update(out, id, "Pulling fs layer")
161 171
 		}
162 172
 		if c8dimages.IsManifestType(desc.MediaType) {
163
-			if !sentPullingFrom {
173
+			if !sentPullingFrom.Load() {
164 174
 				var tagOrDigest string
165 175
 				if tagged, ok := ref.(reference.Tagged); ok {
166 176
 					tagOrDigest = tagged.Tag()
... ...
@@ -168,7 +179,7 @@ func (i *ImageService) pullTag(ctx context.Context, ref reference.Named, platfor
168 168
 					tagOrDigest = ref.String()
169 169
 				}
170 170
 				progress.Message(out, tagOrDigest, "Pulling from "+reference.Path(ref))
171
-				sentPullingFrom = true
171
+				sentPullingFrom.Store(true)
172 172
 			}
173 173
 
174 174
 			available, _, _, missing, err := c8dimages.Check(ctx, i.content, desc, p)
... ...
@@ -254,3 +265,7 @@ func writeStatus(out progress.Output, requestedTag string, newerDownloaded bool)
254 254
 		progress.Message(out, "", "Status: Image is up to date for "+requestedTag)
255 255
 	}
256 256
 }
257
+
258
+func isModelMediaType(mediaType string) bool {
259
+	return strings.HasPrefix(strings.ToLower(mediaType), "application/vnd.docker.ai.")
260
+}
... ...
@@ -3,6 +3,7 @@ package containerd
3 3
 import (
4 4
 	"context"
5 5
 	"fmt"
6
+	"strings"
6 7
 
7 8
 	c8dimages "github.com/containerd/containerd/v2/core/images"
8 9
 	"github.com/containerd/containerd/v2/core/leases"
... ...
@@ -104,6 +105,15 @@ func (i *ImageService) getImageSnapshot(ctx context.Context, descriptor *ocispec
104 104
 		return "", err
105 105
 	}
106 106
 
107
+	cfgDesc, err := platformImg.Config(ctx)
108
+	if err != nil {
109
+		return "", err
110
+	}
111
+
112
+	if strings.HasPrefix(strings.ToLower(cfgDesc.MediaType), "application/vnd.docker.ai.") {
113
+		return "", errors.New("Running AI models directly by the Engine is not supported yet, please use 'docker model run' instead")
114
+	}
115
+
107 116
 	unpacked, err := platformImg.IsUnpacked(ctx, i.snapshotter)
108 117
 	if err != nil {
109 118
 		return "", err
... ...
@@ -203,6 +203,14 @@ func retryOnError(err error) error {
203 203
 	return err
204 204
 }
205 205
 
206
+type AIModelNotSupportedError struct{}
207
+
208
+func (e AIModelNotSupportedError) Error() string {
209
+	return `AI models are not yet supported by the Engine, please use "docker model pull/run" instead`
210
+}
211
+
212
+func (e AIModelNotSupportedError) InvalidParameter() {}
213
+
206 214
 type invalidManifestClassError struct {
207 215
 	mediaType string
208 216
 	class     string
... ...
@@ -498,6 +498,9 @@ func (p *puller) validateMediaType(mediaType string) error {
498 498
 
499 499
 func checkSupportedMediaType(mediaType string) error {
500 500
 	lowerMt := strings.ToLower(mediaType)
501
+	if strings.HasPrefix(lowerMt, "application/vnd.docker.ai.") {
502
+		return AIModelNotSupportedError{}
503
+	}
501 504
 	for _, mt := range supportedMediaTypes {
502 505
 		// The should either be an exact match, or have a valid prefix
503 506
 		// we append a "." when matching prefixes to exclude "false positives";