diff --git a/replays/docker.go b/replays/docker.go index c7d3f17..5689294 100644 --- a/replays/docker.go +++ b/replays/docker.go @@ -75,11 +75,21 @@ func (d *Docker) Pull(ctx context.Context, image string) error { if err := d.call(ctx, http.MethodGet, "/images/"+url.PathEscape(image)+"/json", nil, nil); err == nil { return nil } - name, tag, _ := strings.Cut(image, ":") - if tag == "" { - tag = "latest" + // A digest is the tag the runtime is asked for, as its own client asks: `name@sha256:…` cut at the + // `@`; otherwise the tag after the last `:` that is not part of a registry's port. + name, tag, digested := strings.Cut(image, "@") + if !digested { + name, tag = image, "latest" + if at := strings.LastIndex(image, ":"); at > strings.LastIndex(image, "/") { + name, tag = image[:at], image[at+1:] + } } - return d.call(ctx, http.MethodPost, "/images/create?fromImage="+url.QueryEscape(name)+"&tag="+url.QueryEscape(tag), nil, nil) + err := d.call(ctx, http.MethodPost, "/images/create?fromImage="+url.QueryEscape(name)+"&tag="+url.QueryEscape(tag), nil, nil) + if err != nil { + return err + } + // The runtime answers a pull it could not make with a stream that ends in an error, not a status. + return d.call(ctx, http.MethodGet, "/images/"+url.PathEscape(image)+"/json", nil, nil) } // Network makes a network of its own and answers its id.