moby icon indicating copy to clipboard operation
moby copied to clipboard

Getting image digest after push

Open mfridman opened this issue 1 year ago • 2 comments

Description

Typically users rely on the Aux field to get at the digest following a successful push using ImagePush. Specifically, something like this can be done on the caller side, note the unmarshal into types.PushResult:

if message.Aux != nil {
	var pushResult types.PushResult
	if err := json.Unmarshal(*message.Aux, &pushResult); err == nil {
		imageDigest = pushResult.Digest
	}
}

However, in some scenarios (~when containerd image store is enabled~ newer cli versions), the digest is returned in the Status as a plain string. And afaics the only way to get at the digest is to parse this raw string?

https://github.com/moby/moby/blob/f9522e5e96c3ab5a6b8a643d15a92700ca864da6/daemon/containerd/image_push.go#L126-L133

So, the question (or feature request), is it possible to standardize on a convention for getting the digest when reading from the io.ReadCloser.

I believe this request is in the same vein as https://github.com/moby/moby/issues/46528, but specifically for push.

mfridman avatar Aug 14 '24 13:08 mfridman

Yes, these are things to look into (also as part of https://github.com/moby/moby/issues/46528); the current stream response is not very well defined, which resulted in (effectively) "informational" messages to become part of the logic used.

On a slightly more positive note; unlike the graphdriver (non-containerd) images store, where the image digest was calculated as part of pushing the image (because it didn't store the "distributable" image, only the extracted one), the containerd image store uses the image's digest as ID;

docker build -t localhost:5001/my-image:latest -<<'EOF'
FROM busybox
RUN echo hello > foo.txt
EOF

With the containerd image store, the digest of the image that was built locally is already known;

docker image ls --digests
REPOSITORY                TAG               DIGEST                                                                    IMAGE ID       CREATED          SIZE
localhost:5001/my-image   latest            sha256:ac27b7051d8286f22f0ac45eb7799dac5c1cda08b73ad54eb82bf8aa71dc6e56   ac27b7051d82   15 seconds ago   6.03MB
docker image inspect --format '{{.ID}}' localhost:5001/my-image:latest
sha256:ac27b7051d8286f22f0ac45eb7799dac5c1cda08b73ad54eb82bf8aa71dc6e56

And pushing the image should show the same digest;

docker push localhost:5001/my-image;latest
Using default tag: latest
The push refers to repository [localhost:5001/my-image]
a524f1cde4e9: Pushed
e8a7dfc318ed: Pushed
213a27df5921: Pushed
latest: digest: sha256:ac27b7051d8286f22f0ac45eb7799dac5c1cda08b73ad54eb82bf8aa71dc6e56 size: 855
bash: latest: command not found

thaJeztah avatar Aug 14 '24 14:08 thaJeztah

The ability to inspect an image and retrieve the digest following a build (without pushing) opens opportunities for optimizations and is a welcome improvement!

However, this is all based on the user's local configuration so we'd need to support both.

Is there a recommended approach to determine an image's build method? I.e., are we working with graphdriver images and can get the correct digest only by pushing first, or is this a containerd image and we can rely on the local digest/ID without a push step?

I presume there's a few ways:

  1. Do a docker inspect and look for GraphDriver.Name: overlayfs, which means containerd?
  2. Do a docker info -f '{{ .DriverStatus }}' and check for driver-type io.containerd.snapshotter.v1, although this isn't related to the image, so probably not the best way
  3. Do a docker inspect and check if len(RepoDigests) == 1 && RepoDigests[0] == ID, and if they are the same it means we have a digest we can rely on without doing a push?

Would love to know if there's a more idiomatic way to do this.

mfridman avatar Aug 14 '24 14:08 mfridman