Skip to content

c8d: docker push discards platform variants instead of erroring #48731

@thaJeztah

Description

@thaJeztah

Description

Noticed this while writing a reply to;

When pushing an incomplete multi-platform image with multiple platforms available, an error should be shown, telling the user to use the --platform flag to select what platform to push. The relevant code on the CLI side is here (but the issue is on the daemon side; CLI code is only for reference)

https://github.com/docker/cli/blob/61baf2a3d984a1e59fcadb38e28d77b571434b88/cli/command/image/push.go#L173-L183

if err == nil && missing.ContentMissing {
    note := `You're trying to push a manifest list/index which 
            references multiple platform specific manifests, but not all of them are available locally
            or available to the remote repository.
    
            Make sure you have all the referenced content and try again.
    
            You can also push only a single platform specific manifest directly by specifying the platform you want to push with the --platform flag.`
    notes = append(notes, note)
}

However, it looks like we don't fail the push, and go with the other path, which is to dismantle the image, and to only push a single-arch image;

https://github.com/docker/cli/blob/61baf2a3d984a1e59fcadb38e28d77b571434b88/cli/command/image/push.go#L162-L170

var stripped auxprogress.ManifestPushedInsteadOfIndex
err := json.Unmarshal(b, &stripped)
if err == nil && stripped.ManifestPushedInsteadOfIndex {
    note := fmt.Sprintf("Not all multiplatform-content is present and only the available single-platform image was pushed\n%s -> %s",
        aec.RedF.Apply(stripped.OriginalIndex.Digest.String()),
        aec.GreenF.Apply(stripped.SelectedManifest.Digest.String()),
    )
    notes = append(notes, note)
}

We should only automatically fall back to pushing a single-platform image from the manifest-index if there's only a single platform present.

Reproduce

To reproduce;

Run a local registry for testing;

$ docker run -d --name registry -p 5001:5000 registry:2

Pull 2 variants of the alpine image;

$ docker pull --quiet --platform=linux/arm64/v8 alpine:latest
$ docker pull --quiet --platform=linux/amd64 alpine:latest

Tag the image, and verify that it has 2 variants:

$ docker tag alpine localhost:5001/myimage:latest

$ docker image ls --tree localhost:5001/myimage:latest
IMAGE                           ID             DISK USAGE   CONTENT SIZE   USED
localhost:5001/myimage:latest   beefdbd8a1da       25.7MB         7.72MB
├─ linux/arm64/v8               9cee2b382fe2       13.6MB         4.09MB
├─ linux/amd64                  33735bd63cf8       12.1MB         3.63MB
├─ linux/arm/v6                 50f635c8b04d           0B             0B
├─ linux/arm/v7                 f2f82d424957           0B             0B
├─ linux/386                    b3e87f642f5c           0B             0B
├─ linux/ppc64le                c7a6800e3dc5           0B             0B
├─ linux/riscv64                80cde017a105           0B             0B
└─ linux/s390x                  2b5b26e09ca2           0B             0B

Push the image, and notice that we dismantled the image, and pushed a single-platform image instead, as the Info message describes;

$ docker push localhost:5001/myimage:latest
The push refers to repository [localhost:5001/myimage]
cf04c63912e1: Pushed
latest: digest: sha256:9cee2b382fe2412cd77d5d437d15a93da8de373813621f2e4d406e3df0cf0e7c size: 528

i Info → Not all multiplatform-content is present and only the available single-platform image was pushed
         sha256:beefdbd8a1da6d2915566fde36db9db0b524eb737fc57cd1367effd16dc0d06d -> sha256:9cee2b382fe2412cd77d5d437d15a93da8de373813621f2e4d406e3df0cf0e7c

Verify that we indeed pushed a single-platform image using docker buildx imagetools inspect;

$ docker buildx imagetools inspect localhost:5001/myimage:latest
Name:      localhost:5001/myimage:latest
MediaType: application/vnd.docker.distribution.manifest.v2+json
Digest:    sha256:9cee2b382fe2412cd77d5d437d15a93da8de373813621f2e4d406e3df0cf0e7c

Expected behavior

Docker should refuse to push the image, and inform the user to use the --platform flag to tell it which variant to push.

docker version

Client:
 Version:           27.2.0
 API version:       1.47
 Go version:        go1.21.13
 Git commit:        3ab4256
 Built:             Tue Aug 27 14:14:45 2024
 OS/Arch:           darwin/arm64
 Context:           desktop-linux

