Leave a module out of a send when the store lacks its image, and say it (issue 1742)

This commit is contained in:
2026-10-21 19:43:36 +02:00
parent b62fbeabbf
commit dc06af1f5a
10 changed files with 546 additions and 5 deletions
+24 -4
View File
@@ -59,15 +59,35 @@ func (r Registry) PublishImage(ctx context.Context, localTag, repository string)
}); err != nil {
return "", err
}
out, err := r.Run(ctx, "", "docker", "inspect", "--format", "{{index .RepoDigests 0}}", remote)
// Every digest the daemon lists for the image, one per line: the image may have been pushed under other names before
// (a check's scratch copy, another registry), and the first of them is not the one this push made (novox/hq issue
// 1742: a build recorded a digest the store never held).
out, err := r.Run(ctx, "", "docker", "inspect", "--format", "{{range .RepoDigests}}{{println .}}{{end}}", remote)
if err != nil {
return "", fmt.Errorf("pushed %s and cannot read back what it was pinned as: %w", remote, err)
}
pinned := strings.TrimSpace(out)
if !strings.Contains(pinned, "@sha256:") {
first, pinned := "", ""
for _, line := range strings.Split(out, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
if first == "" {
first = line
}
if strings.HasPrefix(line, remote+"@sha256:") {
pinned = line
break
}
}
if pinned == "" {
if strings.Contains(first, "@sha256:") {
return "", fmt.Errorf("pushed %s, but the daemon lists no digest for that name (only %q): the digest of another "+
"repository is not what this registry serves", remote, first)
}
// A tag is not a pin. It can be made to point at something else, and this file is applied
// on machines with no mesh to ask about anything (novox/hq ADR 0006).
return "", fmt.Errorf("%s came back as %q, which is not pinned by digest", remote, pinned)
return "", fmt.Errorf("%s came back as %q, which is not pinned by digest", remote, first)
}
return pinned, nil
}
+29
View File
@@ -252,3 +252,32 @@ func TestAPushedImageComesBackPinned(t *testing.T) {
t.Fatalf("it did not tag and then push: %v", ran)
}
}
// 1742: an image pushed under other names before lists their digests too, and the digest recorded is the one of the name
// this push made — never the first listed, which may be another repository's.
func TestThePinnedDigestIsTheOneOfTheNamePushed(t *testing.T) {
want := "registry.invalid/shell/server@sha256:" + strings.Repeat("b", 64)
earlier := "registry.invalid/scratch/shell/server@sha256:" + strings.Repeat("a", 64)
r := Registry{Address: "registry.invalid", Run: func(
_ context.Context, _, name string, args ...string) (string, error) {
if name == "docker" && len(args) > 0 && args[0] == "inspect" {
return earlier + "\n" + want + "\n", nil
}
return "", nil
}}
got, err := r.PublishImage(context.Background(), "local", "shell/server")
if err != nil || got != want {
t.Fatalf("pinned %q (%v); want %q", got, err, want)
}
// Only another repository's digest is listed: the push is not taken for pinned.
r.Run = func(_ context.Context, _, name string, args ...string) (string, error) {
if name == "docker" && len(args) > 0 && args[0] == "inspect" {
return earlier + "\n", nil
}
return "", nil
}
if _, err := r.PublishImage(context.Background(), "local", "shell/server"); err == nil ||
!strings.Contains(err.Error(), "no digest for that name") {
t.Fatalf("a digest of another repository was taken for the push's own: %v", err)
}
}