Getting image digest after push
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.
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
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:
- Do a docker inspect and look for
GraphDriver.Name: overlayfs, which means containerd? - Do a
docker info -f '{{ .DriverStatus }}'and check fordriver-type io.containerd.snapshotter.v1, although this isn't related to the image, so probably not the best way - 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.