Server: Docker Desktop 4.34.3 (170107)
 Engine:
  Version:          27.2.0
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.21.13
  Git commit:       3ab5c7d
  Built:            Tue Aug 27 14:15:41 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.7.20
  GitCommit:        8fc6bcff51318944179630522a095cc9dbf9f353
 runc:
  Version:          1.1.13
  GitCommit:        v1.1.13-0-g58aa920
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

docker info

Client:
 Version:    27.2.0
 Context:    desktop-linux
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.16.2-desktop.1
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.29.2-desktop.2
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-compose
  debug: Get a shell into any image or container (Docker Inc.)
    Version:  0.0.34
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-debug
  desktop: Docker Desktop commands (Alpha) (Docker Inc.)
    Version:  v0.0.15
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-desktop
  dev: Docker Dev Environments (Docker Inc.)
    Version:  v0.1.2
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-dev
  extension: Manages Docker extensions (Docker Inc.)
    Version:  v0.2.25
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-extension
  feedback: Provide feedback, right in your terminal! (Docker Inc.)
    Version:  v1.0.5
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-feedback
  init: Creates Docker-related starter files for your project (Docker Inc.)
    Version:  v1.3.0
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-init
  sbom: View the packaged-based Software Bill Of Materials (SBOM) for an image (Anchore Inc.)
    Version:  0.6.0
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-sbom
  scout: Docker Scout (Docker Inc.)
    Version:  v1.13.0
    Path:     /Applications/Docker.app/Contents/Resources/cli-plugins/docker-scout

Server:
 Containers: 1
  Running: 1
  Paused: 0
  Stopped: 0
 Images: 6
 Server Version: 27.2.0
 Storage Driver: overlayfs
  driver-type: io.containerd.snapshotter.v1
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: active
  NodeID: coqxeoka66uwu1t53yj6hp0bf
  Is Manager: true
  ClusterID: rp95hhvjqie1jfa8nxdln8i49
  Managers: 1
  Nodes: 1
  Default Address Pool: 10.0.0.0/8
  SubnetSize: 24
  Data Path Port: 4789
  Orchestration:
   Task History Retention Limit: 5
  Raft:
   Snapshot Interval: 10000
   Number of Old Snapshots to Retain: 0
   Heartbeat Tick: 1
   Election Tick: 10
  Dispatcher:
   Heartbeat Period: 5 seconds
  CA Configuration:
   Expiry Duration: 3 months
   Force Rotate: 0
  Autolock Managers: false
  Root Rotation In Progress: false
  Node Address: 192.168.65.3
  Manager Addresses:
   192.168.65.3:2377
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 8fc6bcff51318944179630522a095cc9dbf9f353
 runc version: v1.1.13-0-g58aa920
 init version: de40ad0
 Security Options:
  seccomp
   Profile: unconfined
 Kernel Version: 6.10.4-linuxkit
 Operating System: Docker Desktop
 OSType: linux
 Architecture: aarch64
 CPUs: 10
 Total Memory: 7.655GiB
 Name: docker-desktop
 ID: 58815d06-8744-4af3-b6f1-7a88003318ad
 Docker Root Dir: /var/lib/docker
 Debug Mode: true
  File Descriptors: 85
  Goroutines: 227
  System Time: 2024-10-23T09:38:13.709729877Z
  EventsListeners: 15
 HTTP Proxy: http.docker.internal:3128
 HTTPS Proxy: http.docker.internal:3128
 No Proxy: hubproxy.docker.internal
 Labels:
  com.docker.desktop.address=unix:///Users/thajeztah/Library/Containers/com.docker.docker/Data/docker-cli.sock
 Experimental: false
 Insecure Registries:
  host.docker.internal:5001
  host.docker.internal:5002
  hubproxy.docker.internal:5555
  127.0.0.0/8
 Live Restore Enabled: false

Additional Info

I noticed this on Docker Desktop with Docker 27.2, but will try reproduce on current master as well

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/distributionImage Distributionarea/imagesImage Distributioncontainerd-integrationIssues and PRs related to containerd integrationkind/bugBugs are bugs. The cause may or may not be known at triage time so debugging may be needed.version/27.2

    Type

    Projects

    Status

    To do

    Status

    Required for default containerd

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions