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";